diff --git a/lib/Makefile.inc b/lib/Makefile.inc index b332b2cf41..d762f72e42 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -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 \ diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 60826b56c1..96a0a6df6c 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -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() * diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 1fa83ff4d6..a339ea3f7c 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -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,22 +333,38 @@ 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 - if(item->delay_ms) { - curlx_wait_ms(item->delay_ms); - } - if(item->delay_fail_ms) { - curlx_wait_ms(item->delay_fail_ms); - return; - } + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + 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; - item = NULL; + --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,17 +692,17 @@ 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, - async->hostname, async->port, async->ip_version); + 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 */ diff --git a/lib/asyn.h b/lib/asyn.h index f4057d2419..de9b5a5604 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -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 @@ -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) diff --git a/lib/cf-dns.c b/lib/cf-dns.c new file mode 100644 index 0000000000..a3bf9a5620 --- /dev/null +++ b/lib/cf-dns.c @@ -0,0 +1,556 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 */ diff --git a/lib/cf-dns.h b/lib/cf-dns.h new file mode 100644 index 0000000000..6ad8721283 --- /dev/null +++ b/lib/cf-dns.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_CF_DNS_H +#define HEADER_CURL_CF_DNS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 */ diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index cafee51eae..1d50fba103 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -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, - struct connectdata *conn, - int sockindex, - enum alpnid *alpn_ids, size_t alpn_count, - uint8_t def_transport) +static CURLcode cf_hc_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + 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; diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index df29c8ddd7..3160c0382a 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -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); diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index d6bc847a43..4df7cf4948 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -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,27 +406,37 @@ 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, - bs->transport, bs->cf_create); + 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", (ai_family == AF_INET) ? "4" : "6", result); @@ -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, - struct Curl_easy *data) +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; } diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 29c5e0e245..547ee4b4ac 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -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, diff --git a/lib/cf-socket.c b/lib/cf-socket.c index f78f3dfdba..4afdb4c54a 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -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, - const struct Curl_addrinfo *ai, - uint8_t transport) +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; diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 014f5f7881..40c001cc14 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -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); /** diff --git a/lib/cfilters.c b/lib/cfilters.c index 9e0e63b53c..23b330c425 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -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) { diff --git a/lib/cfilters.h b/lib/cfilters.h index 9d83331de6..77405b51ad 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -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); diff --git a/lib/connect.c b/lib/connect.c index 85442da333..6a0137c727 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -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) { diff --git a/lib/connect.h b/lib/connect.h index 071b4329c1..72e1ee7fcf 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -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 */ diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 2a492d1fed..fd26c5f0bc 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -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 diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 063011eb5e..da2da872cf 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -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) && \ diff --git a/lib/curl_share.c b/lib/curl_share.c index ab815abc8f..94d53e8911 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -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); } diff --git a/lib/curl_trc.c b/lib/curl_trc.c index a80fb61eff..0bfd3b834e 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -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 }, diff --git a/lib/dnscache.c b/lib/dnscache.c index 9f4bfb095c..8d7dcadb02 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -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 diff --git a/lib/dnscache.h b/lib/dnscache.h index b3d9aecaf0..b2b3e997fe 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -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, diff --git a/lib/easy.c b/lib/easy.c index c0147da6b4..f6d83ea05d 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -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)); diff --git a/lib/ftp.c b/lib/ftp.c index 653d5ca86a..087c564b95 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -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; } diff --git a/lib/hostip.c b/lib/hostip.c index 898e03afab..de40e9751a 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -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; + if(result && (result != CURLE_AGAIN)) { + Curl_dns_entry_unlink(data, pdns); + Curl_async_shutdown(data); + if((result == CURLE_COULDNT_RESOLVE_HOST) || + (result == CURLE_COULDNT_RESOLVE_PROXY)) { + if(cache_dns) + Curl_dnscache_add_negative(data, hostname, port, ip_version); + failf(data, "Could not resolve: %s:%u", hostname, port); } - 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; + else { + failf(data, "Error %d resolving %s:%u", result, hostname, port); } -#endif - result = CURLE_COULDNT_RESOLVE_HOST; } -error: - if(dns) - Curl_dns_entry_unlink(data, &dns); - Curl_async_shutdown(data); - if(result == CURLE_COULDNT_RESOLVE_HOST) - Curl_dnscache_add_negative(data, hostname, port, ip_version); - DEBUGASSERT(result); + 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 */ diff --git a/lib/hostip.h b/lib/hostip.h index 2a936b5647..d69abebe94 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -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 */ diff --git a/lib/multi.c b/lib/multi.c index 32e7e9ec9d..d491586721 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -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,12 +1158,11 @@ 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: - result = mstate_connecting_pollset(data, ps); + if(data->conn && !data->conn->bits.dns_resolved) + result = Curl_resolv_pollset(data, ps); + if(!result) + result = mstate_connecting_pollset(data, ps); break; case MSTATE_PROTOCONNECT: @@ -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,26 +2328,21 @@ 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! */ - mresult = CURLM_CALL_MULTI_PERFORM; + /* 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! */ + mresult = CURLM_CALL_MULTI_PERFORM; - if(connected) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - multistate(data, MSTATE_PROTOCONNECT); - } - else { - multistate(data, MSTATE_CONNECTING); + if(connected) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); } + multistate(data, MSTATE_PROTOCONNECT); + } + else { + multistate(data, MSTATE_CONNECTING); } } *resultp = result; @@ -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; diff --git a/lib/multihandle.h b/lib/multihandle.h index f158f1d771..9631c00131 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -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 */ diff --git a/lib/url.c b/lib/url.c index 953064c6f1..2b1c8ace57 100644 --- a/lib/url.c +++ b/lib/url.c @@ -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,43 +3453,31 @@ 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; + + DEBUGASSERT(conn); + Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(conn->bits.reuse) { + if(conn->attached_xfers > 1) + /* multiplexed */ + *pconnected = TRUE; + } + else if(conn->scheme->flags & PROTOPT_NONETWORK) { + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + *pconnected = TRUE; + } + else { + 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); } - if(!result) { - DEBUGASSERT(conn); - Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(conn->bits.reuse) { - if(conn->attached_xfers > 1) - /* multiplexed */ - *protocol_done = TRUE; - } - else if(conn->scheme->flags & PROTOPT_NONETWORK) { - *asyncp = FALSE; - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - *protocol_done = 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; - } - } - } +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 diff --git a/lib/url.h b/lib/url.h index 0afa7eb26e..3f3c4bef3c 100644 --- a/lib/url.h +++ b/lib/url.h @@ -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); diff --git a/lib/urldata.h b/lib/urldata.h index 16238e29b9..4b7f99769b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 725c5163e0..7815e18357 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -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; diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/curl_ngtcp2.h index 1a87faf00d..185272ace0 100644 --- a/lib/vquic/curl_ngtcp2.h +++ b/lib/vquic/curl_ngtcp2.h @@ -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 */ diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index ebdad0d3a0..a8697f6ec9 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -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; diff --git a/lib/vquic/curl_quiche.h b/lib/vquic/curl_quiche.h index fb5a563ae8..c2c88ddeaf 100644 --- a/lib/vquic/curl_quiche.h +++ b/lib/vquic/curl_quiche.h @@ -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 diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 1eaabb95f5..9713c9d506 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -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; diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 528e18476d..1f0a1ab5e5 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -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; diff --git a/tests/data/test142 b/tests/data/test142 index d85234b31d..a7b85b1b5f 100644 --- a/tests/data/test142 +++ b/tests/data/test142 @@ -188,7 +188,7 @@ RETR %TESTNUMBER QUIT -Allocations: 170 +Allocations: 180 Maximum allocated: 150000 diff --git a/tests/data/test2500 b/tests/data/test2500 index d870d33887..2478245a8c 100644 --- a/tests/data/test2500 +++ b/tests/data/test2500 @@ -72,7 +72,7 @@ via: 1.1 nghttpx s/^server: nghttpx.*\r?\n// -Allocations: 160 +Allocations: 170 Maximum allocated: 1800000 diff --git a/tests/data/test500 b/tests/data/test500 index 7c1b1b1d5f..077b119ff4 100644 --- a/tests/data/test500 +++ b/tests/data/test500 @@ -54,7 +54,7 @@ Accept: */* -Allocations: 82 +Allocations: 90 Maximum allocated: 33400 diff --git a/tests/data/test506 b/tests/data/test506 index 4440a7a582..ed8e4e315e 100644 --- a/tests/data/test506 +++ b/tests/data/test506 @@ -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 diff --git a/tests/libtest/lib506.c b/tests/libtest/lib506.c index 82caa8a2d4..7674130b76 100644 --- a/tests/libtest/lib506.c +++ b/tests/libtest/lib506.c @@ -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) { diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c index 6c1f52a8dd..af9363d75e 100644 --- a/tests/unit/unit1620.c +++ b/tests/unit/unit1620.c @@ -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"); diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 4bd12313e6..b3d6160039 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -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