cf-dns: connection filter for DNS queries

New connection filter `cf-dns` that manages DNS queries. If hands
out addresses and HTTPS-RR records to anyone interested. Used by
HTTPS and IP happy eyeballing.

Information may become available *before* the libcurl "dns entry"
is complete, e.g. all queries have been answered. The cf-ip-happy
filter uses this information to start connection attempts as soon
as the first address is available.

The multi MSTATE_RESOLVING was removed. A new connection always
goes to MSTATE_CONNECTING. The connectdata bit `dns_resolved`
indicates when DNS information is complete. This is used for
error reporting and starting the progress meter.

Removed dns entries `data->state.dns[i]`, as the `cf-dns` filter
now keeps the reference now.

Many minor tweaks for making this work and pass address information
around safely.

Closes #21027
This commit is contained in:
Stefan Eissing 2026-03-25 15:07:10 +01:00 committed by Daniel Stenberg
parent 89741958e8
commit 335dc0e3c5
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
44 changed files with 1785 additions and 1017 deletions

View file

@ -149,6 +149,7 @@ LIB_CFILES = \
asyn-thrdd.c \
bufq.c \
bufref.c \
cf-dns.c \
cf-h1-proxy.c \
cf-h2-proxy.c \
cf-haproxy.c \
@ -278,6 +279,7 @@ LIB_HFILES = \
asyn.h \
bufq.h \
bufref.h \
cf-dns.h \
cf-h1-proxy.h \
cf-h2-proxy.h \
cf-haproxy.h \

View file

