cf-dns: connection filter for DNS queries
No binary (build failed)
—
(log yok)
| @@ -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 \ | |
| @@ -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() | |
| * | |
| @@ -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.queued; |
| + | async->thrdd.done = !async->thrdd.queued; |
| - | async->thrdd.resolved = item; |
| - | async->thrdd.done = TRUE; |
| - | item = NULL; |
| + | #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 */ | |
| @@ -124,6 +124,11 @@ CURLcode Curl_async_await(struct Curl_easy *data, | |
| CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| struct Curl_resolv_async *async); | |
| + | const struct Curl_addrinfo * |
| + | Curl_async_get_ai(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | int ai_family, unsigned int index); |
| + | |
| #ifdef USE_ARES | |
| /* common functions for c-ares and threaded resolver with HTTPSRR */ | |
| #include <ares.h> | |
| @@ -175,7 +180,8 @@ struct async_thrdd_item; | |
| /* Context for threaded resolver */ | |
| struct async_thrdd_ctx { | |
| - | struct async_thrdd_item *resolved; |
| + | struct async_thrdd_item *res_A; /* ipv4 result */ |
| + | struct async_thrdd_item *res_AAAA; /* ipv6 result */ |
| #if defined(USE_HTTPSRR) && defined(USE_ARES) | |
| struct { | |
| ares_channel channel; | |
| @@ -184,7 +190,7 @@ struct async_thrdd_ctx { | |
| BIT(done); | |
| } rr; | |
| #endif | |
| - | BIT(queued); |
| + | uint32_t queued; |
| BIT(done); | |
| }; | |
| @@ -219,7 +225,7 @@ struct doh_probes; | |
| #define Curl_async_await(x, y, z) CURLE_COULDNT_RESOLVE_HOST | |
| #define Curl_async_global_init() CURLE_OK | |
| #define Curl_async_global_cleanup() Curl_nop_stmt | |
| - | |
| + | #define Curl_async_get_ai(a,b,c,d) NULL |
| #endif /* !CURLRES_ASYNCH */ | |
| #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) | |
| @@ -0,0 +1,556 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */ |
| @@ -0,0 +1,70 @@ | |
| + | #ifndef HEADER_CURL_CF_DNS_H |
| + | #define HEADER_CURL_CF_DNS_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */ |
| @@ -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 |
| - | |
| - | /* 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((conn->scheme->protocol != CURLPROTO_HTTPS) || |
| + | !conn->bits.tls_enable_alpn) |
| + | goto out; |
| - | 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; | |
| @@ -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); | |
| @@ -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) { |
| - | 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) { |
| + | |
| + | 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->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; | |
| } | |
| @@ -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, | |
| @@ -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; | |
| @@ -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); | |
| /** | |
| @@ -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) | |
| { | |
| @@ -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); | |
| @@ -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) { | |
| @@ -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 */ | |
| @@ -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 | |
| @@ -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) && \ | |
| @@ -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); | |
| } | |
| @@ -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 }, | |
| @@ -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 | |
| @@ -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, | |
| @@ -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)); | |
| @@ -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; | |
| } | |
| @@ -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,188 +333,286 @@ static CURLcode hostip_async_new(struct Curl_easy *data, | |
| data->state.async = async; | |
| return CURLE_OK; | |
| } | |
| - | #endif |
| - | static CURLcode hostip_resolv(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 **entry) |
| + | static CURLcode hostip_resolv_take_result(struct Curl_easy *data, |
| + | 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; |
| - | |
| - | *entry = NULL; |
| + | struct Curl_resolv_async *async = data->state.async; |
| + | CURLcode result; |
| - | #ifdef USE_CURL_ASYNC |
| - | if(data->state.async) |
| - | Curl_async_destroy(data); |
| - | #else |
| - | (void)timeout_ms; |
| - | #endif |
| + | /* If async resolving is ongoing, this must be set */ |
| + | if(!async) |
| + | return CURLE_FAILED_INIT; |
| #ifndef CURL_DISABLE_DOH | |
| - | data->conn->bits.doh = FALSE; /* default is not */ |
| - | #else |
| - | (void)allowDOH; |
| + | if(data->conn->bits.doh) |
| + | result = Curl_doh_take_result(data, pdns); |
| + | else |
| #endif | |
| + | result = Curl_async_take_result(data, async, pdns); |
| - | /* We should intentionally error and not resolve .onion TLDs */ |
| - | hostname_len = strlen(hostname); |
| - | DEBUGASSERT(hostname_len); |
| - | if(hostname_len >= 7 && |
| - | (curl_strequal(&hostname[hostname_len - 6], ".onion") || |
| - | curl_strequal(&hostname[hostname_len - 7], ".onion."))) { |
| - | failf(data, "Not resolving .onion address (RFC 7686)"); |
| - | goto error; |
| - | } |
| + | if(result == CURLE_AGAIN) |
| + | result = CURLE_OK; |
| + | else if(result) |
| + | Curl_resolver_error(data, NULL); |
| + | else |
| + | DEBUGASSERT(*pdns); |
| - | #ifdef DEBUGBUILD |
| - | CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x)", |
| - | hostname, port, ip_version); |
| - | if((ip_version == CURL_IPRESOLVE_V6) && |
| - | getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { |
| - | infof(data, "DEBUG fail ipv6 resolve"); |
| - | result = Curl_resolver_error(data, NULL); |
| - | goto error; |
| - | } |
| + | 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 | |
| - | /* Let's check our DNS cache first */ |
| - | r2 = Curl_dnscache_get(data, hostname, port, ip_version, &dns); |
| - | if(dns) { |
| - | infof(data, "Hostname %s was found in DNS cache", hostname); |
| - | result = CURLE_OK; |
| - | goto out; |
| - | } |
| - | else if(r2) { |
| - | DEBUGASSERT(!dns); |
| - | infof(data, "Negative DNS entry"); |
| - | result = Curl_resolver_error(data, NULL); |
| - | goto error; |
| - | } |
| + | return NULL; |
| + | } |
| + | |
| + | #endif /* USE_CURL_ASYNC */ |
| - | /* No luck, we need to resolve hostname. Notify user callback. */ |
| + | 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) | |
| - | goto error; |
| + | return result; |
| } | |
| + | |
| result = Curl_async_get_impl(data, data->state.async, &resolver); | |
| if(result) | |
| - | goto error; |
| + | 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) { | |
| - | result = CURLE_ABORTED_BY_CALLBACK; |
| - | goto error; |
| + | return CURLE_ABORTED_BY_CALLBACK; |
| } | |
| } | |
| + | return CURLE_OK; |
| + | } |
| - | if(Curl_is_ipaddr(hostname)) { |
| + | 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); | |
| - | if(result) |
| - | goto error; |
| goto out; | |
| - | #endif |
| } | |
| + | #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); | |
| - | result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY; |
| + | if(!addr) |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| } | |
| + | |
| #ifndef CURL_DISABLE_DOH | |
| - | else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.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 error; |
| + | goto out; |
| } | |
| result = Curl_doh(data, data->state.async); | |
| - | respwait = TRUE; |
| + | goto out; |
| } | |
| + | #else |
| + | (void)allowDOH; |
| #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; |
| - | } |
| + | |
| + | /* 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 | |
| - | 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) |
| + | (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; | |
| - | #endif |
| } | |
| + | #else |
| + | addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport); |
| + | if(!addr) |
| + | result = CURLE_COULDNT_RESOLVE_HOST; |
| + | #endif |
| 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) { |
| + | 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; |
| } | |
| - | show_resolve_info(data, dns); |
| - | *entry = dns; |
| - | return CURLE_OK; |
| + | else if(!*pdns) |
| + | result = CURLE_AGAIN; |
| } | |
| - | else if(respwait) { |
| + | 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, |
| + | uint16_t port, |
| + | uint8_t ip_version, |
| + | uint8_t transport, |
| + | timediff_t timeout_ms, |
| + | bool allowDOH, |
| + | struct Curl_dns_entry **pdns) |
| + | { |
| + | size_t hostname_len; |
| + | CURLcode result = CURLE_COULDNT_RESOLVE_HOST; |
| + | bool cache_dns = FALSE; |
| + | |
| + | *pdns = NULL; |
| + | |
| #ifdef USE_CURL_ASYNC | |
| - | result = Curl_resolv_take_result(data, &dns); |
| - | if(!result) { |
| - | *entry = dns; |
| - | return dns ? CURLE_OK : CURLE_AGAIN; |
| - | } |
| + | if(data->state.async) |
| + | Curl_async_destroy(data); |
| + | #else |
| + | (void)timeout_ms; |
| #endif | |
| - | result = CURLE_COULDNT_RESOLVE_HOST; |
| + | |
| + | #ifndef CURL_DISABLE_DOH |
| + | data->conn->bits.doh = FALSE; /* default is not */ |
| + | #else |
| + | (void)allowDOH; |
| + | #endif |
| + | |
| + | /* We should intentionally error and not resolve .onion TLDs */ |
| + | hostname_len = strlen(hostname); |
| + | DEBUGASSERT(hostname_len); |
| + | if(hostname_len >= 7 && |
| + | (curl_strequal(&hostname[hostname_len - 6], ".onion") || |
| + | curl_strequal(&hostname[hostname_len - 7], ".onion."))) { |
| + | failf(data, "Not resolving .onion address (RFC 7686)"); |
| + | goto out; |
| } | |
| - | 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); |
| + | |
| + | #ifdef DEBUGBUILD |
| + | CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x)", |
| + | hostname, port, ip_version); |
| + | if((ip_version == CURL_IPRESOLVE_V6) && |
| + | getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { |
| + | infof(data, "DEBUG fail ipv6 resolve"); |
| + | result = Curl_resolver_error(data, NULL); |
| + | goto out; |
| + | } |
| + | #endif |
| + | /* Let's check our DNS cache first */ |
| + | 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; |
| + | } |
| + | else if(result) { |
| + | infof(data, "Negative DNS entry"); |
| + | result = Curl_resolver_error(data, NULL); |
| + | } |
| + | else { |
| + | /* 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: |
| + | 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); |
| + | } |
| + | else { |
| + | failf(data, "Error %d resolving %s:%u", result, hostname, port); |
| + | } |
| + | } |
| + | else if(cache_dns && *pdns) { |
| + | result = Curl_dnscache_add(data, *pdns); |
| + | if(result) |
| + | Curl_dns_entry_unlink(data, pdns); |
| + | } |
| + | |
| + | CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x, timeout_ms=%" FMT_TIMEDIFF_T |
| + | ") -> %d, dns %sfound", |
| + | hostname, port, ip_version, timeout_ms, result, |
| + | *pdns ? "" : "not "); |
| return result; | |
| } | |
| @@ -841,33 +872,32 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| else if(result) { | |
| - | result = Curl_resolver_error(data, NULL); |
| - | return result; |
| + | Curl_async_shutdown(data); |
| + | return Curl_resolver_error(data, NULL); |
| } | |
| - | #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); |
| + | result = hostip_resolv_take_result(data, pdns); |
| - | if(!result) { |
| - | DEBUGASSERT(*pdns); |
| - | show_resolve_info(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_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); |
| + | 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 */ |
| @@ -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 */ | |
| @@ -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; | |
| @@ -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 */ | |
| @@ -132,10 +132,6 @@ static void data_priority_cleanup(struct Curl_easy *data); | |
| # error READBUFFER_SIZE is too small | |
| #endif | |
| - | #if !defined(CURL_DISABLE_PROXY) && defined(USE_UNIX_SOCKETS) |
| - | #define UNIX_SOCKET_PREFIX "localhost" |
| - | #endif |
| - | |
| /* Reject URLs exceeding this length */ | |
| #define MAX_URL_LEN 0xffff | |
| @@ -244,8 +240,6 @@ CURLcode Curl_close(struct Curl_easy **datap) | |
| /* release any resolve information this transfer kept */ | |
| Curl_async_destroy(data); | |
| - | Curl_dns_entry_unlink(data, &data->state.dns[0]); /* done with this */ |
| - | Curl_dns_entry_unlink(data, &data->state.dns[1]); |
| data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */ | |
| data->magic = 0; /* force a clear AFTER the possibly enforced removal from | |
| @@ -2951,118 +2945,6 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | static CURLcode resolve_unix(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const char *unix_path, |
| - | struct Curl_dns_entry **pdns) |
| - | { |
| - | struct Curl_dns_entry *hostaddr; |
| - | bool longpath = FALSE; |
| - | |
| - | DEBUGASSERT(unix_path); |
| - | *pdns = NULL; |
| - | |
| - | /* Unix domain sockets are local. The host gets ignored, use the specified |
| - | * domain socket address. Do not cache "DNS entries". There is no DNS |
| - | * involved and we already have the file system path available. */ |
| - | hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry)); |
| - | if(!hostaddr) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | |
| - | hostaddr->addr = Curl_unix2addr(unix_path, &longpath, |
| - | (bool)conn->bits.abstract_unix_socket); |
| - | if(!hostaddr->addr) { |
| - | if(longpath) |
| - | /* Long paths are not supported for now */ |
| - | failf(data, "Unix socket path too long: '%s'", unix_path); |
| - | curlx_free(hostaddr); |
| - | return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; |
| - | } |
| - | |
| - | hostaddr->refcount = 1; /* connection is the only one holding this */ |
| - | *pdns = hostaddr; |
| - | return CURLE_OK; |
| - | } |
| - | #endif |
| - | |
| - | /************************************************************* |
| - | * Resolve the address of the server or proxy |
| - | *************************************************************/ |
| - | static CURLcode resolve_server(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | struct Curl_dns_entry **pdns) |
| - | { |
| - | struct hostname *ehost; |
| - | uint16_t eport; |
| - | timediff_t timeout_ms = Curl_timeleft_ms(data); |
| - | const char *peertype = "host"; |
| - | CURLcode result; |
| - | |
| - | *pdns = NULL; |
| - | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | { |
| - | const char *unix_path = conn->unix_domain_socket; |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && |
| - | !strncmp(UNIX_SOCKET_PREFIX "/", |
| - | conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) |
| - | unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; |
| - | #endif |
| - | |
| - | if(unix_path) { |
| - | /* This only works if previous transport is TRNSPRT_TCP. Check it? */ |
| - | conn->transport_wanted = TRNSPRT_UNIX; |
| - | return resolve_unix(data, conn, unix_path, pdns); |
| - | } |
| - | } |
| - | #endif |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(CONN_IS_PROXIED(conn)) { |
| - | ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : |
| - | &conn->http_proxy.host; |
| - | eport = conn->bits.socksproxy ? conn->socks_proxy.port : |
| - | conn->http_proxy.port; |
| - | peertype = "proxy"; |
| - | } |
| - | else |
| - | #endif |
| - | { |
| - | ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; |
| - | /* If not connecting via a proxy, extract the port from the URL, if it is |
| - | * there, thus overriding any defaults that might have been set above. */ |
| - | eport = conn->bits.conn_to_port ? |
| - | conn->conn_to_port : (uint16_t)conn->remote_port; |
| - | } |
| - | |
| - | result = Curl_resolv(data, ehost->name, eport, |
| - | conn->ip_version, conn->transport_wanted, |
| - | timeout_ms, pdns); |
| - | DEBUGASSERT(!result || !*pdns); |
| - | if(!result) { /* resolved right away, either sync or from dnscache */ |
| - | DEBUGASSERT(*pdns); |
| - | return CURLE_OK; |
| - | } |
| - | else if(result == CURLE_AGAIN) { /* async resolv in progress */ |
| - | return CURLE_AGAIN; |
| - | } |
| - | else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ |
| - | failf(data, "Failed to resolve %s '%s' with timeout after %" |
| - | FMT_TIMEDIFF_T " ms", peertype, ehost->dispname, |
| - | curlx_ptimediff_ms(Curl_pgrs_now(data), |
| - | &data->progress.t_startsingle)); |
| - | return CURLE_OPERATION_TIMEDOUT; |
| - | } |
| - | else { |
| - | DEBUGASSERT(result); |
| - | failf(data, "Could not resolve %s: %s", peertype, ehost->dispname); |
| - | return result; |
| - | } |
| - | } |
| - | |
| static void url_move_hostname(struct hostname *dest, struct hostname *src) | |
| { | |
| Curl_safefree(dest->rawalloc); | |
| @@ -3308,6 +3190,10 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| needle->recv[SECONDARYSOCKET] = Curl_cf_recv; | |
| needle->send[SECONDARYSOCKET] = Curl_cf_send; | |
| needle->bits.tcp_fastopen = data->set.tcp_fastopen; | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | if(Curl_conn_get_unix_path(needle)) |
| + | needle->transport_wanted = TRNSPRT_UNIX; |
| + | #endif |
| } | |
| out: | |
| @@ -3542,9 +3428,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, | |
| CURLcode result = CURLE_OK; | |
| struct connectdata *conn = data->conn; | |
| - | DEBUGASSERT(dns); |
| - | Curl_pgrsTime(data, TIMER_NAMELOOKUP); |
| - | |
| if(!conn->bits.reuse) | |
| result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns, | |
| CURL_CF_SSL_DEFAULT); | |
| @@ -3556,15 +3439,12 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | CURLcode Curl_connect(struct Curl_easy *data, |
| - | bool *asyncp, |
| - | bool *protocol_done) |
| + | CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) |
| { | |
| CURLcode result; | |
| struct connectdata *conn; | |
| - | *asyncp = FALSE; /* assume synchronous resolves by default */ |
| - | *protocol_done = FALSE; |
| + | *pconnected = FALSE; |
| /* Set the request to virgin state based on transfer settings */ | |
| Curl_req_hard_reset(&data->req, data); | |
| @@ -3573,44 +3453,32 @@ CURLcode Curl_connect(struct Curl_easy *data, | |
| result = url_find_or_create_conn(data); | |
| conn = data->conn; | |
| - | if(result == CURLE_NO_CONNECTION_AVAILABLE) { |
| - | DEBUGASSERT(!conn); |
| - | return result; |
| - | } |
| + | if(result) |
| + | goto out; |
| - | if(!result) { |
| - | DEBUGASSERT(conn); |
| - | Curl_pgrsTime(data, TIMER_POSTQUEUE); |
| - | if(conn->bits.reuse) { |
| - | if(conn->attached_xfers > 1) |
| - | /* multiplexed */ |
| - | *protocol_done = TRUE; |
| - | } |
| - | 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; |
| - | } |
| - | } |
| + | 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); |
| } | |
| + | out: |
| + | if(result == CURLE_NO_CONNECTION_AVAILABLE) |
| + | DEBUGASSERT(!conn); |
| + | |
| if(result && conn) { | |
| /* We are not allowed to return failure with memory left allocated in the | |
| connectdata struct, free those here */ | |
| @@ -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); | |
| @@ -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 */ | |
| @@ -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; | |
| @@ -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 */ | |
| @@ -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; | |
| @@ -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 | |
| @@ -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; | |
| @@ -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; | |
| @@ -188,7 +188,7 @@ RETR %TESTNUMBER | |
| QUIT | |
| </protocol> | |
| <limits> | |
| - | Allocations: 170 |
| + | Allocations: 180 |
| Maximum allocated: 150000 | |
| </limits> | |
| </verify> | |
| @@ -72,7 +72,7 @@ via: 1.1 nghttpx | |
| s/^server: nghttpx.*\r?\n// | |
| </stripfile> | |
| <limits> | |
| - | Allocations: 160 |
| + | Allocations: 170 |
| Maximum allocated: 1800000 | |
| </limits> | |
| </verify> | |
| @@ -54,7 +54,7 @@ Accept: */* | |
| </protocol> | |
| <limits> | |
| - | Allocations: 82 |
| + | Allocations: 90 |
| Maximum allocated: 33400 | |
| </limits> | |
| </verify> | |
| @@ -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 | |
| - | lock: cookie [Pigs in space]: 52 |
| - | unlock: cookie [Pigs in space]: 53 |
| - | 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 |
| + | lock: dns [Pigs in space]: 18 |
| + | unlock: dns [Pigs in space]: 19 |
| 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 |
| + | 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]: 90 |
| - | unlock: share [Pigs in space]: 91 |
| + | lock: share [Pigs in space]: 16 |
| + | unlock: share [Pigs in space]: 17 |
| CURLOPT_COOKIELIST ALL | |
| - | lock: cookie [Pigs in space]: 92 |
| - | unlock: cookie [Pigs in space]: 93 |
| + | lock: cookie [Pigs in space]: 54 |
| + | unlock: cookie [Pigs in space]: 55 |
| CURLOPT_COOKIEJAR | |
| CURLOPT_COOKIELIST RELOAD | |
| - | lock: cookie [Pigs in space]: 94 |
| - | unlock: cookie [Pigs in space]: 95 |
| - | lock: cookie [Pigs in space]: 96 |
| - | unlock: cookie [Pigs in space]: 97 |
| + | lock: cookie [Pigs in space]: 56 |
| + | unlock: cookie [Pigs in space]: 57 |
| + | lock: cookie [Pigs in space]: 58 |
| + | unlock: cookie [Pigs in space]: 59 |
| loaded cookies: | |
| ----------------- | |
| www.host.foo.com FALSE / FALSE %days[400] test6 six_more | |
| @@ -229,13 +229,13 @@ loaded cookies: | |
| try SHARE_CLEANUP... | |
| SHARE_CLEANUP failed, correct | |
| CLEANUP | |
| - | lock: cookie [Pigs in space]: 98 |
| - | unlock: cookie [Pigs in space]: 99 |
| - | lock: share [Pigs in space]: 100 |
| - | unlock: share [Pigs in space]: 101 |
| + | lock: cookie [Pigs in space]: 60 |
| + | unlock: cookie [Pigs in space]: 61 |
| + | lock: share [Pigs in space]: 18 |
| + | unlock: share [Pigs in space]: 19 |
| SHARE_CLEANUP | |
| - | lock: share [Pigs in space]: 102 |
| - | unlock: share [Pigs in space]: 103 |
| + | lock: share [Pigs in space]: 20 |
| + | unlock: share [Pigs in space]: 21 |
| GLOBAL_CLEANUP | |
| </stdout> | |
| <file name="%LOGDIR/jar%TESTNUMBER" mode="text"> | |
| @@ -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) { | |
| @@ -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"); | |
| @@ -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 | |