@ -349,6 +349,19 @@ static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares,
return 1000;
}
const struct Curl_addrinfo *
Curl_async_get_ai(struct Curl_easy *data,
struct Curl_resolv_async *async,
int ai_family, unsigned int index)
{
/* Not supported by our implementation yet. */
(void)data;
(void)async;
(void)ai_family;
(void)index;
return NULL;
}
/*
* Curl_async_await()
*

View file

@ -118,9 +118,6 @@ CURLcode Curl_async_get_impl(struct Curl_easy *data,
struct async_thrdd_item {
struct Curl_addrinfo *res;
#ifdef HAVE_GETADDRINFO
struct addrinfo hints;
#endif
#ifdef CURLVERBOSE
char description[CURL_ASYN_ITEM_DESC_LEN];
#endif
@ -156,7 +153,7 @@ async_thrdd_item_create(struct Curl_easy *data,
{
size_t hostlen = strlen(hostname);
struct async_thrdd_item *item;
VERBOSE(const char *qtype = "A");
VERBOSE(const char *qtype);
item = curlx_calloc(1, sizeof(*item) + hostlen);
if(!item)
@ -170,27 +167,9 @@ async_thrdd_item_create(struct Curl_easy *data,
item->mid = data->mid;
item->async_id = async_id;
#ifdef HAVE_GETADDRINFO
{
int pf = PF_INET;
#ifdef CURLRES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
pf = PF_UNSPEC;
}
#endif /* CURLRES_IPV6 */
item->hints.ai_family = pf;
item->hints.ai_socktype = Curl_socktype_for_transport(transport);
#ifdef CURLVERBOSE
qtype = (pf == PF_INET6) ? "AAAA" : "A+AAAA";
#endif
}
#endif /* HAVE_GETADDRINFO */
#ifdef CURLVERBOSE
qtype = (ip_version == CURL_IPRESOLVE_WHATEVER) ? "A+AAAA":
((ip_version == CURL_IPRESOLVE_V6) ? "AAAA" : "A");
curl_msnprintf(item->description, sizeof(item->description),
"[%" FMT_OFF_T "/%d] %s %s:%u",
data->id, item->async_id, qtype, item->hostname, item->port);
@ -245,8 +224,7 @@ static CURLcode async_rr_start(struct Curl_easy *data,
DEBUGASSERT(!thrdd->rr.channel);
if(async->port != 443) {
rrname = curl_maprintf("_%d_.https.%s",
async->port, data->conn->host.name);
rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
@ -271,10 +249,11 @@ static CURLcode async_rr_start(struct Curl_easy *data,
thrdd->rr.hinfo.port = -1;
thrdd->rr.hinfo.rrname = rrname;
ares_query_dnsrec(thrdd->rr.channel,
rrname ? rrname : data->conn->host.name, ARES_CLASS_IN,
rrname ? rrname : async->hostname, ARES_CLASS_IN,
ARES_REC_TYPE_HTTPS,
async_thrdd_rr_done, data, NULL);
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s",
rrname ? rrname : async->hostname);
return CURLE_OK;
}
#endif
@ -309,8 +288,10 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data,
}
Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo);
#endif
async_thrdd_item_destroy(async->thrdd.resolved);
async->thrdd.resolved = NULL;
async_thrdd_item_destroy(async->thrdd.res_A);
async->thrdd.res_A = NULL;
async_thrdd_item_destroy(async->thrdd.res_AAAA);
async->thrdd.res_AAAA = NULL;
}
/*
@ -352,7 +333,9 @@ CURLcode Curl_async_await(struct Curl_easy *data,
static void async_thrdd_item_process(void *arg)
{
struct async_thrdd_item *item = arg;
struct addrinfo hints;
char service[12];
int pf = PF_INET;
int rc;
#ifdef DEBUGBUILD
@ -364,10 +347,24 @@ static void async_thrdd_item_process(void *arg)
return;
}
#endif
curl_msnprintf(service, sizeof(service), "%d", item->port);
rc = Curl_getaddrinfo_ex(item->hostname, service,
&item->hints, &item->res);
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
if(item->ip_version != CURL_IPRESOLVE_V4) {
pf = (item->ip_version == CURL_IPRESOLVE_V6) ? PF_INET6 : PF_UNSPEC;
}
#endif
hints.ai_family = pf;
hints.ai_socktype = Curl_socktype_for_transport(item->transport);
hints.ai_protocol = Curl_protocol_for_transport(item->transport);
#ifdef AI_NUMERICSERV
/* Without service and flags, resolvers might lookup up in more
* places than we want them to, causing a delay. */
hints.ai_flags |= AI_NUMERICSERV;
#endif
curl_msnprintf(service, sizeof(service), "%u", item->port);
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
if(rc) {
item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
if(item->sock_error == 0)
@ -466,6 +463,39 @@ void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
}
}
#ifdef CURLVERBOSE
static void async_thrdd_report_item(struct Curl_easy *data,
struct async_thrdd_item *item)
{
char buf[MAX_IPADR_LEN];
struct dynbuf tmp;
const char *sep = "";
const struct Curl_addrinfo *ai = item->res;
int ai_family = (item->ip_version == CURL_IPRESOLVE_V6) ? AF_INET6 : AF_INET;
CURLcode result;
curlx_dyn_init(&tmp, 1024);
for(; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family) {
Curl_printable_address(ai, buf, sizeof(buf));
result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
if(result) {
CURL_TRC_DNS(data, "too many IP, cannot show");
goto out;
}
sep = ", ";
}
}
infof(data, "Host %s:%u resolved IPv%c: %s",
item->hostname, item->port,
(item->ip_version == CURL_IPRESOLVE_V6) ? '6' : '4',
(curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
out:
curlx_dyn_free(&tmp);
}
#endif /* CURLVERBOSE */
/* Process the receiving end of the thread queue, dispatching
* processed items to their transfer when it can still be found
* and has an `async` state present. Otherwise, destroy the item. */
@ -478,19 +508,28 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
/* dispatch resolve result */
struct async_thrdd_item *item = qitem;
CURL_TRC_DNS(multi->admin, "[async] got %s'%s'",
item->res ? "" : "negative for ", item->description);
data = Curl_multi_get_easy(multi, item->mid);
/* there is a chance that the original resolve was discarded and
* either no new, or a new resolve with a different id is ongoing. */
if(data && data->conn && data->state.async &&
(data->state.async->id == item->async_id)) {
struct Curl_resolv_async *async = data->state.async;
struct async_thrdd_item **pdest = &async->thrdd.res_A;
async->thrdd.resolved = item;
async->thrdd.done = TRUE;
--async->thrdd.queued;
async->thrdd.done = !async->thrdd.queued;
#ifdef CURLRES_IPV6
if(item->ip_version == CURL_IPRESOLVE_V6)
pdest = &async->thrdd.res_AAAA;
#endif
if(!*pdest) {
VERBOSE(async_thrdd_report_item(data, item));
*pdest = item;
item = NULL;
}
else
DEBUGASSERT(0); /* should not receive duplicates here */
Curl_multi_mark_dirty(data);
}
async_thrdd_item_free(item);
@ -509,31 +548,69 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
min_threads, max_threads, idle_time_ms);
}
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
static CURLcode async_thrdd_query(struct Curl_easy *data,
struct Curl_resolv_async *async,
uint8_t query_version)
{
struct async_thrdd_item *item;
CURLcode result;
if(async->thrdd.queued || async->thrdd.done || async->thrdd.resolved)
return CURLE_FAILED_INIT;
item = async_thrdd_item_create(data, async->hostname, async->port,
async->ip_version, async->transport,
query_version, async->transport,
async->id);
if(!item) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
CURL_TRC_DNS(data, "[async] queueing %s", item->description);
/* queue takes ownership of `item` on success */
result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
async_item_description(item), async->timeout_ms);
if(!result)
async->thrdd.queued = TRUE;
else
async_thrdd_item_destroy(item);
if(result)
goto out;
item = NULL;
async->thrdd.queued++;
out:
if(item)
async_thrdd_item_free(item);
return result;
}
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
CURLcode result = CURLE_FAILED_INIT;
if(async->thrdd.queued || async->thrdd.done)
return CURLE_FAILED_INIT;
switch(async->ip_version) {
#ifdef CURL_IPRESOLVE_V6
case CURL_IPRESOLVE_V6:
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
break;
#endif
case CURL_IPRESOLVE_V4:
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
break;
default:
#ifdef CURL_IPRESOLVE_V6
/* When resolving IP addresses, some resolvers (e.g. macOS) will
* happily "embed" an IPv4 address into the IPv6 equivalent.
* This will then confuse FTP that has been told an IPv4 for
* DATA, but suddenly sees IPv6. */
if(Curl_ipv6works(data) && !Curl_is_ipv4addr(async->hostname)) {
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
if(result)
goto out;
}
#endif
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
break;
}
if(result)
goto out;
#ifdef CURLVERBOSE
Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns);
#endif
@ -604,7 +681,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
DEBUGASSERT(pdns);
*pdns = NULL;
if(!thrdd->queued) {
if(!thrdd->queued && !thrdd->done) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
@ -615,16 +692,16 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
(void)Curl_ares_perform(thrdd->rr.channel, 0);
#endif
if(!thrdd->done) {
CURL_TRC_DNS(data, "[async] take %s:%d -> EAGAIN",
async->hostname, async->port);
if(!thrdd->done)
return CURLE_AGAIN;
}
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
if(thrdd->resolved && thrdd->resolved->res) {
if((thrdd->res_A && thrdd->res_A->res) ||
(thrdd->res_AAAA && thrdd->res_AAAA->res)) {
struct Curl_dns_entry *dns =
Curl_dnscache_mk_entry(data, &thrdd->resolved->res,
Curl_dnscache_mk_entry2(data,
thrdd->res_A ? &thrdd->res_A->res : NULL,
thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
async->hostname, async->port, async->ip_version);
if(!dns)
result = CURLE_OUT_OF_MEMORY;
@ -643,8 +720,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
}
#endif
if(!result && dns) {
CURL_TRC_DNS(data, "[async] resolved: %s",
thrdd->resolved->description);
CURL_TRC_DNS(data, "[async] resolving complete");
*pdns = dns;
dns = NULL;
}
@ -666,4 +742,42 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
return result;
}
static const struct Curl_addrinfo *
async_thrdd_get_ai(const struct Curl_addrinfo *ai,
int ai_family, unsigned int index)
{
unsigned int i = 0;
for(i = 0; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family) {
if(i == index)
return ai;
++i;
}
}
return NULL;
}
const struct Curl_addrinfo *
Curl_async_get_ai(struct Curl_easy *data,
struct Curl_resolv_async *async,
int ai_family, unsigned int index)
{
struct async_thrdd_ctx *thrdd = &async->thrdd;
(void)data;
switch(ai_family) {
case AF_INET:
if(thrdd->res_A)
return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
break;
case AF_INET6:
if(thrdd->res_AAAA)
return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
break;
default:
break;
}
return NULL;
}
#endif /* USE_RESOLV_THREADED */

View file

@ -124,6 +124,11 @@ CURLcode Curl_async_await(struct Curl_easy *data,
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async);
const struct Curl_addrinfo *
Curl_async_get_ai(struct Curl_easy *data,
struct Curl_resolv_async *async,
int ai_family, unsigned int index);
#ifdef USE_ARES
/* common functions for c-ares and threaded resolver with HTTPSRR */
#include <ares.h>
@ -175,7 +180,8 @@ struct async_thrdd_item;
/* Context for threaded resolver */
struct async_thrdd_ctx {
struct async_thrdd_item *resolved;
struct async_thrdd_item *res_A; /* ipv4 result */
struct async_thrdd_item *res_AAAA; /* ipv6 result */
#if defined(USE_HTTPSRR) && defined(USE_ARES)
struct {
ares_channel channel;
@ -184,7 +190,7 @@ struct async_thrdd_ctx {
BIT(done);
} rr;
#endif
BIT(queued);
uint32_t queued;
BIT(done);
};
@ -219,7 +225,7 @@ struct doh_probes;
#define Curl_async_await(x, y, z) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_global_init() CURLE_OK
#define Curl_async_global_cleanup() Curl_nop_stmt
#define Curl_async_get_ai(a,b,c,d) NULL
#endif /* !CURLRES_ASYNCH */
#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)

556
lib/cf-dns.c Normal file
View file

@ -0,0 +1,556 @@
/***************************************************************************
* _ _ ____ _
* 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 "urldata.h"
#include "curl_addrinfo.h"
#include "cfilters.h"
#include "connect.h"
#include "dnscache.h"
#include "curl_trc.h"
#include "progress.h"
#include "url.h"
#include "cf-dns.h"
struct cf_dns_ctx {
struct Curl_dns_entry *dns;
CURLcode resolv_result;
uint16_t port;
uint8_t ip_version;
uint8_t transport;
BIT(started);
BIT(announced);
BIT(abstract_unix_socket);
char hostname[1];
};
static struct cf_dns_ctx *
cf_dns_ctx_create(struct Curl_easy *data,
const char *hostname, uint16_t port,
uint8_t ip_version, uint8_t transport,
bool abstract_unix_socket,
struct Curl_dns_entry *dns)
{
struct cf_dns_ctx *ctx;
size_t hlen = strlen(hostname);
ctx = curlx_calloc(1, sizeof(*ctx) + hlen);
if(!ctx)
return NULL;
ctx->port = port;
ctx->ip_version = ip_version;
ctx->transport = transport;
ctx->abstract_unix_socket = abstract_unix_socket;
ctx->dns = Curl_dns_entry_link(data, dns);
ctx->started = !!ctx->dns;
if(hlen)
memcpy(ctx->hostname, hostname, hlen);
return ctx;
}
static void cf_dns_ctx_destroy(struct Curl_easy *data,
struct cf_dns_ctx *ctx)
{
if(ctx) {
Curl_dns_entry_unlink(data, &ctx->dns);
curlx_free(ctx);
}
}
#ifdef CURLVERBOSE
static void cf_dns_report_addr(struct Curl_easy *data,
struct dynbuf *tmp,
const char *label,
int ai_family,
const struct Curl_addrinfo *ai)
{
char buf[MAX_IPADR_LEN];
const char *sep = "";
CURLcode result;
curlx_dyn_reset(tmp);
for(; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family) {
Curl_printable_address(ai, buf, sizeof(buf));
result = curlx_dyn_addf(tmp, "%s%s", sep, buf);
if(result) {
infof(data, "too many IP, cannot show");
return;
}
sep = ", ";
}
}
infof(data, "%s%s", label,
(curlx_dyn_len(tmp) ? curlx_dyn_ptr(tmp) : "(none)"));
}
static void cf_dns_report(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_dns_entry *dns)
{
struct cf_dns_ctx *ctx = cf->ctx;
struct dynbuf tmp;
if(!Curl_trc_is_verbose(data) ||
/* ignore no name or numerical IP addresses */
!dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
return;
switch(ctx->transport) {
case TRNSPRT_UNIX:
#ifdef USE_UNIX_SOCKETS
CURL_TRC_CF(data, cf, "resolved unix domain %s",
Curl_conn_get_unix_path(data->conn));
#else
DEBUGASSERT(0);
#endif
break;
default:
curlx_dyn_init(&tmp, 1024);
infof(data, "Host %s:%d was resolved.", dns->hostname, dns->port);
#ifdef CURLRES_IPV6
cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr);
#endif
cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr);
curlx_dyn_free(&tmp);
break;
}
}
#else
#define cf_dns_report(x, y, z) Curl_nop_stmt
#endif
/*************************************************************
* Resolve the address of the server or proxy
*************************************************************/
static CURLcode cf_dns_start(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_dns_entry **pdns)
{
struct cf_dns_ctx *ctx = cf->ctx;
timediff_t timeout_ms = Curl_timeleft_ms(data);
CURLcode result;
*pdns = NULL;
#ifdef USE_UNIX_SOCKETS
if(ctx->transport == TRNSPRT_UNIX) {
CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname);
return Curl_resolv_unix(data, ctx->hostname,
(bool)cf->conn->bits.abstract_unix_socket, pdns);
}
#endif
/* Resolve target host right on */
CURL_TRC_CF(data, cf, "resolve host %s:%u", ctx->hostname, ctx->port);
result = Curl_resolv(data, ctx->hostname, ctx->port, ctx->ip_version,
ctx->transport, timeout_ms, pdns);
DEBUGASSERT(!result || !*pdns);
if(!result) { /* resolved right away, either sync or from dnscache */
DEBUGASSERT(*pdns);
return CURLE_OK;
}
else if(result == CURLE_AGAIN) { /* async resolv in progress */
return CURLE_OK;
}
else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */
failf(data, "Failed to resolve '%s' with timeout after %"
FMT_TIMEDIFF_T " ms", ctx->hostname,
curlx_ptimediff_ms(Curl_pgrs_now(data),
&data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
else {
DEBUGASSERT(result);
failf(data, "Could not resolve: %s", ctx->hostname);
return result;
}
}
static CURLcode cf_dns_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_dns_ctx *ctx = cf->ctx;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
*done = FALSE;
if(!ctx->started) {
ctx->started = TRUE;
ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns);
}
if(!ctx->dns && !ctx->resolv_result) {
ctx->resolv_result = Curl_resolv_take_result(data, &ctx->dns);
if(!ctx->dns && !ctx->resolv_result)
CURL_TRC_CF(data, cf, "DNS resolution not complete yet");
}
if(ctx->resolv_result) {
CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result);
return ctx->resolv_result;
}
if(ctx->dns && !ctx->announced) {
ctx->announced = TRUE;
if(cf->sockindex == FIRSTSOCKET) {
cf->conn->bits.dns_resolved = TRUE;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
}
cf_dns_report(cf, data, ctx->dns);
}
if(cf->next && !cf->next->connected) {
CURLcode result = Curl_conn_cf_connect(cf->next, data, done);
CURL_TRC_CF(data, cf, "connect subfilters -> %d, done=%d", result, *done);
if(result || !*done)
return result;
}
/* sub filter chain is connected, so are we now.
* Unlink the DNS entry, it is no longer needed and if it
* came from a SHARE in `data`, we need to release it under
* that one's lock. */
DEBUGASSERT(*done);
cf->connected = TRUE;
Curl_async_shutdown(data);
Curl_dns_entry_unlink(data, &ctx->dns);
return CURLE_OK;
}
static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_dns_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "destroy");
cf_dns_ctx_destroy(data, ctx);
}
static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
cf->connected = FALSE;
if(cf->next)
cf->next->cft->do_close(cf->next, data);
}
static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
if(!cf->connected)
return Curl_resolv_pollset(data, ps);
return CURLE_OK;
}
static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct cf_dns_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
(void)arg1;
(void)arg2;
switch(event) {
case CF_CTRL_DATA_DONE:
if(ctx->dns) {
/* Should only come here when the connect attempt failed and
* `data` is giving up on it. On a successful connect, we already
* unlinked the DNS entry. */
Curl_dns_entry_unlink(data, &ctx->dns);
}
break;
default:
break;
}
return result;
}
struct Curl_cftype Curl_cft_dns = {
"DNS",
0,
CURL_LOG_LVL_NONE,
cf_dns_destroy,
cf_dns_connect,
cf_dns_close,
Curl_cf_def_shutdown,
cf_dns_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
cf_dns_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_dns_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
bool abstract_unix_socket,
struct Curl_dns_entry *dns)
{
struct Curl_cfilter *cf = NULL;
struct cf_dns_ctx *ctx;
CURLcode result = CURLE_OK;
(void)data;
ctx = cf_dns_ctx_create(data, hostname, port, ip_version, transport,
abstract_unix_socket, dns);
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = Curl_cf_create(&cf, &Curl_cft_dns, ctx);
out:
*pcf = result ? NULL : cf;
if(result)
cf_dns_ctx_destroy(data, ctx);
return result;
}
/* Create a "resolv" filter for the transfer's connection. Figures
* out the hostname/path and port where to connect to. */
static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
uint8_t transport,
struct Curl_dns_entry *dns)
{
struct connectdata *conn = data->conn;
const char *hostname = NULL;
uint16_t port = 0;
uint8_t ip_version = conn->ip_version;
bool abstract_unix_socket = FALSE;
#ifdef USE_UNIX_SOCKETS
{
const char *unix_path = Curl_conn_get_unix_path(conn);
if(unix_path) {
DEBUGASSERT(transport == TRNSPRT_UNIX);
hostname = unix_path;
abstract_unix_socket = (bool)conn->bits.abstract_unix_socket;
}
}
#endif
#ifndef CURL_DISABLE_PROXY
if(!hostname && CONN_IS_PROXIED(conn)) {
struct hostname *ehost;
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
&conn->http_proxy.host;
hostname = ehost->name;
port = conn->bits.socksproxy ? conn->socks_proxy.port :
conn->http_proxy.port;
}
#endif
if(!hostname) {
struct hostname *ehost;
ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
hostname = ehost->name;
port = conn->bits.conn_to_port ?
conn->conn_to_port : (uint16_t)conn->remote_port;
}
if(!hostname) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
return cf_dns_create(pcf, data, hostname, port, ip_version,
transport, abstract_unix_socket, dns);
}
/* Adds a "resolv" filter at the top of the connection's filter chain.
* For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will
* figure out hostname and port to connect to and start the DNS resolve
* on the first connect attempt.
* For SECONDARYSOCKET, the `dns` parameter must be given.
*/
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t transport,
struct Curl_dns_entry *dns)
{
struct Curl_cfilter *cf = NULL;
CURLcode result;
DEBUGASSERT(data);
if(sockindex == FIRSTSOCKET)
result = cf_dns_conn_create(&cf, data, transport, dns);
else if(dns) {
result = cf_dns_create(&cf, data, dns->hostname, dns->port,
dns->ip_version, transport, FALSE, dns);
}
else {
DEBUGASSERT(0);
result = CURLE_FAILED_INIT;
}
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
/* Insert a new "resolv" filter directly after `cf`. It will
* start a DNS resolve for the given hostnmae and port on the
* first connect attempt.
* See socks.c on how this is used to make a non-blocking DNS
* resolve during connect.
*/
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_dns_create(&cf, data, hostname, port, ip_version,
transport, FALSE, NULL);
if(result)
return result;
Curl_conn_cf_insert_after(cf_at, cf);
return CURLE_OK;
}
/* Return the resolv result from the first "resolv" filter, starting
* the given filter `cf` downwards.
*/
CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_dns) {
struct cf_dns_ctx *ctx = cf->ctx;
if(ctx->dns || ctx->resolv_result)
return ctx->resolv_result;
return CURLE_AGAIN;
}
}
return CURLE_FAILED_INIT;
}
/* Return the result of the DNS resolution. Searches for a "resolv"
* filter from the top of the filter chain down. Returns
* - CURLE_AGAIN when not done yet
* - CURLE_OK when DNS was successfully resolved
* - CURLR_FAILED_INIT when no resolv filter was found
* - error returned by the DNS resolv
*/
CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex)
{
return Curl_cf_dns_result(conn->cfilter[sockindex]);
}
static const struct Curl_addrinfo *
cf_dns_get_nth_ai(const struct Curl_addrinfo *ai,
int ai_family, unsigned int index)
{
unsigned int i = 0;
for(i = 0; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family) {
if(i == index)
return ai;
++i;
}
}
return NULL;
}
/* Return the addrinfo at `index` for the given `family` from the
* first "resolve" filter underneath `cf`. If the DNS resolving is
* not done yet or if no address for the family exists, returns NULL.
*/
const struct Curl_addrinfo *
Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
struct Curl_easy *data,
int ai_family,
unsigned int index)
{
(void)data;
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_dns) {
struct cf_dns_ctx *ctx = cf->ctx;
if(ctx->resolv_result)
return NULL;
else if(ctx->dns)
return cf_dns_get_nth_ai(ctx->dns->addr, ai_family, index);
else
return Curl_resolv_get_ai(data, ai_family, index);
}
}
return NULL;
}
/* Return the addrinfo at `index` for the given `family` from the
* first "resolve" filter at the connection. If the DNS resolving is
* not done yet or if no address for the family exists, returns NULL.
*/
const struct Curl_addrinfo *
Curl_conn_dns_get_ai(struct Curl_easy *data,
int sockindex,
int ai_family,
unsigned int index)
{
struct connectdata *conn = data->conn;
return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data,
ai_family, index);
}
#ifdef USE_HTTPSRR
/* Return the HTTPS-RR info from the first "resolve" filter at the
* connection. If the DNS resolving is not done yet or if there
* is no HTTPS-RR info, returns NULL.
*/
const struct Curl_https_rrinfo *
Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex)
{
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_dns) {
struct cf_dns_ctx *ctx = cf->ctx;
return ctx->dns ? ctx->dns->hinfo : NULL;
}
}
return NULL;
}
#endif /* USE_HTTPSRR */

70
lib/cf-dns.h Normal file
View file

@ -0,0 +1,70 @@
#ifndef HEADER_CURL_CF_DNS_H
#define HEADER_CURL_CF_DNS_H
/***************************************************************************
* _ _ ____ _
* 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"
struct Curl_easy;
struct connectdata;
struct Curl_dns_entry;
struct Curl_addrinfo;
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t transport,
struct Curl_dns_entry *dns);
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport);
CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex);
CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf);
const struct Curl_addrinfo *
Curl_conn_dns_get_ai(struct Curl_easy *data,
int sockindex,
int ai_family,
unsigned int index);
const struct Curl_addrinfo *
Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
struct Curl_easy *data,
int ai_family,
unsigned int index);
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex);
#endif
extern struct Curl_cftype Curl_cft_dns;
#endif /* HEADER_CURL_CF_DNS_H */

View file

@ -28,6 +28,7 @@
#include "urldata.h"
#include "curl_trc.h"
#include "cfilters.h"
#include "cf-dns.h"
#include "connect.h"
#include "hostip.h"
#include "httpsrr.h"
@ -39,6 +40,7 @@
#include "vquic/vquic.h"
typedef enum {
CF_HC_RESOLV,
CF_HC_INIT,
CF_HC_CONNECT,
CF_HC_SUCCESS,
@ -116,8 +118,32 @@ struct cf_hc_ctx {
size_t baller_count;
timediff_t soft_eyeballs_timeout_ms;
timediff_t hard_eyeballs_timeout_ms;
uint8_t def_transport;
};
static void cf_hc_ctx_reset(struct Curl_easy *data,
struct cf_hc_ctx *ctx)
{
if(ctx) {
size_t i;
for(i = 0; i < ctx->baller_count; ++i)
cf_hc_baller_reset(&ctx->ballers[i], data);
ctx->state = CF_HC_RESOLV;
ctx->result = CURLE_OK;
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
}
}
static void cf_hc_ctx_destroy(struct Curl_easy *data,
struct cf_hc_ctx *ctx)
{
if(ctx) {
cf_hc_ctx_reset(data, ctx);
curlx_free(ctx);
}
}
static void cf_hc_baller_assign(struct cf_hc_baller *b,
enum alpnid alpn_id,
uint8_t def_transport)
@ -135,6 +161,9 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b,
case ALPN_h1:
b->name = "h1";
break;
case ALPN_none:
b->name = "no-alpn";
break;
default:
b->result = CURLE_FAILED_INIT;
break;
@ -179,21 +208,6 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
return b->result;
}
static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
if(ctx) {
for(i = 0; i < ctx->baller_count; ++i)
cf_hc_baller_reset(&ctx->ballers[i], data);
ctx->state = CF_HC_INIT;
ctx->result = CURLE_OK;
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
}
}
static CURLcode baller_connected(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_hc_baller *winner)
@ -290,6 +304,157 @@ static bool time_to_start_next(struct Curl_cfilter *cf,
return FALSE;
}
static bool cf_https_alpns_contain(enum alpnid id,
enum alpnid *list, size_t len)
{
size_t i;
for(i = 0; i < len; ++i) {
if(id == list[i])
return TRUE;
}
return FALSE;
}
static CURLcode cf_hc_resolv(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
enum alpnid alpn_ids[2];
size_t alpn_count = 0, i;
CURLcode result = CURLE_OK;
if(cf->conn->bits.tls_enable_alpn) {
#ifdef USE_HTTPSRR
/* Is there an HTTPSRR use its ALPNs here.
* We are here after having selected a connection to a host+port and
* can no longer change that. Any HTTPSRR advice for other hosts and ports
* we need to ignore. */
const struct Curl_https_rrinfo *rr;
bool need_https_rr = FALSE;
if(need_https_rr) {
result = Curl_conn_dns_result(cf->conn, cf->sockindex);
if(result)
return result;
}
/* Do we have HTTPS-RR information? */
rr = Curl_conn_dns_get_https(data, cf->sockindex);
if(rr && !rr->no_def_alpn && /* ALPNs are defaults */
(!rr->target || /* for same host */
!rr->target[0] ||
(rr->target[0] == '.' &&
!rr->target[1])) &&
(rr->port < 0 || /* for same port */
rr->port == cf->conn->remote_port)) {
for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
enum alpnid alpn = rr->alpns[i];
if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
continue;
switch(alpn) {
case ALPN_h3:
if(Curl_conn_may_http3(data, cf->conn, ctx->def_transport))
break; /* not possible */
if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h2:
if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h1:
if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
default: /* ignore */
break;
}
}
}
#endif
/* Add preferred HTTP version ALPN first */
if(data->state.http_neg.preferred &&
(alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.preferred & data->state.http_neg.allowed)) {
enum alpnid alpn_pref = ALPN_none;
switch(data->state.http_neg.preferred) {
case CURL_HTTP_V3x:
if(!Curl_conn_may_http3(data, cf->conn, ctx->def_transport))
alpn_pref = ALPN_h3;
break;
case CURL_HTTP_V2x:
alpn_pref = ALPN_h2;
break;
case CURL_HTTP_V1x:
alpn_pref = ALPN_h1;
break;
default:
break;
}
if(alpn_pref &&
!cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) {
alpn_ids[alpn_count++] = alpn_pref;
}
}
if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V3x) &&
!cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
result = Curl_conn_may_http3(data, cf->conn, ctx->def_transport);
if(!result) {
CURL_TRC_CF(data, cf, "adding wanted h3");
alpn_ids[alpn_count++] = ALPN_h3;
}
else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
goto out; /* only h3 allowed, not possible, error out */
}
if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
!cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h2");
alpn_ids[alpn_count++] = ALPN_h2;
}
else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V1x) &&
!cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h1");
alpn_ids[alpn_count++] = ALPN_h1;
}
}
if(!alpn_count) {
alpn_ids[0] = ALPN_none;
alpn_count = 1;
}
/* Install ballers for the ALPNs we selected. */
DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
failf(data, "https-connect filter create with unsupported %zu ALPN ids",
alpn_count);
result = CURLE_FAILED_INIT;
goto out;
}
for(i = 0; i < alpn_count; ++i)
cf_hc_baller_assign(&ctx->ballers[i], alpn_ids[i], ctx->def_transport);
for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
ctx->ballers[i].alpn_id = ALPN_none;
ctx->baller_count = alpn_count;
out:
return result;
}
static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
@ -305,6 +470,17 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
*done = FALSE;
switch(ctx->state) {
case CF_HC_RESOLV:
result = cf_hc_resolv(cf, data);
if(result) {
if(result == CURLE_AGAIN)
result = CURLE_OK;
goto out;
}
DEBUGASSERT(ctx->baller_count);
ctx->state = CF_HC_INIT;
FALLTHROUGH();
case CF_HC_INIT:
DEBUGASSERT(!cf->next);
for(i = 0; i < ctx->baller_count; i++)
@ -536,7 +712,7 @@ out:
static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
cf_hc_reset(cf, data);
cf_hc_ctx_reset(data, cf->ctx);
cf->connected = FALSE;
if(cf->next) {
@ -550,8 +726,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_hc_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "destroy");
cf_hc_reset(cf, data);
Curl_safefree(ctx);
cf_hc_ctx_destroy(data, ctx);
}
struct Curl_cftype Curl_cft_http_connect = {
@ -574,59 +749,40 @@ struct Curl_cftype Curl_cft_http_connect = {
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
enum alpnid *alpnids, size_t alpn_count,
uint8_t def_transport)
{
struct Curl_cfilter *cf = NULL;
struct cf_hc_ctx *ctx;
CURLcode result = CURLE_OK;
size_t i;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
DEBUGASSERT(alpnids);
DEBUGASSERT(alpn_count);
DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
failf(data, "https-connect filter create with unsupported %zu ALPN ids",
alpn_count);
result = CURLE_FAILED_INIT;
goto out;
}
for(i = 0; i < alpn_count; ++i)
cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport);
for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
ctx->ballers[i].alpn_id = ALPN_none;
ctx->baller_count = alpn_count;
ctx->def_transport = def_transport;
result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
if(result)
goto out;
ctx = NULL;
cf_hc_reset(cf, data);
out:
*pcf = result ? NULL : cf;
curlx_free(ctx);
cf_hc_ctx_destroy(data, ctx);
return result;
}
static CURLcode cf_http_connect_add(struct Curl_easy *data,
static CURLcode cf_hc_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
enum alpnid *alpn_ids, size_t alpn_count,
uint8_t def_transport)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport);
result = cf_hc_create(&cf, data, def_transport);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
@ -634,138 +790,19 @@ out:
return result;
}
static bool cf_https_alpns_contain(enum alpnid id,
enum alpnid *list, size_t len)
{
size_t i;
for(i = 0; i < len; ++i) {
if(id == list[i])
return TRUE;
}
return FALSE;
}
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
enum alpnid alpn_ids[2];
size_t alpn_count = 0;
CURLcode result = CURLE_OK;
struct Curl_cfilter cf_fake, *cf = NULL;
(void)sockindex;
/* we want to log for the filter before we create it, fake it. */
memset(&cf_fake, 0, sizeof(cf_fake));
cf_fake.cft = &Curl_cft_http_connect;
cf = &cf_fake;
DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS);
if(conn->bits.tls_enable_alpn) {
#ifdef USE_HTTPSRR
/* Is there an HTTPSRR use its ALPNs here.
* We are here after having selected a connection to a host+port and
* can no longer change that. Any HTTPSRR advice for other hosts and ports
* we need to ignore. */
struct Curl_dns_entry *dns = data->state.dns[sockindex];
struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL;
if(rr && !rr->no_def_alpn && /* ALPNs are defaults */
(!rr->target || /* for same host */
!rr->target[0] ||
(rr->target[0] == '.' &&
!rr->target[1])) &&
(rr->port < 0 || /* for same port */
rr->port == conn->remote_port)) {
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
enum alpnid alpn = (enum alpnid)rr->alpns[i];
if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
continue;
switch(alpn) {
case ALPN_h3:
if(Curl_conn_may_http3(data, conn, conn->transport_wanted))
break; /* not possible */
if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h2:
if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h1:
if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
default: /* ignore */
break;
}
}
}
#endif
if((conn->scheme->protocol != CURLPROTO_HTTPS) ||
!conn->bits.tls_enable_alpn)
goto out;
/* Add preferred HTTP version ALPN first */
if(data->state.http_neg.preferred &&
(alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.preferred & data->state.http_neg.allowed)) {
enum alpnid alpn_pref = ALPN_none;
switch(data->state.http_neg.preferred) {
case CURL_HTTP_V3x:
if(!Curl_conn_may_http3(data, conn, conn->transport_wanted))
alpn_pref = ALPN_h3;
break;
case CURL_HTTP_V2x:
alpn_pref = ALPN_h2;
break;
case CURL_HTTP_V1x:
alpn_pref = ALPN_h1;
break;
default:
break;
}
if(alpn_pref &&
!cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) {
alpn_ids[alpn_count++] = alpn_pref;
}
}
if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V3x) &&
!cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
result = Curl_conn_may_http3(data, conn, conn->transport_wanted);
if(!result) {
CURL_TRC_CF(data, cf, "adding wanted h3");
alpn_ids[alpn_count++] = ALPN_h3;
}
else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
goto out; /* only h3 allowed, not possible, error out */
}
if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
!cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h2");
alpn_ids[alpn_count++] = ALPN_h2;
}
else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V1x) &&
!cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h1");
alpn_ids[alpn_count++] = ALPN_h1;
}
}
/* If we identified ALPNs to use, install our filter. Otherwise,
* install nothing, so our call will use a default connect setup. */
if(alpn_count) {
result = cf_http_connect_add(data, conn, sockindex,
alpn_ids, alpn_count,
conn->transport_wanted);
}
result = cf_hc_add(data, conn, sockindex, conn->transport_wanted);
out:
return result;

View file

@ -31,15 +31,9 @@ struct Curl_cfilter;
struct Curl_easy;
struct connectdata;
struct Curl_cftype;
struct Curl_dns_entry;
extern struct Curl_cftype Curl_cft_http_connect;
CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
bool try_h3, bool try_h21);
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);

View file

@ -49,6 +49,7 @@
#include "urldata.h"
#include "connect.h"
#include "cfilters.h"
#include "cf-dns.h"
#include "cf-ip-happy.h"
#include "curl_addrinfo.h"
#include "curl_trc.h"
@ -108,59 +109,47 @@ UNITTEST void Curl_debug_set_transport_provider(
#endif /* UNITTESTS */
struct cf_ai_iter {
const struct Curl_addrinfo *head;
const struct Curl_addrinfo *last;
struct Curl_cfilter *cf;
int ai_family;
int n;
unsigned int n;
};
static void cf_ai_iter_init(struct cf_ai_iter *iter,
const struct Curl_addrinfo *list,
struct Curl_cfilter *cf,
int ai_family)
{
iter->head = list;
iter->cf = cf;
iter->ai_family = ai_family;
iter->last = NULL;
iter->n = -1;
iter->n = 0;
}
static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter)
static const struct Curl_addrinfo *
cf_ai_iter_next(struct cf_ai_iter *iter,
struct Curl_easy *data)
{
const struct Curl_addrinfo *addr;
if(iter->n < 0) {
if(!iter->cf)
return NULL;
addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex,
iter->ai_family, iter->n);
if(addr)
iter->n++;
for(addr = iter->head; addr; addr = addr->ai_next) {
if(addr->ai_family == iter->ai_family)
break;
}
iter->last = addr;
}
else if(iter->last) {
iter->n++;
for(addr = iter->last->ai_next; addr; addr = addr->ai_next) {
if(addr->ai_family == iter->ai_family)
break;
}
iter->last = addr;
}
return iter->last;
return addr;
}
static bool cf_ai_iter_has_more(struct cf_ai_iter *iter)
static bool cf_ai_iter_has_more(struct cf_ai_iter *iter,
struct Curl_easy *data)
{
const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next :
((iter->n < 0) ? iter->head : NULL);
while(addr) {
if(addr->ai_family == iter->ai_family)
return TRUE;
addr = addr->ai_next;
}
return FALSE;
return (iter->cf &&
!!Curl_conn_dns_get_ai(data, iter->cf->sockindex,
iter->ai_family, iter->n));
}
struct cf_ip_attempt {
struct cf_ip_attempt *next;
const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
struct Curl_sockaddr_ex addr;
struct Curl_cfilter *cf; /* current sub-cfilter connecting */
cf_ip_connect_create *cf_create;
struct curltime started; /* start of current attempt */
@ -187,7 +176,7 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a,
static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct Curl_addrinfo *addr,
struct Curl_sockaddr_ex *addr,
int ai_family,
uint8_t transport,
cf_ip_connect_create *cf_create)
@ -201,14 +190,14 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
if(!a)
return CURLE_OUT_OF_MEMORY;
a->addr = addr;
a->addr = *addr;
a->ai_family = ai_family;
a->transport = transport;
a->result = CURLE_OK;
a->cf_create = cf_create;
*pa = a;
result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport);
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
if(result)
goto out;
@ -277,7 +266,7 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
a->inconclusive = FALSE;
a->cf = NULL;
result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport);
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
if(!result) {
bool dummy;
/* the new filter might have sub-filters */
@ -307,7 +296,7 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
}
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
const struct Curl_addrinfo *addr_list,
struct Curl_cfilter *cf,
cf_ip_connect_create *cf_create,
uint8_t transport,
timediff_t attempt_delay_ms)
@ -320,7 +309,7 @@ static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
if(transport == TRNSPRT_UNIX) {
#ifdef USE_UNIX_SOCKETS
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX);
cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX);
#else
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
@ -330,15 +319,15 @@ static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
if(ip_version == CURL_IPRESOLVE_V6)
cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET);
else
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
cf_ai_iter_init(&bs->addr_iter, cf, AF_INET);
if(ip_version == CURL_IPRESOLVE_V4)
cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6);
else
cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6);
cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6);
#else
(void)ip_version;
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
cf_ai_iter_init(&bs->addr_iter, cf, AF_INET);
#endif
}
return CURLE_OK;
@ -347,6 +336,7 @@ static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool dns_resolved,
bool *connected)
{
CURLcode result = CURLE_OK;
@ -400,10 +390,10 @@ evaluate:
do_more = TRUE;
}
else {
bool more_possible = cf_ai_iter_has_more(&bs->addr_iter);
bool more_possible = cf_ai_iter_has_more(&bs->addr_iter, data);
#ifdef USE_IPV6
if(!more_possible)
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data);
#endif
do_more = more_possible &&
(curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started) >=
@ -416,26 +406,36 @@ evaluate:
if(do_more) {
/* start the next attempt if there is another ip address to try.
* Alternate between address families when possible. */
const struct Curl_addrinfo *addr = NULL;
const struct Curl_addrinfo *ai = NULL;
int ai_family = 0;
CURL_TRC_CF(data, cf, "want to do more");
#ifdef USE_IPV6
if((bs->last_attempt_ai_family == AF_INET) ||
!cf_ai_iter_has_more(&bs->addr_iter)) {
addr = cf_ai_iter_next(&bs->ipv6_iter);
!cf_ai_iter_has_more(&bs->addr_iter, data)) {
ai = cf_ai_iter_next(&bs->ipv6_iter, data);
ai_family = bs->ipv6_iter.ai_family;
CURL_TRC_CF(data, cf, "check for next AAAA address: %s",
ai ? "found" : "none");
}
#endif
if(!addr) {
addr = cf_ai_iter_next(&bs->addr_iter);
if(!ai) {
ai = cf_ai_iter_next(&bs->addr_iter, data);
ai_family = bs->addr_iter.ai_family;
CURL_TRC_CF(data, cf, "check for next A address: %s",
ai ? "found" : "none");
}
/* We are (re-)starting attempts. We are not interested in
* keeping old failure information. The new attempt will either
* succeed or persist new failure. */
Curl_reset_fail(data);
if(addr) { /* try another address */
result = cf_ip_attempt_new(&a, cf, data, addr, ai_family,
if(ai) { /* try another address */
struct Curl_sockaddr_ex addr;
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport);
if(result)
goto out;
result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family,
bs->transport, bs->cf_create);
CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
bs->running ? "next" : "first",
@ -485,7 +485,7 @@ evaluate:
/* attempt timeout for restart has not expired yet */
goto out;
}
else if(!ongoing) {
else if(!ongoing && dns_resolved) {
/* no more addresses, no inconclusive attempts */
CURL_TRC_CF(data, cf, "no more attempts to try");
result = CURLE_COULDNT_CONNECT;
@ -511,10 +511,10 @@ out:
return CURLE_OPERATION_TIMEDOUT;
}
more_possible = cf_ai_iter_has_more(&bs->addr_iter);
more_possible = cf_ai_iter_has_more(&bs->addr_iter, data);
#ifdef USE_IPV6
if(!more_possible)
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data);
#endif
if(more_possible) {
timediff_t expire_ms, elapsed_ms;
@ -630,6 +630,7 @@ struct cf_ip_happy_ctx {
cf_connect_state state;
struct cf_ip_ballers ballers;
struct curltime started;
BIT(dns_resolved);
};
static CURLcode is_connected(struct Curl_cfilter *cf,
@ -640,7 +641,8 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
struct connectdata *conn = cf->conn;
CURLcode result;
result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected);
result = cf_ip_ballers_run(&ctx->ballers, cf, data,
(bool)ctx->dns_resolved, connected);
if(!result)
return CURLE_OK;
@ -693,18 +695,10 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
return result;
}
/*
* Connect to the given host with timeout, proxy or remote does not matter.
* There might be more than one IP address to try out.
*/
static CURLcode start_connect(struct Curl_cfilter *cf,
static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
if(!dns)
return CURLE_FAILED_INIT;
if(Curl_timeleft_ms(data) < 0) {
/* a precaution, no need to continue if time already is up */
@ -714,8 +708,8 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
ctx->started = *Curl_pgrs_now(data);
return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
dns->addr, ctx->cf_create, ctx->transport,
return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version, cf,
ctx->cf_create, ctx->transport,
data->set.happy_eyeballs_timeout);
}
@ -729,6 +723,12 @@ static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
cf_ip_ballers_clear(cf, data, &ctx->ballers);
}
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx)
{
if(ctx)
curlx_free(ctx);
}
static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
@ -768,6 +768,10 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
/* -Werror=null-dereference finds false positives suddenly. */
if(!data)
return CURLE_FAILED_INIT;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@ -776,13 +780,23 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
DEBUGASSERT(ctx);
*done = FALSE;
if(!ctx->dns_resolved) {
result = Curl_conn_dns_result(cf->conn, cf->sockindex);
if(!result)
ctx->dns_resolved = TRUE;
else if(result == CURLE_AGAIN) /* not complete yet */
result = CURLE_OK;
else /* real error */
goto out;
}
switch(ctx->state) {
case SCFST_INIT:
DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
DEBUGASSERT(!cf->connected);
result = start_connect(cf, data);
result = cf_ip_happy_init(cf, data);
if(result)
return result;
goto out;
ctx->state = SCFST_WAITING;
FALLTHROUGH();
case SCFST_WAITING:
@ -823,6 +837,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
*done = TRUE;
break;
}
out:
return result;
}
@ -896,9 +911,8 @@ static void cf_ip_happy_destroy(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
cf_ip_happy_ctx_clear(cf, data);
cf_ip_happy_ctx_destroy(ctx);
}
/* release any resources held in state */
Curl_safefree(ctx);
}
struct Curl_cftype Curl_cft_ip_happy = {
@ -954,7 +968,7 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
out:
if(result) {
Curl_safefree(*pcf);
curlx_free(ctx);
cf_ip_happy_ctx_destroy(ctx);
}
return result;
}

View file

@ -29,6 +29,7 @@ struct connectdata;
struct Curl_addrinfo;
struct Curl_cfilter;
struct Curl_easy;
struct Curl_sockaddr_ex;
/**
* Create a cfilter for making an "ip" connection to the
@ -44,7 +45,7 @@ struct Curl_easy;
typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,

View file

@ -265,42 +265,30 @@ static void tcpkeepalive(struct Curl_cfilter *cf,
}
/**
* Assign the address `ai` to the Curl_sockaddr_ex `dest` and
* set the transport used.
* Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with
* transport determining socktype and protocol.
*/
static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
const struct Curl_addrinfo *ai,
uint8_t transport)
{
/*
* The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr
* structure with enough space available to directly hold any
* protocol-specific address structures. The variable declared here will be
* used to pass / receive data to/from the fopensocket callback if this has
* been set, before that, it is initialized from parameters.
* The Curl_sockaddr_ex structure is libcurl's external API
* curl_sockaddr structure with enough space available to directly hold
* any protocol-specific address structures. The variable declared here
* will be used to pass / receive data to/from the fopensocket callback
* if this has been set, before that, it is initialized from parameters.
*/
dest->family = ai->ai_family;
switch(transport) {
case TRNSPRT_TCP:
dest->socktype = SOCK_STREAM;
dest->protocol = IPPROTO_TCP;
break;
case TRNSPRT_UNIX:
dest->socktype = SOCK_STREAM;
dest->protocol = IPPROTO_IP;
break;
default: /* UDP and QUIC */
dest->socktype = SOCK_DGRAM;
dest->protocol = IPPROTO_UDP;
break;
}
dest->addrlen = (unsigned int)ai->ai_addrlen;
addr->family = ai->ai_family;
addr->socktype = Curl_socktype_for_transport(transport);
addr->protocol = Curl_protocol_for_transport(transport);
addr->addrlen = (unsigned int)ai->ai_addrlen;
DEBUGASSERT(dest->addrlen <= sizeof(dest->curl_sa_addrbuf));
if(dest->addrlen > sizeof(dest->curl_sa_addrbuf))
DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf));
if(addr->addrlen > sizeof(addr->curl_sa_addrbuf))
return CURLE_TOO_LARGE;
memcpy(&dest->curl_sa_addrbuf, ai->ai_addr, dest->addrlen);
memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen);
return CURLE_OK;
}
@ -412,7 +400,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
/* if the caller does not want info back, use a local temp copy */
addr = &dummy;
result = sock_assign_addr(addr, ai, transport);
result = Curl_socket_addr_from_ai(addr, ai, transport);
if(result)
return result;
@ -900,18 +888,13 @@ struct cf_socket_ctx {
};
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
CURLcode result;
memset(ctx, 0, sizeof(*ctx));
ctx->sock = CURL_SOCKET_BAD;
ctx->transport = transport;
result = sock_assign_addr(&ctx->addr, ai, transport);
if(result)
return result;
ctx->addr = *addr;
#ifdef DEBUGBUILD
{
@ -942,7 +925,7 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
}
#endif
return result;
return CURLE_OK;
}
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@ -1722,7 +1705,7 @@ struct Curl_cftype Curl_cft_tcp = {
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
@ -1732,7 +1715,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
(void)data;
(void)conn;
DEBUGASSERT(transport == TRNSPRT_TCP);
if(!ai) {
if(!addr) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto out;
}
@ -1743,7 +1726,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
goto out;
}
result = cf_socket_ctx_init(ctx, ai, transport);
result = cf_socket_ctx_init(ctx, addr, transport);
if(result)
goto out;
@ -1888,7 +1871,7 @@ struct Curl_cftype Curl_cft_udp = {
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
@ -1904,7 +1887,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
goto out;
}
result = cf_socket_ctx_init(ctx, ai, transport);
result = cf_socket_ctx_init(ctx, addr, transport);
if(result)
goto out;
@ -1942,7 +1925,7 @@ struct Curl_cftype Curl_cft_unix = {
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
@ -1958,7 +1941,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
goto out;
}
result = cf_socket_ctx_init(ctx, ai, transport);
result = cf_socket_ctx_init(ctx, addr, transport);
if(result)
goto out;

View file

@ -60,6 +60,10 @@ struct Curl_sockaddr_ex {
CURLcode Curl_parse_interface(const char *input,
char **dev, char **iface, char **host);
CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
const struct Curl_addrinfo *ai,
uint8_t transport);
/*
* Create a socket based on info from 'conn' and 'ai'.
*
@ -91,7 +95,7 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
/**
@ -104,7 +108,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
/**
@ -117,7 +121,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
/**

View file

@ -508,6 +508,11 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
if(!CONN_SOCK_IDX_VALID(sockindex))
return CURLE_BAD_FUNCTION_ARGUMENT;
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
*done = TRUE;
return CURLE_OK;
}
cf = data->conn->cfilter[sockindex];
if(!cf) {
*done = FALSE;
@ -712,6 +717,18 @@ int Curl_socktype_for_transport(uint8_t transport)
}
}
int Curl_protocol_for_transport(uint8_t transport)
{
switch(transport) {
case TRNSPRT_TCP:
return IPPROTO_TCP;
case TRNSPRT_UNIX:
return IPPROTO_IP;
default: /* UDP and QUIC */
return IPPROTO_UDP;
}
}
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
struct connectdata *conn)
{

View file

@ -346,6 +346,7 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
struct Curl_easy *data);
int Curl_socktype_for_transport(uint8_t transport);
int Curl_protocol_for_transport(uint8_t transport);
const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
struct Curl_easy *data);

View file

@ -54,6 +54,7 @@
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
#include "cf-dns.h"
#include "cf-haproxy.h"
#include "cf-https-connect.h"
#include "cf-ip-happy.h"
@ -343,7 +344,6 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
if(cf->connected) {
*done = TRUE;
@ -352,8 +352,6 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
/* connect current sub-chain */
connect_sub_chain:
if(!dns)
return CURLE_FAILED_INIT;
if(cf->next && !cf->next->connected) {
result = Curl_conn_cf_connect(cf->next, data, done);
@ -562,10 +560,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
DEBUGASSERT(data);
DEBUGASSERT(conn->scheme);
DEBUGASSERT(dns);
Curl_dns_entry_unlink(data, &data->state.dns[sockindex]);
data->state.dns[sockindex] = dns;
DEBUGASSERT(!conn->cfilter[sockindex]);
#ifndef CURL_DISABLE_HTTP
if(!conn->cfilter[sockindex] &&
@ -585,13 +580,29 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
goto out;
}
result = Curl_cf_dns_add(data, conn, sockindex,
conn->transport_wanted, dns);
DEBUGASSERT(conn->cfilter[sockindex]);
out:
if(result)
Curl_dns_entry_unlink(data, &data->state.dns[sockindex]);
return result;
}
#ifdef USE_UNIX_SOCKETS
const char *Curl_conn_get_unix_path(struct connectdata *conn)
{
const char *unix_path = conn->unix_domain_socket;
#ifndef CURL_DISABLE_PROXY
if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
!strncmp(UNIX_SOCKET_PREFIX "/",
conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
#endif
return unix_path;
}
#endif /* USE_UNIX_SOCKETS */
void Curl_conn_set_multiplex(struct connectdata *conn)
{
if(!conn->bits.multiplex) {

View file

@ -127,6 +127,15 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
/* Set conn to allow multiplexing. */
void Curl_conn_set_multiplex(struct connectdata *conn);
#ifdef USE_UNIX_SOCKETS
#ifndef CURL_DISABLE_PROXY
#define UNIX_SOCKET_PREFIX "localhost"
#endif
const char *Curl_conn_get_unix_path(struct connectdata *conn);
#else
#define Curl_conn_get_unix_path(c) NULL
#endif
extern struct Curl_cftype Curl_cft_setup;
#endif /* HEADER_CURL_CONNECT_H */

View file

@ -422,10 +422,15 @@ CURLcode Curl_str2addr(const char *dotted, uint16_t port,
return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */
}
bool Curl_is_ipaddr(const char *address)
bool Curl_is_ipv4addr(const char *address)
{
struct in_addr in;
if(curlx_inet_pton(AF_INET, address, &in) > 0)
return (curlx_inet_pton(AF_INET, address, &in) > 0);
}
bool Curl_is_ipaddr(const char *address)
{
if(Curl_is_ipv4addr(address))
return TRUE;
#ifdef USE_IPV6
{
@ -442,34 +447,31 @@ bool Curl_is_ipaddr(const char *address)
/**
* Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo
* struct initialized with this path.
* Set '*longpath' to TRUE if the error is a too long path.
* Returns CURLE_TOO_LARGE when path is too long.
*/
struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
bool abstract)
CURLcode Curl_unix2addr(const char *path, bool abstract,
struct Curl_addrinfo **paddr)
{
struct Curl_addrinfo *ai;
struct sockaddr_un *sa_un;
size_t path_len;
*longpath = FALSE;
*paddr = NULL;
/* sun_path must be able to store the null-terminated path */
path_len = strlen(path) + 1;
if(path_len > sizeof(sa_un->sun_path))
return CURLE_TOO_LARGE;
ai = curlx_calloc(1,
sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un));
if(!ai)
return NULL;
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
return CURLE_OUT_OF_MEMORY;
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
sa_un = (void *)ai->ai_addr;
sa_un->sun_family = AF_UNIX;
/* sun_path must be able to store the null-terminated path */
path_len = strlen(path) + 1;
if(path_len > sizeof(sa_un->sun_path)) {
curlx_free(ai);
*longpath = TRUE;
return NULL;
}
ai->ai_family = AF_UNIX;
ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */
ai->ai_addrlen = (curl_socklen_t)
@ -481,7 +483,8 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
else
memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */
return ai;
*paddr = ai;
return CURLE_OK;
}
#endif

View file

@ -71,13 +71,14 @@ int Curl_getaddrinfo_ex(const char *nodename,
struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port);
#endif
bool Curl_is_ipv4addr(const char *address);
bool Curl_is_ipaddr(const char *address);
CURLcode Curl_str2addr(const char *dotted, uint16_t port,
struct Curl_addrinfo **addrp);
#ifdef USE_UNIX_SOCKETS
struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
bool abstract);
CURLcode Curl_unix2addr(const char *path, bool abstract,
struct Curl_addrinfo **paddr);
#endif
#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \

View file

@ -423,10 +423,6 @@ CURLcode Curl_share_easy_unlink(struct Curl_easy *data)
if(&share->psl == data->psl)
data->psl = data->multi ? &data->multi->psl : NULL;
#endif
if(share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
Curl_dns_entry_unlink(data, &data->state.dns[0]);
Curl_dns_entry_unlink(data, &data->state.dns[1]);
}
share_unlink(&data->share, data, locked);
}

View file

@ -28,6 +28,7 @@
#include "cfilters.h"
#include "multiif.h"
#include "cf-dns.h"
#include "cf-socket.h"
#include "connect.h"
#include "http2.h"
@ -349,7 +350,6 @@ static const char * const Curl_trc_mstate_names[] = {
"PENDING",
"SETUP",
"CONNECT",
"RESOLVING",
"CONNECTING",
"PROTOCONNECT",
"PROTOCONNECTING",
@ -548,6 +548,7 @@ struct trc_cft_def {
};
static struct trc_cft_def trc_cfts[] = {
{ &Curl_cft_dns, TRC_CT_NETWORK },
{ &Curl_cft_tcp, TRC_CT_NETWORK },
{ &Curl_cft_udp, TRC_CT_NETWORK },
{ &Curl_cft_unix, TRC_CT_NETWORK },

View file

@ -443,35 +443,22 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
static struct Curl_dns_entry *
dnscache_entry_create(struct Curl_easy *data,
struct Curl_addrinfo **paddr,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
size_t hostlen, /* length or zero */
size_t hostlen,
uint16_t port,
uint8_t ip_version,
bool permanent)
{
struct Curl_dns_entry *dns = NULL;
#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses && paddr) {
CURLcode result = Curl_shuffle_addr(data, paddr);
if(result)
goto out;
}
#else
(void)data;
#endif
if(!hostlen)
hostlen = strlen(hostname);
/* Create a new cache entry */
/* Create a new cache entry, struct already has the hostname NUL */
dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
if(!dns)
goto out;
dns->refcount = 1; /* the cache has the first reference */
dns->addr = paddr ? *paddr : NULL; /* this is the address(es) */
if(permanent) {
dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
@ -484,11 +471,42 @@ dnscache_entry_create(struct Curl_easy *data,
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
/* Take the given address lists into the entry */
if(paddr1 && *paddr1) {
dns->addr = *paddr1;
*paddr1 = NULL;
}
if(paddr2 && *paddr2) {
struct Curl_addrinfo **phead = &dns->addr;
while(*phead)
phead = &(*phead)->ai_next;
*phead = *paddr2;
*paddr2 = NULL;
}
#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses && dns->addr) {
CURLcode result = Curl_shuffle_addr(data, &dns->addr);
if(result) {
/* free without lock, we are the sole owner */
dnscache_entry_free(dns);
dns = NULL;
goto out;
}
}
#else
(void)data;
#endif
out:
if(paddr) {
if(!dns)
Curl_freeaddrinfo(*paddr);
*paddr = NULL;
if(paddr1 && *paddr1) {
Curl_freeaddrinfo(*paddr1);
*paddr1 = NULL;
}
if(paddr2 && *paddr2) {
Curl_freeaddrinfo(*paddr2);
*paddr2 = NULL;
}
return dns;
}
@ -500,7 +518,20 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
uint16_t port,
uint8_t ip_version)
{
return dnscache_entry_create(data, paddr, hostname, 0,
return dnscache_entry_create(data, paddr, NULL, hostname,
hostname ? strlen(hostname) : 0,
port, ip_version, FALSE);
}
struct Curl_dns_entry *
Curl_dnscache_mk_entry2(struct Curl_easy *data,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
uint16_t port, uint8_t ip_version)
{
return dnscache_entry_create(data, paddr1, paddr2, hostname,
hostname ? strlen(hostname) : 0,
port, ip_version, FALSE);
}
@ -509,7 +540,7 @@ dnscache_add_addr(struct Curl_easy *data,
struct Curl_dnscache *dnscache,
struct Curl_addrinfo **paddr,
const char *hostname,
size_t hlen, /* length or zero */
size_t hlen,
uint16_t port,
uint8_t ip_version,
bool permanent)
@ -519,7 +550,7 @@ dnscache_add_addr(struct Curl_easy *data,
struct Curl_dns_entry *dns;
struct Curl_dns_entry *dns2;
dns = dnscache_entry_create(data, paddr, hostname, hlen, port,
dns = dnscache_entry_create(data, paddr, NULL, hostname, hlen, port,
ip_version, permanent);
if(!dns)
return NULL;
@ -576,7 +607,7 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
return CURLE_FAILED_INIT;
/* put this new host in the cache */
dns = dnscache_add_addr(data, dnscache, NULL, host, 0,
dns = dnscache_add_addr(data, dnscache, NULL, host, strlen(host),
port, ip_version, FALSE);
if(dns) {
/* release the returned reference; the cache itself will keep the
@ -589,6 +620,20 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
struct Curl_dns_entry *dns)
{
if(!dns)
return NULL;
else {
struct Curl_dnscache *dnscache = dnscache_get(data);
dnscache_lock(data, dnscache);
dns->refcount++;
dnscache_unlock(data, dnscache);
return dns;
}
}
/*
* Curl_dns_entry_unlink() releases a reference to the given cached DNS entry.
* When the reference count reaches 0, the entry is destroyed. It is important

View file

@ -68,6 +68,18 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
uint16_t port,
uint8_t ip_version);
struct Curl_dns_entry *
Curl_dnscache_mk_entry2(struct Curl_easy *data,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
uint16_t port, uint8_t ip_version);
/* Increase the ref counter and return it for storing in another place.
* May be called with NULL, in which case it returns NULL. */
struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
struct Curl_dns_entry *dns);
/* unlink a dns entry, frees all resources if it was the last reference.
* Always clears `*pdns`` */
void Curl_dns_entry_unlink(struct Curl_easy *data,

View file

@ -1094,8 +1094,6 @@ void curl_easy_reset(CURL *d)
Curl_meta_reset(data);
/* clear any resolve data */
Curl_async_shutdown(data);
Curl_dns_entry_unlink(data, &data->state.dns[0]);
Curl_dns_entry_unlink(data, &data->state.dns[1]);
/* zero out UserDefined data: */
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));

View file

@ -2241,6 +2241,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
error:
Curl_dns_entry_unlink(data, &dns);
curlx_free(newhost);
return result;
}

View file

@ -111,73 +111,6 @@
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
#ifdef CURLVERBOSE
static void show_resolve_info(struct Curl_easy *data,
struct Curl_dns_entry *dns)
{
const struct Curl_addrinfo *a;
CURLcode result = CURLE_OK;
#ifdef CURLRES_IPV6
struct dynbuf out[2];
#else
struct dynbuf out[1];
#endif
DEBUGASSERT(data);
DEBUGASSERT(dns);
if(!data->set.verbose ||
/* ignore no name or numerical IP addresses */
!dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
return;
a = dns->addr;
infof(data, "Host %s:%d was resolved.",
(dns->hostname[0] ? dns->hostname : "(none)"), dns->port);
curlx_dyn_init(&out[0], 1024);
#ifdef CURLRES_IPV6
curlx_dyn_init(&out[1], 1024);
#endif
while(a) {
if(
#ifdef CURLRES_IPV6
a->ai_family == PF_INET6 ||
#endif
a->ai_family == PF_INET) {
char buf[MAX_IPADR_LEN];
struct dynbuf *d = &out[(a->ai_family != PF_INET)];
Curl_printable_address(a, buf, sizeof(buf));
if(curlx_dyn_len(d))
result = curlx_dyn_addn(d, ", ", 2);
if(!result)
result = curlx_dyn_add(d, buf);
if(result) {
infof(data, "too many IP, cannot show");
goto fail;
}
}
a = a->ai_next;
}
#ifdef CURLRES_IPV6
infof(data, "IPv6: %s",
(curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
#endif
infof(data, "IPv4: %s",
(curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
fail:
curlx_dyn_free(&out[0]);
#ifdef CURLRES_IPV6
curlx_dyn_free(&out[1]);
#endif
}
#else
#define show_resolve_info(x, y) Curl_nop_stmt
#endif
/*
* Curl_printable_address() stores a printable version of the 1st address
* given in the 'ai' argument. The result will be stored in the buf that is
@ -400,7 +333,196 @@ static CURLcode hostip_async_new(struct Curl_easy *data,
data->state.async = async;
return CURLE_OK;
}
static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = data->state.async;
CURLcode result;
/* If async resolving is ongoing, this must be set */
if(!async)
return CURLE_FAILED_INIT;
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh)
result = Curl_doh_take_result(data, pdns);
else
#endif
result = Curl_async_take_result(data, async, pdns);
if(result == CURLE_AGAIN)
result = CURLE_OK;
else if(result)
Curl_resolver_error(data, NULL);
else
DEBUGASSERT(*pdns);
return result;
}
const struct Curl_addrinfo *
Curl_resolv_get_ai(struct Curl_easy *data, int ai_family,
unsigned int index)
{
#ifdef CURLRES_ASYNCH
struct Curl_resolv_async *async = data->state.async;
if(async)
return Curl_async_get_ai(data, async, ai_family, index);
#else
(void)data;
(void)ai_family;
(void)index;
#endif
return NULL;
}
#endif /* USE_CURL_ASYNC */
static CURLcode hostip_resolv_announce(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms)
{
if(data->set.resolver_start) {
void *resolver = NULL;
int st;
#ifdef CURLRES_ASYNCH
CURLcode result;
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
return result;
}
result = Curl_async_get_impl(data, data->state.async, &resolver);
if(result)
return result;
#else
(void)hostname;
(void)port;
(void)ip_version;
(void)timeout_ms;
(void)transport;
#endif
Curl_set_in_callback(data, TRUE);
st = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
Curl_set_in_callback(data, FALSE);
if(st) {
return CURLE_ABORTED_BY_CALLBACK;
}
}
return CURLE_OK;
}
static CURLcode hostip_resolv_start(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
bool allowDOH,
struct Curl_dns_entry **pdns)
{
struct Curl_addrinfo *addr = NULL;
size_t hostname_len;
CURLcode result = CURLE_OK;
*pdns = NULL;
/* really need to start a resolve operation */
result = hostip_resolv_announce(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto out;
/* Check for "known" things to resolve ourselves. */
#ifndef USE_RESOLVE_ON_IPS
if(Curl_is_ipaddr(hostname)) {
/* shortcut literal IP addresses, if we are not told to resolve them. */
result = Curl_str2addr(hostname, port, &addr);
goto out;
}
#endif
hostname_len = strlen(hostname);
if(curl_strequal(hostname, "localhost") ||
curl_strequal(hostname, "localhost.") ||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
addr = get_localhost(port, hostname);
if(!addr)
result = CURLE_OUT_OF_MEMORY;
goto out;
}
#ifndef CURL_DISABLE_DOH
if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto out;
}
result = Curl_doh(data, data->state.async);
goto out;
}
#else
(void)allowDOH;
#endif
/* Can we provide the requested IP specifics in resolving? */
if(!can_resolve_ip_version(data, ip_version)) {
result = CURLE_COULDNT_RESOLVE_HOST;
goto out;
}
#ifdef CURLRES_ASYNCH
(void)addr;
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto out;
}
result = Curl_async_getaddrinfo(data, data->state.async);
if(result == CURLE_AGAIN) {
/* the answer might be there already. Check. */
CURLcode r2 = hostip_resolv_take_result(data, pdns);
if(r2)
result = r2;
else if(*pdns)
result = CURLE_OK;
}
#else
addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport);
if(!addr)
result = CURLE_COULDNT_RESOLVE_HOST;
#endif
out:
if(!result) {
if(addr) {
/* we got a response, create a dns entry, add to cache, return */
DEBUGASSERT(!*pdns);
*pdns = Curl_dnscache_mk_entry(data, &addr, hostname, port, ip_version);
if(!*pdns)
result = CURLE_OUT_OF_MEMORY;
}
else if(!*pdns)
result = CURLE_AGAIN;
}
else if(*pdns)
Curl_dns_entry_unlink(data, pdns);
else if(addr)
Curl_freeaddrinfo(addr);
return result;
}
static CURLcode hostip_resolv(struct Curl_easy *data,
const char *hostname,
@ -409,15 +531,13 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
uint8_t transport,
timediff_t timeout_ms,
bool allowDOH,
struct Curl_dns_entry **entry)
struct Curl_dns_entry **pdns)
{
struct Curl_dns_entry *dns = NULL;
struct Curl_addrinfo *addr = NULL;
bool respwait = FALSE;
size_t hostname_len;
CURLcode r2, result = CURLE_COULDNT_RESOLVE_HOST;
CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
bool cache_dns = FALSE;
*entry = NULL;
*pdns = NULL;
#ifdef USE_CURL_ASYNC
if(data->state.async)
@ -439,7 +559,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
(curl_strequal(&hostname[hostname_len - 6], ".onion") ||
curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
failf(data, "Not resolving .onion address (RFC 7686)");
goto error;
goto out;
}
#ifdef DEBUGBUILD
@ -449,139 +569,50 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
getenv("CURL_DBG_RESOLV_FAIL_IPV6")) {
infof(data, "DEBUG fail ipv6 resolve");
result = Curl_resolver_error(data, NULL);
goto error;
goto out;
}
#endif
/* Let's check our DNS cache first */
r2 = Curl_dnscache_get(data, hostname, port, ip_version, &dns);
if(dns) {
result = Curl_dnscache_get(data, hostname, port, ip_version, pdns);
if(*pdns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
result = CURLE_OK;
goto out;
}
else if(r2) {
DEBUGASSERT(!dns);
else if(result) {
infof(data, "Negative DNS entry");
result = Curl_resolver_error(data, NULL);
goto error;
}
/* No luck, we need to resolve hostname. Notify user callback. */
if(data->set.resolver_start) {
void *resolver = NULL;
int st;
#ifdef CURLRES_ASYNCH
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto error;
}
result = Curl_async_get_impl(data, data->state.async, &resolver);
if(result)
goto error;
#endif
Curl_set_in_callback(data, TRUE);
st = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
Curl_set_in_callback(data, FALSE);
if(st) {
result = CURLE_ABORTED_BY_CALLBACK;
goto error;
}
}
if(Curl_is_ipaddr(hostname)) {
#ifndef USE_RESOLVE_ON_IPS
/* shortcut literal IP addresses, if we are not told to resolve them. */
result = Curl_str2addr(hostname, port, &addr);
if(result)
goto error;
goto out;
#endif
}
if(curl_strequal(hostname, "localhost") ||
curl_strequal(hostname, "localhost.") ||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
addr = get_localhost(port, hostname);
result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
#ifndef CURL_DISABLE_DOH
else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto error;
}
result = Curl_doh(data, data->state.async);
respwait = TRUE;
}
#endif
else {
/* Can we provide the requested IP specifics in resolving? */
if(!can_resolve_ip_version(data, ip_version)) {
result = CURLE_COULDNT_RESOLVE_HOST;
goto error;
}
#ifdef CURLRES_ASYNCH
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto error;
}
result = Curl_async_getaddrinfo(data, data->state.async);
respwait = TRUE;
#else
respwait = FALSE; /* no async waiting here */
addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport);
if(addr)
result = CURLE_OK;
#endif
/* No luck, we need to start resolving. */
cache_dns = TRUE;
result = hostip_resolv_start(data, hostname, port, ip_version,
transport, timeout_ms, allowDOH, pdns);
}
out:
/* We either have found a `dns` or looked up the `addr` or `respwait` is set
* for an async operation. Everything else is a failure to resolve. */
if(result)
;
else if(dns) {
*entry = dns;
return CURLE_OK;
}
else if(addr) {
/* we got a response, create a dns entry, add to cache, return */
dns = Curl_dnscache_mk_entry(data, &addr, hostname, port, ip_version);
if(!dns || Curl_dnscache_add(data, dns)) {
/* this is OOM or similar, do not store such negative resolves */
result = CURLE_OUT_OF_MEMORY;
goto error;
}
show_resolve_info(data, dns);
*entry = dns;
return CURLE_OK;
}
else if(respwait) {
#ifdef USE_CURL_ASYNC
result = Curl_resolv_take_result(data, &dns);
if(!result) {
*entry = dns;
return dns ? CURLE_OK : CURLE_AGAIN;
}
#endif
result = CURLE_COULDNT_RESOLVE_HOST;
}
error:
if(dns)
Curl_dns_entry_unlink(data, &dns);
if(result && (result != CURLE_AGAIN)) {
Curl_dns_entry_unlink(data, pdns);
Curl_async_shutdown(data);
if(result == CURLE_COULDNT_RESOLVE_HOST)
if((result == CURLE_COULDNT_RESOLVE_HOST) ||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
if(cache_dns)
Curl_dnscache_add_negative(data, hostname, port, ip_version);
DEBUGASSERT(result);
failf(data, "Could not resolve: %s:%u", hostname, port);
}
else {
failf(data, "Error %d resolving %s:%u", result, hostname, port);
}
}
else if(cache_dns && *pdns) {
result = Curl_dnscache_add(data, *pdns);
if(result)
Curl_dns_entry_unlink(data, pdns);
}
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x, timeout_ms=%" FMT_TIMEDIFF_T
") -> %d, dns %sfound",
hostname, port, ip_version, timeout_ms, result,
*pdns ? "" : "not ");
return result;
}
@ -841,33 +872,32 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
return CURLE_OK;
}
else if(result) {
result = Curl_resolver_error(data, NULL);
return result;
}
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh)
result = Curl_doh_take_result(data, pdns);
else
#endif
result = Curl_async_take_result(data, async, pdns);
if(!result) {
DEBUGASSERT(*pdns);
show_resolve_info(data, *pdns);
}
else if(result == CURLE_AGAIN)
result = CURLE_OK;
else {
Curl_dnscache_add_negative(data, async->hostname, async->port,
async->ip_version);
Curl_async_shutdown(data);
Curl_resolver_error(data, NULL);
return Curl_resolver_error(data, NULL);
}
result = hostip_resolv_take_result(data, pdns);
if(*pdns) {
/* Add to cache */
result = Curl_dnscache_add(data, *pdns);
if(result)
Curl_dns_entry_unlink(data, pdns);
}
else if((result == CURLE_COULDNT_RESOLVE_HOST) ||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
Curl_dnscache_add_negative(data, async->hostname,
async->port, async->ip_version);
failf(data, "Could not resolve: %s:%u", async->hostname, async->port);
}
else if(result) {
failf(data, "Error %d resolving %s:%u",
result, async->hostname, async->port);
}
return result;
}
#endif
#endif /* USE_CURL_ASYNC */
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
struct easy_pollset *ps)
@ -911,3 +941,30 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail)
detail ? " (" : "", detail ? detail : "", detail ? ")" : "");
return result;
}
#ifdef USE_UNIX_SOCKETS
CURLcode Curl_resolv_unix(struct Curl_easy *data,
const char *unix_path,
bool abstract_path,
struct Curl_dns_entry **pdns)
{
struct Curl_addrinfo *addr;
CURLcode result;
DEBUGASSERT(unix_path);
*pdns = NULL;
result = Curl_unix2addr(unix_path, abstract_path, &addr);
if(result) {
if(result == CURLE_TOO_LARGE) {
/* Long paths are not supported for now */
failf(data, "Unix socket path too long: '%s'", unix_path);
result = CURLE_COULDNT_RESOLVE_HOST;
}
return result;
}
*pdns = Curl_dnscache_mk_entry(data, &addr, NULL, 0, 0);
return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
#endif /* USE_UNIX_SOCKETS */

View file

@ -111,8 +111,12 @@ CURLcode Curl_resolv_timeout(struct Curl_easy *data,
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
const struct Curl_addrinfo *
Curl_resolv_get_ai(struct Curl_easy *data, int ai_family,
unsigned int index);
#else
#define Curl_resolv_take_result(x, y) CURLE_NOT_BUILT_IN
#define Curl_resolv_get_ai(x,y,z) NULL
#endif
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
@ -134,4 +138,11 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
#endif
#ifdef USE_UNIX_SOCKETS
CURLcode Curl_resolv_unix(struct Curl_easy *data,
const char *unix_path,
bool abstract_path,
struct Curl_dns_entry **pdns);
#endif
#endif /* HEADER_CURL_HOSTIP_H */

View file

@ -140,7 +140,6 @@ static void mstate(struct Curl_easy *data, CURLMstate state
NULL, /* PENDING */
NULL, /* SETUP */
Curl_init_CONNECT, /* CONNECT */
NULL, /* RESOLVING */
NULL, /* CONNECTING */
NULL, /* PROTOCONNECT */
NULL, /* PROTOCONNECTING */
@ -654,8 +653,6 @@ static void multi_done_locked(struct connectdata *conn,
data->state.done = TRUE; /* called now! */
data->state.recent_conn_id = conn->connection_id;
Curl_dns_entry_unlink(data, &data->state.dns[0]); /* done with this */
Curl_dns_entry_unlink(data, &data->state.dns[1]);
Curl_dnscache_prune(data);
if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) {
@ -731,9 +728,12 @@ static CURLcode multi_done(struct Curl_easy *data,
else
result = status;
if(result != CURLE_ABORTED_BY_CALLBACK) {
/* avoid this if we already aborted by callback to avoid this calling
another callback */
if(data->mstate > MSTATE_CONNECTING &&
(result != CURLE_ABORTED_BY_CALLBACK)) {
/* avoid this if
* - the transfer has not connected
* - we already aborted by callback to avoid this calling another callback
*/
int rc = Curl_pgrsDone(data);
if(!result && rc)
result = CURLE_ABORTED_BY_CALLBACK;
@ -1158,11 +1158,10 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data,
/* nothing to poll for yet */
break;
case MSTATE_RESOLVING:
result = Curl_resolv_pollset(data, ps);
break;
case MSTATE_CONNECTING:
if(data->conn && !data->conn->bits.dns_resolved)
result = Curl_resolv_pollset(data, ps);
if(!result)
result = mstate_connecting_pollset(data, ps);
break;
@ -1811,13 +1810,9 @@ static bool multi_handle_timeout(struct Curl_easy *data,
since = data->progress.t_startsingle;
else
since = data->progress.t_startop;
if(data->mstate == MSTATE_RESOLVING)
failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
" milliseconds",
curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
else if(data->mstate == MSTATE_CONNECTING)
failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
" milliseconds",
if(data->mstate == MSTATE_CONNECTING)
failf(data, "%s timed out after %" FMT_TIMEDIFF_T " milliseconds",
data->conn->bits.dns_resolved ? "Connection" : "Resolving",
curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
else {
struct SingleRequest *k = &data->req;
@ -2309,55 +2304,6 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data,
return mresult;
}
static CURLMcode state_resolving(struct Curl_multi *multi,
struct Curl_easy *data,
bool *stream_errorp,
CURLcode *resultp)
{
struct Curl_dns_entry *dns = NULL;
CURLMcode mresult = CURLM_OK;
CURLcode result;
result = Curl_resolv_take_result(data, &dns);
CURL_TRC_DNS(data, "Curl_resolv_take_result() -> %d, %s",
result, dns ? "found" : "missing");
/* Update sockets here, because the socket(s) may have been closed and the
application thus needs to be told, even if it is likely that the same
socket(s) will again be used further down. If the name has not yet been
resolved, it is likely that new sockets have been opened in an attempt to
contact another resolver. */
mresult = Curl_multi_ev_assess_xfer(multi, data);
if(mresult)
return mresult;
if(dns) {
bool connected;
/* Perform the next step in the connection phase, and then move on to the
WAITCONNECT state */
result = Curl_setup_conn(data, dns, &connected);
if(result) {
/* setup failed, terminate connection */
struct connectdata *conn = data->conn;
Curl_detach_connection(data);
Curl_conn_terminate(data, conn, TRUE);
goto out;
}
/* call again please so that we get the next socket setup */
mresult = CURLM_CALL_MULTI_PERFORM;
multistate(data, connected ? MSTATE_PROTOCONNECT : MSTATE_CONNECTING);
}
out:
if(result)
/* failure detected */
*stream_errorp = TRUE;
*resultp = result;
return mresult;
}
static CURLMcode state_connect(struct Curl_multi *multi,
struct Curl_easy *data,
CURLcode *resultp)
@ -2365,9 +2311,8 @@ static CURLMcode state_connect(struct Curl_multi *multi,
/* Connect. We want to get a connection identifier filled in. This state can
be entered from SETUP and from PENDING. */
bool connected;
bool async;
CURLMcode mresult = CURLM_OK;
CURLcode result = Curl_connect(data, &async, &connected);
CURLcode result = Curl_connect(data, &connected);
if(result == CURLE_NO_CONNECTION_AVAILABLE) {
/* There was no connection available. We will go to the pending state and
wait for an available connection. */
@ -2383,10 +2328,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
process_pending_handles(data->multi);
if(!result) {
if(async)
/* We are now waiting for an asynchronous name lookup */
multistate(data, MSTATE_RESOLVING);
else {
/* after the connect has been sent off, go WAITCONNECT unless the
protocol connect is already done and we can go directly to WAITDO or
DO! */
@ -2404,7 +2345,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
multistate(data, MSTATE_CONNECTING);
}
}
}
*resultp = result;
return mresult;
}
@ -2451,7 +2391,7 @@ static CURLcode is_finished(struct Curl_multi *multi,
return result;
}
/* if there is still a connection to use, call the progress function */
else if(data->conn) {
else if(data->conn && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) {
result = Curl_pgrsUpdate(data);
if(result) {
/* aborted due to progress callback return code must close the
@ -2608,15 +2548,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
mresult = state_connect(multi, data, &result);
break;
case MSTATE_RESOLVING:
/* awaiting an asynch name resolve to complete */
mresult = state_resolving(multi, data, &stream_error, &result);
break;
case MSTATE_CONNECTING:
/* awaiting a completion of an asynch TCP connect */
DEBUGASSERT(data->conn);
if(!Curl_xfer_recv_is_paused(data)) {
if(!data->conn) {
DEBUGASSERT(0);
result = CURLE_FAILED_INIT;
break;
}
else if(!Curl_xfer_recv_is_paused(data)) {
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
if(connected && !result) {
if(!data->conn->bits.reuse &&
@ -2629,6 +2568,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else if(result) {
/* failure detected */
CURL_TRC_M(data, "connect failed -> %d", result);
multi_posttransfer(data);
multi_done(data, result, TRUE);
stream_error = TRUE;

View file

@ -53,7 +53,6 @@ typedef enum {
MSTATE_PENDING, /* no connections, waiting for one */
MSTATE_SETUP, /* start a new transfer */
MSTATE_CONNECT, /* resolve/connect has been sent off */
MSTATE_RESOLVING, /* awaiting the resolve to finalize */
MSTATE_CONNECTING, /* awaiting the TCP connect to finalize */
MSTATE_PROTOCONNECT, /* initiate protocol connect procedure */
MSTATE_PROTOCONNECTING, /* completing the protocol-specific connect phase */

170
lib/url.c
View file

@ -132,10 +132,6 @@ static void data_priority_cleanup(struct Curl_easy *data);
# error READBUFFER_SIZE is too small
#endif
#if !defined(CURL_DISABLE_PROXY) && defined(USE_UNIX_SOCKETS)
#define UNIX_SOCKET_PREFIX "localhost"
#endif
/* Reject URLs exceeding this length */
#define MAX_URL_LEN 0xffff
@ -244,8 +240,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
/* release any resolve information this transfer kept */
Curl_async_destroy(data);
Curl_dns_entry_unlink(data, &data->state.dns[0]); /* done with this */
Curl_dns_entry_unlink(data, &data->state.dns[1]);
data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
@ -2951,118 +2945,6 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
return result;
}
#ifdef USE_UNIX_SOCKETS
static CURLcode resolve_unix(struct Curl_easy *data,
struct connectdata *conn,
const char *unix_path,
struct Curl_dns_entry **pdns)
{
struct Curl_dns_entry *hostaddr;
bool longpath = FALSE;
DEBUGASSERT(unix_path);
*pdns = NULL;
/* Unix domain sockets are local. The host gets ignored, use the specified
* domain socket address. Do not cache "DNS entries". There is no DNS
* involved and we already have the file system path available. */
hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry));
if(!hostaddr)
return CURLE_OUT_OF_MEMORY;
hostaddr->addr = Curl_unix2addr(unix_path, &longpath,
(bool)conn->bits.abstract_unix_socket);
if(!hostaddr->addr) {
if(longpath)
/* Long paths are not supported for now */
failf(data, "Unix socket path too long: '%s'", unix_path);
curlx_free(hostaddr);
return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
}
hostaddr->refcount = 1; /* connection is the only one holding this */
*pdns = hostaddr;
return CURLE_OK;
}
#endif
/*************************************************************
* Resolve the address of the server or proxy
*************************************************************/
static CURLcode resolve_server(struct Curl_easy *data,
struct connectdata *conn,
struct Curl_dns_entry **pdns)
{
struct hostname *ehost;
uint16_t eport;
timediff_t timeout_ms = Curl_timeleft_ms(data);
const char *peertype = "host";
CURLcode result;
*pdns = NULL;
#ifdef USE_UNIX_SOCKETS
{
const char *unix_path = conn->unix_domain_socket;
#ifndef CURL_DISABLE_PROXY
if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
!strncmp(UNIX_SOCKET_PREFIX "/",
conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
#endif
if(unix_path) {
/* This only works if previous transport is TRNSPRT_TCP. Check it? */
conn->transport_wanted = TRNSPRT_UNIX;
return resolve_unix(data, conn, unix_path, pdns);
}
}
#endif
#ifndef CURL_DISABLE_PROXY
if(CONN_IS_PROXIED(conn)) {
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
&conn->http_proxy.host;
eport = conn->bits.socksproxy ? conn->socks_proxy.port :
conn->http_proxy.port;
peertype = "proxy";
}
else
#endif
{
ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
eport = conn->bits.conn_to_port ?
conn->conn_to_port : (uint16_t)conn->remote_port;
}
result = Curl_resolv(data, ehost->name, eport,
conn->ip_version, conn->transport_wanted,
timeout_ms, pdns);
DEBUGASSERT(!result || !*pdns);
if(!result) { /* resolved right away, either sync or from dnscache */
DEBUGASSERT(*pdns);
return CURLE_OK;
}
else if(result == CURLE_AGAIN) { /* async resolv in progress */
return CURLE_AGAIN;
}
else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */
failf(data, "Failed to resolve %s '%s' with timeout after %"
FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
curlx_ptimediff_ms(Curl_pgrs_now(data),
&data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
else {
DEBUGASSERT(result);
failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
return result;
}
}
static void url_move_hostname(struct hostname *dest, struct hostname *src)
{
Curl_safefree(dest->rawalloc);
@ -3308,6 +3190,10 @@ static CURLcode url_create_needle(struct Curl_easy *data,
needle->recv[SECONDARYSOCKET] = Curl_cf_recv;
needle->send[SECONDARYSOCKET] = Curl_cf_send;
needle->bits.tcp_fastopen = data->set.tcp_fastopen;
#ifdef USE_UNIX_SOCKETS
if(Curl_conn_get_unix_path(needle))
needle->transport_wanted = TRNSPRT_UNIX;
#endif
}
out:
@ -3542,9 +3428,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
DEBUGASSERT(dns);
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(!conn->bits.reuse)
result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns,
CURL_CF_SSL_DEFAULT);
@ -3556,15 +3439,12 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
return result;
}
CURLcode Curl_connect(struct Curl_easy *data,
bool *asyncp,
bool *protocol_done)
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
{
CURLcode result;
struct connectdata *conn;
*asyncp = FALSE; /* assume synchronous resolves by default */
*protocol_done = FALSE;
*pconnected = FALSE;
/* Set the request to virgin state based on transfer settings */
Curl_req_hard_reset(&data->req, data);
@ -3573,44 +3453,32 @@ CURLcode Curl_connect(struct Curl_easy *data,
result = url_find_or_create_conn(data);
conn = data->conn;
if(result == CURLE_NO_CONNECTION_AVAILABLE) {
DEBUGASSERT(!conn);
return result;
}
if(result)
goto out;
if(!result) {
DEBUGASSERT(conn);
Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(conn->bits.reuse) {
if(conn->attached_xfers > 1)
/* multiplexed */
*protocol_done = TRUE;
*pconnected = TRUE;
}
else if(conn->scheme->flags & PROTOPT_NONETWORK) {
*asyncp = FALSE;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
*protocol_done = TRUE;
*pconnected = TRUE;
}
else {
/*************************************************************
* Resolve the address of the server or proxy
*************************************************************/
struct Curl_dns_entry *dns;
result = resolve_server(data, conn, &dns);
if(!result) {
DEBUGASSERT(dns);
/* DNS resolution is done: that is either because this is a reused
connection, in which case DNS was unnecessary, or because DNS
really did finish already (synch resolver/fast async resolve) */
result = Curl_setup_conn(data, dns, protocol_done);
}
else if(result == CURLE_AGAIN) {
*asyncp = TRUE;
result = CURLE_OK;
}
}
result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL,
CURL_CF_SSL_DEFAULT);
if(!result)
result = Curl_headers_init(data);
CURL_TRC_M(data, "Curl_setup_conn() -> %d", result);
}
out:
if(result == CURLE_NO_CONNECTION_AVAILABLE)
DEBUGASSERT(!conn);
if(result && conn) {
/* We are not allowed to return failure with memory left allocated in the
connectdata struct, free those here */

View file

@ -36,9 +36,7 @@ void Curl_init_userdefined(struct Curl_easy *data);
void Curl_freeset(struct Curl_easy *data);
CURLcode Curl_uc_to_curlcode(CURLUcode uc);
CURLcode Curl_close(struct Curl_easy **datap); /* opposite of Curl_open() */
CURLcode Curl_connect(struct Curl_easy *data,
bool *asyncp,
bool *protocol_done);
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected);
CURLcode Curl_setup_conn(struct Curl_easy *data,
struct Curl_dns_entry *dns,
bool *protocol_done);

View file

@ -310,6 +310,7 @@ struct ConnectBits {
BIT(shutdown_handler); /* connection shutdown: handler shut down */
BIT(shutdown_filters); /* connection shutdown: filters shut down */
BIT(in_cpool); /* connection is kept in a connection pool */
BIT(dns_resolved); /* DNS records for connection were resolved */
};
struct hostname {
@ -720,7 +721,6 @@ struct UrlState {
struct auth authhost; /* auth details for host */
struct auth authproxy; /* auth details for proxy */
struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */
#ifdef USE_CURL_ASYNC
struct Curl_resolv_async *async; /* asynchronous name resolver data */
uint32_t next_async_id; /* id of the next async resolve operation */

View file

@ -2940,7 +2940,7 @@ struct Curl_cftype Curl_cft_http3 = {
CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai)
struct Curl_sockaddr_ex *addr)
{
struct cf_ngtcp2_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -2958,7 +2958,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC);
result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;

View file

@ -53,7 +53,7 @@ void Curl_ngtcp2_ver(char *p, size_t len);
CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
struct Curl_sockaddr_ex *addr);
#endif
#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */

View file

@ -1633,7 +1633,7 @@ struct Curl_cftype Curl_cft_http3 = {
CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai)
struct Curl_sockaddr_ex *addr)
{
struct cf_quiche_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1651,7 +1651,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC);
result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;

View file

@ -38,7 +38,7 @@ void Curl_quiche_ver(char *p, size_t len);
CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
struct Curl_sockaddr_ex *addr);
#endif

View file

@ -702,15 +702,15 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
(void)transport;
DEBUGASSERT(transport == TRNSPRT_QUIC);
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
return Curl_cf_ngtcp2_create(pcf, data, conn, addr);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_create(pcf, data, conn, ai);
return Curl_cf_quiche_create(pcf, data, conn, addr);
#else
*pcf = NULL;
(void)data;

View file

@ -42,7 +42,7 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
extern struct Curl_cftype Curl_cft_http3;

View file

@ -188,7 +188,7 @@ RETR %TESTNUMBER
QUIT
</protocol>
<limits>
Allocations: 170
Allocations: 180
Maximum allocated: 150000
</limits>
</verify>

View file

@ -72,7 +72,7 @@ via: 1.1 nghttpx
s/^server: nghttpx.*\r?\n//
</stripfile>
<limits>
Allocations: 160
Allocations: 170
Maximum allocated: 1800000
</limits>
</verify>

View file

@ -54,7 +54,7 @@ Accept: */*
</protocol>
<limits>
Allocations: 82
Allocations: 90
Maximum allocated: 33400
</limits>
</verify>

View file

@ -93,128 +93,128 @@ CURLOPT_SHARE
lock: share [Pigs in space]: 0
unlock: share [Pigs in space]: 1
CURLOPT_COOKIELIST injected_and_clobbered
lock: cookie [Pigs in space]: 0
unlock: cookie [Pigs in space]: 1
CURLOPT_COOKIELIST ALL
lock: cookie [Pigs in space]: 2
unlock: cookie [Pigs in space]: 3
CURLOPT_COOKIELIST ALL
CURLOPT_COOKIELIST session
lock: cookie [Pigs in space]: 4
unlock: cookie [Pigs in space]: 5
CURLOPT_COOKIELIST session
CURLOPT_COOKIELIST injected
lock: cookie [Pigs in space]: 6
unlock: cookie [Pigs in space]: 7
CURLOPT_COOKIELIST injected
CURLOPT_COOKIELIST SESS
lock: cookie [Pigs in space]: 8
unlock: cookie [Pigs in space]: 9
CURLOPT_COOKIELIST SESS
CLEANUP
lock: cookie [Pigs in space]: 10
unlock: cookie [Pigs in space]: 11
CLEANUP
lock: cookie [Pigs in space]: 12
unlock: cookie [Pigs in space]: 13
lock: share [Pigs in space]: 14
unlock: share [Pigs in space]: 15
lock: share [Pigs in space]: 2
unlock: share [Pigs in space]: 3
*** run 1
CURLOPT_SHARE
lock: share [Pigs in space]: 16
unlock: share [Pigs in space]: 17
lock: share [Pigs in space]: 4
unlock: share [Pigs in space]: 5
PERFORM
lock: cookie [Pigs in space]: 12
unlock: cookie [Pigs in space]: 13
lock: cookie [Pigs in space]: 14
unlock: cookie [Pigs in space]: 15
lock: dns [Pigs in space]: 0
unlock: dns [Pigs in space]: 1
lock: dns [Pigs in space]: 2
unlock: dns [Pigs in space]: 3
lock: dns [Pigs in space]: 4
unlock: dns [Pigs in space]: 5
lock: cookie [Pigs in space]: 16
unlock: cookie [Pigs in space]: 17
lock: cookie [Pigs in space]: 18
unlock: cookie [Pigs in space]: 19
lock: cookie [Pigs in space]: 20
unlock: cookie [Pigs in space]: 21
lock: dns [Pigs in space]: 22
unlock: dns [Pigs in space]: 23
lock: dns [Pigs in space]: 24
unlock: dns [Pigs in space]: 25
lock: cookie [Pigs in space]: 22
unlock: cookie [Pigs in space]: 23
run 1: set cookie 1, 2 and 3
lock: dns [Pigs in space]: 6
unlock: dns [Pigs in space]: 7
CLEANUP
lock: cookie [Pigs in space]: 24
unlock: cookie [Pigs in space]: 25
lock: share [Pigs in space]: 6
unlock: share [Pigs in space]: 7
*** run 2
CURLOPT_SHARE
lock: share [Pigs in space]: 8
unlock: share [Pigs in space]: 9
PERFORM
lock: cookie [Pigs in space]: 26
unlock: cookie [Pigs in space]: 27
lock: cookie [Pigs in space]: 28
unlock: cookie [Pigs in space]: 29
lock: dns [Pigs in space]: 8
unlock: dns [Pigs in space]: 9
lock: dns [Pigs in space]: 10
unlock: dns [Pigs in space]: 11
lock: cookie [Pigs in space]: 30
unlock: cookie [Pigs in space]: 31
lock: cookie [Pigs in space]: 32
unlock: cookie [Pigs in space]: 33
run 1: set cookie 1, 2 and 3
lock: dns [Pigs in space]: 34
unlock: dns [Pigs in space]: 35
lock: dns [Pigs in space]: 36
unlock: dns [Pigs in space]: 37
lock: cookie [Pigs in space]: 34
unlock: cookie [Pigs in space]: 35
run 2: set cookie 4 and 5
lock: dns [Pigs in space]: 12
unlock: dns [Pigs in space]: 13
CLEANUP
lock: cookie [Pigs in space]: 36
unlock: cookie [Pigs in space]: 37
lock: share [Pigs in space]: 10
unlock: share [Pigs in space]: 11
*** run 3
CURLOPT_SHARE
lock: share [Pigs in space]: 12
unlock: share [Pigs in space]: 13
CURLOPT_COOKIEJAR
CURLOPT_COOKIELIST FLUSH
lock: cookie [Pigs in space]: 38
unlock: cookie [Pigs in space]: 39
lock: share [Pigs in space]: 40
unlock: share [Pigs in space]: 41
*** run 2
CURLOPT_SHARE
lock: share [Pigs in space]: 42
unlock: share [Pigs in space]: 43
PERFORM
lock: cookie [Pigs in space]: 40
unlock: cookie [Pigs in space]: 41
lock: dns [Pigs in space]: 14
unlock: dns [Pigs in space]: 15
lock: dns [Pigs in space]: 16
unlock: dns [Pigs in space]: 17
lock: cookie [Pigs in space]: 42
unlock: cookie [Pigs in space]: 43
lock: cookie [Pigs in space]: 44
unlock: cookie [Pigs in space]: 45
lock: cookie [Pigs in space]: 46
unlock: cookie [Pigs in space]: 47
lock: dns [Pigs in space]: 48
unlock: dns [Pigs in space]: 49
lock: cookie [Pigs in space]: 48
unlock: cookie [Pigs in space]: 49
lock: cookie [Pigs in space]: 50
unlock: cookie [Pigs in space]: 51
run 3: overwrite cookie 1 and 4, set cookie 6 with and without tailmatch
lock: dns [Pigs in space]: 18
unlock: dns [Pigs in space]: 19
CLEANUP
lock: cookie [Pigs in space]: 52
unlock: cookie [Pigs in space]: 53
lock: share [Pigs in space]: 14
unlock: share [Pigs in space]: 15
CURLOPT_SHARE
lock: share [Pigs in space]: 16
unlock: share [Pigs in space]: 17
CURLOPT_COOKIELIST ALL
lock: cookie [Pigs in space]: 54
unlock: cookie [Pigs in space]: 55
run 2: set cookie 4 and 5
lock: dns [Pigs in space]: 56
unlock: dns [Pigs in space]: 57
lock: dns [Pigs in space]: 58
unlock: dns [Pigs in space]: 59
CLEANUP
lock: cookie [Pigs in space]: 60
unlock: cookie [Pigs in space]: 61
lock: share [Pigs in space]: 62
unlock: share [Pigs in space]: 63
*** run 3
CURLOPT_SHARE
lock: share [Pigs in space]: 64
unlock: share [Pigs in space]: 65
CURLOPT_COOKIEJAR
CURLOPT_COOKIELIST FLUSH
lock: cookie [Pigs in space]: 66
unlock: cookie [Pigs in space]: 67
PERFORM
lock: cookie [Pigs in space]: 68
unlock: cookie [Pigs in space]: 69
lock: dns [Pigs in space]: 70
unlock: dns [Pigs in space]: 71
lock: cookie [Pigs in space]: 72
unlock: cookie [Pigs in space]: 73
lock: cookie [Pigs in space]: 74
unlock: cookie [Pigs in space]: 75
lock: cookie [Pigs in space]: 76
unlock: cookie [Pigs in space]: 77
lock: cookie [Pigs in space]: 78
unlock: cookie [Pigs in space]: 79
lock: cookie [Pigs in space]: 80
unlock: cookie [Pigs in space]: 81
run 3: overwrite cookie 1 and 4, set cookie 6 with and without tailmatch
lock: dns [Pigs in space]: 82
unlock: dns [Pigs in space]: 83
lock: dns [Pigs in space]: 84
unlock: dns [Pigs in space]: 85
CLEANUP
lock: cookie [Pigs in space]: 86
unlock: cookie [Pigs in space]: 87
lock: share [Pigs in space]: 88
unlock: share [Pigs in space]: 89
CURLOPT_SHARE
lock: share [Pigs in space]: 90
unlock: share [Pigs in space]: 91
CURLOPT_COOKIELIST ALL
lock: cookie [Pigs in space]: 92
unlock: cookie [Pigs in space]: 93
CURLOPT_COOKIEJAR
CURLOPT_COOKIELIST RELOAD
lock: cookie [Pigs in space]: 94
unlock: cookie [Pigs in space]: 95
lock: cookie [Pigs in space]: 96
unlock: cookie [Pigs in space]: 97
lock: cookie [Pigs in space]: 56
unlock: cookie [Pigs in space]: 57
lock: cookie [Pigs in space]: 58
unlock: cookie [Pigs in space]: 59
loaded cookies:
-----------------
www.host.foo.com FALSE / FALSE %days[400] test6 six_more
@ -229,13 +229,13 @@ loaded cookies:
try SHARE_CLEANUP...
SHARE_CLEANUP failed, correct
CLEANUP
lock: cookie [Pigs in space]: 98
unlock: cookie [Pigs in space]: 99
lock: share [Pigs in space]: 100
unlock: share [Pigs in space]: 101
lock: cookie [Pigs in space]: 60
unlock: cookie [Pigs in space]: 61
lock: share [Pigs in space]: 18
unlock: share [Pigs in space]: 19
SHARE_CLEANUP
lock: share [Pigs in space]: 102
unlock: share [Pigs in space]: 103
lock: share [Pigs in space]: 20
unlock: share [Pigs in space]: 21
GLOBAL_CLEANUP
</stdout>
<file name="%LOGDIR/jar%TESTNUMBER" mode="text">

View file

@ -35,7 +35,9 @@ struct t506_Tdata {
struct t506_userdata {
const char *text;
int counter;
int share_counter;
int dns_counter;
int cookie_counter;
};
static int locks[3];
@ -46,7 +48,7 @@ static void t506_test_lock(CURL *curl, curl_lock_data data,
{
const char *what;
struct t506_userdata *user = (struct t506_userdata *)useptr;
int locknum;
int locknum, *pcounter;
(void)curl;
(void)laccess;
@ -55,14 +57,17 @@ static void t506_test_lock(CURL *curl, curl_lock_data data,
case CURL_LOCK_DATA_SHARE:
what = "share";
locknum = 0;
pcounter = &user->share_counter;
break;
case CURL_LOCK_DATA_DNS:
what = "dns";
locknum = 1;
pcounter = &user->dns_counter;
break;
case CURL_LOCK_DATA_COOKIE:
what = "cookie";
locknum = 2;
pcounter = &user->cookie_counter;
break;
default:
curl_mfprintf(stderr, "lock: no such data: %d\n", data);
@ -76,8 +81,8 @@ static void t506_test_lock(CURL *curl, curl_lock_data data,
}
locks[locknum]++;
curl_mprintf("lock: %-6s [%s]: %d\n", what, user->text, user->counter);
user->counter++;
curl_mprintf("lock: %-6s [%s]: %d\n", what, user->text, *pcounter);
(*pcounter)++;
}
/* unlock callback */
@ -85,19 +90,22 @@ static void t506_test_unlock(CURL *curl, curl_lock_data data, void *useptr)
{
const char *what;
struct t506_userdata *user = (struct t506_userdata *)useptr;
int locknum;
int locknum, *pcounter;
(void)curl;
switch(data) {
case CURL_LOCK_DATA_SHARE:
what = "share";
locknum = 0;
pcounter = &user->share_counter;
break;
case CURL_LOCK_DATA_DNS:
what = "dns";
locknum = 1;
pcounter = &user->dns_counter;
break;
case CURL_LOCK_DATA_COOKIE:
what = "cookie";
pcounter = &user->cookie_counter;
locknum = 2;
break;
default:
@ -112,8 +120,8 @@ static void t506_test_unlock(CURL *curl, curl_lock_data data, void *useptr)
}
locks[locknum]--;
curl_mprintf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter);
user->counter++;
curl_mprintf("unlock: %-6s [%s]: %d\n", what, user->text, *pcounter);
(*pcounter)++;
}
/* build host entry */
@ -178,8 +186,8 @@ static CURLcode test_lib506(const char *URL)
const char *jar = libtest_arg2;
memset(&user, 0, sizeof(user));
user.text = "Pigs in space";
user.counter = 0;
curl_mprintf("GLOBAL_INIT\n");
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {

View file

@ -73,16 +73,14 @@ static CURLcode test_unit1620(const char *arg)
CURLcode result;
struct Curl_easy *empty;
enum dupstring i;
bool async = FALSE;
bool protocol_connect = FALSE;
bool connected = FALSE;
result = Curl_open(&empty);
if(result)
goto unit_test_abort;
fail_unless(result == CURLE_OK, "Curl_open() failed");
result = Curl_connect(empty, &async, &protocol_connect);
result = Curl_connect(empty, &connected);
fail_unless(result == CURLE_URL_MALFORMAT,
"Curl_connect() failed to return CURLE_URL_MALFORMAT");
@ -90,7 +88,7 @@ static CURLcode test_unit1620(const char *arg)
"empty->magic should be equal to CURLEASY_MAGIC_NUMBER");
/* double invoke to ensure no dependency on internal state */
result = Curl_connect(empty, &async, &protocol_connect);
result = Curl_connect(empty, &connected);
fail_unless(result == CURLE_URL_MALFORMAT,
"Curl_connect() failed to return CURLE_URL_MALFORMAT");

View file

@ -161,7 +161,7 @@ static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf,
static CURLcode cf_test_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
static const struct Curl_cftype cft_test = {
@ -195,7 +195,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
goto out;
}
ctx->idx = test_idx++;
ctx->ai_family = ai->ai_family;
ctx->ai_family = addr->family;
ctx->transport = transport;
ctx->started = curlx_now();
#ifdef USE_IPV6