From ddb30354f68038629290688f4c5f58fef283727b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Apr 2026 21:51:43 +0200 Subject: [PATCH 0001/1062] GHA/linux: work around Linuxbrew install failure Root cause unknown, it appeared today without any local change: ``` ==> Installing dependencies for libssh2: openssl@3 and zlib-ng-compat ==> Installing libssh2 dependency: openssl@3 ==> Pouring openssl@3--3.6.2.x86_64_linux.bottle.tar.gz Error: A `brew install openssl@4 libssh2 libngtcp2 libnghttp3 c-ares` process has already locked /home/linuxbrew/.linuxbrew/Cellar/openssl@4. Please wait for it to finish or terminate it to continue. Error: Process completed with exit code 1. ``` Ref: https://github.com/curl/curl/actions/runs/25129061781/job/73650161844?pr=21468#step:2:407 Last known good run: https://github.com/curl/curl/actions/runs/25038989485/job/73337289504 Ref: 1fbffe7f08f0d551038520b569b817f58084f77b #21379 Closes #21469 --- .github/workflows/linux.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d9e09b518782..eeda1085d7bb 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -345,8 +345,9 @@ jobs: -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 - name: 'address-sanitizer' - install_packages: clang-20 libssl-dev libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 + install_packages: clang-20 libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 install_steps: pytest randcurl + install_steps_brew: openssl@4 CC: clang-20 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined @@ -357,12 +358,12 @@ jobs: -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined -ldl -lubsan - generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON + generate: -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_USE_LIBSSH=ON - name: 'address-sanitizer H3 c-ares' install_packages: clang-20 libubsan1 libasan8 libtsan2 install_steps: pytest - install_steps_brew: openssl@4 libssh2 libngtcp2 libnghttp3 c-ares + install_steps_brew: openssl libssh2 libngtcp2 libnghttp3 c-ares CC: clang-20 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined @@ -379,7 +380,7 @@ jobs: /home/linuxbrew/.linuxbrew/opt/libnghttp3/lib/pkgconfig:\ /home/linuxbrew/.linuxbrew/opt/c-ares/lib/pkgconfig" generate: >- - -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl@4 -DUSE_ECH=ON -DUSE_NGTCP2=ON + -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON - name: 'thread-sanitizer' From 2bb5c9b5552d37f08a439f2bec400009321d325c Mon Sep 17 00:00:00 2001 From: Raymond Steen Date: Wed, 29 Apr 2026 10:27:39 +0300 Subject: [PATCH 0002/1062] mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per MQTT 3.1.1 sections 3.13.1 and 3.14.1, PINGRESP and DISCONNECT fixed headers must have remaining_length set to zero. The previous code dispatched to mqtt->nextstate based on the queued state alone without validating remaining_length for these no-payload packet types, allowing a malicious broker to send a PINGRESP with non-zero remaining_length whose trailing bytes would be interpreted as the payload of whatever message type was queued (CONNACK, SUBACK, etc.). The exploitation path turned out to be narrow — curl sends data to the server the user chose to talk to — but the spec violation and the resulting protocol-state error are real. Reject the malformed packets with CURLE_WEIRD_SERVER_REPLY before state dispatch. Reported-by: Raymond Steen Found by VORTIQ-X VXF Framework Bug: https://hackerone.com/reports/3702718 Signed-off-by: Raymond Steen Closes #21465 --- lib/mqtt.c | 18 +++++++++++++ tests/data/Makefile.am | 2 +- tests/data/test2206 | 59 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test2207 | 59 ++++++++++++++++++++++++++++++++++++++++++ tests/server/mqttd.c | 54 ++++++++++++++++++++++++++++++++------ 5 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 tests/data/test2206 create mode 100644 tests/data/test2207 diff --git a/lib/mqtt.c b/lib/mqtt.c index 84fd272e21e7..d28a25bb50dd 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -892,6 +892,24 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) break; } mq->npacket = 0; + /* PINGRESP and DISCONNECT must have remaining_length == 0 and + * reserved bits (low nibble) must be zero per MQTT 3.1.1 + * sections 2.2.2, 3.13.1 and 3.14.1. Reject before state + * dispatch to prevent nextstate confusion. */ + { + const unsigned char type = mq->firstbyte & 0xF0; + const unsigned char reserved = mq->firstbyte & 0x0F; + if((type == MQTT_MSG_DISCONNECT || type == MQTT_MSG_PINGRESP) && + (mq->remaining_length || reserved)) { + failf(data, + "Broker sent malformed %s " + "(remaining_length=%zu, header byte=0x%02x)", + type == MQTT_MSG_DISCONNECT ? "DISCONNECT" : "PINGRESP", + mq->remaining_length, mq->firstbyte); + result = CURLE_WEIRD_SERVER_REPLY; + break; + } + } if(mq->remaining_length) { mqstate(data, mqtt->nextstate, MQTT_NOSTATE); break; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 5a517df9f3c9..706a4c89ed91 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -261,7 +261,7 @@ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 \ test2100 test2101 test2102 test2103 test2104 \ \ -test2200 test2201 test2202 test2203 test2204 test2205 \ +test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ \ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 \ diff --git a/tests/data/test2206 b/tests/data/test2206 new file mode 100644 index 000000000000..31530221f88b --- /dev/null +++ b/tests/data/test2206 @@ -0,0 +1,59 @@ + + + + +MQTT +MQTT SUBSCRIBE + + + +# Server-side + + +hello + + +# Send a PINGRESP (0xD0) with remaining_length=2 in place of the +# expected CONNACK. MQTT 3.1.1 s. 3.13.1 requires PINGRESP to have +# remaining_length == 0. Curl must reject this with +# CURLE_WEIRD_SERVER_REPLY rather than dispatching to the CONNACK +# handler. + +PINGRESP-as-CONNACK TRUE + + + +# Client-side + + +mqtt + + +mqtt + + +MQTT reject PINGRESP with nonzero remaining_length in place of CONNACK + + +mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + +# Strip out the random part of the client id from the CONNECT message +# before comparison + +s/^(.* 00044d5154540402003c000c6375726c).*/$1/ + + +client CONNECT 18 00044d5154540402003c000c6375726c +server PINGRESP-as-CONNACK 2 d0020000 + + +# 8 is CURLE_WEIRD_SERVER_REPLY + +8 + + + diff --git a/tests/data/test2207 b/tests/data/test2207 new file mode 100644 index 000000000000..2aa3bd2636cb --- /dev/null +++ b/tests/data/test2207 @@ -0,0 +1,59 @@ + + + + +MQTT +MQTT SUBSCRIBE + + + +# Server-side + + +hello + + +# Send a DISCONNECT with remaining_length=2 after the PUBLISH. +# MQTT 3.1.1 s. 3.14.1 requires DISCONNECT to have remaining_length == 0. +# Curl must reject this with CURLE_WEIRD_SERVER_REPLY. + +DISCONNECT-malformed TRUE + + + +# Client-side + + +mqtt + + +mqtt + + +MQTT reject DISCONNECT with nonzero remaining_length + + +mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +s/^(.* 00044d5154540402003c000c6375726c).*/$1/ + + +client CONNECT 18 00044d5154540402003c000c6375726c +server CONNACK 2 20020000 +client SUBSCRIBE 9 000100043232303700 +server SUBACK 3 9003000100 +server PUBLISH c 300c00043232303768656c6c6f0a +server DISCONNECT-malformed 2 e0020000 + + +# 8 is CURLE_WEIRD_SERVER_REPLY + +8 + + + diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 0a3a6ee02499..ad4aa3a6409f 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -40,6 +40,7 @@ /* #define MQTT_MSG_PUBACK 0x40 */ #define MQTT_MSG_SUBSCRIBE 0x82 #define MQTT_MSG_SUBACK 0x90 +#define MQTT_MSG_PINGRESP 0xd0 #define MQTT_MSG_DISCONNECT 0xe0 struct mqttd_configurable { @@ -49,6 +50,8 @@ struct mqttd_configurable { bool publish_before_suback; bool short_publish; bool excessive_remaining; + bool pingresp_as_connack; /* send PINGRESP with payload instead of CONNACK */ + bool disconnect_malformed; /* DISCONNECT with nonzero remlen */ unsigned char error_connack; unsigned char remlen_connack; }; @@ -65,6 +68,8 @@ static void mqttd_resetdefaults(void) m_config.publish_before_suback = FALSE; m_config.short_publish = FALSE; m_config.excessive_remaining = FALSE; + m_config.pingresp_as_connack = FALSE; + m_config.disconnect_malformed = FALSE; m_config.error_connack = 0; m_config.remlen_connack = 0; m_config.testnum = 0; @@ -98,6 +103,14 @@ static void mqttd_getconfig(void) logmsg("short-PUBLISH set"); m_config.short_publish = TRUE; } + else if(!strcmp(key, "PINGRESP-as-CONNACK")) { + logmsg("PINGRESP-as-CONNACK set"); + m_config.pingresp_as_connack = TRUE; + } + else if(!strcmp(key, "DISCONNECT-malformed")) { + logmsg("DISCONNECT-malformed set"); + m_config.disconnect_malformed = TRUE; + } else if(!strcmp(key, "error-CONNACK")) { pval = value; if(!curlx_str_number(&pval, &num, 0xff)) { @@ -166,6 +179,16 @@ static int connack(FILE *dump, curl_socket_t fd) 0x00, 0x00 }; ssize_t rc; + const char *label = "CONNACK"; + + if(m_config.pingresp_as_connack) { + /* Send a PINGRESP (0xD0) with remaining_length=2 and payload + mimicking a successful CONNACK. MQTT 3.1.1 s. 3.13.1 requires + PINGRESP to have remaining_length=0, so this is malformed. */ + packet[0] = MQTT_MSG_PINGRESP; + label = "PINGRESP-as-CONNACK"; + logmsg("Sending malformed PINGRESP in place of CONNACK"); + } if(m_config.remlen_connack) packet[1] = m_config.remlen_connack; @@ -173,10 +196,10 @@ static int connack(FILE *dump, curl_socket_t fd) rc = swrite(fd, packet, sizeof(packet)); if(rc > 0) { - logmsg("WROTE %zd bytes [CONNACK]", rc); + logmsg("WROTE %zd bytes [%s]", rc, label); loghex(packet, rc); - logprotocol(FROM_SERVER, "CONNACK", packet[1], dump, - packet, sizeof(packet)); + logprotocol(FROM_SERVER, label, packet[1], dump, + packet, rc); } if(rc == sizeof(packet)) { return 0; @@ -235,15 +258,30 @@ static int disconnect(FILE *dump, curl_socket_t fd) { unsigned char packet[] = { MQTT_MSG_DISCONNECT, 0x00, + 0x00, 0x00 /* extra bytes for malformed variant */ }; - ssize_t rc = swrite(fd, packet, sizeof(packet)); - if(rc == sizeof(packet)) { - logmsg("WROTE %zd bytes [DISCONNECT]", rc); + size_t pktlen = 2; + const char *label = "DISCONNECT"; + ssize_t rc; + + if(m_config.disconnect_malformed) { + /* Send DISCONNECT with remaining_length=2 (must be 0 per spec) */ + packet[1] = 0x02; + pktlen = 4; + label = "DISCONNECT-malformed"; + logmsg("Sending malformed DISCONNECT with nonzero remaining_length"); + } + + rc = swrite(fd, packet, pktlen); + if(rc > 0) { + logmsg("WROTE %zd bytes [%s]", rc, label); loghex(packet, rc); - logprotocol(FROM_SERVER, "DISCONNECT", 0, dump, packet, rc); + logprotocol(FROM_SERVER, label, packet[1], dump, packet, rc); + } + if(rc == (ssize_t)pktlen) { return 0; } - logmsg("Failed sending [DISCONNECT]"); + logmsg("Failed sending [%s]", label); return 1; } From ceaa5dfba001223132ed2e125cf7bb688e07cda2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Apr 2026 16:06:35 +0200 Subject: [PATCH 0003/1062] GHA/curl-for-win: switch riscv job to debian:stable (testing broke) ``` The following packages have unmet dependencies: [...] E: Unable to satisfy dependencies. Reached two conflicting assignments: 1. musl-dev:amd64=1.2.5-3+b1 is selected for install 2. musl-dev:amd64 is not selected for install because: 1. musl-dev:riscv64=1.2.5-3 is selected for install 2. musl-dev:amd64 Breaks musl-dev:riscv64 (!= 1.2.5-3+b1) ``` Ref: https://github.com/curl/curl/actions/runs/25168601672/job/73785600341#step:3:154 Closes #21475 --- .github/workflows/curl-for-win.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 6469448b5044..2f47321a1996 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -112,14 +112,16 @@ jobs: export CW_CONFIG='-main-werror-unitybatch-nocertdata-linux-musl-r64-x64' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh + export CW_CCSUFFIX='-19' + export CW_GCCSUFFIX='-14' sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${OCI_IMAGE_DEBIAN}" + time podman pull "${OCI_IMAGE_DEBIAN_STABLE}" podman images --digests time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ --env-file <(env | grep -a -E \ '^(CW_|DO_NOT_TRACK|GITHUB_)') \ - "${OCI_IMAGE_DEBIAN}" \ + "${OCI_IMAGE_DEBIAN_STABLE}" \ sh -c ./_ci-linux-debian.sh mac-clang: From 91232fc2a23eb01e55fdbce17a412a0efcd414d3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Apr 2026 15:27:37 +0200 Subject: [PATCH 0004/1062] tidy-up: miscellaneous - sha256: fix backend priority in comment. - URLs: link to IETF URLs to the HTML document, to match others. - VERSIONS.md: use unified date format for recent entries too. Ref: https://github.com/curl/curl-www/commit/ce5d32032f8d3d8601f3ef022bbca485020d1bb9 - GHA/labeler.yml: alpha-sort file masks in a label block. - tests/server/mqttd: fix call arg list in a disabled function. - tests/server/mqttd: fix comment. Closes #21473 --- .github/labeler.yml | 4 ++-- docs/ECH.md | 2 +- docs/TODO.md | 2 +- docs/VERSIONS.md | 12 ++++++------ lib/sha256.c | 4 ++-- tests/server/mqttd.c | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 32117e710751..a54e13c05ffc 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -548,9 +548,9 @@ Windows: lib/curlx/fopen.*,\ lib/curlx/multibyte.*,\ lib/curlx/winapi.*,\ + lib/libcurl.def,\ lib/vtls/schannel*,\ m4/curl-schannel.m4,\ projects/Windows/**,\ - src/tool_doswin.c,\ - lib/libcurl.def\ + src/tool_doswin.c\ }" diff --git a/docs/ECH.md b/docs/ECH.md index 2a670edd01ca..6314abb5f357 100644 --- a/docs/ECH.md +++ b/docs/ECH.md @@ -135,7 +135,7 @@ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo There is a reason to want this command line option - for use before publishing an ECHConfigList in the DNS as per the Internet-draft [A well-known URI for -publishing ECHConfigList values](https://datatracker.ietf.org/doc/draft-ietf-tls-wkech/). +publishing ECHConfigList values](https://datatracker.ietf.org/doc/html/draft-ietf-tls-wkech/). If you do use a wrong ECHConfigList value, then the server might return a good value, via the `retry_configs` mechanism. You can see that value in diff --git a/docs/TODO.md b/docs/TODO.md index 2be796f8c92f..74a0c4ce8615 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -791,7 +791,7 @@ done, and thus maintain its connection pool, DNS cache and more. Consider a command line option that can make curl do multiple serial requests while acknowledging server specified [rate -limits](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/). +limits](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers/). See [curl issue 5406](https://github.com/curl/curl/issues/5406) diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 0aebbff49731..007f448e6e6d 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -69,12 +69,12 @@ dates. The tool was called `httpget` before 2.0, `urlget` before 4.0 then same version numbers. - 8.21.0: pending -- 8.20.0: April 29, 2026 -- 8.19.0: March 11, 2026 -- 8.18.0: January 7, 2026 -- 8.17.0: November 5, 2025 -- 8.16.0: September 10, 2025 -- 8.15.0: July 16, 2025 +- 8.20.0: April 29 2026 +- 8.19.0: March 11 2026 +- 8.18.0: January 7 2026 +- 8.17.0: November 5 2025 +- 8.16.0: September 10 2025 +- 8.15.0: July 16 2025 - 8.14.1: June 4 2025 - 8.14.0: May 28 2025 - 8.13.0: April 2 2025 diff --git a/lib/sha256.c b/lib/sha256.c index d97f45f05f9b..eeeec6c6967e 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -43,8 +43,8 @@ * 2. USE_WOLFSSL * 3. USE_GNUTLS * 4. USE_MBEDTLS - * 5. USE_COMMON_CRYPTO - * 6. USE_WIN32_CRYPTO + * 5. USE_WIN32_CRYPTO + * 6. USE_COMMON_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index ad4aa3a6409f..b583575730c5 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -245,7 +245,7 @@ static int puback(FILE *dump, curl_socket_t fd, unsigned short packetid) if(rc == sizeof(packet)) { logmsg("WROTE %zd bytes [PUBACK]", rc); loghex(packet, rc); - logprotocol(FROM_SERVER, dump, packet, rc); + logprotocol(FROM_SERVER, "PUBACK", 0, dump, packet, rc); return 0; } logmsg("Failed sending [PUBACK]"); @@ -704,7 +704,7 @@ static bool mqttd_incoming(curl_socket_t listenfd) } #ifdef HAVE_GETPPID - /* As a last resort, quit if socks5 process becomes orphan. */ + /* As a last resort, quit if mqttd process becomes orphan. */ if(getppid() <= 1) { logmsg("process becomes orphan, exiting"); return FALSE; From ecc8bf6be281dfa5aeedbb7a655472bfbafd0a3d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 30 Apr 2026 14:51:47 +0200 Subject: [PATCH 0005/1062] tool_formparse: simplify get_param_part Introduce a few sub functions to reduce complexity Closes #21478 --- src/tool_formparse.c | 239 +++++++++++++++++++++++++------------------ 1 file changed, 139 insertions(+), 100 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index e129452d4362..67e73fc47926 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -472,6 +472,137 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) return err; } +static void param_type(char **ptr, char **ptype, char **endct, char *sep) +{ + char *p = *ptr; + size_t tlen; + for(p += sizeof("type=") - 1; ISBLANK(*p); p++) + ; + /* set type pointer */ + *ptype = p; + + /* find end of content-type */ + tlen = strcspn(p, "()<>@,;:\\\"[]?=\r\n "); + p += tlen; + *endct = p; + *sep = *p; + *ptr = p; +} + +static void param_filename(char **ptr, char **endct, char **pfilename, + char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + for(p += sizeof("filename=") - 1; ISBLANK(*p); p++) + ; + tp = p; + *pfilename = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(*pfilename == tp) + while(endpos > *pfilename && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + *ptr = p; +} + +static int param_headers(char **ptr, char **endct, + struct curl_slist **pheaders, char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + p += sizeof("headers=") - 1; + if(*p == '@' || *p == '<') { + char *hdrfile; + FILE *fp; + /* Read headers from a file. */ + do { + p++; + } while(ISBLANK(*p)); + tp = p; + hdrfile = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdrfile == tp) + while(endpos > hdrfile && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + fp = curlx_fopen(hdrfile, FOPEN_READTEXT); + if(!fp) { + char errbuf[STRERROR_LEN]; + warnf("Cannot read from %s: %s", hdrfile, + curlx_strerror(errno, errbuf, sizeof(errbuf))); + } + else { + int i = read_field_headers(fp, pheaders); + + curlx_fclose(fp); + if(i) { + curl_slist_free_all(*pheaders); + return -1; + } + } + } + else { + char *hdr; + + while(ISBLANK(*p)) + p++; + tp = p; + hdr = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdr == tp) + while(endpos > hdr && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + if(slist_append(pheaders, hdr)) { + errorf("Out of memory for field header"); + curl_slist_free_all(*pheaders); + return -1; + } + } + *ptr = p; + return 0; +} + +static void param_encoder(char **ptr, char **endct, char **pencoder, + char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + for(p += sizeof("encoder=") - 1; ISBLANK(*p); p++) + ; + tp = p; + *pencoder = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(*pencoder == tp) + while(endpos > *pencoder && ISSPACE(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + *ptr = p; +} + static int get_param_part(char endchar, char **str, char **pdata, char **ptype, char **pfilename, char **pencoder, @@ -509,108 +640,16 @@ static int get_param_part(char endchar, while(p++ && ISBLANK(*p)) ; - if(!endct && checkprefix("type=", p)) { - size_t tlen; - for(p += 5; ISBLANK(*p); p++) - ; - /* set type pointer */ - type = p; - - /* find end of content-type */ - tlen = strcspn(p, "()<>@,;:\\\"[]?=\r\n "); - p += tlen; - endct = p; - sep = *p; - } - else if(checkprefix("filename=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - for(p += 9; ISBLANK(*p); p++) - ; - tp = p; - filename = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(filename == tp) - while(endpos > filename && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - } + if(!endct && checkprefix("type=", p)) + param_type(&p, &type, &endct, &sep); + else if(checkprefix("filename=", p)) + param_filename(&p, &endct, &filename, endchar, &sep); else if(checkprefix("headers=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - p += 8; - if(*p == '@' || *p == '<') { - char *hdrfile; - FILE *fp; - /* Read headers from a file. */ - do { - p++; - } while(ISBLANK(*p)); - tp = p; - hdrfile = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(hdrfile == tp) - while(endpos > hdrfile && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - fp = curlx_fopen(hdrfile, FOPEN_READTEXT); - if(!fp) { - char errbuf[STRERROR_LEN]; - warnf("Cannot read from %s: %s", hdrfile, - curlx_strerror(errno, errbuf, sizeof(errbuf))); - } - else { - int i = read_field_headers(fp, &headers); - - curlx_fclose(fp); - if(i) { - curl_slist_free_all(headers); - return -1; - } - } - } - else { - char *hdr; - - while(ISBLANK(*p)) - p++; - tp = p; - hdr = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(hdr == tp) - while(endpos > hdr && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - if(slist_append(&headers, hdr)) { - errorf("Out of memory for field header"); - curl_slist_free_all(headers); - return -1; - } - } - } - else if(checkprefix("encoder=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - for(p += 8; ISBLANK(*p); p++) - ; - tp = p; - encoder = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(encoder == tp) - while(endpos > encoder && ISSPACE(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; + if(param_headers(&p, &endct, &headers, endchar, &sep)) + return -1; } + else if(checkprefix("encoder=", p)) + param_encoder(&p, &endct, &encoder, endchar, &sep); else if(endct) { /* This is part of content type. */ for(endct = p; *p && *p != ';' && *p != endchar; p++) From d0717acaf0fcf102bf8e59a1e0c6dce3a00feeb0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 30 Apr 2026 22:50:27 +0200 Subject: [PATCH 0006/1062] user-agent.md: mention double quotes too Reported-by: Jeremy Nicoll Bug: https://curl.se/mail/archive-2026-04/0029.html Closes #21477 --- docs/cmdline-opts/user-agent.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cmdline-opts/user-agent.md b/docs/cmdline-opts/user-agent.md index a24bd28449ad..d81b65d34fa4 100644 --- a/docs/cmdline-opts/user-agent.md +++ b/docs/cmdline-opts/user-agent.md @@ -19,8 +19,8 @@ Example: # `--user-agent` Specify the User-Agent string to send to the HTTP server. To encode blanks in -the string, surround the string with single quote marks. This header can also -be set with the --header or the --proxy-header options. +the string, surround the string with single or double quote marks. This header +can also be set with the --header or the --proxy-header options. If you give an empty argument to --user-agent (""), it removes the header completely from the request. If you prefer a blank header, you can set it to a From c29278cc83f31a3e5113eb5c68604fc48ce22fcb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 30 Apr 2026 16:53:02 +0200 Subject: [PATCH 0007/1062] asyn-thrdd: fix result processing without wakeup socketpair When building curl 8.20.0 with socketpair disabled, there is no wakeup socket and the resolve results are not processed. This fix performs result processing in the absence of a wakeup socket before checking the resolve result. Closes #21476 --- lib/asyn-thrdd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 90f055c2a159..477075590682 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -701,6 +701,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(thrdd->rr.channel) (void)Curl_ares_perform(thrdd->rr.channel, 0); #endif +#ifndef ENABLE_WAKEUP + Curl_async_thrdd_multi_process(data->multi); +#endif if(!async->done) return CURLE_AGAIN; From ea392e6b36d875056cf9e28f841bdc8cdc2efbb6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 1 May 2026 11:34:15 +0200 Subject: [PATCH 0008/1062] RELEASE-NOTES: synced Also bump the curlver to tenative 8.20.1 --- RELEASE-NOTES | 603 +---------------------------------------- include/curl/curlver.h | 6 +- 2 files changed, 14 insertions(+), 595 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index fcf392d822ba..11e2a62d8358 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,6 +1,6 @@ -curl and libcurl 8.20.0 +curl and libcurl 8.20.1 - Public curl releases: 274 + Public curl releases: 275 Command line options: 273 curl_easy_setopt() options: 308 Public functions in libcurl: 100 @@ -9,299 +9,12 @@ curl and libcurl 8.20.0 This release includes the following changes: - o async-thrdd: use thread queue for resolving [144] - o build: make NTLM disabled by default [90] - o cmake: drop support for CMake 3.17 and older [108] - o lib: add thread pool and queue [74] - o lib: drop support for < c-ares 1.16.0 [64] - o lib: make SMB support opt-in [18] - o multi.h: add CURLMNWC_CLEAR_ALL [127] - o rtmp: drop support [91] This release includes the following bugfixes: - o altsvc: cap the list at 5,000 entries [183] - o altsvc: drop the prio field from the struct [185] - o altsvc: skip expired entries read from file [187] - o asyn-ares: connect async [220] - o asyn-ares: drop orphaned variable references [86] - o asyn-ares: fix HTTPS-lookup when not on port 443 [100] - o asyn-thrdd: drop redundant `result` check [291] - o asyn-thrdd: fix clang-tidy unused value warning [125] - o async-ares: fix query counter handling [195] - o autotools: limit checksrc target to ignore non-repo test sources [12] - o badwords-all: exit with correct code on errors [50] - o badwords: combine the whitelisting into a single regex [1] - o badwords: detect the the and with with [51] - o badwords: only check comments and strings in source code [61] - o badwords: rework exceptions, fix many of them [15] - o boringssl: fix more coexist cases with Schannel/WinCrypt [170] - o build: adjust/add casts to fix `-Wformat-signedness` [218] - o build: assume `snprintf()` in `mprintf`, drop feature check [107] - o build: compiler warning silencing tidy-ups [4] - o build: drop `openssl` module dependency for BoringSSL from `libcurl.pc` [33] - o build: drop duplicate `pthread.h` includes [158] - o build: drop redundant `USE_QUICHE` guards [159] - o build: enable `-Wimplicit-int-enum-cast` compiler warning, fix issues [84] - o build: fix `-Wformat-signedness` by adjusting printf masks [226] - o build: link `bcrypt.lib` via vcxproj files [239] - o build: skip detecting `pipe2()` for Apple targets [227] - o build: stop building and installing `runtests.1` and `testcurl.1` [235] - o cf-https-connect: silence `-Wimplicit-int-enum-cast` with HTTPS-RR [132] - o cf-https-connect: silence `-Wimplicit-int-enum-cast` with HTTPS-RR [63] - o cf-ip-happy: limit concurrent attempts [191] - o cf-socket: avoid low risk integer overflow on ancient Solaris [56] - o cfilters: fix Curl_pollset_poll() return code mixup [206] - o clang-tidy: avoid assignments in `if` expressions [175] - o clang-tidy: enable more checks, fix fallouts [254] - o cmake: add CMake Config-based dependency detection [87] - o cmake: add CMake Config-based dependency detection for c-ares, wolfSSL [134] - o cmake: do not install `wcurl` when `BUILD_CURL_EXE=OFF` [265] - o cmake: do not install shell completions when `BUILD_CURL_EXE=OFF` [263] - o cmake: document functions used from Windows system DLLs [103] - o cmake: enable pthreads for BoringSSL/AWS-LC [196] - o cmake: resolve targets recursively when generating `libcurl.pc` [45] - o cmake: rework binutils ld hack to not read `LOCATION` property [41] - o cmake: silence bad library `Threads::Threads` warning [131] - o cmake: use `AIX` built-in variable (with CMake 4.0+) [163] - o config2setopts: make --capath work in proxy disabled builds [113] - o configure: fix `--with-ngtcp2=` option for crypto libs [26] - o configure: fix LibreSSL ngtcp2 1.15.0+ crypto lib selection logic [3] - o configure: prefer dependency-specific variables over `$withval` [35] - o configure: remove superfluous experimental warning for HTTP/3 [169] - o configure: silence useless clang warnings in C89 builds [156] - o configure: tidy up comments [202] - o connect: fix typo on error message - o cookie: fix rejection when tabs in value [189] - o curl-wolfssl.m4: fix to use the correct value for pkg-config directory [36] - o curl.h: replace macros with C++-friendly method to enforce 3 args [110] - o curl_ctype.h: fix spelling in a couple of locally used macros [28] - o curl_get_line: error out on read errors [9] - o curl_get_line: fix potential infinite loop when filename is a directory [46] - o curl_ngtcp2: extend and update callbacks for 1.22.0+ [165] - o curl_ntlm_core: drop redundant PP condition [140] - o curl_ntlm_core: use wolfCrypt DES API with wolfSSL [200] - o curl_setup.h: drop stray/unused `USE_OPENSSL_QUIC` guard [210] - o curl_sha512_256: support delegating to wolfSSL API [149] - o curl_version_info.md: clarify age details [69] - o CURLOPT_HAPROXY_CLIENT_IP.md: mention assumption on data format [96] - o CURLOPT_RTSP_SESSION_ID.md: clarify reuse "dangers" [270] - o CURLOPT_RTSP_SESSION_ID.md: expand the comment [267] - o CURLOPT_RTSP_SESSION_ID.md: minor language fix - o CURLOPT_SOCKS5_AUTH.md: an access property [212] - o CURLOPT_SSL_CTX_FUNCTION.md: expand on effects connection reuse [105] - o CURLOPT_UPLOAD_FLAGS.md: expand [223] - o curlx_now(), prevent zero timestamp [93] - o DEPRECATE: fix minor release number typo - o digest: pass in the user name quoted (as well) [34] - o dns: https-eyeballing async [229] - o dnscache: own source file, improvements [116] - o docs/cmdline-opts/write-out.md: tls_earlydata was adeded in 8.13.0 - o docs/cmdline-opts: tidy up retry-connrefused [190] - o docs/lib: fix typos [53] - o docs/libcurl: improve easy setopt examples [266] - o docs: clarify retry-max-time timing [294] - o docs: CURLOPT_LOGIN_OPTIONS is a login property [228] - o docs: enable more compiler warnings for C snippets, fix 3 finds [71] - o docs: list more dependencies for running Python HTTP tests [123] - o docs: mention more zip bomb precautions [166] - o docs: minor wording tweaks - o docs: noproxy wants the punycoded hostname version [214] - o docs: SSH host verification is done at connect time [197] - o docs: use the correct CURLOPT_WRITEFUNCTION signature [142] - o doh: fix memory-leak when doing a second DoH resolve [55] - o doh: remove superfluous doh_req check [222] - o examples/websocket: fix to sleep more on Windows [92] - o examples: drop warning silencers no longer hit [14] - o examples: fix typo in comment [75] - o file: init fd to -1 to prevent close fd 0 on early failure [40] - o fopen: for temp files, inherit permissions only for owner [146] - o ftp: do not strdup DATA hostname [29] - o ftp: make the MDTM date parser stricter (again) [115] - o ftp: reject PWD responses containing control characters [95] - o gcc: guard `#pragma diagnostic` in core code for <4.6 [94] - o generate.bat: remove extra % from VC11 and VC12 runs - o genserv.pl: make external calls safe [119] - o getinfo: initialize `PureInfo` field `used_proxy` [43] - o getinfo: repair CURLINFO_TLS_SESSION [193] - o gnutls: fix clang-tidy warning with !verbose [126] - o gtls: fail for large files in `load_file()` [174] - o h3: HTTPS-RR use in HTTP/3 [221] - o Happy Eyeballs: add resolution time delay [238] - o haproxy: use correct ip version on client supplied address [275] - o hostip: clear the sockaddr_in6 structure before use [20] - o hostip: init the curl_jmpenv_lock appropriately [278] - o hostip: resolve user supplied ip addresses [259] - o HSTS: cap the list [177] - o hsts: make the HSTS read callback handle name dupes [141] - o hsts: skip expired HSTS entries read from file [188] - o hsts: when a dupe host adds subdomains, use that [130] - o http2: clear the h2 session at delete [99] - o http2: prevent secure schemes pushed over insecure connections [181] - o http2: return error on OOM in push headers [65] - o HTTP3.md: drop outdated mentions of OpenSSL-QUIC [2] - o http: clear credentials better on redirect [204] - o http: clear digest nonce on cross-orgin redirect [269] - o http: clear the proxy credentials as well on port or scheme change [246] - o http: fix auth_used and auth_avail [154] - o http: fix Curl_compareheader for multi value headers [11] - o http: make Curl_compareheader handle multiple commas in header - o http: on 303, switch to GET [208] - o http: use header_has_value() instead of duplicate code [251] - o imap: reset the UIDVALIDITY state between transfers [7] - o include: drop 'will' from public headers [73] - o INSTALL.md: update Cygwin instructions [198] - o keylog.h: replace literal number with macro in declaration [171] - o keylog: drop unused/redundant includes and guards [172] - o ldap: drop duplicate `ldap_set_option()` on Windows [42] - o ldap: fix to initialize cleartext connection on Windows [49] - o lib1560: fix comment typo - o lib1960: fix test failure [255] - o lib: accept larger input to md5/hmac/sha256/sha512 functions [194] - o lib: always use Curl_1st_fatal instead of Curl_1st_err [89] - o lib: fix typos in comments [240] - o lib: make resolving HTTPS DNS records reliable: [176] - o lib: minor comment typos [237] - o lib: move request specific allocations to the request struct [256] - o lib: replace `PRI*32` printf masks with C89 ones [201] - o libssh2: allocate libssh2-friendly memory in kbd_callback [225] - o libssh2: fix error handling on quote errors [21] - o libssh: fix 64-bit printf mask for mingw-w64 <=6.0.0 [215] - o libssh: fix `-Wsign-compare` in 32-bit builds [217] - o libssh: path length precaution [164] - o libssh: propagate error back in SFTP function [178] - o libtest: drop duplicate include [111] - o location/follow: mention netrc [138] - o man: fix argument type for `CURLSHOPT_[UN]SHARE` options [211] - o mbedtls: cleanup more without care for 'initialized' [262] - o mbedtls: fix ECJPAKE matching [135] - o mbedtls: remove failf() call with first argument as NULL [249] - o md4, md5: switch to wolfCrypt API in wolfSSL builds [139] - o mime: only allow 40 levels of calls [241] - o misc: fix code quality findings [209] - o mk-ca-bundle.pl: make `ca-bundle.crt` timestamp match `certdata.txt`'s [44] - o multi: enhance pending handles fairness [284] - o multi: fix connection retry for non-http [180] - o multi: improve wakeup and wait code [118] - o netrc: find login-less password when user is given in URL [6] - o netrc: remove unused parsenetrc() macro for netrc-disabled [121] - o netrc: skip malformed macdef lines [67] - o openssl channel_binding: lookup digest algorithm without NID [117] - o openssl: drop obsolete SSLv2 logic [27] - o openssl: fix build with 4.0.0-beta1 no-deprecated [184] - o openssl: fix memory leaks in ECH code (OpenSSL 3) [78] - o openssl: fix unused variable warnings in !verbose builds [252] - o openssl: trace count of found / imported Windows native CA roots [8] - o OS400: add new definitions to the ILE/RPG binding. [153] - o os400sys: fix typo in comment (symetry -> symmetry) [58] - o parsedate: bsearch the time zones [232] - o parsedate: fix wrong treatment of "military time zones" [182] - o parsedate: refactor [230] - o perl: harden external command invocations [133] - o progress: count amount of data "delivered" to application [66] - o protocol.h: fix the CURLPROTO_MASK [31] - o protocol: disable connection reuse for SMB(S) [199] - o protocol: use scheme names lowercase [38] - o proxy: chunked response, error code [143] - o pytest: add additional quiche check for flaky test_05_01 [22] - o pytest: check 429 handling [268] - o rand: use `BCryptGenRandom()` in UWP builds [88] - o ratelimit: reset on start [150] - o request: reset resp_trailer in new requests [186] - o runtests: skip setting ed25519 SSH key format [264] - o rustls: fix memory leak on repeated SSLKEYLOGFILE fails [280] - o rustls: handle EOF during initial handshake [203] - o schannel: increase renegotiation timeout to 60 seconds [261] - o scripts: drop redundant double-quotes: `"$var"` -> `$var` (Perl) [109] - o scripts: harden / tidy up more Perl `system()` calls [70] - o sectrust: fail on missing OCSP stapling [250] - o sendf: fix CR detection if no LF is in the chunk [219] - o setopt: clear proxy auth properties when switching [192] - o setopt: fix typos in comments [257] - o setopt: move CURLOPT_CURLU [260] - o setup connection filter: mark as setup [234] - o sha256, sha512_256: switch to wolfCrypt API [147] - o sha256: support delegating to wolfSSL API [148] - o share: concurrency handling, easy updates [104] - o share: do bitshifts after the type is checked to be valid [216] - o socks: reject zero-length GSSAPI/SSPI tokens from proxy [157] - o socks: use dns filter for resolving [244] - o spelling: fix typos [173] - o src: use ftruncate() unconditionally [128] - o sshserver.pl: harden more `system()` calls [81] - o sshserver.pl: pass command-line to `system()` safely [82] - o strerr: correct the strerror_s() return code condition [25] - o sws: fix potential OOB write [80] - o synctime: fix off-by-one read and write to a read-only buffer (Windows) [85] - o test 766: flag as timing-dependent [136] - o test1675: unit tests for URL API helper functions [248] - o test459: switch to mode="warn" for stderr check [5] - o testcurl.pl: replace shell commands with Perl `rmtree()` [76] - o tests/unit/README: describe how to unit test static functions [60] - o tests: avoid infinite recursion for `make check` [253] - o tests: use %b64[] instead of "raw" base64 [245] - o tool: check for curlinfo->age when determining if ssh backend [77] - o tool: fix memory mixups [106] - o tool: fix retries in parallel mode [137] - o tool: fix two more allocator mismatches [155] - o tool_cb_hdr: only truncate etags output when regular file [129] - o tool_cb_rea: make waitfd() return void [168] - o tool_cb_wrt: fix no-clobber error handling [39] - o tool_cfgable: free the SSL signature algorithms [62] - o tool_dirhie: fix to create drive-relative directory [276] - o tool_formparse: propagate my_get_line errors when reading headers [102] - o tool_getparam: use correct free function for libcurl memory [68] - o tool_ipfs: accept IPFS gateway URL without set port number [13] - o tool_msgs: avoid null pointer deref for early errors [98] - o tool_operate: actually apply the --parallel-max-host limit [167] - o tool_operate: drop the scheme-guessing in the -G handling [54] - o tool_operate: fix condition for loading `curl-ca-bundle.crt` (Windows) [79] - o tool_operate: fix memory-leak on failed uploads [124] - o tool_operate: fix minor memory-leak on early error [23] - o tool_operate: reset the upload glob counter for next URL [162] - o tool_operhlp: fix `add_file_name_to_url()` result on OOM [32] - o tool_operhlp: iterate through all slashes to find name [114] - o tool_operhlp: propagate low-level OOM in `add_file_name_to_url()` [112] - o tool_setopt: return error on OOM correctly [152] - o tool_urlglob: fix memory-leak on glob range overflow [19] - o top-complexity: prevent filename-based shell injection risk [101] - o transfer: clear the old autoreferer [236] - o transfer: clear the URL pointer in OOM to avoid UAF [179] - o transfer: enable custom methods again on next transfer [30] - o transfer: enhance secure check [10] - o unit1675: fix `-Wformat-signedness` [274] - o url: do not reuse a non-tls starttls connection if new requires TLS [145] - o url: improve connection reuse on negotiate [160] - o url: init req.no_body in DO so that it works for h2 push [161] - o url: set default upload flags to CURLULFLAG_SEEN [224] - o url: use the socks type for socks proxy [47] - o url: use URL for url even in comments [52] - o urlapi: fix handling of "file:///" [122] - o urlapi: make dedotdotify handle leading dots correctly [97] - o urlapi: same origin tests [213] - o urlapi: stop extracting hostname from file:// URLs on Windows [247] - o urlapi: verify the last letter of a scheme when set explicitly [16] - o urldata.h: fix typo and lingering backtick [279] - o urldata: connection bit ipv6_ip is wrong [59] - o urldata: import port types and conn destination format [57] - o urldata: make hstslist only present in HSTS builds [120] - o urldata: make speeder_c uint32 [37] - o urldata: move cookiehost to struct SingleRequest [242] - o urldata: remove trailers_state [17] - o vquic: fix variable name in fallback code [207] - o vtls: fix comment typos and tidy up a type [285] - o vtls: log when key logging is enabled. [288] - o vtls_scache: check reentrancy [243] - o vtls_scache: include cert_blob independently of verifypeer [231] - o wolfssl: document v5.0.0 (2021-11-01) as minimum required [151] - o wolfssl: fix `-Wmissing-prototypes` [233] - o wolfssl: fix handling of abrupt connection close [24] - o write-out.md: minor language fix [273] - o write-out.md: tls_earlydata was adeded in 8.13.0 - o ws: fix a blocking curl_ws_send() to report written length correctly [258] - o x509asn1: fix to return error in an error case from `encodeOID()` [83] - o x509asn1: fixed and adapted for ASN1tostr unit testing [48] - o x509asn1: improve encodeOID [72] + o asyn-thrdd: fix result processing without wakeup socketpair [2] + o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o user-agent.md: mention double quotes too [3] This release includes the following known bugs: @@ -323,306 +36,12 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Alex Hamilton, am-perip on hackerone, Arkadi Vainbrand, bird on github, - BlackFuffey on github, Carlos Carrillo, Carlos Henrique Lima Melara, - crawfordxx, Cutiapreta on hackerone, Dag-Erling Smørgrav, Dan Arnfield, - Dan Fandrich, Daniel McCarney, Daniel Schulte, Daniel Stenberg, - dependabot[bot], Dexter Gerig, Dio Putra, Dwij Mehta, Ercan Ermis, - fds242 on github, finkjsc on github, Fiona Klute, Flavio Amieiro, - Geeknik Labs, Greg Kroah-Hartman, Harry Sintonen, Henrique Pereira, - herbenderbler on github, Ian Spence, Izan on hackerone, James Fuller, - Jason Stangroome, John Haugabook, Juan Belón, Kai Pastor, Kaixuan Li, kpcyrd, - lg_oled77c5pua on hackerone, M42kL33 on hackerone, m777m0 on hackerone, - Marcel Raad, Martin Dürrmeier, Mehtab Zafar, Michael Hendricks, - Michael Kaufmann, Muhamad Arga Reksapati, Ngoc Hieu, nitrogene on github, - Orgad Shaneh, Osama Hamad, Otis Cui Lei, Patrick Monnerat, Quac Tran, - Ray Satiro, renovate[bot], Richard Tollerton, Rob Crittenden, - Samuel Henrique, Scott Boudreaux, Sergey Fedorov, sergio-nsk on github, - Stefan Eissing, Ted Lyngmo, Terrance Wong, Tim Omta, Viktor Szakats, - Vladimír Marek, xkilua on hackerone, Yalguun Tumenkhuu, Yedaya Katsman, - Yiwei Hou, Yoshiro Yoneya - (73 contributors) + Daniel Stenberg, Jeremy Nicoll, Raymond Steen, Stefan Eissing, + Viktor Szakats + (5 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=20880 - [2] = https://curl.se/bug/?i=20914 - [3] = https://curl.se/bug/?i=20889 - [4] = https://curl.se/bug/?i=20908 - [5] = https://curl.se/bug/?i=20910 - [6] = https://curl.se/bug/?i=20950 - [7] = https://curl.se/bug/?i=20962 - [8] = https://curl.se/bug/?i=20899 - [9] = https://curl.se/bug/?i=20958 - [10] = https://curl.se/bug/?i=20951 - [11] = https://curl.se/bug/?i=20894 - [12] = https://curl.se/bug/?i=20898 - [13] = https://curl.se/bug/?i=20957 - [14] = https://curl.se/bug/?i=20896 - [15] = https://curl.se/bug/?i=20886 - [16] = https://curl.se/bug/?i=20893 - [17] = https://curl.se/bug/?i=20960 - [18] = https://curl.se/bug/?i=20846 - [19] = https://curl.se/bug/?i=20956 - [20] = https://curl.se/bug/?i=20885 - [21] = https://curl.se/bug/?i=20883 - [22] = https://curl.se/bug/?i=20952 - [23] = https://curl.se/bug/?i=20954 - [24] = https://curl.se/bug/?i=21002 - [25] = https://curl.se/bug/?i=20955 - [26] = https://curl.se/bug/?i=18022 - [27] = https://curl.se/bug/?i=20945 - [28] = https://curl.se/bug/?i=20810 - [29] = https://curl.se/bug/?i=20953 - [30] = https://curl.se/bug/?i=21037 - [31] = https://curl.se/bug/?i=21031 - [32] = https://curl.se/bug/?i=21011 - [33] = https://curl.se/bug/?i=20926 - [34] = https://curl.se/bug/?i=20940 - [35] = https://curl.se/bug/?i=20944 - [36] = https://curl.se/bug/?i=20943 - [37] = https://curl.se/bug/?i=21036 - [38] = https://curl.se/bug/?i=21033 - [39] = https://curl.se/bug/?i=20939 - [40] = https://curl.se/bug/?i=21029 - [41] = https://curl.se/bug/?i=20839 - [42] = https://curl.se/bug/?i=20930 - [43] = https://curl.se/bug/?i=21020 - [44] = https://curl.se/bug/?i=20528 - [45] = https://curl.se/bug/?i=20840 - [46] = https://curl.se/bug/?i=20823 - [47] = https://curl.se/bug/?i=21025 - [48] = https://curl.se/bug/?i=21013 - [49] = https://curl.se/bug/?i=20927 - [50] = https://curl.se/bug/?i=20934 - [51] = https://curl.se/bug/?i=20934 - [52] = https://curl.se/bug/?i=20935 - [53] = https://curl.se/bug/?i=20933 - [54] = https://curl.se/bug/?i=20992 - [55] = https://curl.se/bug/?i=20929 - [56] = https://curl.se/bug/?i=21111 - [57] = https://curl.se/bug/?i=20918 - [58] = https://curl.se/bug/?i=20923 - [59] = https://curl.se/bug/?i=20919 - [60] = https://curl.se/bug/?i=21018 - [61] = https://curl.se/bug/?i=20909 - [62] = https://curl.se/bug/?i=20915 - [63] = https://curl.se/bug/?i=21057 - [64] = https://curl.se/bug/?i=20911 - [65] = https://hackerone.com/reports/3636044 - [66] = https://curl.se/bug/?i=20787 - [67] = https://curl.se/bug/?i=21049 - [68] = https://curl.se/bug/?i=21075 - [69] = https://curl.se/bug/?i=21052 - [70] = https://curl.se/bug/?i=21007 - [71] = https://curl.se/bug/?i=21006 - [72] = https://curl.se/bug/?i=21003 - [73] = https://curl.se/bug/?i=21005 - [74] = https://curl.se/bug/?i=20916 - [75] = https://curl.se/bug/?i=21001 - [76] = https://curl.se/bug/?i=21053 - [77] = https://curl.se/bug/?i=21050 - [78] = https://curl.se/bug/?i=20993 - [79] = https://curl.se/bug/?i=20989 - [80] = https://curl.se/bug/?i=20988 - [81] = https://curl.se/bug/?i=20997 - [82] = https://curl.se/bug/?i=20996 - [83] = https://curl.se/bug/?i=20991 - [84] = https://curl.se/bug/?i=20990 - [85] = https://curl.se/bug/?i=20987 - [86] = https://curl.se/bug/?i=20999 - [87] = https://curl.se/bug/?i=20814 - [88] = https://curl.se/bug/?i=20983 - [89] = https://curl.se/bug/?i=20980 - [90] = https://curl.se/bug/?i=20698 - [91] = https://curl.se/bug/?i=20673 - [92] = https://curl.se/bug/?i=20978 - [93] = https://curl.se/bug/?i=21034 - [94] = https://curl.se/bug/?i=20892 - [95] = https://curl.se/bug/?i=20949 - [96] = https://curl.se/bug/?i=21042 - [97] = https://curl.se/bug/?i=20974 - [98] = https://curl.se/bug/?i=20967 - [99] = https://curl.se/bug/?i=20975 - [100] = https://curl.se/bug/?i=20966 - [101] = https://curl.se/bug/?i=20969 - [102] = https://curl.se/bug/?i=20963 - [103] = https://curl.se/bug/?i=20965 - [104] = https://curl.se/bug/?i=20870 - [105] = https://curl.se/bug/?i=21164 - [106] = https://curl.se/bug/?i=21099 - [107] = https://curl.se/bug/?i=20763 - [108] = https://curl.se/bug/?i=20407 - [109] = https://curl.se/bug/?i=21009 - [110] = https://curl.se/bug/?i=20709 - [111] = https://curl.se/bug/?i=21046 - [112] = https://curl.se/bug/?i=21011 - [113] = https://curl.se/bug/?i=21063 - [114] = https://curl.se/bug/?i=21165 - [115] = https://curl.se/bug/?i=21041 - [116] = https://curl.se/bug/?i=20864 - [117] = https://curl.se/bug/?i=20590 - [118] = https://curl.se/bug/?i=20832 - [119] = https://curl.se/bug/?i=20971 - [120] = https://curl.se/bug/?i=21068 - [121] = https://curl.se/bug/?i=21067 - [122] = https://curl.se/bug/?i=21070 - [123] = https://curl.se/bug/?i=21110 - [124] = https://curl.se/bug/?i=21062 - [125] = https://curl.se/bug/?i=21061 - [126] = https://curl.se/bug/?i=21060 - [127] = https://curl.se/bug/?i=20968 - [128] = https://curl.se/bug/?i=21109 - [129] = https://curl.se/bug/?i=21103 - [130] = https://curl.se/bug/?i=21108 - [131] = https://curl.se/bug/?i=21170 - [132] = https://curl.se/bug/?i=21167 - [133] = https://curl.se/bug/?i=21097 - [134] = https://curl.se/bug/?i=21098 - [135] = https://curl.se/bug/?i=21264 - [136] = https://curl.se/bug/?i=21155 - [137] = https://curl.se/bug/?i=20669 - [138] = https://curl.se/bug/?i=21091 - [139] = https://curl.se/bug/?i=21093 - [140] = https://curl.se/bug/?i=21096 - [141] = https://curl.se/bug/?i=21201 - [142] = https://curl.se/bug/?i=21265 - [143] = https://curl.se/bug/?i=21084 - [144] = https://curl.se/bug/?i=20936 - [145] = https://curl.se/bug/?i=21082 - [146] = https://curl.se/bug/?i=21092 - [147] = https://curl.se/bug/?i=21090 - [148] = https://curl.se/bug/?i=21078 - [149] = https://curl.se/bug/?i=21077 - [150] = https://curl.se/bug/?i=21086 - [151] = https://curl.se/bug/?i=21080 - [152] = https://curl.se/bug/?i=21083 - [153] = https://curl.se/bug/?i=20672 - [154] = https://curl.se/bug/?i=21274 - [155] = https://curl.se/bug/?i=21150 - [156] = https://curl.se/bug/?i=21263 - [157] = https://curl.se/bug/?i=21159 - [158] = https://curl.se/bug/?i=21144 - [159] = https://curl.se/bug/?i=21135 - [160] = https://curl.se/bug/?i=21203 - [161] = https://curl.se/bug/?i=21194 - [162] = https://curl.se/bug/?i=21402 - [163] = https://curl.se/bug/?i=21134 - [164] = https://curl.se/bug/?i=21193 - [165] = https://curl.se/bug/?i=21152 - [166] = https://curl.se/bug/?i=21143 - [167] = https://curl.se/bug/?i=21147 - [168] = https://curl.se/bug/?i=21127 - [169] = https://curl.se/bug/?i=21139 - [170] = https://curl.se/bug/?i=21136 - [171] = https://curl.se/bug/?i=21141 - [172] = https://curl.se/bug/?i=21137 - [173] = https://curl.se/bug/?i=21198 - [174] = https://curl.se/bug/?i=21256 - [175] = https://curl.se/bug/?i=21256 - [176] = https://curl.se/bug/?i=21175 - [177] = https://curl.se/bug/?i=21190 - [178] = https://curl.se/bug/?i=21122 - [179] = https://curl.se/bug/?i=21123 - [180] = https://curl.se/bug/?i=21121 - [181] = https://curl.se/bug/?i=21113 - [182] = https://curl.se/bug/?i=21251 - [183] = https://curl.se/bug/?i=21183 - [184] = https://curl.se/bug/?i=21119 - [185] = https://curl.se/bug/?i=21188 - [186] = https://curl.se/bug/?i=21112 - [187] = https://curl.se/bug/?i=21187 - [188] = https://curl.se/bug/?i=21186 - [189] = https://curl.se/bug/?i=21185 - [190] = https://curl.se/bug/?i=21182 - [191] = https://curl.se/bug/?i=21252 - [192] = https://curl.se/bug/?i=21453 - [193] = https://curl.se/bug/?i=21290 - [194] = https://curl.se/bug/?i=21174 - [195] = https://curl.se/bug/?i=21399 - [196] = https://curl.se/bug/?i=21168 - [197] = https://curl.se/bug/?i=21173 - [198] = https://curl.se/bug/?i=20995 - [199] = https://curl.se/bug/?i=21238 - [200] = https://curl.se/bug/?i=21247 - [201] = https://curl.se/bug/?i=21234 - [202] = https://curl.se/bug/?i=21246 - [203] = https://curl.se/bug/?i=21242 - [204] = https://curl.se/bug/?i=21345 - [206] = https://curl.se/bug/?i=21231 - [207] = https://curl.se/bug/?i=21281 - [208] = https://curl.se/bug/?i=20715 - [209] = https://curl.se/bug/?i=21393 - [210] = https://curl.se/bug/?i=21235 - [211] = https://curl.se/bug/?i=21232 - [212] = https://curl.se/bug/?i=21230 - [213] = https://curl.se/bug/?i=21328 - [214] = https://curl.se/bug/?i=21228 - [215] = https://curl.se/bug/?i=21229 - [216] = https://curl.se/bug/?i=21224 - [217] = https://curl.se/bug/?i=21225 - [218] = https://curl.se/bug/?i=21339 - [219] = https://curl.se/bug/?i=21221 - [220] = https://curl.se/bug/?i=21205 - [221] = https://curl.se/bug/?i=21253 - [222] = https://curl.se/bug/?i=21216 - [223] = https://curl.se/bug/?i=21218 - [224] = https://curl.se/bug/?i=21217 - [225] = https://curl.se/bug/?i=21336 - [226] = https://curl.se/bug/?i=21335 - [227] = https://curl.se/bug/?i=21236 - [228] = https://curl.se/bug/?i=21215 - [229] = https://curl.se/bug/?i=21267 - [230] = https://curl.se/bug/?i=21394 - [231] = https://curl.se/bug/?i=21222 - [232] = https://curl.se/bug/?i=21266 - [233] = https://curl.se/bug/?i=21392 - [234] = https://curl.se/bug/?i=21437 - [235] = https://curl.se/bug/?i=21461 - [236] = https://curl.se/bug/?i=21322 - [237] = https://curl.se/bug/?i=21388 - [238] = https://curl.se/bug/?i=21354 - [239] = https://curl.se/bug/?i=21386 - [240] = https://curl.se/bug/?i=21385 - [241] = https://curl.se/bug/?i=21384 - [242] = https://curl.se/bug/?i=21312 - [243] = https://curl.se/bug/?i=21383 - [244] = https://curl.se/bug/?i=21297 - [245] = https://curl.se/bug/?i=21313 - [246] = https://curl.se/bug/?i=21304 - [247] = https://curl.se/bug/?i=21296 - [248] = https://curl.se/bug/?i=21296 - [249] = https://curl.se/bug/?i=21441 - [250] = https://curl.se/bug/?i=21444 - [251] = https://curl.se/bug/?i=21302 - [252] = https://curl.se/bug/?i=21380 - [253] = https://curl.se/bug/?i=21378 - [254] = https://curl.se/bug/?i=20794 - [255] = https://curl.se/bug/?i=21377 - [256] = https://curl.se/bug/?i=21301 - [257] = https://curl.se/bug/?i=21303 - [258] = https://curl.se/bug/?i=21372 - [259] = https://curl.se/bug/?i=21146 - [260] = https://curl.se/bug/?i=21298 - [261] = https://curl.se/bug/?i=21270 - [262] = https://curl.se/bug/?i=21440 - [263] = https://curl.se/bug/?i=21460 - [264] = https://curl.se/bug/?i=21360 - [265] = https://curl.se/bug/?i=21458 - [266] = https://curl.se/bug/?i=21364 - [267] = https://curl.se/bug/?i=21363 - [268] = https://curl.se/bug/?i=21357 - [269] = https://curl.se/bug/?i=21359 - [270] = https://curl.se/bug/?i=21358 - [273] = https://curl.se/bug/?i=21455 - [274] = https://curl.se/bug/?i=21351 - [275] = https://curl.se/bug/?i=21340 - [276] = https://curl.se/bug/?i=21449 - [278] = https://curl.se/bug/?i=21432 - [279] = https://curl.se/bug/?i=21430 - [280] = https://curl.se/bug/?i=21427 - [284] = https://curl.se/bug/?i=21396 - [285] = https://curl.se/bug/?i=21421 - [288] = https://curl.se/bug/?i=19814 - [291] = https://curl.se/bug/?i=21415 - [294] = https://curl.se/bug/?i=21411 + [2] = https://curl.se/bug/?i=21476 + [3] = https://curl.se/mail/archive-2026-04/0029.html + [7] = https://hackerone.com/reports/3702718 diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 144f5fea17b3..231adf743a93 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.20.0-DEV" +#define LIBCURL_VERSION "8.20.1-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 #define LIBCURL_VERSION_MINOR 20 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always follows this syntax: @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081400 +#define LIBCURL_VERSION_NUM 0x081401 /* * This is the date and time when the full source package was created. The From 3f9baa890e05471d7cc4a434237f819baaa4239d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 1 May 2026 11:13:27 +0200 Subject: [PATCH 0009/1062] url: simplify parseurlandfillconn Introduce two helper functions: - hsts_upgrade() - setup_hostname() Closes #21479 --- lib/url.c | 144 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 62 deletions(-) diff --git a/lib/url.c b/lib/url.c index 5fe68033d9d6..2f1d6e5f2fe7 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1521,6 +1521,81 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, #define zonefrom_url(a, b, c) Curl_nop_stmt #endif + +#ifndef CURL_DISABLE_HSTS +static CURLcode hsts_upgrade(struct Curl_easy *data, + struct connectdata *conn, + CURLU *uh) +{ + /* HSTS upgrade */ + if(data->hsts && curl_strequal("http", data->state.up.scheme) && + /* This MUST use the IDN decoded name */ + Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { + char *url; + CURLUcode uc; + curlx_safefree(data->state.up.scheme); + uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); + if(uc) + return Curl_uc_to_curlcode(uc); + Curl_bufref_free(&data->state.url); + /* after update, get the updated version */ + uc = curl_url_get(uh, CURLUPART_URL, &url, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); + if(uc) { + curlx_free(url); + return Curl_uc_to_curlcode(uc); + } + Curl_bufref_set(&data->state.url, url, 0, curl_free); + infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); + } + return CURLE_OK; +} +#else +#define hsts_upgrade(x, y, z) CURLE_OK +#endif + +static CURLcode setup_hostname(struct Curl_easy *data, + struct connectdata *conn, + CURLU *uh) +{ + const char *hostname; + size_t hlen; + CURLUcode uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); + if(uc) { + /* file:// URLs are allowed to not have a host, all other errors need to + be passed back */ + if(!curl_strequal("file", data->state.up.scheme) || + (uc != CURLUE_NO_HOST)) + return Curl_uc_to_curlcode(uc); + } + else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); + return CURLE_URL_MALFORMAT; + } + + hostname = data->state.up.hostname; + hlen = hostname ? strlen(hostname) : 0; + + if(hostname && hostname[0] == '[') { + /* This looks like an IPv6 address literal. See if there is an address + scope. */ + hostname++; + hlen -= 2; + + zonefrom_url(uh, data, conn); + } + + /* make sure the connect struct gets its own copy of the hostname */ + conn->host.rawalloc = curlx_memdup0(hostname, hlen); + if(!conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + conn->host.name = conn->host.rawalloc; + + return CURLE_OK; +} + /* * Parse URL and fill in the relevant members of the connection struct. */ @@ -1530,8 +1605,6 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, CURLcode result; CURLU *uh; CURLUcode uc; - char *hostname; - size_t hlen; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); up_free(data); /* cleanup previous leftovers first */ @@ -1581,70 +1654,17 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(uc) return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); - if(uc) { - if(!curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { - failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); - return CURLE_URL_MALFORMAT; - } - - hostname = data->state.up.hostname; - hlen = hostname ? strlen(hostname) : 0; - - if(hostname && hostname[0] == '[') { - /* This looks like an IPv6 address literal. See if there is an address - scope. */ - /* cut off the brackets after copying this! */ - hostname++; - hlen -= 2; - - zonefrom_url(uh, data, conn); - } - - /* make sure the connect struct gets its own copy of the hostname */ - conn->host.rawalloc = curlx_strdup(hostname ? hostname : ""); - if(!conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */ - conn->host.name = conn->host.rawalloc; + result = setup_hostname(data, conn, uh); /************************************************************* * IDN-convert the hostnames *************************************************************/ - result = Curl_idnconvert_hostname(&conn->host); - if(result) - return result; - -#ifndef CURL_DISABLE_HSTS - /* HSTS upgrade */ - if(data->hsts && curl_strequal("http", data->state.up.scheme)) { - /* This MUST use the IDN decoded name */ - if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { - char *url; - curlx_safefree(data->state.up.scheme); - uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); - if(uc) - return Curl_uc_to_curlcode(uc); - Curl_bufref_free(&data->state.url); - /* after update, get the updated version */ - uc = curl_url_get(uh, CURLUPART_URL, &url, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) { - curlx_free(url); - return Curl_uc_to_curlcode(uc); - } - Curl_bufref_set(&data->state.url, url, 0, curl_free); - infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); - } - } -#endif - - result = findprotocol(data, conn, data->state.up.scheme); + if(!result) + result = Curl_idnconvert_hostname(&conn->host); + if(!result) + result = hsts_upgrade(data, conn, uh); + if(!result) + result = findprotocol(data, conn, data->state.up.scheme); if(result) return result; From faa4b0692d30986c498d91ad36223cbf02796ad1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 1 May 2026 11:28:30 +0200 Subject: [PATCH 0010/1062] tool_formparse.c: fix two minor comment typos Pointed out by Copilot Closes #21480 --- src/tool_formparse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 67e73fc47926..eb43b06ed981 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -731,7 +731,7 @@ static int get_param_part(char endchar, * file and do like this: * * 'name=foo;headers=@headerfile' or why not - * 'name=@filemame;headers=@headerfile' + * 'name=@filename;headers=@headerfile' * * To upload a file, but to fake the filename that is included in the * formpost, do like this: @@ -740,8 +740,8 @@ static int get_param_part(char endchar, * 'name=@filename;filename="play, play, and play.txt"' * * If filename/path contains ',' or ';', it must be quoted by double-quotes, - * else curl fails to figure out the correct filename. if the filename - * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. + * else curl fails to figure out the correct filename. if the filename to be + * quoted contains '"' or '\', '"' and '\' must be escaped by backslash. * ***************************************************************************/ From 47755c4e69710552f5951190980114ccf3a42707 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 17:50:42 +0000 Subject: [PATCH 0011/1062] GHA: bump actions and pips - update action `actions/cache` from 5.0.4 to 5.0.5 - update action `actions/upload-artifact` from 7.0.0 to 7.0.1 - update action `github/codeql-action` from 4.32.4 to 4.35.2 - update action `msys2/setup-msys2` from 2.31.0 to 2.31.1 - update pip `filelock` from 3.25.2 to 3.29.0 - update pip `impacket` to 0.13.0 - update pip `ruff` from 0.15.10 to 0.15.12 Closes #21483 Closes #21482 --- .github/scripts/requirements.txt | 2 +- .github/workflows/codeql.yml | 8 ++--- .github/workflows/distcheck.yml | 4 +-- .github/workflows/http3-linux.yml | 54 +++++++++++++++---------------- .github/workflows/linux.yml | 28 ++++++++-------- .github/workflows/macos.yml | 2 +- .github/workflows/non-native.yml | 2 +- .github/workflows/windows.yml | 20 ++++++------ tests/http/requirements.txt | 2 +- tests/requirements.txt | 2 +- 10 files changed, 62 insertions(+), 62 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 19993371414f..a23dce588260 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -6,4 +6,4 @@ cmakelang==0.6.13 codespell==2.4.2 pytype==2024.10.11 reuse==6.2.0 -ruff==0.15.10 +ruff==0.15.12 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8d4f6f9dbf0d..ea8927aaf1a4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -49,13 +49,13 @@ jobs: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -92,7 +92,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: languages: cpp build-mode: manual @@ -139,4 +139,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index bbe1e3864431..9d09bc94b02b 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -49,7 +49,7 @@ jobs: - name: 'maketgz' run: SOURCE_DATE_EPOCH=1711526400 ./scripts/maketgz 99.98.97 - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: 'release-tgz' path: 'curl-99.98.97.tar.gz' @@ -268,7 +268,7 @@ jobs: matrix: image: [ubuntu-24.04-arm, macos-latest, windows-2022] steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ contains(matrix.image, 'windows') }} with: msystem: mingw64 diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 91699e18b212..63d60c119e3c 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -69,7 +69,7 @@ jobs: steps: - name: 'cache openssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-http3-no-deprecated env: cache-name: cache-openssl-http3-no-deprecated @@ -78,7 +78,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - name: 'cache openssl-prev' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-prev-http3-no-deprecated env: cache-name: cache-openssl-prev-http3-no-deprecated @@ -87,7 +87,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - name: 'cache libressl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl env: cache-name: cache-libressl @@ -96,7 +96,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'cache awslc' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -105,7 +105,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'cache boringssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -114,7 +114,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - name: 'cache nettle' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nettle env: cache-name: cache-nettle @@ -123,7 +123,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NETTLE_VERSION }} - name: 'cache gnutls' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-gnutls env: cache-name: cache-gnutls @@ -132,7 +132,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} - name: 'cache wolfssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -141,7 +141,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'cache nghttp3' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -150,7 +150,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP3_VERSION }} - name: 'cache ngtcp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -160,7 +160,7 @@ jobs: ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" - name: 'cache ngtcp2 openssl-prev' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-openssl-prev env: cache-name: cache-ngtcp2-openssl-prev @@ -169,7 +169,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - name: 'cache ngtcp2 boringssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -178,7 +178,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} - name: 'cache nghttp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -571,7 +571,7 @@ jobs: echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'cache openssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-http3-no-deprecated env: cache-name: cache-openssl-http3-no-deprecated @@ -582,7 +582,7 @@ jobs: - name: 'cache openssl-prev' if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-prev-http3-no-deprecated env: cache-name: cache-openssl-prev-http3-no-deprecated @@ -593,7 +593,7 @@ jobs: - name: 'cache libressl' if: ${{ contains(matrix.build.name, 'libressl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl env: cache-name: cache-libressl @@ -604,7 +604,7 @@ jobs: - name: 'cache awslc' if: ${{ contains(matrix.build.name, 'awslc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -615,7 +615,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -626,7 +626,7 @@ jobs: - name: 'cache nettle' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nettle env: cache-name: cache-nettle @@ -637,7 +637,7 @@ jobs: - name: 'cache gnutls' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-gnutls env: cache-name: cache-gnutls @@ -648,7 +648,7 @@ jobs: - name: 'cache wolfssl' if: ${{ contains(matrix.build.name, 'wolfssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -658,7 +658,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp3' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -668,7 +668,7 @@ jobs: fail-on-cache-miss: true - name: 'cache ngtcp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -680,7 +680,7 @@ jobs: - name: 'cache ngtcp2 openssl-prev' if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-openssl-prev env: cache-name: cache-ngtcp2-openssl-prev @@ -691,7 +691,7 @@ jobs: - name: 'cache ngtcp2 boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -701,7 +701,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -712,7 +712,7 @@ jobs: - name: 'cache quiche' if: ${{ contains(matrix.build.name, 'quiche') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-quiche env: cache-name: cache-quiche diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index eeda1085d7bb..8b39455a352b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -514,7 +514,7 @@ jobs: - name: 'cache libressl (c-arm)' if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl-c-arm env: cache-name: cache-libressl-c-arm @@ -535,7 +535,7 @@ jobs: - name: 'cache libressl (filc)' if: ${{ contains(matrix.build.install_steps, 'libressl-filc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl-filc env: cache-name: cache-libressl-filc @@ -557,7 +557,7 @@ jobs: - name: 'cache nghttp2 (filc)' if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2-filc env: cache-name: cache-nghttp2-filc @@ -580,7 +580,7 @@ jobs: - name: 'cache wolfssl (all-arm)' if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-all-arm env: cache-name: cache-wolfssl-all-arm @@ -602,7 +602,7 @@ jobs: - name: 'cache wolfssl (opensslextra-intel)' # does support `OPENSSL_COEXIST` if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-opensslextra-intel env: cache-name: cache-wolfssl-opensslextra-intel @@ -624,7 +624,7 @@ jobs: - name: 'cache wolfssl (opensslextra-arm)' # does support `OPENSSL_COEXIST` if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-opensslextra-arm env: cache-name: cache-wolfssl-opensslextra-arm @@ -646,7 +646,7 @@ jobs: - name: 'cache mbedtls (latest-intel)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-latest-intel env: cache-name: cache-mbedtls-latest-intel @@ -670,7 +670,7 @@ jobs: - name: 'cache mbedtls (latest-arm)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-latest-arm env: cache-name: cache-mbedtls-latest-arm @@ -694,7 +694,7 @@ jobs: - name: 'cache mbedtls (prev)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-prev env: cache-name: cache-mbedtls-prev @@ -718,7 +718,7 @@ jobs: - name: 'cache openldap (static)' if: ${{ contains(matrix.build.install_steps, 'openldap-static') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openldap-static env: cache-name: cache-openldap-static @@ -739,7 +739,7 @@ jobs: - name: 'cache openssl (thread sanitizer)' if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-tsan env: cache-name: cache-openssl-tsan @@ -758,7 +758,7 @@ jobs: - name: 'cache awslc' if: ${{ contains(matrix.build.install_steps, 'awslc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -779,7 +779,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.install_steps, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -801,7 +801,7 @@ jobs: - name: 'cache rustls' if: ${{ contains(matrix.build.install_steps, 'rustls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-rustls env: cache-name: cache-rustls diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4ce54f39d2c5..edc877b38342 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -114,7 +114,7 @@ jobs: - name: 'cache libressl' if: ${{ contains(matrix.build.install_steps, 'libressl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl env: cache-name: cache-libressl diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c8a0f2156bea..1f2753fe8f1d 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -354,7 +354,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 install libfl2 - name: 'cache compiler (djgpp)' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-compiler with: path: ~/djgpp diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6b4366a7f916..696fd09bab9a 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -51,7 +51,7 @@ jobs: image: [windows-11-arm, windows-2022] # Cannot share cache between arm and intel: https://github.com/actions/cache/issues/1622 steps: - name: 'cache test prereqs (stunnel)' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -192,7 +192,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -322,7 +322,7 @@ jobs: install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ matrix.sys == 'msys' }} with: msystem: ${{ matrix.sys }} @@ -338,7 +338,7 @@ jobs: libpsl-devel ${{ matrix.install }} - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ matrix.sys != 'msys' }} with: msystem: ${{ matrix.sys }} @@ -463,7 +463,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -631,7 +631,7 @@ jobs: chkprefill: '' # Set it once to silence actionlint fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 with: msystem: ${{ matrix.sys }} release: false @@ -643,7 +643,7 @@ jobs: ${{ matrix.install }} - name: 'cache compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-compiler with: path: D:\my-cache @@ -727,7 +727,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -971,7 +971,7 @@ jobs: fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 with: msystem: ${{ matrix.arch == 'arm64' && 'clangarm64' || 'ucrt64' }} release: ${{ contains(matrix.image, 'arm') }} @@ -1117,7 +1117,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel diff --git a/tests/http/requirements.txt b/tests/http/requirements.txt index feb0fedf81a2..62c680420d5a 100644 --- a/tests/http/requirements.txt +++ b/tests/http/requirements.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: curl cryptography==46.0.7 -filelock==3.25.2 +filelock==3.29.0 psutil==7.2.2 pytest==9.0.3 pytest-xdist==3.8.0 diff --git a/tests/requirements.txt b/tests/requirements.txt index 0d08837bd745..cb30e3e29827 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: curl -impacket>=0.11.0,<=0.13.0 +impacket==0.13.0 From f59733be23913c5aeb069df2af18ac4c26902fc8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 2 May 2026 17:18:00 +0200 Subject: [PATCH 0012/1062] setopt: changing the proxy port is also a proxy change Test 1589 verifies. Closes #21485 --- lib/setopt.c | 19 ++++-- tests/data/Makefile.am | 12 ++-- tests/data/test1589 | 108 ++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/first.c | 4 ++ tests/libtest/first.h | 1 + tests/libtest/lib1589.c | 132 +++++++++++++++++++++++++++++++++++++ 7 files changed, 266 insertions(+), 12 deletions(-) create mode 100644 tests/data/test1589 create mode 100644 tests/libtest/lib1589.c diff --git a/lib/setopt.c b/lib/setopt.c index b8a632748c7a..330595876ae9 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1027,16 +1027,24 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, #endif /* !USE_SSL */ } +#ifndef CURL_DISABLE_PROXY +static void changeproxy(struct Curl_easy *data) +{ + Curl_auth_digest_cleanup(&data->state.proxydigest); + memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); +} + static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, long arg) { -#ifndef CURL_DISABLE_PROXY struct UserDefined *s = &data->set; switch(option) { case CURLOPT_PROXYPORT: if((arg < 0) || (arg > UINT16_MAX)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(arg != s->proxyport) + changeproxy(data); s->proxyport = (uint16_t)arg; break; case CURLOPT_PROXYAUTH: @@ -1055,13 +1063,17 @@ static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, return CURLE_UNKNOWN_OPTION; } return CURLE_OK; +} #else +static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, + long arg) +{ (void)data; (void)option; (void)arg; return CURLE_UNKNOWN_OPTION; -#endif } +#endif static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, long arg) @@ -1630,8 +1642,7 @@ static CURLcode setproxy(struct Curl_easy *data, const char *proxy) !strcmp(data->set.str[STRING_PROXY], proxy)) return CURLE_OK; /* same one as before */ - Curl_auth_digest_cleanup(&data->state.proxydigest); - memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); + changeproxy(data); return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 706a4c89ed91..0abf6a0998b9 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -209,13 +209,11 @@ test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 \ test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 \ test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 \ test1580 test1581 test1582 test1583 test1584 test1585 test1586 test1587 \ -test1588 \ -\ -test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \ -test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \ -test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \ -test1614 test1615 test1616 test1617 test1618 test1619 test1620 test1621 \ -test1622 test1623 test1624 test1625 test1626 test1627 \ +test1588 test1589 test1590 test1591 test1592 test1593 test1594 test1595 \ +test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ +test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ +test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ +test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ \ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ diff --git a/tests/data/test1589 b/tests/data/test1589 new file mode 100644 index 000000000000..527edb54cab4 --- /dev/null +++ b/tests/data/test1589 @@ -0,0 +1,108 @@ + + + + +HTTP +HTTP proxy +HTTP proxy Digest auth + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + +# then this is returned when we get proxy-auth + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +# Client-side + + +http +http-proxy + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +!SSPI +crypto +proxy +digest + + +HTTP proxy auth Digest, then change proxy port and do it again + + +http://test.remote.example.com/path/%TESTNUMBER %HOSTIP %HTTPPORT %PROXYPORT silly:person + + + +# Verify data after the test has been "shot" + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="9a547f8fa81cff330c68095603f3819e" +Accept: */* +Proxy-Connection: Keep-Alive + + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="9a547f8fa81cff330c68095603f3819e" +Accept: */* +Proxy-Connection: Keep-Alive + + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 724636464aab..b412cbc9b281 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -96,7 +96,7 @@ TESTS_C = \ lib1552.c lib1553.c lib1554.c lib1555.c lib1556.c lib1557.c lib1558.c \ lib1559.c lib1560.c lib1564.c lib1565.c \ lib1567.c lib1568.c lib1569.c lib1571.c \ - lib1576.c lib1582.c lib1587.c lib1588.c \ + lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ lib1662.c \ diff --git a/tests/libtest/first.c b/tests/libtest/first.c index e4e9dbd8c8e4..a57277b205ee 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -55,6 +55,7 @@ int select_wrapper(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, const char *libtest_arg2 = NULL; const char *libtest_arg3 = NULL; const char *libtest_arg4 = NULL; +const char *libtest_arg5 = NULL; int test_argc; const char **test_argv; int testnum; @@ -272,6 +273,9 @@ int main(int argc, const char **argv) if(argc > 5) libtest_arg4 = argv[5]; + if(argc > 6) + libtest_arg5 = argv[6]; + testnum = 0; env = getenv("CURL_TESTNUM"); if(env) { diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 062cd169be09..9ed8a9c4582b 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -75,6 +75,7 @@ extern int unitfail; /* for unittests */ extern const char *libtest_arg2; /* set by first.c to the argv[2] or NULL */ extern const char *libtest_arg3; /* set by first.c to the argv[3] or NULL */ extern const char *libtest_arg4; /* set by first.c to the argv[4] or NULL */ +extern const char *libtest_arg5; /* set by first.c to the argv[5] or NULL */ /* argc and argv as passed in to the main() function */ extern int test_argc; diff --git a/tests/libtest/lib1589.c b/tests/libtest/lib1589.c new file mode 100644 index 000000000000..e8d029034aa4 --- /dev/null +++ b/tests/libtest/lib1589.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * argv1 = URL + * argv2 = proxy host + * argv3 = proxy1 port + * argv4 = proxy2 port + * argv5 = proxyuser:password + */ + +#include "first.h" + +static CURLcode init1589(CURL *curl, const char *url, + const char *userpwd, const char *proxy, + int port) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY, proxy); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYPORT, (long)port); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; +#if 0 + res_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + if(result) + goto init_failed; +#endif + + res_easy_setopt(curl, CURLOPT_HEADER, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1589(CURL *curl, const char *url, const char *userpwd, + const char *proxy, int port) +{ + CURLcode result = CURLE_OK; + + result = init1589(curl, url, userpwd, proxy, port); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1589(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + const char *proxy = libtest_arg2; + /* !checksrc! disable BANNEDFUNC 2 */ + int port1 = atoi(libtest_arg3); + int port2 = atoi(libtest_arg4); + const char *proxyuserpwd = libtest_arg5; + + if(test_argc < 5) + return TEST_ERR_MAJOR_BAD; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + result = run1589(curl, URL, proxyuserpwd, proxy, port1); + if(result) + goto test_cleanup; + + curl_mfprintf(stderr, "lib1589: now we do the request again\n"); + + result = run1589(curl, URL, proxyuserpwd, proxy, port2); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} From 1963b2382c0a7e65bbaf5531bf35c912816d90dc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 09:55:26 +0200 Subject: [PATCH 0013/1062] gtls: simplify Curl_gtls_verifyserver Move peer certificate verification logic into gtls_verify_cert() Closes #21488 --- lib/vtls/gtls.c | 130 +++++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 9b6a4fab2e39..e0a689eecb48 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1555,21 +1555,75 @@ static CURLcode gtls_chain_get_der(struct Curl_cfilter *cf, *pder_len = (size_t)chain->certs[i].size; return CURLE_OK; } +#endif /* USE_APPLE_SECTRUST */ -static CURLcode glts_apple_verify(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - struct gtls_cert_chain *chain, - bool *pverified) +/* This function verifies the peer's certificate and returns CURLE_OK on + success or an appropriate CURLcode on error. The certificate verification + status bitmask (trusted, invalid etc.) is stored in + ssl_config->certverifyresult as one or more gnutls_certificate_status_t + enumerated elements bitwise or'd. */ +static CURLcode gtls_verify_cert(struct Curl_easy *data, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + gnutls_session_t session, + struct Curl_cfilter *cf, + struct ssl_peer *peer, + struct gtls_cert_chain *chain) { - CURLcode result; + bool verified = FALSE; + unsigned int verify_status = 0; + long * const certverifyresult = &ssl_config->certverifyresult; + int rc = gnutls_certificate_verify_peers2(session, &verify_status); + if(rc < 0) { + failf(data, "server cert verify failed: %d", rc); + *certverifyresult = rc; + return CURLE_SSL_CONNECT_ERROR; + } + *certverifyresult = verify_status; + verified = !(verify_status & GNUTLS_CERT_INVALID); + if(verified) + infof(data, " SSL certificate verified by GnuTLS"); - result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, - gtls_chain_get_der, chain, NULL, 0); - *pverified = !result; - return result; +#ifdef USE_APPLE_SECTRUST + if(!verified && ssl_config->native_ca_store) { + CURLcode result = + Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, + gtls_chain_get_der, chain, NULL, 0); + if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) + return result; /* unexpected error */ + verified = !result; + if(verified) { + infof(data, "SSL certificate verified via Apple SecTrust."); + *certverifyresult = 0; + } + } +#else + (void)cf; + (void)peer; + (void)chain; +#endif + + if(!verified) { + /* verify_status is a bitmask of gnutls_certificate_status bits */ + const char *cause = "certificate error, no details available"; + if(verify_status & GNUTLS_CERT_EXPIRED) + cause = "certificate has expired"; + else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) + cause = "certificate signer not trusted"; + else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) + cause = "certificate uses insecure algorithm"; + else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) + cause = "attached OCSP status response is invalid"; + failf(data, "SSL certificate verification failed: %s. (CAfile: %s " + "CRLfile: %s)", cause, + config->CAfile ? config->CAfile : "none", + ssl_config->primary.CRLfile ? + ssl_config->primary.CRLfile : "none"); + + return CURLE_PEER_FAILED_VERIFICATION; + } + return CURLE_OK; } -#endif /* USE_APPLE_SECTRUST */ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1643,58 +1697,10 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } if(config->verifypeer) { - bool verified = FALSE; - unsigned int verify_status = 0; - /* This function tries to verify the peer's certificate and return - its status (trusted, invalid etc.). The value of status should be - one or more of the gnutls_certificate_status_t enumerated elements - bitwise or'd. To avoid denial of service attacks some default - upper limits regarding the certificate key size and chain size - are set. To override them use - gnutls_certificate_set_verify_limits(). */ - rc = gnutls_certificate_verify_peers2(session, &verify_status); - if(rc < 0) { - failf(data, "server cert verify failed: %d", rc); - *certverifyresult = rc; - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - *certverifyresult = verify_status; - verified = !(verify_status & GNUTLS_CERT_INVALID); - if(verified) - infof(data, " SSL certificate verified by GnuTLS"); - -#ifdef USE_APPLE_SECTRUST - if(!verified && ssl_config->native_ca_store) { - result = glts_apple_verify(cf, data, peer, &chain, &verified); - if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) - goto out; /* unexpected error */ - if(verified) { - infof(data, "SSL certificate verified via Apple SecTrust."); - *certverifyresult = 0; - } - } -#endif - - if(!verified) { - /* verify_status is a bitmask of gnutls_certificate_status bits */ - const char *cause = "certificate error, no details available"; - if(verify_status & GNUTLS_CERT_EXPIRED) - cause = "certificate has expired"; - else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) - cause = "certificate signer not trusted"; - else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) - cause = "certificate uses insecure algorithm"; - else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) - cause = "attached OCSP status response is invalid"; - failf(data, "SSL certificate verification failed: %s. (CAfile: %s " - "CRLfile: %s)", cause, - config->CAfile ? config->CAfile : "none", - ssl_config->primary.CRLfile ? - ssl_config->primary.CRLfile : "none"); - result = CURLE_PEER_FAILED_VERIFICATION; + result = gtls_verify_cert(data, config, ssl_config, session, + cf, peer, &chain); + if(result) goto out; - } } else infof(data, " SSL certificate verification SKIPPED"); From 6f26ecb734d1caa50ca47ceb4700236f3638cf33 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 10:28:10 +0200 Subject: [PATCH 0014/1062] tool_formparse: cleanups - explain the get_param_part() function - make it parse only blanks like the rest of this code - check for commas explicitly when scanning multiple files (to help code understanding) Closes #21489 --- src/tool_formparse.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index eb43b06ed981..b8161813b0bc 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -596,13 +596,40 @@ static void param_encoder(char **ptr, char **endct, char **pencoder, *pencoder = get_param_word(&p, &endpos, endchar); /* If not quoted, strip trailing spaces. */ if(*pencoder == tp) - while(endpos > *pencoder && ISSPACE(endpos[-1])) + while(endpos > *pencoder && ISBLANK(endpos[-1])) endpos--; *sep = *p; *endpos = '\0'; *ptr = p; } +/** + * Parses a single parameter part and its associated metadata from a string. + * + * This function extracts a primary data word and scans for optional + * semicolon-separated attributes including 'type=', 'filename=', 'headers=', + * and 'encoder='. + * + * Used for parsing command-line form arguments or multipart/form-data + * attributes. + * + * @param endchar The character that signifies the end of the entire + * parameter block (e.g., ',' or '\0'). + * @param str Pointer to the current position in the input string. + * Updated to point at the delimiter or terminator that + * ended the parsed part. + * @param pdata Pointer to a char * that will receive the primary data + * word. + * @param ptype [out] Optional. Receives the extracted 'type=' value. + * @param pfilename [out] Optional. Receives the extracted 'filename=' value. + * @param pencoder [out] Optional. Receives the extracted 'encoder=' value. + * @param pheaders [out] Optional. Receives a pointer to a curl_slist + * containing extracted 'headers='. + * + * @return The character that terminated the parsing (casted to int), + * or -1 on memory or parsing error. + */ + static int get_param_part(char endchar, char **str, char **pdata, char **ptype, char **pfilename, char **pencoder, @@ -865,7 +892,7 @@ int formparse(const char *input, SET_TOOL_MIME_PTR(part, encoder); /* *contp could be '\0', so we check with the delimiter */ - } while(sep); /* loop if there is another filename */ + } while(sep == ','); /* loop if there is another filename */ part = (*mimecurrent)->subparts; /* Set name on group. */ } else { From f69ba0408e79b116b9fd9a0fc130bd78a37f2470 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 10:50:50 +0200 Subject: [PATCH 0015/1062] mime: simplify Curl_mime_prepare_headers Make add_content_disposition() a sub function for that single purpose. Closes #21490 --- lib/mime.c | 86 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/lib/mime.c b/lib/mime.c index b7e51aae4e96..a984254731bd 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1692,6 +1692,52 @@ static bool content_type_match(const char *contenttype, return FALSE; } +static CURLcode add_content_disposition(struct Curl_easy *data, + curl_mimepart *part, + const char *disposition, + const char *contenttype, + enum mimestrategy strategy) +{ + if(!disposition) + if(part->filename || part->name || + (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) + disposition = DISPOSITION_DEFAULT; + if(disposition && curl_strequal(disposition, "attachment") && + !part->name && !part->filename) + disposition = NULL; + if(disposition) { + CURLcode result = CURLE_OK; + char *name = NULL; + char *filename = NULL; + + if(part->name) { + name = escape_string(data, part->name, strategy); + if(!name) + return CURLE_OUT_OF_MEMORY; + } + if(part->filename) { + filename = escape_string(data, part->filename, strategy); + if(!filename) + result = CURLE_OUT_OF_MEMORY; + } + if(!result) + result = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name ? "; name=\"" : "", + name ? name : "", + name ? "\"" : "", + filename ? "; filename=\"" : "", + filename ? filename : "", + filename ? "\"" : ""); + curlx_safefree(name); + curlx_safefree(filename); + if(result) + return result; + } + return CURLE_OK; +} + CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, curl_mimepart *part, const char *contenttype, @@ -1750,42 +1796,10 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, /* Issue content-disposition header only if not already set by caller. */ if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { - if(!disposition) - if(part->filename || part->name || - (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) - disposition = DISPOSITION_DEFAULT; - if(disposition && curl_strequal(disposition, "attachment") && - !part->name && !part->filename) - disposition = NULL; - if(disposition) { - char *name = NULL; - char *filename = NULL; - - if(part->name) { - name = escape_string(data, part->name, strategy); - if(!name) - result = CURLE_OUT_OF_MEMORY; - } - if(!result && part->filename) { - filename = escape_string(data, part->filename, strategy); - if(!filename) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) - result = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name ? "; name=\"" : "", - name ? name : "", - name ? "\"" : "", - filename ? "; filename=\"" : "", - filename ? filename : "", - filename ? "\"" : ""); - curlx_safefree(name); - curlx_safefree(filename); - if(result) - return result; - } + result = add_content_disposition(data, part, disposition, + contenttype, strategy); + if(result) + return result; } /* Issue Content-Type header. */ From a790b634c0e24e026971d25e921b60e4df9f6964 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 11:13:19 +0200 Subject: [PATCH 0016/1062] libcurl-easy.md: minor clarifications Closes #21491 --- docs/libcurl/libcurl-easy.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/libcurl/libcurl-easy.md b/docs/libcurl/libcurl-easy.md index 782108835678..fdbc75582371 100644 --- a/docs/libcurl/libcurl-easy.md +++ b/docs/libcurl/libcurl-easy.md @@ -38,14 +38,14 @@ you see what libcurl is doing under the hood, which is useful when debugging for example. The curl_easy_setopt(3) man page has a full index of the over 300 available options. -If you at any point would like to blank all previously set options for a -single easy handle, you can call curl_easy_reset(3) and you can also make a +If you at any point would like to factory-reset all previously set options for +a single easy handle, you can call curl_easy_reset(3). You can also make a clone of an easy handle (with all its set options) using curl_easy_duphandle(3). -When all is setup, you tell libcurl to perform the transfer using -curl_easy_perform(3). It performs the entire transfer operation and does not -return until it is done (successfully or not). +When all necessary options have been set on the handle, you tell libcurl to +perform the transfer with curl_easy_perform(3). It performs the entire +transfer operation and does not return until it is done (successfully or not). After the transfer has been made, you can set new options and make another transfer, or if you are done, cleanup the session by calling From 46e9c65c8fdfe42182190c2133a8a808a4208c37 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 12:47:12 +0200 Subject: [PATCH 0017/1062] socks_gssapi: tiny Curl_SOCKS5_gssapi_negotiate cleanups - use 'result' instead of 'code' for CURLcode variable - use aprintf() instead of malloc + snprintf Closes #21493 --- lib/socks_gssapi.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 32db07044a92..254e84935546 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -103,7 +103,6 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, { struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; size_t actualread; size_t nwritten; CURLcode result; @@ -121,7 +120,6 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, unsigned char socksreq[4]; /* room for GSS-API exchange header only */ const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - const size_t serviceptr_length = strlen(serviceptr); gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; /* GSS-API request looks like @@ -134,7 +132,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, /* prepare service name */ if(strchr(serviceptr, '/')) { - service.length = serviceptr_length; + service.length = strlen(serviceptr); service.value = curlx_memdup(serviceptr, service.length); if(!service.value) return CURLE_OUT_OF_MEMORY; @@ -143,14 +141,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (gss_OID)GSS_C_NULL_OID, &server); } else { - service.value = curlx_malloc(serviceptr_length + - strlen(conn->socks_proxy.host.name) + 2); + service.value = curl_maprintf("%s@%s", + serviceptr, conn->socks_proxy.host.name); if(!service.value) return CURLE_OUT_OF_MEMORY; - service.length = serviceptr_length + - strlen(conn->socks_proxy.host.name) + 1; - curl_msnprintf(service.value, service.length + 1, "%s@%s", - serviceptr, conn->socks_proxy.host.name); + service.length = strlen(service.value); gss_major_status = gss_import_name(&gss_minor_status, &service, GSS_C_NT_HOSTBASED_SERVICE, &server); @@ -203,8 +198,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((unsigned short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, + &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_send_token); @@ -212,10 +208,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_COULDNT_CONNECT; } - code = Curl_conn_cf_send(cf->next, data, - gss_send_token.value, - gss_send_token.length, FALSE, &nwritten); - if(code || (gss_send_token.length != nwritten)) { + result = Curl_conn_cf_send(cf->next, data, + gss_send_token.value, + gss_send_token.length, FALSE, &nwritten); + if(result || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_send_token); @@ -406,8 +402,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -416,17 +412,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); - if(code || (nwritten != 1)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); + if(result || (nwritten != 1)) { failf(data, "Failed to send GSS-API encryption type."); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - code = Curl_conn_cf_send(cf->next, data, gss_w_token.value, - gss_w_token.length, FALSE, &nwritten); - if(code || (gss_w_token.length != nwritten)) { + result = Curl_conn_cf_send(cf->next, data, gss_w_token.value, + gss_w_token.length, FALSE, &nwritten); + if(result || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); From a575601b5b0d6e0fa6843de74b5d1c69ee2bc262 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 17:19:04 +0200 Subject: [PATCH 0018/1062] show-headers.md: mention bold headers and --no-styled-output Mentioned-by: Sollace on github Fixes #21495 Closes #21497 --- .github/scripts/pyspelling.words | 1 + docs/cmdline-opts/show-headers.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 0c986f14c6c2..7d9f6ffb36c5 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -896,6 +896,7 @@ trustless Tse Tsujikawa TTL +tty tvOS txt typedef diff --git a/docs/cmdline-opts/show-headers.md b/docs/cmdline-opts/show-headers.md index d733784ac22d..40c5ae790bec 100644 --- a/docs/cmdline-opts/show-headers.md +++ b/docs/cmdline-opts/show-headers.md @@ -24,6 +24,12 @@ non-HTTP protocols, the "headers" are other server communication. This option makes the response headers get saved in the same stream/output as the data. --dump-header exists to save headers in a separate stream. +When HTTP headers are output to a tty, curl may use escape codes to make the +header field names appear in bold and URLs in `Location:` headers be +especially marked as such. Disable the use of terminal escape codes with +--no-styled-output. (This means using the --styled-output option with a +`--no-` prefix to disable it.) + To view the request headers, consider the --verbose option. Prior to 7.75.0 curl did not print the headers if --fail was used in From cb9cfee9b0a1e92adc47cec03f5ecdc90ae59184 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 16:17:11 +0200 Subject: [PATCH 0019/1062] lib: two minor typos Spotted by Copilot Closes #21496 --- lib/url.c | 2 +- lib/vtls/gtls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/url.c b/lib/url.c index 2f1d6e5f2fe7..accaaaa3adfc 100644 --- a/lib/url.c +++ b/lib/url.c @@ -651,7 +651,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, Curl_attach_connection(data, conn); dead = !Curl_conn_is_alive(data, conn, &input_pending); if(input_pending) { - /* For reuse, we want a "clean" connection state. The includes + /* For reuse, we want a "clean" connection state. This includes * that we expect - in general - no waiting input data. Input * waiting might be a TLS Notify Close, for example. We reject * that. diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index e0a689eecb48..53d687046b65 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -127,7 +127,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "gtls_pull(len=%zu) -> %d, %zu", blen, result, nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ From 1ff399c3f7ec07f5bbe52b416b8b92a824c6a959 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 23:33:49 +0200 Subject: [PATCH 0020/1062] gtls: fix some typos Also make gtls_get_ietf_proto() static Found by Copilot Closes #21498 --- lib/vtls/gtls.c | 40 ++++++++++++++++++++-------------------- lib/vtls/gtls.h | 2 -- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 53d687046b65..c0de44416e8a 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -692,6 +692,24 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, } #ifdef CURL_GNUTLS_EARLY_DATA +static int gtls_get_ietf_proto(gnutls_session_t session) +{ + switch(gnutls_protocol_get_version(session)) { + case GNUTLS_SSL3: + return CURL_IETF_PROTO_SSL3; + case GNUTLS_TLS1_0: + return CURL_IETF_PROTO_TLS1; + case GNUTLS_TLS1_1: + return CURL_IETF_PROTO_TLS1_1; + case GNUTLS_TLS1_2: + return CURL_IETF_PROTO_TLS1_2; + case GNUTLS_TLS1_3: + return CURL_IETF_PROTO_TLS1_3; + default: + return CURL_IETF_PROTO_UNKNOWN; + } +} + CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -740,7 +758,7 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } result = Curl_ssl_session_create2(sdata, sdata_len, - Curl_glts_get_ietf_proto(session), + gtls_get_ietf_proto(session), alpn, valid_until, earlydata_max, qtp_clone, quic_tp_len, &sc_session); @@ -753,24 +771,6 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } #endif -int Curl_glts_get_ietf_proto(gnutls_session_t session) -{ - switch(gnutls_protocol_get_version(session)) { - case GNUTLS_SSL3: - return CURL_IETF_PROTO_SSL3; - case GNUTLS_TLS1_0: - return CURL_IETF_PROTO_TLS1; - case GNUTLS_TLS1_1: - return CURL_IETF_PROTO_TLS1_1; - case GNUTLS_TLS1_2: - return CURL_IETF_PROTO_TLS1_2; - case GNUTLS_TLS1_3: - return CURL_IETF_PROTO_TLS1_3; - default: - return CURL_IETF_PROTO_UNKNOWN; - } -} - #ifdef CURL_GNUTLS_EARLY_DATA static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -839,7 +839,7 @@ static CURLcode gtls_set_priority(struct Curl_cfilter *cf, if((conn_config->cipher_list[0] == '+') || (conn_config->cipher_list[0] == '-') || (conn_config->cipher_list[0] == '!')) { - /* add it to out own */ + /* add it to our own */ if(!curlx_dyn_len(&buf)) { /* not added yet */ result = curlx_dyn_add(&buf, priority); if(result) diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 49106dd869af..4c662003eb82 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -48,8 +48,6 @@ struct ssl_peer; struct ssl_connect_data; struct Curl_ssl_session; -int Curl_glts_get_ietf_proto(gnutls_session_t session); - struct gtls_shared_creds { gnutls_certificate_credentials_t creds; char *CAfile; /* CAfile path used to generate X509 store */ From 117d50b4bf48ca04908f87dd665ba183573587b6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 23:44:25 +0200 Subject: [PATCH 0021/1062] thrdqueue: make thrdq_await_done only for unit tests It is not used for anything else, so drop Curl_ and make it conditional accordingly. Closes #21499 --- lib/thrdqueue.c | 9 +++++++-- tests/unit/unit3301.c | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c index 1521ccfbee10..f68f8e17973c 100644 --- a/lib/thrdqueue.c +++ b/lib/thrdqueue.c @@ -351,11 +351,16 @@ void Curl_thrdq_clear(struct curl_thrdq *tqueue, Curl_mutex_release(&tqueue->lock); } -CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, - uint32_t timeout_ms) +#ifdef UNITTESTS +/* @unittest 3301 */ +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) { return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); } +#endif CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, uint32_t max_len, diff --git a/tests/unit/unit3301.c b/tests/unit/unit3301.c index 9ded7bd93f0f..94b435da4956 100644 --- a/tests/unit/unit3301.c +++ b/tests/unit/unit3301.c @@ -110,7 +110,7 @@ static CURLcode test_unit3301(const char *arg) fail_unless(!r, "queue-b send"); } - r = Curl_thrdq_await_done(tqueue, 0); + r = thrdq_await_done(tqueue, 0); fail_unless(!r, "queue-b await done"); nrecvd = 0; From 03b547f73f8dcfddb108bbfdbdc979f0b55595f1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 09:20:47 +0200 Subject: [PATCH 0022/1062] tool_formparse.c: use define instead of magic number The longest header lines accepted for the -F option is now a define instead of a magic number. I also bumped it to be an even 8K. When fixing, I noticed that for some OOM errors curl would display two error messages. Also fixed here. Closes #21501 --- src/tool_formparse.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index b8161813b0bc..4db2dce96ba2 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -408,14 +408,17 @@ static int slist_append(struct curl_slist **plist, const char *data) return 0; } -/* Read headers from a file and append to list. */ +#define HEADER_LINE_BUFFER_SIZE 8192 + +/* Read headers from a file and append to list. + Return zero on success, non-zero on error. */ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) { struct dynbuf line; bool error = FALSE; int err = 0; - curlx_dyn_init(&line, 8092); + curlx_dyn_init(&line, HEADER_LINE_BUFFER_SIZE); while(my_get_line(fp, &line, &error)) { const char *ptr = curlx_dyn_ptr(&line); size_t len = curlx_dyn_len(&line); @@ -436,7 +439,7 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) /* append this new line onto the previous line */ struct dynbuf amend; struct curl_slist *l = *pheaders; - curlx_dyn_init(&amend, 8092); + curlx_dyn_init(&amend, HEADER_LINE_BUFFER_SIZE); /* find the last node */ while(l && l->next) l = l->next; @@ -450,14 +453,12 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) curl_slist_append */ l->data = curl_maprintf("%s", curlx_dyn_ptr(&amend)); curlx_dyn_free(&amend); - if(!l->data) { - errorf("Out of memory for field headers"); - err = 1; - } + if(!l->data) + err = -1; } - else { + else err = slist_append(pheaders, ptr); - } + if(err) { errorf("Out of memory for field headers"); err = -1; From 484f724a30ed4af50d53ddbb4d308aa51e40c242 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 11:37:03 +0200 Subject: [PATCH 0023/1062] thrdqueue.h: forward declare curl_thrdq unconditionally This allows the unit tests to have a prototype involving such a struct pointer - even when the build is done without threaded resolver. Follow-up to 117d50b4bf48ca04908f87dd665ba Closes #21503 --- lib/thrdqueue.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h index d267f5d09105..8bcdcee03af6 100644 --- a/lib/thrdqueue.h +++ b/lib/thrdqueue.h @@ -26,10 +26,11 @@ #include "curl_setup.h" #include "curlx/timediff.h" +struct curl_thrdq; + #ifdef USE_THREADS struct Curl_easy; -struct curl_thrdq; typedef enum { CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ From 9c9a4f3eabbb6f24277538d28a00afa25ba2839a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 14:34:27 +0200 Subject: [PATCH 0024/1062] thrdqueue.h: minor language polish in comments --- lib/thrdqueue.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h index 8bcdcee03af6..1144bb4185e8 100644 --- a/lib/thrdqueue.h +++ b/lib/thrdqueue.h @@ -36,14 +36,14 @@ typedef enum { CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ } Curl_thrdq_event; -/* Notification callback when "events" happen in the queue. May be - * call from any thread, queue is not locked. */ +/* Notification callback when "events" happen in the queue. May be called from + * any thread, queue is not locked. */ typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue, Curl_thrdq_event ev, void *user_data); -/* Process a queued item. Maybe call from any thread. Queue is - * not locked. */ +/* Process a queued item. May be called from any thread. Queue is not + * locked. */ typedef void Curl_thrdq_item_process_cb(void *item); /* Free an item. May be called from any thread at any time for an From bc40e09f63889a8bc14fa8f7221921eb5b4a559e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 5 May 2026 12:58:22 +0200 Subject: [PATCH 0025/1062] lib: introduce Curl_peer `struct Curl_peer` keeps information about a communication endpoint together. It will replace `conn->host` and `conn->conn_to_host` and proxyinfo host. It will also become part of `struct ssl_peer`. It has a reference counter, so an instance can be shared between connections and filters. Elminiates `conn->host` and `conn->connect_to_host`, used in the proxyinfo structures. Passed to DNS resolution and socks filters, etc. Pass peer to http proxy and socks tunnel filters. Use peer in dns filter and resolving. Make `Curl_peer` a member in the `struct ssl_peer`. Add `docs/internals/PEERS.md` for documentation. Closes #21472 --- docs/Makefile.am | 1 + docs/internals/PEERS.md | 105 +++++ lib/Makefile.inc | 2 + lib/cf-dns.c | 154 ++---- lib/cf-dns.h | 5 +- lib/cf-h1-proxy.c | 88 +++- lib/cf-h1-proxy.h | 6 +- lib/cf-h2-proxy.c | 36 +- lib/cf-h2-proxy.h | 3 +- lib/cf-haproxy.c | 3 +- lib/cf-ip-happy.c | 44 +- lib/cf-socket.c | 2 +- lib/cfilters.c | 43 +- lib/cfilters.h | 7 +- lib/connect.c | 88 ++-- lib/connect.h | 19 +- lib/curl_addrinfo.c | 43 ++ lib/curl_addrinfo.h | 5 + lib/curl_sasl.c | 5 +- lib/ftp.c | 46 +- lib/hostip.c | 23 +- lib/hostip.h | 4 +- lib/hsts.c | 6 + lib/hsts.h | 5 + lib/http.c | 42 +- lib/http2.c | 8 +- lib/http_aws_sigv4.c | 2 +- lib/http_negotiate.c | 4 +- lib/http_ntlm.c | 4 +- lib/http_proxy.c | 124 ++--- lib/http_proxy.h | 15 +- lib/httpsrr.c | 2 +- lib/idn.c | 26 +- lib/idn.h | 9 + lib/ldap.c | 14 +- lib/openldap.c | 4 +- lib/peer.c | 712 ++++++++++++++++++++++++++++ lib/peer.h | 105 +++++ lib/protocol.c | 106 +++-- lib/protocol.h | 8 + lib/rtsp.c | 10 +- lib/smb.c | 7 +- lib/socks.c | 77 +-- lib/socks.h | 11 +- lib/socks_gssapi.c | 4 +- lib/socks_sspi.c | 2 +- lib/url.c | 926 ++++++++++--------------------------- lib/url.h | 3 + lib/urldata.h | 53 +-- lib/vauth/digest.c | 2 +- lib/vauth/digest_sspi.c | 2 +- lib/vauth/vauth.c | 7 +- lib/vquic/vquic-tls.c | 2 +- lib/vssh/libssh.c | 6 +- lib/vssh/libssh2.c | 20 +- lib/vtls/apple.c | 2 +- lib/vtls/gtls.c | 7 +- lib/vtls/mbedtls.c | 5 +- lib/vtls/openssl.c | 30 +- lib/vtls/rustls.c | 2 +- lib/vtls/schannel.c | 11 +- lib/vtls/schannel_verify.c | 2 +- lib/vtls/vtls.c | 41 +- lib/vtls/vtls.h | 4 +- lib/vtls/vtls_scache.c | 14 +- lib/vtls/wolfssl.c | 6 +- scripts/schemetable.c | 5 + 67 files changed, 1898 insertions(+), 1291 deletions(-) create mode 100644 docs/internals/PEERS.md create mode 100644 lib/peer.c create mode 100644 lib/peer.h diff --git a/docs/Makefile.am b/docs/Makefile.am index 77971ac77176..01c223ccb397 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -61,6 +61,7 @@ INTERNALDOCS = \ internals/MQTT.md \ internals/MULTI-EV.md \ internals/NEW-PROTOCOL.md \ + internals/PEERS.md \ internals/PORTING.md \ internals/RATELIMITS.md \ internals/README.md \ diff --git a/docs/internals/PEERS.md b/docs/internals/PEERS.md new file mode 100644 index 000000000000..7eb2bc000cd7 --- /dev/null +++ b/docs/internals/PEERS.md @@ -0,0 +1,105 @@ + + +# curl peers + +A `peer` in curl internals is represented by a `struct Curl_peer`. It has the following members: + +* `scheme`: a `struct Curl_scheme` of the URL schemes known to curl +* `user_hostname`: the hostname as supplied by the user/application +* `hostname`: a *normalized* version of `user_hostname` +* `port`: the network port +* `ipv6`: if `hostname` is an IPv6 address +* `unix_socket`: if `hostname` is a path to a `unix domain socket` +* `user_ipv6zone`: user supplied IPv6 zone name or `NULL` +* `ipv6scope_id`: IPv6 address scope or 0 +* `abstract`: (if `unix_socket`) if the socket is abstract + +A peer, in short, is a communication endpoint. + +## peers and connections + +A network connection always goes *somewhere*. That *somewhere* is called +the `origin` of the connection (e.g. the source of responses/downloads). +It is kept in `conn->origin` and is always present in a connection. + +The `origin` is *logical* endpoint a connection talks to. + +For most connections, the `origin` is connected to *directly*. It +can be directed to another peer, however. + +### `connect-to` + +With the command line option `--connect-to` or the `libcurl` option +`CURLOPT_CONNECT_TO`, a connection can be told to make the network connection +to another endpoint *while keeping the `origin` unchanged*. + +This other endpoint is also a peer and is available as `conn->via_peer`. +This may be a peer for a different hostname and port or it may be a +`unix domain socket`. + +### proxies + +When a connection uses a proxy, the endpoint for contacting the proxy server +is also represented as a peer and is kept at `conn->socks_proxy.peer` and/or +`conn->http_proxy.peer`. `SOCKS` proxies always come first, so a connection +might connect as: + +``` +1. curl -------------------------------------------> conn->origin +2. curl -------------------------------------------> conn->via_peer (acting as conn->origin) +3. curl --> socks_proxy.peer ----------------------> conn->via_peer/origin +4. curl -----------------------> http_proxy.peer --> conn->via_peer/origin +5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin +``` + +The connection filter `SETUP`, that assembles the filters for a connection, +figures out which peer to pass to which filter in order to make it all work. +The individual filters get passed a specific peer and do not need be concerned +with the whole chain. + +For example, IP connection goes to `origin`(1), `via_peer`(2), +`socks_proxy.peer`(3+5), `http_proxy.peer`(4) and that is the peer that gets +passed to the `DNS` and `HAPPY-EYEBALLS` filters. + +### TLS + +TLS filters' task is to verify the peer they talk to (unless that is +switched off). They either talk to the `conn->origin` or the +`conn->http_proxy.peer` (`SOCKS` does not have TLS). The `conn->via_peer` is +irrelevant. A `via_peer` endpoint needs to present a certificate matching +`conn->origin` or the connect must fail. + +### `unix domain socket`s + +Peers that represent a `unix domain socket` may be used in two places: + +1. `via_peer`: curl can connect to an `origin` server via `unix domain socket`s. + This disables any proxy settings a transfer might carry. +2. `socks_proxy.peer`: a `SOCKS` proxy may be contacted over a `unix domain + socket`. + +It is not supported to contact an http proxy over `unix domain socket`s. + +## peers and credentials + +There have been several vulnerabilities by leaking credentials in requests +where they should not appear. In future work we plan to tie credentials to +`peers` and use them only when their `peer` still matches the current +connection use. + +## peers internals + +A `struct Curl_peer` is allocated with space of the `user_hostname`. +Only when the user supplied value needs conversions (removing `[]` or +IDN encoding) is `hostname` an extra allocation. This keeps the number +of allocations the same as before. + +A `Curl_peer` is not expected to be modified after it has been created. +However, each `Curl_peer` has a reference counter. Code needs to use +`Curl_peer_link()` and `Curl_peer_unlink()` to keep/release references. +This makes it safe and cheap to keep references to peers in connections +and filters. diff --git a/lib/Makefile.inc b/lib/Makefile.inc index d762f72e42c9..f1b0ef8f0d93 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -233,6 +233,7 @@ LIB_CFILES = \ noproxy.c \ openldap.c \ parsedate.c \ + peer.c \ pingpong.c \ pop3.c \ progress.c \ @@ -364,6 +365,7 @@ LIB_HFILES = \ netrc.h \ noproxy.h \ parsedate.h \ + peer.h \ pingpong.h \ pop3.h \ progress.h \ diff --git a/lib/cf-dns.c b/lib/cf-dns.c index e763b8ed3849..6044868164ba 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -37,48 +37,41 @@ struct cf_dns_ctx { struct Curl_dns_entry *dns; + struct Curl_peer *peer; CURLcode resolv_result; uint32_t resolv_id; - uint16_t port; uint8_t dns_queries; uint8_t transport; BIT(started); BIT(announced); - BIT(abstract_unix_socket); BIT(complete_resolve); BIT(for_proxy); - char hostname[1]; }; static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, + uint8_t transport, bool for_proxy, bool complete_resolve, struct Curl_dns_entry *dns) { struct cf_dns_ctx *ctx; - size_t hlen = strlen(hostname); - ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) return NULL; - ctx->port = port; + Curl_peer_link(&ctx->peer, peer); ctx->dns_queries = dns_queries; ctx->transport = transport; - ctx->abstract_unix_socket = abstract_unix_socket; ctx->for_proxy = for_proxy; ctx->complete_resolve = complete_resolve; ctx->dns = Curl_dns_entry_link(data, dns); ctx->started = !!ctx->dns; - if(hlen) - memcpy(ctx->hostname, hostname, hlen); CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", - ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); + peer->hostname, peer->port, ctx->transport, ctx->dns_queries); return ctx; } @@ -86,6 +79,7 @@ static void cf_dns_ctx_destroy(struct Curl_easy *data, struct cf_dns_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->peer); Curl_dns_entry_unlink(data, &ctx->dns); curlx_free(ctx); } @@ -131,16 +125,14 @@ static void cf_dns_report(struct Curl_cfilter *cf, !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) return; - switch(ctx->transport) { - case TRNSPRT_UNIX: + if(ctx->peer->unix_socket) { #ifdef USE_UNIX_SOCKETS - CURL_TRC_CF(data, cf, "resolved unix domain %s", - Curl_conn_get_unix_path(data->conn)); + CURL_TRC_CF(data, cf, "resolved unix://%s", ctx->peer->hostname); #else DEBUGASSERT(0); #endif - break; - default: + } + else { curlx_dyn_init(&tmp, 1024); infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); #ifdef CURLRES_IPV6 @@ -161,7 +153,6 @@ static void cf_dns_report(struct Curl_cfilter *cf, } #endif curlx_dyn_free(&tmp); - break; } } #else @@ -181,24 +172,19 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, *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, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); - if(Curl_is_ipv4addr(ctx->hostname)) + CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", + ctx->peer->unix_socket ? "unix-domain-socket" : "host", + ctx->peer->hostname, ctx->peer->port); + if(ctx->peer->unix_socket) + ctx->dns_queries = 0; + else if(Curl_is_ipv4addr(ctx->peer->hostname)) ctx->dns_queries |= CURL_DNSQ_A; #ifdef USE_IPV6 - else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + else if(ctx->peer->ipv6) ctx->dns_queries |= CURL_DNSQ_AAAA; #endif - result = Curl_resolv(data, ctx->dns_queries, - ctx->hostname, ctx->port, ctx->transport, + + result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, (bool)ctx->for_proxy, timeout_ms, &ctx->resolv_id, pdns); DEBUGASSERT(!result || !*pdns); @@ -211,14 +197,14 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ failf(data, "Failed to resolve '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", ctx->hostname, + FMT_TIMEDIFF_T " ms", ctx->peer->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); + failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } } @@ -393,11 +379,9 @@ struct Curl_cftype Curl_cft_dns = { static CURLcode cf_dns_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, bool for_proxy, bool complete_resolve, struct Curl_dns_entry *dns) @@ -407,9 +391,8 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, - abstract_unix_socket, for_proxy, - complete_resolve, dns); + ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, + for_proxy, complete_resolve, dns); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -424,60 +407,6 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, 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 dns_queries, - uint8_t transport, - bool complete_resolve, - struct Curl_dns_entry *dns) -{ - struct connectdata *conn = data->conn; - const char *hostname = NULL; - uint16_t port = 0; - bool abstract_unix_socket = FALSE, for_proxy = 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->bits.proxy) { - for_proxy = TRUE; - hostname = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.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, dns_queries, - hostname, port, transport, - abstract_unix_socket, for_proxy, - complete_resolve, 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 @@ -487,25 +416,24 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, struct Curl_dns_entry *dns) { struct Curl_cfilter *cf = NULL; + bool for_proxy = FALSE; CURLcode result; - DEBUGASSERT(data); - if(sockindex == FIRSTSOCKET) - result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); - else if(dns) { - result = cf_dns_create(&cf, data, dns_queries, - dns->hostname, dns->port, transport, - FALSE, FALSE, FALSE, dns); - } - else { - DEBUGASSERT(0); - result = CURLE_FAILED_INIT; - } + if(!peer) + return CURLE_FAILED_INIT; +#ifndef CURL_DISABLE_PROXY + for_proxy = (peer == conn->socks_proxy.peer) || + (peer == conn->http_proxy.peer); +#endif + + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + for_proxy, FALSE, dns); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -514,7 +442,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, } /* Insert a new "resolv" filter directly after `cf`. It will - * start a DNS resolve for the given hostnmae and port on the + * start a DNS resolve for the given peer on the * first connect attempt. * See socks.c on how this is used to make a non-blocking DNS * resolve during connect. @@ -522,17 +450,15 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve) { struct Curl_cfilter *cf; CURLcode result; - result = cf_dns_create(&cf, data, dns_queries, - hostname, port, transport, - FALSE, FALSE, complete_resolve, NULL); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + FALSE, complete_resolve, NULL); if(result) return result; diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 3c46b1bf3dc5..12767b005c02 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -29,10 +29,12 @@ struct Curl_easy; struct connectdata; struct Curl_dns_entry; struct Curl_addrinfo; +struct Curl_peer; CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, struct Curl_dns_entry *dns); @@ -40,8 +42,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve); diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 3c2c8374d14b..c5de52c5f4a1 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -52,11 +52,13 @@ typedef enum { /* struct for HTTP CONNECT tunneling */ struct h1_tunnel_state { + struct Curl_peer *dest; struct dynbuf rcvbuf; struct dynbuf request_data; size_t nsent; size_t headerlines; struct Curl_chunker ch; + int httpversion; enum keeponval { KEEPON_DONE, KEEPON_CONNECT, @@ -177,17 +179,26 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, } } -static void tunnel_free(struct Curl_cfilter *cf, +static void tunnel_free(struct h1_tunnel_state *ts, struct Curl_easy *data) +{ + if(ts) { + Curl_peer_unlink(&ts->dest); + curlx_dyn_free(&ts->rcvbuf); + curlx_dyn_free(&ts->request_data); + Curl_httpchunk_free(data, &ts->ch); + curlx_free(ts); + } +} + +static void cf_tunnel_free(struct Curl_cfilter *cf, + struct Curl_easy *data) { if(cf) { struct h1_tunnel_state *ts = cf->ctx; if(ts) { h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - curlx_dyn_free(&ts->rcvbuf); - curlx_dyn_free(&ts->request_data); - Curl_httpchunk_free(data, &ts->ch); - curlx_free(ts); + tunnel_free(ts, data); cf->ctx = NULL; } } @@ -210,7 +221,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, and we do not really use the newly cloned URL here then. Free it. */ curlx_safefree(data->req.newurl); - result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); + result = Curl_http_proxy_create_CONNECT(&req, cf, data, + ts->dest, ts->httpversion); if(result) goto out; @@ -219,7 +231,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, curlx_dyn_reset(&ts->request_data); ts->nsent = 0; ts->headerlines = 0; - http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; + http_minor = ts->httpversion % 10; result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); if(!result) @@ -701,7 +713,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, Curl_client_reset(data); Curl_pgrsReset(data); - tunnel_free(cf, data); + cf_tunnel_free(cf, data); } return result; } @@ -737,7 +749,7 @@ static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "destroy"); - tunnel_free(cf, data); + cf_tunnel_free(cf, data); } static void cf_h1_proxy_close(struct Curl_cfilter *cf, @@ -754,6 +766,30 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf, } } +static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct h1_tunnel_state *ts = cf->ctx; + switch(query) { + case CF_QUERY_HOST_PORT: + *pres1 = (int)ts->dest->port; + *((const char **)pres2) = ts->dest->hostname; + return CURLE_OK; + case CF_QUERY_ALPN_NEGOTIATED: { + const char **palpn = pres2; + DEBUGASSERT(palpn); + *palpn = NULL; + return CURLE_OK; + } + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + struct Curl_cftype Curl_cft_h1_proxy = { "H1-PROXY", CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, @@ -769,19 +805,43 @@ struct Curl_cftype Curl_cft_h1_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_h1_proxy_query, }; CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion) { struct Curl_cfilter *cf; + struct h1_tunnel_state *ts; CURLcode result; (void)data; - result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); + if(!dest) + return CURLE_FAILED_INIT; + if((httpversion < 10) || (httpversion >= 20)) + return CURLE_FAILED_INIT; + + ts = curlx_calloc(1, sizeof(*ts)); + if(!ts) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_peer_link(&ts->dest, dest); + ts->httpversion = httpversion; + curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); + curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); + Curl_httpchunk_init(data, &ts->ch, TRUE); + + result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, ts); + if(result) + goto out; + ts = NULL; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + tunnel_free(ts, data); return result; } diff --git a/lib/cf-h1-proxy.h b/lib/cf-h1-proxy.h index 6544ec58d026..10adcdfb4fdb 100644 --- a/lib/cf-h1-proxy.h +++ b/lib/cf-h1-proxy.h @@ -27,8 +27,12 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +struct Curl_peer; + CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion); extern struct Curl_cftype Curl_cft_h1_proxy; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 2f8cc41dd52b..8938d149a2f6 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -76,24 +76,20 @@ struct tunnel_stream { BIT(reset); }; -static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, - struct tunnel_stream *ts) +static CURLcode tunnel_stream_init(struct tunnel_stream *ts, + struct Curl_peer *dest) { - const char *hostname; - uint16_t port; - bool ipv6_ip; - ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; @@ -171,6 +167,7 @@ struct cf_h2_proxy_ctx { struct bufq inbufq; /* network receive buffer */ struct bufq outbufq; /* network send buffer */ + struct Curl_peer *dest; /* where to tunnel to */ struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ int32_t goaway_error; int32_t last_stream_id; @@ -202,6 +199,7 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) { if(ctx) { cf_h2_proxy_ctx_clear(ctx); + Curl_peer_unlink(&ctx->dest); curlx_free(ctx); } } @@ -750,7 +748,7 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, CURLcode result; struct httpreq *req = NULL; - result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); + result = Curl_http_proxy_create_CONNECT(&req, cf, data, ctx->dest, 20); if(result) goto out; result = Curl_creader_set_null(data); @@ -896,7 +894,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); - if(tunnel_stream_init(cf, &ctx->tunnel)) + if(tunnel_stream_init(&ctx->tunnel, ctx->dest)) goto out; rc = nghttp2_session_callbacks_new(&cbs); @@ -1410,8 +1408,8 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->dest->port; + *((const char **)pres2) = ctx->dest->hostname; return CURLE_OK; case CF_QUERY_NEED_FLUSH: { if(!Curl_bufq_is_empty(&ctx->outbufq) || @@ -1477,7 +1475,8 @@ struct Curl_cftype Curl_cft_h2_proxy = { }; CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest) { struct Curl_cfilter *cf_h2_proxy = NULL; struct cf_h2_proxy_ctx *ctx; @@ -1487,17 +1486,16 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; + Curl_peer_link(&ctx->dest, dest); result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); if(result) goto out; - + ctx = NULL; Curl_conn_cf_insert_after(cf, cf_h2_proxy); - result = CURLE_OK; out: - if(result) - cf_h2_proxy_ctx_free(ctx); + cf_h2_proxy_ctx_free(ctx); return result; } diff --git a/lib/cf-h2-proxy.h b/lib/cf-h2-proxy.h index 318ce1973fa1..1056a329076c 100644 --- a/lib/cf-h2-proxy.h +++ b/lib/cf-h2-proxy.h @@ -28,7 +28,8 @@ #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest); extern struct Curl_cftype Curl_cft_h2_proxy; diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 9ee5e790ebb5..afa7b55b7813 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "connect.h" #include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -78,7 +79,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, DEBUGASSERT(ctx); DEBUGASSERT(ctx->state == HAPROXY_INIT); #ifdef USE_UNIX_SOCKETS - if(cf->conn->unix_domain_socket) + if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket) /* the buffer is large enough to hold this! */ result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); else { diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index f67273e48963..17b2821b087f 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -674,40 +674,38 @@ static CURLcode is_connected(struct Curl_cfilter *cf, if(!result) return CURLE_OK; else { - const char *hostname, *proxy_name = NULL; + struct Curl_peer *peer = NULL, *proxy_peer = NULL; char viamsg[160]; + + peer = Curl_conn_get_first_peer(conn, cf->sockindex); + if(!conn->origin || !peer) + return CURLE_FAILED_INIT; + #ifndef CURL_DISABLE_PROXY if(conn->bits.socksproxy) - proxy_name = conn->socks_proxy.host.name; + proxy_peer = conn->socks_proxy.peer; else if(conn->bits.httpproxy) - proxy_name = conn->http_proxy.host.name; + proxy_peer = conn->http_proxy.peer; #endif - hostname = conn->bits.conn_to_host ? conn->conn_to_host.name : - conn->host.name; + viamsg[0] = 0; + if((peer != conn->origin) && (peer != proxy_peer)) { #ifdef USE_UNIX_SOCKETS - if(conn->unix_domain_socket) - curl_msnprintf(viamsg, sizeof(viamsg), "over %s", - conn->unix_domain_socket); - else -#endif - { - uint16_t port; - if(cf->sockindex == SECONDARYSOCKET) - port = conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - port = conn->conn_to_port; + if(peer->unix_socket) + curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s", + peer->hostname); else - port = conn->remote_port; - curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); +#endif + curl_msnprintf(viamsg, sizeof(viamsg), " via %s:%u", + peer->hostname, peer->port); } - failf(data, "Failed to connect to %s %s %s%s%safter " + failf(data, "Failed to connect to %s:%u%s %s%s%safter " "%" FMT_TIMEDIFF_T " ms: %s", - hostname, viamsg, - proxy_name ? "via " : "", - proxy_name ? proxy_name : "", - proxy_name ? " " : "", + conn->origin->hostname, conn->origin->port, viamsg, + proxy_peer ? "over proxy " : "", + proxy_peer ? proxy_peer->hostname : "", + proxy_peer ? " " : "", curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle), curl_easy_strerror(result)); diff --git a/lib/cf-socket.c b/lib/cf-socket.c index b99bcdef5518..fc99ff39ed54 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1549,7 +1549,7 @@ static void cf_socket_update_data(struct Curl_cfilter *cf, struct cf_socket_ctx *ctx = cf->ctx; data->info.primary = ctx->ip; /* not sure if this is redundant... */ - data->info.conn_remote_port = cf->conn->remote_port; + data->info.conn_remote_port = cf->conn->origin->port; } } diff --git a/lib/cfilters.c b/lib/cfilters.c index 6d7d8ef7385e..f287ebfc7421 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -117,29 +117,28 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, } #ifdef CURLVERBOSE -static void conn_trc_filters(struct Curl_easy *data, - int sockindex, - const char *info) +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info) { if(CURL_TRC_M_is_verbose(data) && data->conn) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; if(cf) { - struct dynbuf msg; - CURLcode result = CURLE_OK; - - curlx_dyn_init(&msg, 1024); - result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); - for(; cf && !result; cf = cf->next) { - result = curlx_dyn_addf(&msg, "[%s%s]", - cf->connected ? "" : "!", cf->cft->name); + char msg[256], *buf; + int blen, n; + + buf = msg; + blen = sizeof(msg) - 1; + n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex); + buf += n; + blen -= n; + for(; cf && blen; cf = cf->next) { + n = curl_msnprintf(buf, blen, "[%s%s]", + cf->connected ? "" : "!", cf->cft->name); + buf += n; + blen -= n; } - if(!result) - CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); - else - CURL_TRC_M(data, "%s [%d] error %d tracing chain", - info, sockindex, result); - curlx_dyn_free(&msg); + CURL_TRC_M(data, "%s%s", msg, blen ? "" : "..."); } else CURL_TRC_M(data, "%s [%d][-]", info, sockindex); @@ -591,14 +590,14 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, conn_report_connect_stats(cf, data); data->conn->keepalive = *Curl_pgrs_now(data); VERBOSE(result = cf_verboseconnect(data, cf)); - VERBOSE(conn_trc_filters(data, sockindex, "connected")); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); conn_remove_setup_filters(data, sockindex); - VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); goto out; } else if(result) { CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); - VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); conn_report_connect_stats(cf, data); goto out; } @@ -953,8 +952,8 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ - *phost = data->conn->host.name; - portarg = data->conn->remote_port; + *phost = data->conn->origin->hostname; + portarg = data->conn->origin->port; } if(pport) *pport = portarg; diff --git a/lib/cfilters.h b/lib/cfilters.h index ac56737ccb99..f4a03b8a2b81 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -582,7 +582,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, * Get the remote hostname and port that the connection is currently * talking to (or will talk to). * Once connected or before connect starts, - * it is `conn->host.name` and `conn->remote_port`. + * it is `conn->origin->hostname` and `conn->origin->port`. * During connect, when tunneling proxies are involved (http or socks), * it will be the name and port the proxy currently negotiates with. */ @@ -604,6 +604,11 @@ int Curl_conn_get_stream_error(struct Curl_easy *data, struct connectdata *conn, int sockindex); +#ifdef CURLVERBOSE +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info); +#endif + /** * Get the index of the given socket in the connection's sockets. * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the diff --git a/lib/connect.c b/lib/connect.c index b13d496848a1..2aa22c766058 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -355,6 +355,7 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, /* connect current sub-chain */ connect_sub_chain: + VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); if(cf->next && !cf->next->connected) { result = Curl_conn_cf_connect(cf->next, data, done); @@ -374,27 +375,23 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, /* sub-chain connected, do we need to add more? */ #ifndef CURL_DISABLE_PROXY if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - const char *hostname = - cf->conn->bits.httpproxy ? - cf->conn->http_proxy.host.name : - cf->conn->bits.conn_to_host ? - cf->conn->conn_to_host.name : - cf->sockindex == SECONDARYSOCKET ? - cf->conn->secondaryhostname : cf->conn->host.name; - uint16_t port = - cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : - cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : - cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : - cf->conn->remote_port; - const char *user = cf->conn->socks_proxy.user; - const char *passwd = cf->conn->socks_proxy.passwd; + struct Curl_peer *dest; /* where SOCKS should tunnel to */ + + if(cf->conn->bits.httpproxy) + dest = cf->conn->http_proxy.peer; + else + dest = Curl_conn_get_destination(cf->conn, cf->sockindex); + if(!dest) + return CURLE_FAILED_INIT; result = Curl_cf_socks_proxy_insert_after( - cf, data, hostname, port, cf->conn->ip_version, - cf->conn->socks_proxy.proxytype, user, passwd); + cf, data, dest, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, + cf->conn->socks_proxy.user, + cf->conn->socks_proxy.passwd); + + CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", + dest->hostname, dest->port, result); if(result) return result; ctx->state = CF_SETUP_CNNCT_SOCKS; @@ -414,7 +411,10 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, #ifndef CURL_DISABLE_HTTP if(cf->conn->bits.tunnel_proxy) { - result = Curl_cf_http_proxy_insert_after(cf, data); + struct Curl_peer *dest; /* where HTTP should tunnel to */ + dest = Curl_conn_get_destination(cf->conn, cf->sockindex); + result = Curl_cf_http_proxy_insert_after( + cf, data, dest, cf->conn->http_proxy.proxytype); if(result) return result; } @@ -580,12 +580,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, int ssl_mode) { CURLcode result = CURLE_OK; + struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); DEBUGASSERT(!conn->cfilter[sockindex]); + if(!peer) + return CURLE_FAILED_INIT; + #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && conn->scheme->protocol == CURLPROTO_HTTPS) { @@ -609,29 +613,13 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, if(sockindex == FIRSTSOCKET) dns_queries |= CURL_DNSQ_HTTPS; #endif - result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, + result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, conn->transport_wanted, dns); DEBUGASSERT(conn->cfilter[sockindex]); out: 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->bits.proxy && 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) { @@ -641,3 +629,29 @@ void Curl_conn_set_multiplex(struct connectdata *conn) } } } + +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->http_proxy.peer && !conn->bits.tunnel_proxy) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} + +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} diff --git a/lib/connect.h b/lib/connect.h index 40f1c9c57297..380b2fc6116b 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -30,6 +30,7 @@ struct Curl_dns_entry; struct ip_quadruple; +struct Curl_peer; struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); @@ -126,14 +127,16 @@ 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 +/* Get the peer the connection actually connects to at sockindex. + * Often the same as "origin", but can be redirected via "connect-to" + * or "alt-svc". May tunnel through proxies. */ +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex); + +/* Get the peer curl connects its socket to. + * Can be origin, "connect-to" or the first proxy. */ +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex); extern struct Curl_cftype Curl_cft_setup; diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index fd26c5f0bcec..1efb4b701757 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -49,6 +49,7 @@ #include "curl_addrinfo.h" #include "fake_addrinfo.h" #include "curlx/inet_pton.h" +#include "curlx/strparse.h" /* * Curl_freeaddrinfo() @@ -443,6 +444,48 @@ bool Curl_is_ipaddr(const char *address) return FALSE; } +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone) +{ + const char *zonep = NULL; + size_t i = 0, hlen = 0, zlen = 0; + + if(host) + memset(host, 0, sizeof(*host)); + if(zone) + memset(zone, 0, sizeof(*zone)); + + for(i = 0; i < len; ++i, ++hlen) { + if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) + break; + } + + if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ + i += 1; + if(maybe_url_encoded && !strncmp("25", s + i, 2)) + i += 2; + zonep = s + i; + for(; i < len; ++i, ++zlen) { + /* Allow unreserved characters as defined in RFC 3986 */ + if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || + (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) + break; + } + } + + if(i != len) + return FALSE; /* invalid chars in zone */ + if(host && hlen) { + host->str = s; + host->len = hlen; + } + if(zone && zlen) { + zone->str = zonep; + zone->len = zlen; + } + return TRUE; +} + #ifdef USE_UNIX_SOCKETS /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index da2da872cfcc..046be238169d 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -40,6 +40,8 @@ # include #endif +struct Curl_str; + /* * Curl_addrinfo is our internal struct definition that we use to allow * consistent internal handling of this data. We use this even when the system @@ -73,6 +75,9 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); bool Curl_is_ipv4addr(const char *address); bool Curl_is_ipaddr(const char *address); +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone); + CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp); diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 60f085901f95..00eff8e6a999 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -332,7 +332,8 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : Curl_auth_create_gssapi_user_message(data, sctx->conn->user, sctx->conn->passwd, - service, sctx->conn->host.name, + service, + sctx->conn->origin->hostname, (bool)sctx->sasl->mutual_auth, NULL, krb5, &sctx->resp); } @@ -711,7 +712,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, - service, conn->host.name, + service, conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); newstate = SASL_GSSAPI_TOKEN; diff --git a/lib/ftp.c b/lib/ftp.c index 9d3700df5f7b..4f537db2b26b 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2020,7 +2020,7 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) not the ftp host. */ #ifndef CURL_DISABLE_PROXY if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - *newhostp = curlx_strdup(conn->host.name); + *newhostp = curlx_strdup(conn->origin->hostname); else #endif if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && @@ -2060,7 +2060,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result; struct Curl_dns_entry *dns = NULL; - unsigned short connectport; /* the local port connect() should use! */ const struct pingpong *pp = &ftpc->pp; char *newhost = NULL; unsigned short newport = 0; @@ -2125,7 +2124,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* told to ignore the remotely given IP but instead use the host we used for the control connection */ infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", - ip[0], ip[1], ip[2], ip[3], conn->host.name); + ip[0], ip[1], ip[2], ip[3], conn->origin->hostname); result = ftp_control_addr_dup(data, &newhost); if(result) return result; @@ -2154,8 +2153,13 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, * expired now, instead we remake the lookup here and now! */ struct ip_quadruple ipquad; bool is_ipv6; - const char * const host_name = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; + const struct Curl_peer *dest = conn->bits.socksproxy ? + conn->socks_proxy.peer : conn->http_proxy.peer; + + if(!dest) { + result = CURLE_FAILED_INIT; + goto error; + } result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, &is_ipv6, &ipquad); @@ -2164,13 +2168,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, (void)Curl_resolv_blocking( data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, - host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), + dest->hostname, dest->port, Curl_conn_get_transport(data, conn), &dns); - /* we connect to the proxy's port */ - connectport = (unsigned short)ipquad.remote_port; if(!dns) { - failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); + failf(data, "cannot resolve proxy host %s:%hu", + dest->hostname, dest->port); result = CURLE_COULDNT_RESOLVE_PROXY; goto error; } @@ -2192,20 +2195,31 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, (void)Curl_resolv_blocking( data, Curl_resolv_dns_queries(data, conn->ip_version), newhost, newport, Curl_conn_get_transport(data, conn), &dns); - connectport = newport; /* we connect to the remote port */ if(!dns) { - failf(data, "cannot resolve new host %s:%hu", newhost, connectport); + failf(data, "cannot resolve new host %s:%hu", newhost, newport); result = CURLE_FTP_CANT_GET_HOST; goto error; } } DEBUGASSERT(newhost); - curlx_free(conn->secondaryhostname); - conn->secondary_port = newport; - conn->secondaryhostname = newhost; - newhost = NULL; + Curl_peer_unlink(&conn->origin2); + result = Curl_peer_create(data, conn->scheme, newhost, newport, + &conn->origin2); + if(result) + goto error; + + /* If FIRSTSOCKET goes via another peer, SECONDARY needs as well, + * but with its new port. */ + if(conn->via_peer) { + Curl_peer_unlink(&conn->via_peer2); + result = Curl_peer_create(data, conn->via_peer->scheme, + conn->via_peer->hostname, newport, + &conn->via_peer2); + if(result) + goto error; + } result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, conn->bits.ftp_use_data_ssl ? @@ -2233,7 +2247,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, char buf[256]; Curl_printable_address(dns->addr, buf, sizeof(buf)); infof(data, "Connecting to %s (%s) port %d", - conn->secondaryhostname, buf, connectport); + conn->origin2->hostname, buf, conn->origin2->port); } #endif diff --git a/lib/hostip.c b/lib/hostip.c index 85f53c4ef4a4..7b85f9ff8105 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -956,16 +956,14 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, * any other CURLcode error, *pdns == NULL */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, uint32_t *presolv_id, struct Curl_dns_entry **pdns) { - DEBUGASSERT(hostname && *hostname); *presolv_id = 0; *pdns = NULL; @@ -975,14 +973,24 @@ CURLcode Curl_resolv(struct Curl_easy *data, else if(!timeout_ms) timeout_ms = CURL_TIMEOUT_RESOLVE_MS; +#ifdef USE_UNIX_SOCKETS + if(peer->unix_socket) + return Curl_resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, + pdns); +#else + if(peer->unix_socket) + return hostip_resolv_failed(data, peer->hostname, for_proxy); +#endif + #ifdef USE_ALARM_TIMEOUT if(timeout_ms && data->set.no_signal) { /* Cannot use ALARM when signals are disabled */ timeout_ms = 0; } if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, presolv_id, pdns); + return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, presolv_id, + pdns); } #endif /* !USE_ALARM_TIMEOUT */ @@ -991,8 +999,9 @@ CURLcode Curl_resolv(struct Curl_easy *data, infof(data, "timeout on name lookup is not supported"); #endif - return hostip_resolv(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, TRUE, presolv_id, pdns); + return hostip_resolv(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, TRUE, presolv_id, + pdns); } #ifdef USE_CURL_ASYNC diff --git a/lib/hostip.h b/lib/hostip.h index 780fb4dc13df..2ba586ce9722 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -45,6 +45,7 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; struct Curl_dns_entry; +struct Curl_peer; /* DNS query types */ #define CURL_DNSQ_A (1U << 0) @@ -96,9 +97,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, diff --git a/lib/hsts.c b/lib/hsts.c index 400b4423da14..261dffc4792c 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -610,6 +610,12 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) return result; } +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) +{ + return !!Curl_hsts(h, dest->hostname, + strlen(dest->hostname), TRUE); +} + #if defined(DEBUGBUILD) || defined(UNITTESTS) #undef time #endif diff --git a/lib/hsts.h b/lib/hsts.h index 0e6585f11606..93b998072926 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -28,6 +28,8 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" +struct Curl_peer; + #define MAX_HSTS_ENTRIES 10000 #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -61,6 +63,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); CURLcode Curl_hsts_loadfiles(struct Curl_easy *data); + +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest); + #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x, y) CURLE_OK diff --git a/lib/http.c b/lib/http.c index 9118c7e716ac..6d483b70744d 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2005,17 +2005,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr; - if(!data->state.this_is_a_follow) { - /* Free to avoid leaking memory on multiple requests */ - curlx_free(data->state.first_host); + if(!data->state.this_is_a_follow) + Curl_peer_link(&data->state.first_origin, conn->origin); - data->state.first_host = curlx_strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->scheme->protocol; - } curlx_safefree(aptr->host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); @@ -2023,7 +2015,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || - curl_strequal(data->state.first_host, conn->host.name))) { + Curl_peer_equal(data->state.first_origin, conn->origin))) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2068,17 +2060,17 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) else { /* Use the hostname as present in the URL if it was IPv6. */ char *host = (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name; + data->state.up.hostname : conn->origin->hostname; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && - (conn->remote_port == PORT_HTTPS)) || + (conn->origin->port == PORT_HTTPS)) || ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && - (conn->remote_port == PORT_HTTP))) + (conn->origin->port == PORT_HTTP))) /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ aptr->host = curl_maprintf("Host: %s\r\n", host); else - aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -2120,8 +2112,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(conn->host.dispname != conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(conn->origin->user_hostname != conn->origin->hostname) { + uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; @@ -2551,7 +2543,7 @@ static CURLcode http_cookies(struct Curl_easy *data, if(data->cookies && data->state.cookie_engine) { bool okay; const char *host = data->req.cookiehost ? - data->req.cookiehost : data->conn->host.name; + data->req.cookiehost : data->conn->origin->hostname; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); if(!result && okay) { @@ -2966,10 +2958,10 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) + if(conn->bits.altused && conn->via_peer && + !Curl_checkheaders(data, STRCONST("Alt-Used"))) result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", - conn->conn_to_host.name, - conn->conn_to_port); + conn->via_peer->hostname, conn->via_peer->port); break; #endif @@ -3224,8 +3216,8 @@ static CURLcode http_header_a(struct Curl_easy *data, struct SingleRequest *k = &data->req; enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; - return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, - curlx_uitous((unsigned int)conn->remote_port)); + return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname, + curlx_uitous((unsigned int)conn->origin->port)); } #else (void)data; @@ -3552,7 +3544,7 @@ static CURLcode http_header_s(struct Curl_easy *data, /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ const char *host = data->req.cookiehost ? - data->req.cookiehost : conn->host.name; + data->req.cookiehost : conn->origin->hostname; const bool secure_context = Curl_secure_context(conn, host); CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -3576,7 +3568,7 @@ static CURLcode http_header_s(struct Curl_easy *data, ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; if(v) { CURLcode result = - Curl_hsts_parse(data->hsts, conn->host.name, v); + Curl_hsts_parse(data->hsts, conn->origin->hostname, v); if(result) { if(result == CURLE_OUT_OF_MEMORY) return result; diff --git a/lib/http2.c b/lib/http2.c index 7be5abdd3147..c8ecb28b5a68 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1438,14 +1438,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { /* pseudo headers are lower case */ int rc = 0; - char *check = curl_maprintf("%s:%d", cf->conn->host.name, - cf->conn->remote_port); + char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname, + cf->conn->origin->port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; if(!curl_strequal(check, (const char *)value) && - ((cf->conn->remote_port != cf->conn->given->defport) || - !curl_strequal(cf->conn->host.name, (const char *)value))) { + ((cf->conn->origin->port != cf->conn->given->defport) || + !curl_strequal(cf->conn->origin->hostname, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 55efa120478f..cb99c6d45ef2 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -827,7 +827,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) struct Curl_str provider1; struct Curl_str region = { NULL, 0 }; struct Curl_str service = { NULL, 0 }; - const char *hostname = conn->host.name; + const char *hostname = conn->origin->hostname; time_t clock; struct tm tm; char timestamp[TIMESTAMP_SIZE]; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 74d63d6cc0bb..8cced878219e 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -68,7 +68,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, passwdp = conn->http_proxy.passwd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - host = conn->http_proxy.host.name; + host = conn->http_proxy.peer->hostname; state = conn->proxy_negotiate_state; #else return CURLE_NOT_BUILT_IN; @@ -79,7 +79,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, passwdp = conn->passwd; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; - host = conn->host.name; + host = conn->origin->hostname; state = conn->http_negotiate_state; } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 82b050529e00..9c234a8e7dc2 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -144,7 +144,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) passwdp = data->state.aptr.proxypasswd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - hostname = conn->http_proxy.host.name; + hostname = conn->http_proxy.peer->hostname; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; #else @@ -157,7 +157,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) passwdp = data->state.aptr.passwd; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; - hostname = conn->host.name; + hostname = conn->origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; } diff --git a/lib/http_proxy.c b/lib/http_proxy.c index a4bdd7e36107..361f1f3287ef 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -162,52 +162,27 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, return CURLE_OK; } -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip) -{ - DEBUGASSERT(cf); - DEBUGASSERT(cf->conn); - - if(cf->conn->bits.conn_to_host) - *phostname = cf->conn->conn_to_host.name; - else if(cf->sockindex == SECONDARYSOCKET) - *phostname = cf->conn->secondaryhostname; - else - *phostname = cf->conn->host.name; - - if(cf->sockindex == SECONDARYSOCKET) - *pport = cf->conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - *pport = cf->conn->conn_to_port; - else - *pport = cf->conn->remote_port; - - *pipv6_ip = (strchr(*phostname, ':') != NULL); -} - struct cf_proxy_ctx { - int httpversion; /* HTTP version used to CONNECT */ + struct Curl_peer *dest; /* tunnel destination */ + uint8_t proxytype; BIT(sub_filter_installed); }; CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, - int http_version_major) + struct Curl_peer *dest, + int httpversion) { - struct cf_proxy_ctx *ctx = cf->ctx; - const char *hostname = NULL; char *authority = NULL; - uint16_t port; - bool ipv6_ip; CURLcode result; struct httpreq *req = NULL; - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - - authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + authority = curl_maprintf("%s%s%s:%u", + dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!authority) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -226,7 +201,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; /* If user is not overriding Host: header, we add for HTTP/1.x */ - if(http_version_major == 1 && + if(httpversion < 20 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { result = Curl_dynhds_cadd(&req->headers, "Host", authority); if(result) @@ -248,14 +223,14 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(http_version_major == 1 && + if(httpversion < 20 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; } - result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); + result = dynhds_add_custom(data, TRUE, httpversion, &req->headers); out: if(result && req) { @@ -287,36 +262,46 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->sub_filter_installed) { - int httpversion = 0; const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); if(alpn) infof(data, "CONNECT: '%s' negotiated", alpn); - else + else if(!alpn) { + /* No ALPN, proxytype rules. Fake ALPN */ infof(data, "CONNECT: no ALPN negotiated"); + switch(ctx->proxytype) { + case CURLPROXY_HTTP_1_0: + alpn = "http/1.0"; + break; + case CURLPROXY_HTTPS2: + alpn = "h2"; + break; + default: + alpn = "http/1.1"; + break; + } + } - if(alpn && !strcmp(alpn, "http/1.0")) { + if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10); if(result) goto out; - httpversion = 10; } - else if(!alpn || !strcmp(alpn, "http/1.1")) { - CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + else if(!strcmp(alpn, "http/1.1")) { + int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", + httpversion % 10); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion); if(result) goto out; - /* Assume that without an ALPN, we are talking to an ancient one */ - httpversion = 11; } #ifdef USE_NGHTTP2 else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); - result = Curl_cf_h2_proxy_insert_after(cf, data); + result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest); if(result) goto out; - httpversion = 20; } #endif else { @@ -326,7 +311,6 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, } ctx->sub_filter_installed = TRUE; - ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ @@ -348,14 +332,15 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { + struct cf_proxy_ctx *ctx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->dest->port; + *((const char **)pres2) = ctx->dest->hostname; return CURLE_OK; case CF_QUERY_ALPN_NEGOTIATED: { const char **palpn = pres2; @@ -371,13 +356,22 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } +static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx) +{ + if(ctx) { + Curl_peer_unlink(&ctx->dest); + curlx_free(ctx); + } +} + static void http_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_proxy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); + if(ctx) { + CURL_TRC_CF(data, cf, "destroy"); + cf_https_proxy_ctx_free(ctx); + } } static void http_proxy_cf_close(struct Curl_cfilter *cf, @@ -404,22 +398,30 @@ struct Curl_cftype Curl_cft_http_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_http_proxy_query, }; CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + uint8_t proxytype) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; CURLcode result; (void)data; + if(!dest) + return CURLE_FAILED_INIT; + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + Curl_peer_link(&ctx->dest, dest); + ctx->proxytype = proxytype; + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) goto out; @@ -427,7 +429,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_conn_cf_insert_after(cf_at, cf); out: - curlx_free(ctx); + cf_https_proxy_ctx_free(ctx); return result; } diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 155b222edc0d..c122aa6dd87f 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -35,24 +35,19 @@ enum Curl_proxy_use { HEADER_CONNECT /* sending CONNECT to a proxy */ }; -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip); - CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, - int http_version_major); + struct Curl_peer *dest, + int httpversion); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600 * 1000) -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2); - CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + uint8_t proxytype); extern struct Curl_cftype Curl_cft_http_proxy; diff --git a/lib/httpsrr.c b/lib/httpsrr.c index 90fcb524e868..53647b81ed25 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -251,7 +251,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, return FALSE; return (!rr->target || !rr->target[0] || (rr->target[0] == '.' && !rr->target[1])) && - (!rr->port_set || rr->port == data->conn->remote_port); + (!rr->port_set || rr->port == data->conn->origin->port); } #ifdef USE_ARES diff --git a/lib/idn.c b/lib/idn.c index f2b954e2d03e..b26f251d975a 100644 --- a/lib/idn.c +++ b/lib/idn.c @@ -27,6 +27,7 @@ #include "curl_setup.h" #include "urldata.h" +#include "curlx/strparse.h" #include "idn.h" #ifdef USE_LIBIDN2 @@ -222,15 +223,24 @@ static CURLcode win32_ascii_to_idn(const char *in, char **out) */ bool Curl_is_ASCII_name(const char *hostname) { - /* get an UNSIGNED local version of the pointer */ - const unsigned char *ch = (const unsigned char *)hostname; - - if(!hostname) /* bad input, consider it ASCII! */ - return TRUE; + if(hostname) { + struct Curl_str s; + s.str = hostname; + s.len = strlen(hostname); + return Curl_is_ASCII_str(&s); + } + return TRUE; +} - while(*ch) { - if(*ch++ & 0x80) - return FALSE; +bool Curl_is_ASCII_str(struct Curl_str *s) +{ + if(s && s->len) { + const unsigned char *ch = (const unsigned char *)s->str; + size_t i; + for(i = 0; i < s->len; ++i) { + if(ch[i] & 0x80) + return FALSE; + } } return TRUE; } diff --git a/lib/idn.h b/lib/idn.h index 90d8e811b1a6..b0ac981ba893 100644 --- a/lib/idn.h +++ b/lib/idn.h @@ -23,12 +23,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + +struct Curl_str; + bool Curl_is_ASCII_name(const char *hostname); +bool Curl_is_ASCII_str(struct Curl_str *s); + +#ifdef HEADER_CURL_URLDATA_H /* HACK */ CURLcode Curl_idnconvert_hostname(struct hostname *host); +#endif #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #define USE_IDN +#ifdef HEADER_CURL_URLDATA_H /* HACK */ void Curl_free_idnconverted_hostname(struct hostname *host); +#endif CURLcode Curl_idn_decode(const char *input, char **output); CURLcode Curl_idn_encode(const char *puny, char **output); #else diff --git a/lib/ldap.c b/lib/ldap.c index 16c93eeca253..236e0204084f 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -284,14 +284,14 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_ssl ? "encrypted" : "cleartext"); #ifdef USE_WIN32_LDAP - host = curlx_convert_UTF8_to_tchar(conn->host.name); + host = curlx_convert_UTF8_to_tchar(conn->origin->hostname); if(!host) { result = CURLE_OUT_OF_MEMORY; goto quit; } #else - host = conn->host.name; + host = conn->origin->hostname; #endif if(data->state.aptr.user) { @@ -307,7 +307,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); if(!server) { failf(data, "LDAP: cannot setup connect to %s:%u", - conn->host.dispname, ipquad.remote_port); + conn->origin->user_hostname, ipquad.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -681,8 +681,8 @@ static size_t num_entries(const char *s) * Syntax: * ldap://:/???? * - * already known from 'conn->host.name'. - * already known from 'conn->remote_port'. + * already known from 'conn->origin->hostname'. + * already known from 'conn->origin->port'. * extract the rest from 'data->state.path+1'. All fields are optional. * e.g. * ldap://:/??? @@ -708,8 +708,8 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; - ludp->lud_port = conn->remote_port; - ludp->lud_host = conn->host.name; + ludp->lud_port = conn->origin->port; + ludp->lud_host = conn->origin->hostname; /* Duplicate the path */ p = path = curlx_strdup(data->state.up.path + 1); diff --git a/lib/openldap.c b/lib/openldap.c index 48bf5b746d12..30e4bcc7521c 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -617,8 +617,8 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) hosturl = curl_maprintf("%s://%s:%d", conn->scheme->name, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name, - conn->remote_port); + data->state.up.hostname : conn->origin->hostname, + conn->origin->port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/peer.c b/lib/peer.c new file mode 100644 index 000000000000..52b40a5da7ad --- /dev/null +++ b/lib/peer.c @@ -0,0 +1,712 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * IDN conversions + */ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_IPHLPAPI_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) +#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) +#include /* workaround for old mingw-w64 missing to include it */ +#endif +#include +#endif + +#include "curl_addrinfo.h" +#include "curl_trc.h" +#include "protocol.h" +#include "http_proxy.h" +#include "idn.h" +#include "curlx/strdup.h" +#include "curlx/strparse.h" +#include "peer.h" +#include "urldata.h" +#include "url.h" +#include "vtls/vtls.h" + +struct peer_parse { + const struct Curl_scheme *scheme; + struct Curl_str host_user; + struct Curl_str host; + struct Curl_str zoneid; + char *tmp_host_user; + char *tmp_host; + char *tmp_zoneid; + uint32_t scopeid; + uint16_t port; + bool ipv6; + bool unix_socket; + bool abstract_uds; +}; + +static void peer_parse_clear(struct peer_parse *pp) +{ + curlx_free(pp->tmp_host_user); + curlx_free(pp->tmp_host); + curlx_free(pp->tmp_zoneid); + memset(pp, 0, sizeof(*pp)); +} + +static CURLcode peer_create(struct peer_parse *pp, + struct Curl_peer **ppeer) +{ + struct Curl_peer *peer = NULL; + CURLcode result = CURLE_OK; + size_t zone_alen = 0, host_alen = 0; + + if(!pp || !pp->scheme) + return CURLE_FAILED_INIT; + if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK)) + return CURLE_FAILED_INIT; + + if((pp->host.str != pp->host_user.str) || + (pp->host.len != pp->host_user.len)) { + host_alen = pp->host.len + 1; + } + zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; + + /* NUL terminator already part of struct */ + peer = curlx_calloc(1, sizeof(*peer) + + pp->host_user.len + host_alen + zone_alen); + if(!peer) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + peer->refcount = 1; + peer->scheme = pp->scheme; + peer->hostname = peer->user_hostname; + peer->port = pp->port; + peer->scopeid = pp->scopeid; + peer->ipv6 = pp->ipv6; + peer->unix_socket = pp->unix_socket; + peer->abstract_uds = pp->abstract_uds; + + if(pp->host_user.len) + memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len); + + if(host_alen) { + peer->hostname = peer->user_hostname + pp->host_user.len + 1; + memcpy(peer->hostname, pp->host.str, pp->host.len); + } + + if(zone_alen) { + peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen; + memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len); +#ifdef USE_IPV6 + /* Determine scope_id if not already provided */ + if(!peer->scopeid) { + const char *p = peer->zoneid; + curl_off_t scope; + if(!curlx_str_number(&p, &scope, UINT_MAX)) { + /* A plain number, use it directly as a scope id. */ + peer->scopeid = (uint32_t)scope; + } +#ifdef HAVE_IF_NAMETOINDEX + else { + /* Zone identifier is not numeric */ + unsigned int idx = 0; + idx = if_nametoindex(peer->zoneid); + if(idx) { + peer->scopeid = (uint32_t)idx; + } + else { + /* Do we want to return an error here? */ + } + } +#endif /* HAVE_IF_NAMETOINDEX */ + } +#endif /* USE_IPV6 */ + } + +out: + if(!result) + *ppeer = peer; + else + Curl_peer_unlink(&peer); + return result; +} + +static CURLcode peer_parse_host(struct Curl_easy *data, + struct peer_parse *pp, + bool scan_for_ipv6) +{ + if(!pp || !pp->host_user.str || !pp->host_user.len) + return CURLE_FAILED_INIT; + + if(pp->host_user.str[0] == '[') { + const char *s = pp->host_user.str + 1; + struct Curl_str tmp; + if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']')) + return CURLE_URL_MALFORMAT; + + if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE, + &pp->host, &pp->zoneid)) { + failf(data, "Invalid IPv6 address format in '%.*s'", + (int)pp->host_user.len, pp->host_user.str); + return CURLE_URL_MALFORMAT; + } + pp->ipv6 = TRUE; + } + else { +#ifdef USE_IDN + if(!Curl_is_ASCII_str(&pp->host_user)) { + CURLcode result; + if(!pp->tmp_host_user) { + /* need a null-terminated string for IDN */ + pp->tmp_host_user = curlx_memdup0(pp->host_user.str, + pp->host_user.len); + if(!pp->tmp_host_user) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host); + if(result) + return result; + pp->host.str = pp->tmp_host; + pp->host.len = strlen(pp->host.str); + } + else +#endif + if(scan_for_ipv6 && + Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, + &pp->host, &pp->zoneid)) { + pp->ipv6 = TRUE; + } + else + pp->host = pp->host_user; + } + return CURLE_OK; +} + +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.scheme = scheme; + pp.host_user.str = hostname; + pp.host_user.len = strlen(hostname); + pp.port = port; + + result = peer_parse_host(data, &pp, TRUE); + if(!result) + result = peer_create(&pp, ppeer); + + peer_parse_clear(&pp); + return result; +} + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + size_t pathlen = path ? strlen(path) : 0; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!scheme) + return CURLE_FAILED_INIT; + if(!pathlen) + return CURLE_FAILED_INIT; + + pp.scheme = scheme; + pp.host_user.str = pp.host.str = path; + pp.host_user.len = pp.host.len = pathlen; + pp.unix_socket = TRUE; + pp.abstract_uds = abstract_unix_socket; + + result = peer_create(&pp, ppeer); + peer_parse_clear(&pp); + return result; +} +#endif /* USE_UNIX_SOCKETS */ + +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src) +{ + if(*pdest != src) { + Curl_peer_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_peer_unlink(struct Curl_peer **ppeer) +{ + if(*ppeer) { + struct Curl_peer *peer = *ppeer; + + DEBUGASSERT(peer->refcount); + *ppeer = NULL; + if(peer->refcount) + peer->refcount--; + if(!peer->refcount) { + curlx_free(peer); + } + } +} + +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->scheme == p2->scheme) && + Curl_peer_same_destination(p1, p2)); +} + +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->port == p2->port) && + curl_strequal(p1->hostname, p2->hostname) && + (p1->ipv6 == p2->ipv6) && + (p1->unix_socket == p2->unix_socket) && + (p1->abstract_uds == p2->abstract_uds) && + (p1->scopeid == p2->scopeid) && + (p1->scopeid || curl_strequal(p1->zoneid, p2->zoneid))); +} + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + char *zoneid = NULL; + CURLUcode uc; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + + curlx_safefree(up->scheme); + uc = curl_url_get(uh, CURLUPART_SCHEME, &up->scheme, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + pp.scheme = Curl_get_scheme(up->scheme); + if(!pp.scheme) { + failf(data, "Protocol \"%s\" not supported%s", up->scheme, + data->state.this_is_a_follow ? " (in redirect)" : ""); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + + curlx_safefree(up->hostname); + uc = curl_url_get(uh, CURLUPART_HOST, &up->hostname, 0); + if(uc) { + if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) + ; /* acceptable */ + else { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(strlen(up->hostname) > MAX_URL_LEN) { + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); + result = CURLE_URL_MALFORMAT; + goto out; + } + + pp.host_user.str = up->hostname ? up->hostname : ""; + pp.host_user.len = strlen(pp.host_user.str); + if(pp.host_user.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + else + pp.host = pp.host_user; + + curlx_safefree(up->port); + if(port_override) { + /* if set, we use this instead of the port possibly given in the URL */ + char portbuf[16]; + curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override); + uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else + pp.port = port_override; + } + else { + uc = curl_url_get(uh, CURLUPART_PORT, &up->port, CURLU_DEFAULT_PORT); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) { + result = CURLE_URL_MALFORMAT; + goto out; + } + /* no port ok when not a network scheme */ + } + else { + const char *p = up->port; + curl_off_t offt; + if(curlx_str_number(&p, &offt, 0xffff)) + return CURLE_URL_MALFORMAT; + pp.port = (uint16_t)offt; + } + } + + if(scopeid_override) + /* Override any scope id from an url zone. */ + pp.scopeid = scopeid_override; + else { + if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) == + CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(zoneid) { + pp.zoneid.str = zoneid; + pp.zoneid.len = strlen(zoneid); + } + } + + result = peer_create(&pp, ppeer); + if(result) + failf(data, "Error %d creating peer for %s:%u", + result, pp.host_user.str, pp.port); + +out: + peer_parse_clear(&pp); + curlx_free(zoneid); + return result; +} + +/* Parse a "host:port" string to connect to into a peer. + * IPv6 addresses might appear in brackets or without them. */ +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + const char *portstr = NULL; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!connect_to || !*connect_to) + return CURLE_FAILED_INIT; + + pp.scheme = dest->scheme; + + /* detect and extract RFC6874-style IPv6-addresses */ + if(connect_to[0] == '[') { + const char *s = strchr(connect_to + 1, ']'); + if(!s) { + failf(data, "Invalid IPv6 address format in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + portstr = strchr(s, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = s - pp.host_user.str + 1; + pp.ipv6 = TRUE; + } + else { + portstr = strchr(connect_to, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = portstr ? + (size_t)(portstr - connect_to) : strlen(connect_to); + } + + if(!pp.host_user.len) { /* no hostname found, only port switch */ + pp.host_user.str = dest->user_hostname; + pp.host_user.len = strlen(dest->user_hostname); + } + + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + + if(portstr && portstr[1]) { + const char *p = portstr + 1; + curl_off_t portparse; + if(curlx_str_number(&p, &portparse, 0xffff)) { + failf(data, "No valid port number in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + pp.port = (uint16_t)portparse; /* we know it will fit */ + } + else + pp.port = dest->port; + +#ifndef USE_IPV6 + if(pp.ipv6) { + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); + result = CURLE_NOT_BUILT_IN; + goto out; + } +#endif + + result = peer_create(&pp, ppeer); + CURL_TRC_M(data, "connect-to peer_create2 -> %d", result); + +out: + CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, result); + peer_parse_clear(&pp); + return result; +} + +#ifndef CURL_DISABLE_PROXY + +#ifdef USE_UNIX_SOCKETS +#define UNIX_SOCKET_PREFIX "localhost" +#endif + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype) +{ + struct peer_parse pp; + char *scheme = NULL; + char *portptr = NULL; +#ifdef USE_UNIX_SOCKETS + bool is_socks = FALSE; +#endif + CURLUcode uc; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.port = CURL_DEFAULT_PROXY_PORT; + uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, + CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* url came without scheme, the passed `proxytype` determines it */ + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + pp.scheme = &Curl_scheme_http; + break; + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + pp.scheme = &Curl_scheme_https; + break; + case CURLPROXY_SOCKS4: + pp.scheme = &Curl_scheme_socks4; + break; + case CURLPROXY_SOCKS4A: + pp.scheme = &Curl_scheme_socks4a; + break; + case CURLPROXY_SOCKS5: + pp.scheme = &Curl_scheme_socks5; + break; + case CURLPROXY_SOCKS5_HOSTNAME: + pp.scheme = &Curl_scheme_socks5h; + break; + default: + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + } + else { + pp.scheme = Curl_get_scheme(scheme); + if(pp.scheme == &Curl_scheme_https) { + proxytype = (proxytype != CURLPROXY_HTTPS2) ? + CURLPROXY_HTTPS : CURLPROXY_HTTPS2; + } + else if(pp.scheme == &Curl_scheme_socks5h) + proxytype = CURLPROXY_SOCKS5_HOSTNAME; + else if(pp.scheme == &Curl_scheme_socks5) + proxytype = CURLPROXY_SOCKS5; + else if(pp.scheme == &Curl_scheme_socks4a) + proxytype = CURLPROXY_SOCKS4A; + else if((pp.scheme == &Curl_scheme_socks4) || + (pp.scheme == &Curl_scheme_socks)) + proxytype = CURLPROXY_SOCKS4; + else if(pp.scheme == &Curl_scheme_http) { + proxytype = (uint8_t)((proxytype != CURLPROXY_HTTP_1_0) ? + CURLPROXY_HTTP : CURLPROXY_HTTP_1_0); + } + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", url); + result = CURLE_COULDNT_CONNECT; + goto out; + } + } + DEBUGASSERT(pp.scheme); + + if(IS_HTTPS_PROXY(proxytype) && + !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", url); + result = CURLE_NOT_BUILT_IN; + goto out; + } + + switch(pp.scheme->family) { + case CURLPROTO_SOCKS: +#ifdef USE_UNIX_SOCKETS + is_socks = TRUE; +#endif + break; + case CURLPROTO_HTTP: + break; + default: + failf(data, "Unsupported proxy protocol for \'%s\'", url); + result = CURLE_COULDNT_CONNECT; + goto out; + } + + uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(portptr) { + curl_off_t num; + const char *p = portptr; + if(!curlx_str_number(&p, &num, UINT16_MAX)) + pp.port = (uint16_t)num; + /* Should we not error out when the port number is invalid? */ + curlx_free(portptr); + } + else { + /* No port in url, take the set one or the scheme's default */ + if(data->set.proxyport) + pp.port = data->set.proxyport; + else + pp.port = pp.scheme->defport; + } + + /* now, clone the proxy hostname */ + uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pp.host_user.str = pp.tmp_host_user; + pp.host_user.len = strlen(pp.tmp_host_user); + +#ifdef USE_UNIX_SOCKETS + if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) { + uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* path will be "/", if no path was found */ + if(strcmp("/", pp.tmp_host)) { + pp.host.str = pp.tmp_host; + pp.host.len = strlen(pp.tmp_host); + pp.unix_socket = TRUE; + } + else { + pp.host = pp.host_user; + } + } +#endif /* USE_UNIX_SOCKETS */ + + if(!pp.host.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + + uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(pp.tmp_zoneid) { + pp.zoneid.str = pp.tmp_zoneid; + pp.zoneid.len = strlen(pp.tmp_zoneid); + } + + *pproxytype = proxytype; + result = peer_create(&pp, ppeer); + +out: + peer_parse_clear(&pp); + curlx_free(scheme); +#ifdef DEBUGBUILD + if(!result) + DEBUGASSERT(*ppeer); +#endif + return result; +} + +#endif /* !CURL_DISABLE_PROXY */ diff --git a/lib/peer.h b/lib/peer.h new file mode 100644 index 000000000000..daa01db8ff65 --- /dev/null +++ b/lib/peer.h @@ -0,0 +1,105 @@ +#ifndef HEADER_CURL_PEER_H +#define HEADER_CURL_PEER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_scheme; +struct urlpieces; + +/* if peer hostname starts with this, the peer is a unix domain socket + * path, e.g. the remainder after 'localhost'. */ +#define CURL_PEER_UDS_PREFIX "localhost/" + +struct Curl_peer { + const struct Curl_scheme *scheme; /* url scheme */ + char *hostname; /* normalized hostname (IDN decoded when supported) */ + char *zoneid; /* NULL or ipv6 zone identifier */ + uint32_t refcount; /* created with 1, freed when dropping to 0 */ + uint32_t scopeid; /* != 0, ipv6 scope to use */ + uint16_t port; + BIT(unix_socket); /* hostname is a UDS path without the prefix */ + BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */ + BIT(ipv6); /* hostname is an IPv6 address stripped of '[]' */ + char user_hostname[1]; /* hostname supplied by user/url */ +}; + +/* Create a new peer: + * - `peer->user_hostname` is the passed `hostname` + * - `peer->hostname` is the normalized `hostname` via + * + IDN conversion if it has non-ASCII characters + * + stripping of surrounding '[]' for URL formatted ipv6 addresses + * + the path alone in case of a unix domain socket, e.g. hostname + * starts with CURL_PEER_UDS_PREFIX and is longer + * Will scam for IPv6 addresses even without surrounding '[]'. + * - `zoneid` ipv6 zone identifier or NULL + * - `scopeid` ipv6 scopeid of zoneid, when known. + */ +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer); + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer); +#endif + +/* Unlink any peer in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src); + +/* Drop a reference, peer may be passed as NULL */ +void Curl_peer_unlink(struct Curl_peer **ppeer); + +/* TRUE if both peers are NULL or have completely same properties. */ +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2); + +/* TRUE if both peers are NULL or have properties except the scheme. */ +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer); + +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer); + +#ifndef CURL_DISABLE_PROXY + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype); +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_PEER_H */ diff --git a/lib/protocol.c b/lib/protocol.c index c8d43251cf7e..36ad97618103 100644 --- a/lib/protocol.c +++ b/lib/protocol.c @@ -361,6 +361,51 @@ const struct Curl_scheme Curl_scheme_smtps = { PORT_SMTPS, /* defport */ }; +const struct Curl_scheme Curl_scheme_socks = { + "socks", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4 = { + "socks4", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4a = { + "socks4a", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5 = { + "socks5", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5h = { + "socks5h", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + const struct Curl_scheme Curl_scheme_telnet = { "telnet", /* scheme */ #ifdef CURL_DISABLE_TELNET @@ -430,49 +475,54 @@ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) 6. make sure this function uses the same hash function that worked for schemetable.c */ - static const struct Curl_scheme * const all_schemes[47] = { - &Curl_scheme_mqtt, - &Curl_scheme_smtp, - &Curl_scheme_tftp, - &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &Curl_scheme_ldaps, - &Curl_scheme_dict, NULL, - &Curl_scheme_file, NULL, - &Curl_scheme_pop3s, - &Curl_scheme_ftp, + static const struct Curl_scheme * const all_schemes[59] = { NULL, + &Curl_scheme_pop3, NULL, + &Curl_scheme_smtps, + &Curl_scheme_socks, + &Curl_scheme_socks4, + &Curl_scheme_socks5, NULL, NULL, + &Curl_scheme_gophers, + &Curl_scheme_ws, + &Curl_scheme_sftp, + &Curl_scheme_socks4a, &Curl_scheme_scp, - &Curl_scheme_mqtts, - &Curl_scheme_imaps, + &Curl_scheme_rtsp, + &Curl_scheme_dict, NULL, NULL, + &Curl_scheme_gopher, NULL, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_smb, NULL, &Curl_scheme_ldap, - &Curl_scheme_http, - &Curl_scheme_smb, NULL, NULL, - &Curl_scheme_telnet, + &Curl_scheme_ldaps, + &Curl_scheme_imap, NULL, NULL, NULL, + &Curl_scheme_imaps, &Curl_scheme_https, - &Curl_scheme_gopher, - &Curl_scheme_rtsp, NULL, NULL, - &Curl_scheme_wss, NULL, - &Curl_scheme_gophers, - &Curl_scheme_smtps, - &Curl_scheme_pop3, - &Curl_scheme_ws, NULL, NULL, - &Curl_scheme_sftp, - &Curl_scheme_ftps, NULL, - &Curl_scheme_smbs, NULL, + &Curl_scheme_tftp, + &Curl_scheme_telnet, NULL, NULL, NULL, + &Curl_scheme_file, + &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ftp, + &Curl_scheme_mqtt, NULL, + &Curl_scheme_socks5h, + &Curl_scheme_http, + &Curl_scheme_pop3s, NULL, + &Curl_scheme_mqtts, NULL, + &Curl_scheme_smbs, + &Curl_scheme_ftps, }; if(len && (len <= 7)) { const char *s = scheme; size_t l = len; const struct Curl_scheme *h; - unsigned int c = 792; + unsigned int c = 443; while(l) { - c <<= 4; + c <<= 5; c += (unsigned int)Curl_raw_tolower(*s); s++; l--; } - h = all_schemes[c % 47]; + h = all_schemes[c % 59]; if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) return h; } diff --git a/lib/protocol.h b/lib/protocol.h index f8254096e235..fc2c844db708 100644 --- a/lib/protocol.h +++ b/lib/protocol.h @@ -51,6 +51,7 @@ struct easy_pollset; #define PORT_SMTPS 465 /* sometimes called SSMTP */ #define PORT_RTSP 554 #define PORT_GOPHER 70 +#define PORT_SOCKS 1080 #define PORT_MQTT 1883 #define PORT_MQTTS 8883 @@ -62,6 +63,7 @@ struct easy_pollset; #define CURLPROTO_WS (1L << 30) #define CURLPROTO_WSS ((curl_prot_t)1 << 31) #define CURLPROTO_MQTTS (1LL << 32) +#define CURLPROTO_SOCKS (1LL << 33) #define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) @@ -224,6 +226,7 @@ struct Curl_protocol { SSL connection in the same family without having PROTOPT_SSL. */ #define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ +#define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */ /* Everything about a URI scheme. */ struct Curl_scheme { @@ -268,6 +271,11 @@ extern const struct Curl_scheme Curl_scheme_smb; extern const struct Curl_scheme Curl_scheme_smbs; extern const struct Curl_scheme Curl_scheme_smtp; extern const struct Curl_scheme Curl_scheme_smtps; +extern const struct Curl_scheme Curl_scheme_socks; +extern const struct Curl_scheme Curl_scheme_socks4; +extern const struct Curl_scheme Curl_scheme_socks4a; +extern const struct Curl_scheme Curl_scheme_socks5; +extern const struct Curl_scheme Curl_scheme_socks5h; extern const struct Curl_scheme Curl_scheme_telnet; extern const struct Curl_scheme Curl_scheme_tftp; extern const struct Curl_scheme Curl_scheme_ws; diff --git a/lib/rtsp.c b/lib/rtsp.c index b08767f377cd..78cb6847b5bd 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -304,14 +304,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* Setup the first_* fields to allow auth details get sent to this origin */ - if(!data->state.first_host) { - data->state.first_host = curlx_strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->scheme->protocol; - } + if(!data->state.first_origin) + Curl_peer_link(&data->state.first_origin, conn->origin); /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to diff --git a/lib/smb.c b/lib/smb.c index 6a97d2e00685..8a75b2dbae27 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -500,7 +500,7 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) } else { smbc->user = conn->user; - smbc->domain = curlx_strdup(conn->host.name); + smbc->domain = curlx_strdup(conn->origin->hostname); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; } @@ -720,7 +720,8 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, struct smb_tree_connect msg; struct connectdata *conn = data->conn; char *p = msg.bytes; - const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) + + const size_t byte_count = strlen(conn->origin->hostname) + + strlen(smbc->share) + strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ if(byte_count > sizeof(msg.bytes)) @@ -735,7 +736,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, "\\\\%s\\" /* hostname */ "%s%c" /* share */ "%s", /* service */ - conn->host.name, smbc->share, 0, SERVICENAME); + conn->origin->hostname, smbc->share, 0, SERVICENAME); p++; /* count the final null-termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); diff --git a/lib/socks.c b/lib/socks.c index b3da5be0a367..ed60063ab5d6 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -98,7 +98,7 @@ static const char * const cf_socks_statename[] = { struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - uint16_t remote_port; + struct Curl_peer *dest; const char *user; const char *passwd; CURLproxycode presult; @@ -109,7 +109,6 @@ struct socks_ctx { BIT(resolve_local); BIT(start_resolving); BIT(socks4a); - char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -273,8 +272,8 @@ static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, (void)data; buf[0] = 4; /* version (SOCKS4) */ buf[1] = 1; /* connect */ - buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ - buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ + buf[2] = (unsigned char)((sx->dest->port >> 8) & 0xffU); /* MSB */ + buf[3] = (unsigned char)(sx->dest->port & 0xffU); /* LSB */ result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); if(result || (nwritten != 4)) @@ -329,7 +328,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -340,7 +339,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->hostname); + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) @@ -365,7 +364,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, } else { /* No ipv4 address resolved */ - failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + failf(data, "SOCKS4 connection to %s not supported", sx->dest->hostname); return CURLPX_RESOLVE_HOST; } @@ -487,7 +486,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* SOCKS4 can only do IPv4, insist! */ sx->ip_version = CURL_IPRESOLVE_V4; CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", - sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); + sx->socks4a ? "a" : "", + sx->dest->hostname, sx->dest->port); /* * Compose socks4 request @@ -508,7 +508,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* socks4a, not resolving locally, sends the hostname. * add an invalid address + user + hostname */ unsigned char buf[4] = { 0, 0, 0, 1 }; - size_t hlen = strlen(sx->hostname) + 1; /* including NUL */ + size_t hlen = strlen(sx->dest->hostname) + 1; /* including NUL */ if(hlen > 255) { failf(data, "SOCKS4: too long hostname"); @@ -520,7 +520,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, presult = socks4_req_add_user(sx, data); if(presult) return socks_failed(sx, cf, data, presult); - result = Curl_bufq_cwrite(&sx->iobuf, sx->hostname, hlen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->dest->hostname, hlen, + &nwritten); if(result || (nwritten != hlen)) return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); /* request complete */ @@ -591,7 +592,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, (void)cf; /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!sx->resolve_local && strlen(sx->hostname) > 255) { + if(!sx->resolve_local && strlen(sx->dest->hostname) > 255) { failf(data, "SOCKS5: the destination hostname is too long to be " "resolved remotely by the proxy."); return CURLPX_LONG_HOSTNAME; @@ -779,28 +780,28 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, /* remote resolving, send what type+addr/string to resolve */ #ifdef USE_IPV6 - if(strchr(sx->hostname, ':')) { + if(strchr(sx->dest->hostname, ':')) { desttype = 4; destination = ipbuf; destlen = 16; - if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1) + if(curlx_inet_pton(AF_INET6, sx->dest->hostname, ipbuf) != 1) return CURLPX_BAD_ADDRESS_TYPE; } else #endif - if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) { + if(curlx_inet_pton(AF_INET, sx->dest->hostname, ipbuf) == 1) { desttype = 1; destination = ipbuf; destlen = 4; } else { - const size_t hostname_len = strlen(sx->hostname); + const size_t hostname_len = strlen(sx->dest->hostname); /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so this cast to unsigned char is safe. Assert to guard against future refactoring that might remove or reorder that earlier check. */ DEBUGASSERT(hostname_len <= 255); desttype = 3; - destination = (const unsigned char *)sx->hostname; + destination = (const unsigned char *)sx->dest->hostname; destlen = (unsigned char)hostname_len; /* one byte length */ } @@ -814,13 +815,13 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, if(result || (nwritten != destlen)) return CURLPX_SEND_REQUEST; /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xff); - req[1] = (unsigned char)(sx->remote_port & 0xff); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xff); + req[1] = (unsigned char)(sx->dest->port & 0xff); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", - sx->hostname, sx->remote_port); + sx->dest->hostname, sx->dest->port); return CURLPX_OK; } @@ -845,7 +846,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -855,7 +856,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, /* resolve the hostname by connecting the DNS filter */ result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) @@ -869,7 +871,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); if(!ai) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } @@ -883,7 +886,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", - dest, sx->remote_port); + dest, sx->dest->port); } #ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) { @@ -893,7 +896,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", - dest, sx->remote_port); + dest, sx->dest->port); } #endif @@ -915,8 +918,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, goto out; } /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); - req[1] = (unsigned char)(sx->remote_port & 0xffU); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xffU); + req[1] = (unsigned char)(sx->dest->port & 0xffU); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) { presult = CURLPX_SEND_REQUEST; @@ -971,7 +974,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = resp[1]; failf(data, "cannot complete SOCKS5 connection to %s. (%d)", - sx->hostname, code); + sx->dest->hostname, code); if(code < 9) { /* RFC 1928 section 6 lists: */ static const CURLproxycode lookup[] = { @@ -1043,7 +1046,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_START: CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", - sx->hostname, sx->remote_port); + sx->dest->hostname, sx->dest->port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1181,6 +1184,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, static void socks_proxy_ctx_free(struct socks_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->dest); Curl_bufq_free(&ctx->iobuf); curlx_free(ctx); } @@ -1244,7 +1248,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, "(via %s port %u)", (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - ctx->hostname, ctx->remote_port, + ctx->dest->hostname, ctx->dest->port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", @@ -1315,8 +1319,8 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: if(sx) { - *pres1 = sx->remote_port; - *((const char **)pres2) = sx->hostname; + *pres1 = sx->dest->port; + *((const char **)pres2) = sx->dest->hostname; return CURLE_OK; } break; @@ -1354,8 +1358,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, const char *user, @@ -1363,10 +1366,9 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, { struct Curl_cfilter *cf; struct socks_ctx *ctx; - size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - if(!hostlen) + if(!dest) return CURLE_FAILED_INIT; switch(proxy_type) { @@ -1381,13 +1383,12 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, } /* NUL byte already part of struct size */ - ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { return CURLE_OUT_OF_MEMORY; } - memcpy(ctx->hostname, hostname, hostlen); - ctx->remote_port = port; + Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; ctx->user = user; diff --git a/lib/socks.h b/lib/socks.h index ea368326d2bf..e17b761f1c81 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -26,6 +26,9 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY + +struct Curl_peer; + /* * Helper read-from-socket functions. Does the same as Curl_read() but it * blocks until all bytes amount of buffersize will be read. No more, no less. @@ -46,15 +49,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data); #endif -/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` - * and `port` with optional credentials. - * Credentials are NOT duplicated and are +/* Insert a SOCKS filter after `cf_at` for connecting to `dest`. + * Credentials are optional and NOT duplicated and are * expected to exist during connect phase. */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, const char *user, diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 254e84935546..d54c00fc2747 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -141,8 +141,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (gss_OID)GSS_C_NULL_OID, &server); } else { - service.value = curl_maprintf("%s@%s", - serviceptr, conn->socks_proxy.host.name); + service.value = curl_maprintf("%s@%s", serviceptr, + conn->socks_proxy.peer->hostname); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(service.value); diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 385312a3681c..cc520a49d0cf 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -71,7 +71,7 @@ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, *service_namep = curlx_strdup(service); else *service_namep = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); + service, conn->socks_proxy.peer->hostname); if(!*service_namep) return CURLE_OUT_OF_MEMORY; diff --git a/lib/url.c b/lib/url.c index accaaaa3adfc..912e47175b33 100644 --- a/lib/url.c +++ b/lib/url.c @@ -132,9 +132,6 @@ static void data_priority_cleanup(struct Curl_easy *data); # error READBUFFER_SIZE is too small #endif -/* Reject URLs exceeding this length */ -#define MAX_URL_LEN 0xffff - /* * get_protocol_family() * @@ -252,7 +249,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - curlx_safefree(data->state.first_host); + Curl_peer_unlink(&data->state.first_origin); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -513,34 +510,28 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_conn_cf_discard_all(data, conn, (int)i); } - Curl_free_idnconverted_hostname(&conn->host); - Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY - Curl_free_idnconverted_hostname(&conn->http_proxy.host); - Curl_free_idnconverted_hostname(&conn->socks_proxy.host); curlx_safefree(conn->http_proxy.user); curlx_safefree(conn->socks_proxy.user); curlx_safefree(conn->http_proxy.passwd); curlx_safefree(conn->socks_proxy.passwd); - curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ - curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ + Curl_peer_unlink(&conn->http_proxy.peer); + Curl_peer_unlink(&conn->socks_proxy.peer); #endif curlx_safefree(conn->user); curlx_safefree(conn->passwd); curlx_safefree(conn->sasl_authzid); curlx_safefree(conn->options); curlx_safefree(conn->oauth_bearer); - curlx_safefree(conn->host.rawalloc); /* hostname buffer */ - curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ - curlx_safefree(conn->secondaryhostname); curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); -#ifdef USE_UNIX_SOCKETS - curlx_safefree(conn->unix_domain_socket); -#endif curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); + Curl_peer_unlink(&conn->origin); + Curl_peer_unlink(&conn->via_peer); + Curl_peer_unlink(&conn->origin2); + Curl_peer_unlink(&conn->via_peer2); curlx_free(conn); /* free all the connection oriented data */ } @@ -576,8 +567,7 @@ static bool proxy_info_matches(const struct proxy_info *data, const struct proxy_info *needle) { if((data->proxytype == needle->proxytype) && - (data->port == needle->port) && - curl_strequal(data->host.name, needle->host.name)) { + Curl_peer_same_destination(data->peer, needle->peer)) { if(Curl_timestrcmp(data->user, needle->user) || Curl_timestrcmp(data->passwd, needle->passwd)) @@ -753,30 +743,11 @@ static bool url_match_connect_config(struct connectdata *conn, return FALSE; } - if(m->needle->bits.conn_to_host != conn->bits.conn_to_host) + if(!m->needle->via_peer != !conn->via_peer) /* do not mix connections that use the "connect to host" feature and * connections that do not use this feature */ return FALSE; - if(m->needle->bits.conn_to_port != conn->bits.conn_to_port) - /* do not mix connections that use the "connect to port" feature and - * connections that do not use this feature */ - return FALSE; - - /* Does `conn` use the correct protocol? */ -#ifdef USE_UNIX_SOCKETS - if(m->needle->unix_domain_socket) { - if(!conn->unix_domain_socket) - return FALSE; - if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket)) - return FALSE; - if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket) - return FALSE; - } - else if(conn->unix_domain_socket) - return FALSE; -#endif - return TRUE; } @@ -1025,7 +996,7 @@ static bool url_match_destination(struct connectdata *conn, || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy #endif ) { - if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) { + if(m->needle->scheme != conn->scheme) { /* `needle` and `conn` do not have the same scheme... */ if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { /* and `conn`s protocol family is not the protocol `needle` wants. @@ -1041,16 +1012,10 @@ static bool url_match_destination(struct connectdata *conn, } } - /* If needle has "conn_to_*" set, conn must match this */ - if((m->needle->bits.conn_to_host && !curl_strequal( - m->needle->conn_to_host.name, conn->conn_to_host.name)) || - (m->needle->bits.conn_to_port && - m->needle->conn_to_port != conn->conn_to_port)) - return FALSE; - - /* hostname and port must match */ - if(!curl_strequal(m->needle->host.name, conn->host.name) || - m->needle->remote_port != conn->remote_port) + /* `needle` must have the same hostname and port in origin and + * via_peer (if present, NULL peers are equal) */ + if(!Curl_peer_same_destination(m->needle->origin, conn->origin) || + !Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) return FALSE; } return TRUE; @@ -1362,7 +1327,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ conn->created = *Curl_pgrs_now(data); @@ -1429,36 +1393,25 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) return NULL; } -static CURLcode findprotocol(struct Curl_easy *data, - struct connectdata *conn, - const char *protostr) +static CURLcode url_set_conn_scheme(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_scheme *scheme) { - const struct Curl_scheme *p = Curl_get_scheme(protostr); - - if(p && p->run && /* Protocol found supported. Check if allowed */ - (data->set.allowed_protocols & p->protocol)) { - - /* it is allowed for "normal" request, now do an extra check if this is - the result of a redirect */ - if(data->state.this_is_a_follow && - !(data->set.redir_protocols & p->protocol)) - /* nope, get out */ - ; - else { - /* Perform setup complement if some. */ - conn->scheme = conn->given = p; - /* 'port' and 'remote_port' are set in setup_connection_internals() */ - return CURLE_OK; - } + /* URL scheme is usable for connection when it is + * - allowed + * - not from a redirect or an allowed redirect protocol */ + if(scheme->run && + (data->set.allowed_protocols & scheme->protocol) && + (!data->state.this_is_a_follow || + (data->set.redir_protocols & scheme->protocol))) { + conn->scheme = conn->given = scheme; + return CURLE_OK; } - - /* The protocol was not found in the table, but we do not have to assign it - to anything since it is already assigned to a dummy-struct in the - create_conn() function when the connectdata struct is allocated. */ - failf(data, "Protocol \"%s\" %s%s", protostr, - p ? "disabled" : "not supported", - data->state.this_is_a_follow ? " (in redirect)" : ""); - + if(scheme->flags & PROTOPT_NO_TRANSFER) + failf(data, "Protocol \"%s\" is not for transfers", scheme->name); + else + failf(data, "Protocol \"%s\" is disabled%s", scheme->name, + data->state.this_is_a_follow ? " (in redirect)" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -1476,63 +1429,20 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } } -#ifdef USE_IPV6 -/* - * If the URL was set with an IPv6 numerical address with a zone id part, set - * the scope_id based on that! - */ - -static void zonefrom_url(CURLU *uh, struct Curl_easy *data, - struct connectdata *conn) -{ - char *zoneid; - CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) - (void)data; -#endif - - if(!uc && zoneid) { - const char *p = zoneid; - curl_off_t scope; - if(!curlx_str_number(&p, &scope, UINT_MAX)) - /* A plain number, use it directly as a scope id. */ - conn->scope_id = (unsigned int)scope; -#ifdef HAVE_IF_NAMETOINDEX - else { - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; - scopeidx = if_nametoindex(zoneid); - if(!scopeidx) { -#ifdef CURLVERBOSE - char buffer[STRERROR_LEN]; - infof(data, "Invalid zoneid: %s; %s", zoneid, - curlx_strerror(errno, buffer, sizeof(buffer))); -#endif - } - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX */ - - curlx_free(zoneid); - } -} -#else -#define zonefrom_url(a, b, c) Curl_nop_stmt -#endif - - #ifndef CURL_DISABLE_HSTS static CURLcode hsts_upgrade(struct Curl_easy *data, struct connectdata *conn, - CURLU *uh) + CURLU *uh, + uint16_t port_override, + uint32_t scope_id) { /* HSTS upgrade */ - if(data->hsts && curl_strequal("http", data->state.up.scheme) && - /* This MUST use the IDN decoded name */ - Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { + if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) && + Curl_hsts_applies(data->hsts, conn->origin)) { char *url; CURLUcode uc; + CURLcode result; + curlx_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); if(uc) @@ -1542,60 +1452,20 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, uc = curl_url_get(uh, CURLUPART_URL, &url, 0); if(uc) return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) { - curlx_free(url); - return Curl_uc_to_curlcode(uc); - } Curl_bufref_set(&data->state.url, url, 0, curl_free); + + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &conn->origin); + if(result) + return result; infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); } return CURLE_OK; } #else -#define hsts_upgrade(x, y, z) CURLE_OK +#define hsts_upgrade(x, y, z, a, b) CURLE_OK #endif -static CURLcode setup_hostname(struct Curl_easy *data, - struct connectdata *conn, - CURLU *uh) -{ - const char *hostname; - size_t hlen; - CURLUcode uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); - if(uc) { - /* file:// URLs are allowed to not have a host, all other errors need to - be passed back */ - if(!curl_strequal("file", data->state.up.scheme) || - (uc != CURLUE_NO_HOST)) - return Curl_uc_to_curlcode(uc); - } - else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { - failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); - return CURLE_URL_MALFORMAT; - } - - hostname = data->state.up.hostname; - hlen = hostname ? strlen(hostname) : 0; - - if(hostname && hostname[0] == '[') { - /* This looks like an IPv6 address literal. See if there is an address - scope. */ - hostname++; - hlen -= 2; - - zonefrom_url(uh, data, conn); - } - - /* make sure the connect struct gets its own copy of the hostname */ - conn->host.rawalloc = curlx_memdup0(hostname, hlen); - if(!conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.name = conn->host.rawalloc; - - return CURLE_OK; -} - /* * Parse URL and fill in the relevant members of the connection struct. */ @@ -1606,20 +1476,21 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, CURLU *uh; CURLUcode uc; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); + uint16_t port_override = data->state.allow_port ? data->set.use_port : 0; + uint32_t scope_id = 0; up_free(data); /* cleanup previous leftovers first */ /* parse the URL */ - if(use_set_uh) { + if(use_set_uh) uh = data->state.uh = curl_url_dup(data->set.uh); - } - else { + else uh = data->state.uh = curl_url(); - } - if(!uh) return CURLE_OUT_OF_MEMORY; + /* Calculate the *real* URL this transfer uses, applying defaults + * where information is missing. */ if(data->set.str[STRING_DEFAULT_PROTOCOL] && !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { char *url = curl_maprintf("%s://%s", @@ -1650,21 +1521,22 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, Curl_bufref_set(&data->state.url, newurl, 0, curl_free); } - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) - return Curl_uc_to_curlcode(uc); +#ifdef USE_IPV6 + scope_id = data->set.scope_id; +#endif + + /* `uh` is now as the connection should use it, probably. */ + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &conn->origin); + if(result) + return result; - result = setup_hostname(data, conn, uh); + result = hsts_upgrade(data, conn, uh, port_override, scope_id); + if(result) + return result; - /************************************************************* - * IDN-convert the hostnames - *************************************************************/ - if(!result) - result = Curl_idnconvert_hostname(&conn->host); - if(!result) - result = hsts_upgrade(data, conn, uh); - if(!result) - result = findprotocol(data, conn, data->state.up.scheme); + /* now that the origin is fixed, check and set the connection scheme */ + result = url_set_conn_scheme(data, conn, conn->origin->scheme); if(result) return result; @@ -1727,35 +1599,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(uc) return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, - CURLU_DEFAULT_PORT); - if(uc) { - if((uc == CURLUE_OUT_OF_MEMORY) || - !curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else { - curl_off_t port; - bool valid = TRUE; - if(data->set.use_port && data->state.allow_port) - port = data->set.use_port; - else { - const char *p = data->state.up.port; - if(curlx_str_number(&p, &port, 0xffff)) - valid = FALSE; - } - if(valid) - conn->remote_port = (unsigned short)port; - } - uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); if(uc && (uc != CURLUE_NO_QUERY)) return CURLE_OUT_OF_MEMORY; #ifdef USE_IPV6 - if(data->set.scope_id) - /* Override any scope that was set above. */ - conn->scope_id = data->set.scope_id; + /* Fill in the conn parts that do not use authority, yet. */ + conn->scope_id = conn->origin->scopeid; #endif return CURLE_OK; @@ -1804,8 +1654,7 @@ static CURLcode setup_range(struct Curl_easy *data) static CURLcode setup_connection_internals(struct Curl_easy *data, struct connectdata *conn) { - const char *hostname; - uint16_t port; + struct Curl_peer *peer = NULL; CURLcode result; DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); @@ -1817,29 +1666,21 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); /* Now create the destination name */ -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - hostname = conn->http_proxy.host.name; - port = conn->http_proxy.port; - } - else -#endif - { - port = conn->bits.conn_to_port ? - conn->conn_to_port : conn->remote_port; - hostname = conn->bits.conn_to_host ? - conn->conn_to_host.name : conn->host.name; - } + peer = Curl_conn_get_destination(conn, FIRSTSOCKET); + if(!peer) + return CURLE_FAILED_INIT; -#ifdef USE_IPV6 /* IPv6 addresses with a scope_id (0 is default == global) have a * printable representation with a '%' suffix. */ - if(conn->scope_id) - conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port, - conn->scope_id); + if(peer->ipv6) + if(peer->scopeid) + conn->destination = curl_maprintf("[%s%%%u]:%u", + peer->hostname, peer->scopeid, peer->port); + else + conn->destination = curl_maprintf("[%s]:%u", + peer->hostname, peer->port); else -#endif - conn->destination = curl_maprintf("%s:%u", hostname, port); + conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -1857,8 +1698,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, * name and is not limited to HTTP proxies only. * The returned pointer must be freed by the caller (unless NULL) ****************************************************************/ -static char *detect_proxy(struct Curl_easy *data, - struct connectdata *conn) +static char *url_detect_proxy(struct Curl_easy *data, + struct connectdata *conn) { char *proxy = NULL; @@ -1944,87 +1785,58 @@ static char *detect_proxy(struct Curl_easy *data, */ static CURLcode parse_proxy(struct Curl_easy *data, struct connectdata *conn, const char *proxy, - long proxytype) + uint8_t proxytype) { - char *portptr = NULL; char *proxyuser = NULL; char *proxypasswd = NULL; - char *host = NULL; - bool sockstype; - CURLUcode uc; - struct proxy_info *proxyinfo; - CURLU *uhp = curl_url(); + struct proxy_info *proxyinfo = NULL; CURLcode result = CURLE_OK; - char *scheme = NULL; -#ifdef USE_UNIX_SOCKETS - char *path = NULL; - bool is_unix_proxy = FALSE; -#endif + struct Curl_peer *peer = NULL; + CURLU *uhp = curl_url(); + CURLUcode uc; if(!uhp) { result = CURLE_OUT_OF_MEMORY; goto error; } - /* When parsing the proxy, allowing non-supported schemes since we have these made up ones for proxies. Guess scheme for URLs without it. */ uc = curl_url_set(uhp, CURLUPART_URL, proxy, CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); - if(!uc) { - /* parsed okay as a URL */ - uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(curl_strequal("https", scheme)) { - if(proxytype != CURLPROXY_HTTPS2) - proxytype = CURLPROXY_HTTPS; - else - proxytype = CURLPROXY_HTTPS2; - } - else if(curl_strequal("socks5h", scheme)) - proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(curl_strequal("socks5", scheme)) - proxytype = CURLPROXY_SOCKS5; - else if(curl_strequal("socks4a", scheme)) - proxytype = CURLPROXY_SOCKS4A; - else if(curl_strequal("socks4", scheme) || - curl_strequal("socks", scheme)) - proxytype = CURLPROXY_SOCKS4; - else if(curl_strequal("http", scheme)) - ; /* leave it as HTTP or HTTP/1.0 */ - else { - /* Any other xxx:// reject! */ - failf(data, "Unsupported proxy scheme for \'%s\'", proxy); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - else { + if(uc) { failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, curl_url_strerror(uc)); result = CURLE_COULDNT_RESOLVE_PROXY; goto error; } - if(IS_HTTPS_PROXY(proxytype) && - !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; + result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, + &peer, &proxytype); + if(result) goto error; - } - sockstype = - proxytype == CURLPROXY_SOCKS5_HOSTNAME || - proxytype == CURLPROXY_SOCKS5 || - proxytype == CURLPROXY_SOCKS4A || - proxytype == CURLPROXY_SOCKS4; + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + proxyinfo = &conn->http_proxy; + break; + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + proxyinfo = &conn->socks_proxy; + break; + default: + break; + } - proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; - proxyinfo->proxytype = (unsigned char)proxytype; + if(!proxyinfo) { + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } /* Is there a username and password given in this proxy URL? */ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); @@ -2061,88 +1873,20 @@ static CURLcode parse_proxy(struct Curl_easy *data, conn->bits.proxy_user_passwd = TRUE; /* enable it */ } - uc = curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); - if(uc == CURLUE_OUT_OF_MEMORY) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(portptr) { - curl_off_t num; - const char *p = portptr; - if(!curlx_str_number(&p, &num, UINT16_MAX)) - proxyinfo->port = (uint16_t)num; - /* Should we not error out when the port number is invalid? */ - curlx_free(portptr); - } - else { - if(data->set.proxyport) - /* None given in the proxy string, then get the default one if it is - given */ - proxyinfo->port = data->set.proxyport; - else { - if(IS_HTTPS_PROXY(proxytype)) - proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT; - else - proxyinfo->port = CURL_DEFAULT_PROXY_PORT; - } - } - - /* now, clone the proxy hostname */ - uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } -#ifdef USE_UNIX_SOCKETS - if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) { - uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* path will be "/", if no path was found */ - if(strcmp("/", path)) { - is_unix_proxy = TRUE; - curlx_free(host); - host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path); - if(!host) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - curlx_free(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - proxyinfo->host.name = host; - host = NULL; - } - } - - if(!is_unix_proxy) { -#endif - curlx_free(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - if(host[0] == '[') { - /* this is a numerical IPv6, strip off the brackets */ - size_t len = strlen(host); - host[len - 1] = 0; /* clear the trailing bracket */ - host++; - zonefrom_url(uhp, data, conn); - } - proxyinfo->host.name = host; - host = NULL; -#ifdef USE_UNIX_SOCKETS - } -#endif + Curl_peer_link(&proxyinfo->peer, peer); + proxyinfo->proxytype = proxytype; error: curlx_free(proxyuser); curlx_free(proxypasswd); - curlx_free(host); - curlx_free(scheme); -#ifdef USE_UNIX_SOCKETS - curlx_free(path); -#endif + Curl_peer_unlink(&peer); curl_url_cleanup(uhp); +#ifdef DEBUGBUILD + if(!result) { + DEBUGASSERT(proxyinfo); + DEBUGASSERT(proxyinfo->peer); + } +#endif return result; } @@ -2169,10 +1913,8 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, return result; } -/* create_conn helper to parse and init proxy values. to be called after Unix - socket init but before any proxy vars are evaluated. */ -static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode url_set_conn_proxies(struct Curl_easy *data, + struct connectdata *conn) { char *proxy = NULL; char *socksproxy = NULL; @@ -2223,7 +1965,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } } - if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? + if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { curlx_safefree(proxy); curlx_safefree(socksproxy); @@ -2231,18 +1973,10 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, #ifndef CURL_DISABLE_HTTP else if(!proxy && !socksproxy) /* if the host is not in the noproxy list, detect proxy. */ - proxy = detect_proxy(data, conn); + proxy = url_detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ curlx_safefree(no_proxy); -#ifdef USE_UNIX_SOCKETS - /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ - if(proxy && conn->unix_domain_socket) { - curlx_free(proxy); - proxy = NULL; - } -#endif - if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { curlx_free(proxy); /* Do not bother with an empty proxy string or if the protocol does not work with network */ @@ -2278,7 +2012,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, goto out; } - if(conn->http_proxy.host.rawalloc) { + if(conn->http_proxy.peer) { #ifdef CURL_DISABLE_HTTP /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ result = CURLE_UNSUPPORTED_PROTOCOL; @@ -2301,8 +2035,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ } - if(conn->socks_proxy.host.rawalloc) { - if(!conn->http_proxy.host.rawalloc) { + if(conn->socks_proxy.peer) { + if(!conn->http_proxy.peer) { /* once a socks proxy */ if(!conn->socks_proxy.user) { conn->socks_proxy.user = conn->http_proxy.user; @@ -2437,31 +2171,6 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, return CURLE_OUT_OF_MEMORY; } -/************************************************************* - * Figure out the remote port number and fix it in the URL - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * The port number embedded in the URL is replaced, if necessary. - *************************************************************/ -static CURLcode parse_remote_port(struct Curl_easy *data, - struct connectdata *conn) -{ - if(data->set.use_port && data->state.allow_port) { - /* if set, we use this instead of the port possibly given in the URL */ - char portbuf[16]; - CURLUcode uc; - conn->remote_port = data->set.use_port; - curl_msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); - uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); - if(uc) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - #ifndef CURL_DISABLE_NETRC static bool str_has_ctrl(const char *input) { @@ -2514,7 +2223,8 @@ static CURLcode override_login(struct Curl_easy *data, } if(!*passwdp) { - NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, + NETRCcode ret = Curl_parsenetrc(&data->state.netrc, + conn->origin->hostname, userp, passwdp, data->set.str[STRING_NETRC_FILE]); if(ret == NETRC_OUT_OF_MEMORY) @@ -2522,7 +2232,7 @@ static CURLcode override_login(struct Curl_easy *data, else if(ret && ((ret == NETRC_NO_MATCH) || (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { infof(data, "Could not find host %s in the %s file; using defaults", - conn->host.name, + conn->origin->hostname, (data->set.str[STRING_NETRC_FILE] ? data->set.str[STRING_NETRC_FILE] : ".netrc")); } @@ -2638,121 +2348,21 @@ static CURLcode set_login(struct Curl_easy *data, return result; } -/* - * Parses a "host:port" string to connect to. - * The hostname and the port may be empty; in this case, NULL is returned for - * the hostname and -1 for the port. - */ -static CURLcode parse_connect_to_host_port(struct Curl_easy *data, - const char *host, - char **hostname_result, - int *port_result) -{ - char *host_dup; - char *hostptr; - char *host_portno; - char *portptr; - int port = -1; - CURLcode result = CURLE_OK; - - *hostname_result = NULL; - *port_result = -1; - - if(!host || !*host) - return CURLE_OK; - - host_dup = curlx_strdup(host); - if(!host_dup) - return CURLE_OUT_OF_MEMORY; - - hostptr = host_dup; - - /* start scanning for port number at this point */ - portptr = hostptr; - - /* detect and extract RFC6874-style IPv6-addresses */ - if(*hostptr == '[') { -#ifdef USE_IPV6 - char *ptr = ++hostptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) - ptr++; - if(*ptr == '%') { - /* There might be a zone identifier */ - if(strncmp("%25", ptr, 3)) - infof(data, "Please URL encode %% as %%25, see RFC 6874."); - ptr++; - /* Allow unreserved characters as defined in RFC 3986 */ - while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || - (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) - ptr++; - } - if(*ptr == ']') - /* yeps, it ended nicely with a bracket as well */ - *ptr++ = '\0'; - else - infof(data, "Invalid IPv6 address format"); - portptr = ptr; - /* Note that if this did not end with a bracket, we still advanced the - * hostptr first, but I cannot see anything wrong with that as no host - * name nor a numeric can legally start with a bracket. - */ -#else - failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); - result = CURLE_NOT_BUILT_IN; - goto error; -#endif - } - - /* Get port number off server.com:1080 */ - host_portno = strchr(portptr, ':'); - if(host_portno) { - *host_portno = '\0'; /* cut off number from hostname */ - host_portno++; - if(*host_portno) { - curl_off_t portparse; - const char *p = host_portno; - if(curlx_str_number(&p, &portparse, 0xffff)) { - failf(data, "No valid port number in connect to host string (%s)", - host_portno); - result = CURLE_SETOPT_OPTION_SYNTAX; - goto error; - } - port = (int)portparse; /* we know it will fit */ - } - } - - /* now, clone the cleaned hostname */ - DEBUGASSERT(hostptr); - *hostname_result = curlx_strdup(hostptr); - if(!*hostname_result) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - *port_result = port; - -error: - curlx_free(host_dup); - return result; -} - /* * Parses one "connect to" string in the form: * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". */ static CURLcode parse_connect_to_string(struct Curl_easy *data, - struct connectdata *conn, - const char *conn_to_host, - char **host_result, - int *port_result) + const struct Curl_peer *dest, + const char *conn_to_line, + struct Curl_peer **pvia_dest) { CURLcode result = CURLE_OK; - const char *ptr = conn_to_host; + const char *ptr = conn_to_line; bool host_match = FALSE; bool port_match = FALSE; - *host_result = NULL; - *port_result = -1; + *pvia_dest = NULL; if(*ptr == ':') { /* an empty hostname always matches */ @@ -2763,9 +2373,8 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, /* check whether the URL's hostname matches. Use the URL hostname * when it was an IPv6 address. Otherwise use the connection's hostname * that has IDN conversion. */ - char *hostname_to_match = - (data->state.up.hostname && data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name; + const char *hostname_to_match = (dest->user_hostname[0] == '[') ? + dest->user_hostname : dest->hostname; size_t hlen = strlen(hostname_to_match); host_match = curl_strnequal(ptr, hostname_to_match, hlen); ptr += hlen; @@ -2786,68 +2395,44 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, if(ptr_next) { curl_off_t port_to_match; if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && - (port_to_match == (curl_off_t)conn->remote_port)) + ((uint16_t)port_to_match == dest->port)) { port_match = TRUE; + } ptr = ptr_next + 1; } } } - if(host_match && port_match) { - /* parse the hostname and port to connect to */ - result = parse_connect_to_host_port(data, ptr, host_result, port_result); - } + if(host_match && port_match && ptr && *ptr) + result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest); return result; } -/* - * Processes all strings in the "connect to" slist, and uses the "connect - * to host" and "connect to port" of the first string that matches. - */ -static CURLcode parse_connect_to_slist(struct Curl_easy *data, - struct connectdata *conn, - struct curl_slist *conn_to_host) +/* With `conn->origin` known, determine if we should talk to that + * directly or via another peer. This is the result of inspecting + * the "connect to" slist and "alt-svc" settings. */ +static CURLcode url_set_conn_peer(struct Curl_easy *data, + struct connectdata *conn) { CURLcode result = CURLE_OK; - char *host = NULL; - int port = -1; + const struct Curl_peer *origin = conn->origin; + struct Curl_peer *via_peer = NULL; + struct curl_slist *conn_to_entry = data->set.connect_to; - while(conn_to_host && !host && port == -1) { - result = parse_connect_to_string(data, conn, conn_to_host->data, - &host, &port); + DEBUGASSERT(!conn->via_peer); + Curl_peer_unlink(&conn->via_peer); + + while(conn_to_entry && !via_peer) { + result = parse_connect_to_string(data, origin, conn_to_entry->data, + &via_peer); if(result) return result; - - if(host && *host) { - conn->conn_to_host.rawalloc = host; - conn->conn_to_host.name = host; - conn->bits.conn_to_host = TRUE; - - infof(data, "Connecting to hostname: %s", host); - } - else { - /* no "connect to host" */ - conn->bits.conn_to_host = FALSE; - curlx_safefree(host); - } - - if(port >= 0) { - conn->conn_to_port = (uint16_t)port; - conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %u", conn->conn_to_port); - } - else { - /* no "connect to port" */ - conn->bits.conn_to_port = FALSE; - port = -1; - } - - conn_to_host = conn_to_host->next; + conn_to_entry = conn_to_entry->next; } #ifndef CURL_DISABLE_ALTSVC - if(data->asi && !host && (port == -1) && + if(data->asi && !via_peer && ((conn->scheme->protocol == CURLPROTO_HTTPS) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ @@ -2878,13 +2463,13 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, allowed_alpns |= ALPN_h1; allowed_alpns &= (int)data->asi->flags; - host = conn->host.rawalloc; - DEBUGF(infof(data, "check Alt-Svc for host %s", host)); + DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname)); #ifdef USE_HTTP3 if(!hit && (neg->wanted & CURL_HTTP_V3x)) { srcalpnid = ALPN_h3; hit = Curl_altsvc_lookup(data->asi, - ALPN_h3, host, conn->remote_port, /* from */ + ALPN_h3, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2894,7 +2479,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->h2_prior_knowledge) { srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h2, host, conn->remote_port, /* from */ + ALPN_h2, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2903,7 +2489,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->only_10) { srcalpnid = ALPN_h1; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, host, conn->remote_port, /* from */ + ALPN_h1, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2928,18 +2515,16 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } } else if(hit) { - char *hostd = curlx_strdup(as->dst.host); - if(!hostd) - return CURLE_OUT_OF_MEMORY; - conn->conn_to_host.rawalloc = hostd; - conn->conn_to_host.name = hostd; - conn->bits.conn_to_host = TRUE; - conn->conn_to_port = as->dst.port; - conn->bits.conn_to_port = TRUE; - conn->bits.altused = TRUE; + result = Curl_peer_create(data, conn->origin->scheme, + as->dst.host, as->dst.port, + &via_peer); + if(result) + return result; infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", - Curl_alpnid2str(srcalpnid), host, conn->remote_port, - Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + Curl_alpnid2str(srcalpnid), origin->hostname, origin->port, + Curl_alpnid2str(as->dst.alpnid), + via_peer->hostname, via_peer->port); + conn->bits.altused = TRUE; if(srcalpnid != as->dst.alpnid) { /* protocol version switch */ switch(as->dst.alpnid) { @@ -2962,15 +2547,10 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } #endif - return result; -} + if(via_peer) + conn->via_peer = via_peer; -static void url_move_hostname(struct hostname *dest, struct hostname *src) -{ - curlx_safefree(dest->rawalloc); - Curl_free_idnconverted_hostname(dest); - *dest = *src; - memset(src, 0, sizeof(*src)); + return result; } /* @@ -3014,20 +2594,19 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, /* Finding a connection for reuse in the cpool matches, among other * things on the "remote-relevant" hostname. This is not necessarily - * the authority of the URL, e.g. conn->host. For example: + * the authority of the URL, e.g. conn->origin. For example: * - we use a proxy (not tunneling). we want to send all requests * that use the same proxy on this connection. * - we have a "connect-to" setting that may redirect the hostname of * a new request to the same remote endpoint of an existing conn. * We want to reuse an existing conn to the remote endpoint. - * Since connection reuse does not match on conn->host necessarily, we + * Since connection reuse does not match on conn->origin necessarily, we * switch conn to needle's host settings. */ - url_move_hostname(&conn->host, &needle->host); - url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - - conn->conn_to_port = needle->conn_to_port; - conn->remote_port = needle->remote_port; + Curl_peer_link(&conn->origin, needle->origin); + Curl_peer_link(&conn->via_peer, needle->via_peer); + Curl_peer_link(&conn->origin2, needle->origin2); + Curl_peer_link(&conn->via_peer2, needle->via_peer2); } static void conn_meta_freeentry(void *p) @@ -3044,6 +2623,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, { struct connectdata *needle = NULL; CURLcode result = CURLE_OK; + bool network_scheme = TRUE; /* almost all are */ /************************************************************* * Check input data @@ -3067,9 +2647,58 @@ static CURLcode url_create_needle(struct Curl_easy *data, Curl_hash_init(&needle->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); + /************************************************************* + * Determine `conn->origin` and propulate `data->state.up` and + * other URL related properties. + *************************************************************/ result = parseurlandfillconn(data, needle); if(result) goto out; + DEBUGASSERT(needle->origin); + network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK); + +#ifdef USE_UNIX_SOCKETS + /************************************************************* + * Set UDS first. It overrides "via_peer" and proxy settings. + *************************************************************/ + if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) { + result = Curl_peer_uds_create(needle->origin->scheme, + data->set.str[STRING_UNIX_SOCKET_PATH], + (bool)data->set.abstract_unix_socket, + &needle->via_peer); + if(result) + goto out; + } +#endif /* USE_UNIX_SOCKETS */ + + if(network_scheme && !needle->via_peer) { + /************************************************************* + * If the `via_peer` is not already set (via UDS above), + * determine if we talk to `conn->origin` directly or use + * `conn->via_peer` using "connect to" and "alt-svc" properties. + *************************************************************/ + result = url_set_conn_peer(data, needle); + if(result) + goto out; + } + +#ifndef CURL_DISABLE_PROXY + /* After the Unix socket init but before the proxy vars are used, parse and + * initialize the proxy settings. + * Any UDS `via_peer` disables proxies. */ + if(network_scheme && !(needle->via_peer && needle->via_peer->unix_socket)) { + result = url_set_conn_proxies(data, needle); + if(result) + goto out; + + /************************************************************* + * If the protocol is using SSL and HTTP proxy is used, we set + * the tunnel_proxy bit. + *************************************************************/ + if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) + needle->bits.tunnel_proxy = TRUE; + } +#endif /* CURL_DISABLE_PROXY */ if(data->set.str[STRING_SASL_AUTHZID]) { needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); @@ -3087,40 +2716,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, } } -#ifdef USE_UNIX_SOCKETS - if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - needle->unix_domain_socket = - curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!needle->unix_domain_socket) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - needle->bits.abstract_unix_socket = data->set.abstract_unix_socket; - } -#endif - - /* After the Unix socket init but before the proxy vars are used, parse and - initialize the proxy vars */ -#ifndef CURL_DISABLE_PROXY - result = create_conn_helper_init_proxy(data, needle); - if(result) - goto out; - - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) - needle->bits.tunnel_proxy = TRUE; -#endif - - /************************************************************* - * Figure out the remote port number and fix it in the URL - *************************************************************/ - result = parse_remote_port(data, needle); - if(result) - goto out; - /* Check for overridden login details and set them accordingly so that they are known when protocol->setup_connection is called! */ result = override_login(data, needle); @@ -3131,51 +2726,12 @@ static CURLcode url_create_needle(struct Curl_easy *data, if(result) goto out; - /************************************************************* - * Process the "connect to" linked list of hostname/port mappings. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - result = parse_connect_to_slist(data, needle, data->set.connect_to); - if(result) - goto out; - - /************************************************************* - * IDN-convert the proxy hostnames - *************************************************************/ -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - result = Curl_idnconvert_hostname(&needle->http_proxy.host); - if(result) - goto out; - } - if(needle->bits.socksproxy) { - result = Curl_idnconvert_hostname(&needle->socks_proxy.host); - if(result) - goto out; - } -#endif - if(needle->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&needle->conn_to_host); - if(result) - goto out; - } - /************************************************************* * Check whether the host and the "connect to host" are equal. * Do this after the hostnames have been IDN-converted. *************************************************************/ - if(needle->bits.conn_to_host && - curl_strequal(needle->conn_to_host.name, needle->host.name)) { - needle->bits.conn_to_host = FALSE; - } - - /************************************************************* - * Check whether the port and the "connect to port" are equal. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - if(needle->bits.conn_to_port && - needle->conn_to_port == needle->remote_port) { - needle->bits.conn_to_port = FALSE; + if(Curl_peer_equal(needle->origin, needle->via_peer)) { + Curl_peer_unlink(&needle->via_peer); } #ifndef CURL_DISABLE_PROXY @@ -3183,8 +2739,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, * If the "connect to" feature is used with an HTTP proxy, * we set the tunnel_proxy bit. *************************************************************/ - if((needle->bits.conn_to_host || needle->bits.conn_to_port) && - needle->bits.httpproxy) + if(needle->via_peer && needle->bits.httpproxy) needle->bits.tunnel_proxy = TRUE; #endif @@ -3203,7 +2758,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, needle->bits.tls_enable_alpn = TRUE; } - if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { + if(network_scheme) { /* Setup callbacks for network connections */ needle->recv[FIRSTSOCKET] = Curl_cf_recv; needle->send[FIRSTSOCKET] = Curl_cf_send; @@ -3211,7 +2766,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, 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)) + if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket) needle->transport_wanted = TRNSPRT_UNIX; #endif } @@ -3219,6 +2774,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, out: if(!result) { DEBUGASSERT(needle); + DEBUGASSERT(needle->origin); *pneedle = needle; } else { @@ -3324,14 +2880,14 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", conn->bits.proxy ? "proxy" : "host", - conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.name ? conn->http_proxy.host.dispname : - conn->host.dispname); + conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : + conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : + conn->origin->hostname); #else infof(data, "Reusing existing %s: connection%s with host %s", conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", - conn->host.dispname); + conn->origin->hostname); #endif } else { diff --git a/lib/url.h b/lib/url.h index 66baf7017428..ebbe9d53c47f 100644 --- a/lib/url.h +++ b/lib/url.h @@ -25,6 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" +/* Reject URLs exceeding this length */ +#define MAX_URL_LEN 0xffff + /* * Prototypes for library-wide functions */ diff --git a/lib/urldata.h b/lib/urldata.h index f35bfee053d0..8dec816a16bc 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -60,6 +60,7 @@ #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" +#include "peer.h" #include "splay.h" #include "curlx/dynbuf.h" #include "bufref.h" @@ -259,10 +260,6 @@ struct ConnectBits { BIT(close); /* if set, we close the connection after this request */ BIT(reuse); /* if set, this is a reused connection */ BIT(altused); /* this is an alt-svc "redirect" */ - BIT(conn_to_host); /* if set, this connection has a "connect to host" - that overrides the host in the URL */ - BIT(conn_to_port); /* if set, this connection has a "connect to port" - that overrides the port in the URL (remote port) */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -289,9 +286,6 @@ struct ConnectBits { BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifdef USE_UNIX_SOCKETS - BIT(abstract_unix_socket); -#endif BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with accept() */ BIT(parallel_connect); /* set TRUE when a parallel connect attempt has @@ -334,8 +328,7 @@ struct ip_quadruple { ((x)->transport == TRNSPRT_QUIC)) struct proxy_info { - struct hostname host; - uint16_t port; + struct Curl_peer *peer; /* proxy to this peer */ uint8_t proxytype; /* what kind of proxy that is in use */ char *user; /* proxy username string, allocated */ char *passwd; /* proxy password string, allocated */ @@ -369,10 +362,11 @@ struct connectdata { * the connection is cleaned up (see Curl_hash_add2()).*/ struct Curl_hash meta_hash; - struct hostname host; - char *secondaryhostname; /* secondary socket hostname (ftp) */ - struct hostname conn_to_host; /* the host to connect to. valid only if - bits.conn_to_host is set */ + /* Who the connection is talking to, ultimately */ + struct Curl_peer *origin; /* connection ultimately talks to this */ + struct Curl_peer *via_peer; /* if set, connection really talks to this */ + struct Curl_peer *origin2; /* origin of SECONDARYSOCKET */ + struct Curl_peer *via_peer2; /* peer of SECONDARYSOCKET */ #ifndef CURL_DISABLE_PROXY struct proxy_info socks_proxy; struct proxy_info http_proxy; @@ -438,10 +432,6 @@ struct connectdata { curlnegotiate proxy_negotiate_state; #endif -#ifdef USE_UNIX_SOCKETS - char *unix_domain_socket; -#endif - /* When this connection is created, store the conditions for the local end bind. This is stored before the actual bind and before any connection is made and will serve the purpose of being used for comparison reasons so @@ -456,14 +446,8 @@ struct connectdata { #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif - /* The field below gets set in connect.c:connecthost() */ - uint16_t remote_port; /* the remote port, not the proxy port! */ - uint16_t conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ uint16_t localportrange; uint16_t localport; - uint16_t secondary_port; /* secondary socket remote port to connect to - (ftp) */ uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily the transport the connection ends using due to Alt-Svc and happy eyeballing. Use Curl_conn_get_transport() for actual value once the @@ -477,14 +461,13 @@ struct connectdata { #ifndef CURL_DISABLE_PROXY #define CURL_CONN_HOST_DISPNAME(c) \ - ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ - (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname) + ((c)->bits.socksproxy ? (c)->socks_proxy.peer->user_hostname : \ + (c)->bits.httpproxy ? (c)->http_proxy.peer->user_hostname : \ + (c)->via_peer ? (c)->via_peer->user_hostname : \ + (c)->origin->user_hostname) #else #define CURL_CONN_HOST_DISPNAME(c) \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname + ((c)->via_peer ? (c)->via_peer->user_hostname : (c)->origin->user_hostname) #endif /* The end of connectdata. */ @@ -692,13 +675,11 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* hostname, port number and protocol of the first (not followed) request. - if set, this should be the hostname that we will sent authorization to, - no else. Used to make Location: following not keep sending user+password. - This is strdup()ed data. */ - char *first_host; - int first_remote_port; - curl_prot_t first_remote_protocol; + /* origin of the first (not followed) request. + if set, this is the origin we sent authorization to, none else. + Used to make Location: following not keep sending user+password. */ + struct Curl_peer *first_origin; + int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 5ecfd60aad6f..3c6f6e8e9559 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -418,7 +418,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); if(!spn) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index f0b6780fca6f..f351a76986e2 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -131,7 +131,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); if(!spn) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index a00078fce193..81c29cd497d4 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -137,13 +137,10 @@ bool Curl_auth_user_contains_domain(const char *user) */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) { - struct connectdata *conn = data->conn; return !data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts || - (data->state.first_host && - curl_strequal(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->scheme->protocol)); + (data->state.first_origin && + Curl_peer_equal(data->state.first_origin, data->conn->origin)); } #ifdef USE_NTLM diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index d81f2a9e6bd5..ad4c713fa9b1 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -178,7 +178,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, NULL) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; else if(!peer->sni && - (wolfSSL_X509_check_ip_asc(cert, peer->hostname, + (wolfSSL_X509_check_ip_asc(cert, peer->dest->hostname, 0) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; wolfSSL_X509_free(cert); diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 44094e466c84..c6a6e0cfdfbe 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2551,7 +2551,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name); + data->state.up.hostname : conn->origin->hostname); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2595,9 +2595,9 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) } } - if(conn->remote_port) { + if(conn->origin->port) { rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT, - &conn->remote_port); + &conn->origin->port); if(rc != SSH_OK) { failf(data, "Could not set remote port"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 4e2a72269ff5..1f36934f6dda 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -361,9 +361,9 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, rc = CURLKHSTAT_REJECT; else { keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->host.name, - (conn->remote_port != PORT_SSH) ? - conn->remote_port : -1, + conn->origin->hostname, + (conn->origin->port != PORT_SSH) ? + conn->origin->port : -1, remotekey, keylen, LIBSSH2_KNOWNHOST_TYPE_PLAIN| LIBSSH2_KNOWNHOST_KEYENC_RAW| @@ -427,14 +427,14 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, /* the found host+key did not match but has been told to be fine anyway so we add it in memory */ int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, + conn->origin->hostname, NULL, remotekey, keylen, LIBSSH2_KNOWNHOST_TYPE_PLAIN| LIBSSH2_KNOWNHOST_KEYENC_RAW| keybit, NULL); if(addrc) infof(data, "WARNING: adding the known host %s failed", - conn->host.name); + conn->origin->hostname); else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || rc == CURLKHSTAT_FINE_REPLACE) { /* now we write the entire in-memory list of known hosts to the @@ -642,16 +642,16 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } p = kh_name_end + 2; /* start of port number */ if(!curlx_str_number(&p, &port, 0xffff) && - (kh_name_end && (port == conn->remote_port))) { + (kh_name_end && (port == conn->origin->port))) { kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); if(strncmp(store->name + 1, - conn->host.name, kh_name_size) == 0) { + conn->origin->hostname, kh_name_size) == 0) { found = TRUE; break; } } } - else if(strcmp(store->name, conn->host.name) == 0) { + else if(strcmp(store->name, conn->origin->hostname) == 0) { found = TRUE; break; } @@ -667,7 +667,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, int rc; const char *hostkey_method = NULL; infof(data, "Found host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { case LIBSSH2_KNOWNHOST_KEY_ED25519: @@ -710,7 +710,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } else { infof(data, "Did not find host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); } } diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index e28a40cc0e0a..5bd800b8cb84 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, if(conn_config->verifyhost) { host_str = CFStringCreateWithCString(NULL, - peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); + peer->sni ? peer->sni : peer->dest->hostname, kCFStringEncodingUTF8); if(!host_str) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index c0de44416e8a..e60e5a5ecc70 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1361,11 +1361,12 @@ static void gtls_msg_verify_result(struct Curl_easy *data, if(!was_verified) { if(needs_verified) { failf(data, "SSL: certificate subject name (%s) does not match " - "target hostname '%s'", certname, peer->dispname); + "target hostname '%s'", certname, + peer->dest->user_hostname); } else infof(data, " common name: %s (does not match '%s')", - certname, peer->dispname); + certname, peer->dest->user_hostname); } else infof(data, " common name: %s (matched)", certname); @@ -1821,7 +1822,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, IP addresses) */ rc = (int)gnutls_x509_crt_check_hostname(x509_cert, peer->sni ? peer->sni : - peer->hostname); + peer->dest->hostname); result = (!rc && config->verifyhost) ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index e2822668f085..4396c703ace6 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -791,7 +791,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, char errorbuf[128]; infof(data, "mbedTLS: Connecting to %s:%d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.dest->hostname, connssl->peer.dest->port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -932,7 +932,8 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, } if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? - connssl->peer.sni : connssl->peer.hostname)) { + connssl->peer.sni : + connssl->peer.dest->hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. Thus even if curl connects to a host specified as an IP address, this function must be used. */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4629ca444435..30b5c1e2e98c 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ - size_t hostlen = strlen(peer->hostname); + size_t hostlen = strlen(peer->dest->hostname); (void)conn; switch(peer->type) { case CURL_SSL_PEER_IPV4: - if(!curlx_inet_pton(AF_INET, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET, peer->dest->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in_addr); break; #ifdef USE_IPV6 case CURL_SSL_PEER_IPV6: - if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET6, peer->dest->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in6_addr); @@ -2115,10 +2115,11 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == strlen(altptr)) && /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ - Curl_cert_hostcheck(altptr, altlen, peer->hostname, hostlen)) { + Curl_cert_hostcheck(altptr, altlen, + peer->dest->hostname, hostlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", - peer->dispname, (int)altlen, altptr); + peer->dest->user_hostname, (int)altlen, altptr); } break; @@ -2128,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's IP address!", - peer->dispname); + peer->dest->user_hostname); } break; } @@ -2144,9 +2145,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : (peer->type == CURL_SSL_PEER_IPV4) ? "ipv4 address" : "ipv6 address"; - infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); + infof(data, " subjectAltName does not match %s %s", tname, + peer->dest->user_hostname); failf(data, "SSL: no alternative certificate subject name matches " - "target %s '%s'", tname, peer->dispname); + "target %s '%s'", tname, peer->dest->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2206,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)cn, cnlen, - peer->hostname, hostlen)) { + peer->dest->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target hostname '%s'", cn, peer->dispname); + "target hostname '%s'", cn, peer->dest->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -3532,9 +3534,9 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, #else if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", - peer->hostname ? peer->hostname : "NULL", outername); + peer->dest->hostname ? peer->dest->hostname : "NULL", outername); result = SSL_ech_set1_server_names(octx->ssl, - peer->hostname, outername, + peer->dest->hostname, outername, 0 /* do send outer */); if(result != 1) { infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); @@ -4263,7 +4265,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, curlx_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->peer.hostname, connssl->peer.port); + connssl->peer.dest->hostname, connssl->peer.dest->port); } return result; @@ -4310,7 +4312,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config->verifypeer && !conn_config->verifyhost && - inner && !strcmp(inner, connssl->peer.hostname)) { + inner && !strcmp(inner, connssl->peer.dest->hostname)) { VERBOSE(status = "bad name (tolerated without peer verification)"); rv = SSL_ECH_STATUS_SUCCESS; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index d886c8505207..8c56ede7fcf8 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1095,7 +1095,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, DEBUGASSERT(rconn == NULL); rr = rustls_client_connection_new(backend->config, - connssl->peer.hostname, + connssl->peer.dest->hostname, &rconn); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "rustls_client_connection_new"); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index fb5ff0e9d8e6..3eadeaef2548 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -843,7 +843,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.dest->hostname, connssl->peer.dest->port)); #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; @@ -895,7 +895,8 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; + snihost = connssl->peer.sni ? + connssl->peer.sni : connssl->peer.dest->hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; @@ -1238,7 +1239,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.dest->hostname, connssl->peer.dest->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1590,7 +1591,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.dest->hostname, connssl->peer.dest->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -2428,7 +2429,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, *done = FALSE; if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.dest->hostname, connssl->peer.dest->port); } if(!backend->ctxt || cf->shutdown) { diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 9be6fe311ccd..47c52af280ee 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -509,7 +509,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) SECURITY_STATUS sspi_status; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->peer.hostname; + const char *conn_hostname = connssl->peer.dest->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 891facfabb43..c83f6e667858 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1180,11 +1180,8 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { + Curl_peer_unlink(&peer->dest); curlx_safefree(peer->sni); - if(peer->dispname != peer->hostname) - curlx_free(peer->dispname); - peer->dispname = NULL; - curlx_safefree(peer->hostname); curlx_safefree(peer->scache_key); peer->type = CURL_SSL_PEER_DNS; } @@ -1224,13 +1221,12 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, const char *tls_id, uint8_t transport) { - const char *ehostname, *edispname; + struct Curl_peer *dest = NULL; CURLcode result = CURLE_OUT_OF_MEMORY; /* We expect a clean struct, e.g. called only ONCE */ DEBUGASSERT(peer); - DEBUGASSERT(!peer->hostname); - DEBUGASSERT(!peer->dispname); + DEBUGASSERT(!peer->dest); DEBUGASSERT(!peer->sni); /* We need the hostname for SNI negotiation. Once handshaked, this remains * the SNI hostname for the TLS connection. When the connection is reused, @@ -1240,46 +1236,33 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, peer->transport = transport; #ifndef CURL_DISABLE_PROXY if(Curl_ssl_cf_is_proxy(cf)) { - ehostname = cf->conn->http_proxy.host.name; - edispname = cf->conn->http_proxy.host.dispname; - peer->port = cf->conn->http_proxy.port; + dest = cf->conn->http_proxy.peer; } else #endif { - ehostname = cf->conn->host.name; - edispname = cf->conn->host.dispname; - peer->port = (uint16_t)cf->conn->remote_port; + dest = cf->conn->origin; } /* hostname MUST exist and not be empty */ - if(!ehostname || !ehostname[0]) { + if(!dest) { result = CURLE_FAILED_INIT; goto out; } - peer->hostname = curlx_strdup(ehostname); - if(!peer->hostname) - goto out; - if(!edispname || !strcmp(ehostname, edispname)) - peer->dispname = peer->hostname; - else { - peer->dispname = curlx_strdup(edispname); - if(!peer->dispname) - goto out; - } - peer->type = get_peer_type(peer->hostname); + Curl_peer_link(&peer->dest, dest); + peer->type = get_peer_type(dest->hostname); if(peer->type == CURL_SSL_PEER_DNS) { /* not an IP address, normalize according to RCC 6066 ch. 3, * max len of SNI is 2^16-1, no trailing dot */ - size_t len = strlen(peer->hostname); - if(len && (peer->hostname[len - 1] == '.')) + size_t len = strlen(dest->hostname); + if(len && (dest->hostname[len - 1] == '.')) len--; if(len < USHRT_MAX) { peer->sni = curlx_calloc(1, len + 1); if(!peer->sni) goto out; - Curl_strntolower(peer->sni, peer->hostname, len); + Curl_strntolower(peer->sni, dest->hostname, len); peer->sni[len] = 0; } } @@ -1353,7 +1336,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->prefs_checked = TRUE; } - if(!connssl->peer.hostname) { + if(!connssl->peer.dest) { char tls_id[80]; connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 6db67cf7482e..54933169545f 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -91,12 +91,10 @@ typedef enum { } ssl_peer_type; struct ssl_peer { - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ + struct Curl_peer *dest; char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ - uint16_t port; /* port we are talking to */ uint8_t transport; /* one of TRNSPRT_* defines */ }; diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 59fa256bc31d..9efb8208ea4d 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -148,7 +148,8 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, *ppeer_key = NULL; curlx_dyn_init(&buf, 10 * 1024); - r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); + r = curlx_dyn_addf(&buf, "%s:%d", + peer->dest->hostname, peer->dest->port); if(r) goto out; @@ -187,13 +188,10 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out; } if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->bits.conn_to_host) { - r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); - if(r) - goto out; - } - if(cf->conn->bits.conn_to_port) { - r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); + if(cf->conn->via_peer) { + r = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", + cf->conn->via_peer->hostname, + cf->conn->via_peer->port); if(r) goto out; } diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 15c81c2874bd..7de03b36d50b 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1765,9 +1765,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "unable to get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } - ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.hostname, 0); + ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.dest->hostname, 0); CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", - connssl->peer.hostname, ret); + connssl->peer.dest->hostname, ret); if(ret != WOLFSSL_SUCCESS) detail = DOMAIN_NAME_MISMATCH; wolfSSL_X509_free(cert); @@ -1790,7 +1790,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->peer.dispname); + connssl->peer.dest->hostname); return CURLE_PEER_FAILED_VERIFICATION; } else if(ASN_NO_SIGNER_E == detail) { diff --git a/scripts/schemetable.c b/scripts/schemetable.c index afa54c39aff7..8127ecd2a16a 100644 --- a/scripts/schemetable.c +++ b/scripts/schemetable.c @@ -53,6 +53,11 @@ static const char *scheme[] = { "smbs", "smtp", "smtps", + "socks", + "socks4", + "socks4a", + "socks5", + "socks5h", "telnet", "tftp", "ws", From f97f01f5928fedfd7a5d25a739165d0266223e7c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 11:13:07 +0200 Subject: [PATCH 0026/1062] socks_gssapi: simplify Curl_SOCKS5_gssapi_negotiate Also: pass in NULL when 'conf_state' is not wanted for gss_wrap() and gss_unwrap() Closes #21502 --- lib/socks_gssapi.c | 218 ++++++++++++++++++++++++++++----------------- 1 file changed, 138 insertions(+), 80 deletions(-) diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index d54c00fc2747..79359be22327 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -98,37 +98,13 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_gss_create_service_name(struct Curl_easy *data, + struct connectdata *conn, + const char *serviceptr, + gss_name_t *server) { - struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - size_t actualread; - size_t nwritten; - CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; - OM_uint32 gss_ret_flags; - int gss_conf_state, gss_enc; - gss_buffer_desc service = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; - gss_name_t server = GSS_C_NO_NAME; - gss_name_t gss_client_name = GSS_C_NO_NAME; - unsigned short us_length; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ - const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + gss_buffer_desc service = GSS_C_EMPTY_BUFFER; /* prepare service name */ if(strchr(serviceptr, '/')) { @@ -138,7 +114,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; gss_major_status = gss_import_name(&gss_minor_status, &service, - (gss_OID)GSS_C_NULL_OID, &server); + (gss_OID)GSS_C_NULL_OID, server); } else { service.value = curl_maprintf("%s@%s", serviceptr, @@ -148,7 +124,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, service.length = strlen(service.value); gss_major_status = gss_import_name(&gss_minor_status, &service, - GSS_C_NT_HOSTBASED_SERVICE, &server); + GSS_C_NT_HOSTBASED_SERVICE, server); } curlx_safefree(service.value); @@ -157,25 +133,50 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_import_name()")) { failf(data, "Failed to create service name."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server); return CURLE_COULDNT_CONNECT; } - (void)curlx_nonblock(sock, FALSE); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_name_t *server_ptr, + gss_ctx_id_t *gss_context, + OM_uint32 *gss_ret_flags) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + + /* GSS-API request looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ /* As long as we need to keep sending some context info, and there is no * errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, - &gss_context, - server, + gss_context, + *server_ptr, &Curl_krb5_mech_oid, NULL, gss_token, &gss_send_token, TRUE, - &gss_ret_flags); + gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) { curlx_safefree(gss_recv_token.value); @@ -185,10 +186,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_minor_status, "gss_init_sec_context") || /* the size needs to fit in a 16-bit field */ (gss_send_token.length > 0xffff)) { - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); - failf(data, "Failed to initial GSS-API token."); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); + failf(data, "Failed to initialize GSS-API token."); return CURLE_COULDNT_CONNECT; } @@ -202,9 +203,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &nwritten); if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API authentication request."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -213,9 +214,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_send_token.length, FALSE, &nwritten); if(result || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } @@ -237,8 +238,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -246,16 +247,16 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / message type */ failf(data, "Invalid GSS-API authentication response type (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -264,8 +265,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!us_length) { failf(data, "Invalid zero-length GSS-API authentication token."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -275,8 +276,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Could not allocate memory for GSS-API authentication " "response token."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } @@ -285,25 +286,34 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_verify(struct Curl_easy *data, + gss_ctx_id_t *gss_context) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_name_t gss_client_name = GSS_C_NO_NAME; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; /* Everything is good so far, user was authenticated! */ - gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, + gss_major_status = gss_inquire_context(&gss_minor_status, *gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_inquire_context")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; @@ -312,7 +322,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &gss_send_token, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_display_name")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine username."); @@ -325,6 +335,26 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); + return CURLE_OK; +} + +static CURLcode socks5_gss_negotiate_enc(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_ctx_id_t *gss_context, + OM_uint32 gss_ret_flags) +{ + struct connectdata *conn = cf->conn; + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + int gss_enc; + /* Do encryption */ socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 2; /* encryption message type */ @@ -379,19 +409,19 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_send_token.length = 1; gss_send_token.value = curlx_memdup(&gss_enc, gss_send_token.length); if(!gss_send_token.value) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, + gss_major_status = gss_wrap(&gss_minor_status, *gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, - &gss_conf_state, &gss_w_token); + NULL, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { curlx_safefree(gss_send_token.value); gss_send_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -406,7 +436,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -415,7 +445,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); if(result || (nwritten != 1)) { failf(data, "Failed to send GSS-API encryption type."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } @@ -425,7 +455,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); @@ -434,7 +464,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -442,14 +472,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid GSS-API encryption response type (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -458,14 +488,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!us_length) { failf(data, "Invalid zero-length GSS-API encryption token."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, @@ -475,20 +505,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to receive GSS-API encryption type."); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { - gss_major_status = gss_unwrap(&gss_minor_status, gss_context, + gss_major_status = gss_unwrap(&gss_minor_status, *gss_context, &gss_recv_token, &gss_w_token, - 0, GSS_C_QOP_DEFAULT); + NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -499,7 +529,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API encryption response length (%zu).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -512,7 +542,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -521,8 +551,6 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length = 0; } - (void)curlx_nonblock(sock, TRUE); - infof(data, "SOCKS5 access with%s protection granted.", (socksreq[0] == 0) ? "out GSS-API data" : ((socksreq[0] == 1) ? " GSS-API integrity" : @@ -530,11 +558,41 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OK; } +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + OM_uint32 gss_ret_flags = 0; + gss_name_t server = GSS_C_NO_NAME; + const char *serviceptr = + data->set.str[STRING_PROXY_SERVICE_NAME] ? + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; + + result = socks5_gss_create_service_name(data, conn, serviceptr, &server); + if(!result) { + (void)curlx_nonblock(sock, FALSE); + result = socks5_gss_auth_loop(cf, data, &server, &gss_context, + &gss_ret_flags); + } + if(!result) + result = socks5_gss_auth_verify(data, &gss_context); + if(!result) + result = socks5_gss_negotiate_enc(cf, data, &gss_context, gss_ret_flags); + + /* unconditionally put it back to non-blocking */ + (void)curlx_nonblock(sock, TRUE); + + return result; +} + #if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif From 80214dca6b7bbc6aaeb9fbf5a5e0344b9fe94171 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 4 May 2026 14:25:47 +0200 Subject: [PATCH 0027/1062] GHA: verify function-lengths No production code function is allowed to be longer than 500 lines. The lib/setopt.c:setopt_cptr function is currently exempt, as a single exception until we make it smaller. Closes #21492 --- .github/workflows/checksrc.yml | 7 +- scripts/Makefile.am | 2 +- scripts/top-length | 134 +++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 3 deletions(-) create mode 100755 scripts/top-length diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 1571983ced9e..cc20781527c3 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -99,7 +99,7 @@ jobs: scripts/pythonlint.sh complexity: - name: 'complexity' + name: 'complexity and function sizes' runs-on: ubuntu-slim timeout-minutes: 3 steps: @@ -117,9 +117,12 @@ jobs: with: persist-credentials: false - - name: 'check scores' + - name: 'check function complexity' run: ./scripts/top-complexity + - name: 'check function lengths' + run: ./scripts/top-length + xmllint: name: 'xmllint' runs-on: ubuntu-slim diff --git a/scripts/Makefile.am b/scripts/Makefile.am index e0f433422a8d..7ffa98ed9bb6 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ cdall cd2cd managen dmaketgz maketgz release-tools.sh verify-release \ cmakelint.sh mdlinkcheck CMakeLists.txt perlcheck.sh pythonlint.sh \ spacecheck.pl randdisable wcurl top-complexity extract-unit-protos \ - .checksrc badwords badwords-all badwords.txt + .checksrc badwords badwords-all badwords.txt top-length dist_bin_SCRIPTS = wcurl diff --git a/scripts/top-length b/scripts/top-length new file mode 100755 index 000000000000..eaf69b3025f6 --- /dev/null +++ b/scripts/top-length @@ -0,0 +1,134 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +use strict; +use warnings; + +####################################################################### +# Check for a command in the PATH of the test server. +# +sub checkcmd { + my ($cmd)=@_; + my @paths; + if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { + # PATH separator is different + @paths=(split(';', $ENV{'PATH'})); + } + else { + @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); + } + for(@paths) { + if(-x "$_/$cmd" && ! -d "$_/$cmd") { + # executable bit but not a directory! + return "$_/$cmd"; + } + } + return ""; +} + +my $pmccabe = checkcmd("pmccabe"); +if(!$pmccabe) { + print "Make sure 'pmccabe' exists in your PATH\n"; + exit 1; +} +if(! -r "lib/url.c" || ! -r "lib/urldata.h") { + print "Invoke this script in the curl source tree root\n"; + exit 1; +} + +my @files; +open(my $git, "-|", "git", "ls-files", "*.c") or die "git ls-files failed: $!"; +while(<$git>) { + chomp $_; + my $file = $_; + # we cannot filter these with git so do it here + if($file =~ /^(lib|src)/) { + push @files, $file; + } +} +close($git); + +open(my $pmc, "-|", $pmccabe, @files) or die "pmccabe failed: $!"; +my @output = <$pmc>; +close($pmc); + +# these functions can be this long, but not longer +my %whitelist = ( + 'setopt_cptr' => 674, + ); + +# function length above this level is treated as an error and contributes to +# the script's exit code +my $cutoff = 500; + +# show this many from the top +my $top = $ARGV[0] ? $ARGV[0] : 25; + +my $error = 0; +my %where; +my %perm; +my $funcs = 0; +my $alllines = 0; +# each line starts with the complexity score +# 142 417 809 1677 1305 src/tool_getparam.c(1677): getparameter +for my $l (@output) { + chomp $l; + if($l =~/^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { + my ($score, $length, $path, $func)=($1, $2, $3, $4); + + my $allow = 0; + if($whitelist{$func} && + ($length <= $whitelist{$func})) { + $allow = 1; + } + $where{"$path:$func"}=$length; + $perm{"$path:$func"}=$allow; + if(($length > $cutoff) && !$allow) { + $error++; + } + + $alllines += $length; + $funcs++; + } +} + +my $showncutoff; +for my $e (sort {$where{$b} <=> $where{$a}} keys %where) { + if(!$showncutoff && + ($where{$e} <= $cutoff)) { + print "\n---- threshold: $cutoff ----\n\n"; + $showncutoff = 1; + } + printf "%-5d %s%s\n", $where{$e}, $e, + $perm{$e} ? " [ALLOWED]" : ""; + if(!--$top) { + last; + } +} + +printf "\nAverage function length: %.2f lines\n", $alllines/$funcs; + +exit $error; From 2cb6ba672da5fc000a1b1b8b5496c6459eb34378 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 17:01:41 +0200 Subject: [PATCH 0028/1062] hsts: rename Curl_hsts() to hsts_check() and make it static It is no longer used outside of hsts.c Closes #21507 --- lib/hsts.c | 117 ++++++++++++++++++++++-------------------- lib/hsts.h | 4 +- tests/unit/unit1660.c | 4 +- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/lib/hsts.c b/lib/hsts.c index 261dffc4792c..856b525a6244 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -130,6 +130,62 @@ static CURLcode hsts_create(struct hsts *h, return CURLE_OK; } +/* + * Return the matching HSTS entry, or NULL if the given hostname is not + * currently an HSTS one. + * + * The 'subdomain' argument tells the function if subdomain matching should be + * attempted. + * + * @unittest 1660 + */ +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain); +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain) +{ + struct stsentry *bestsub = NULL; + if(h) { + time_t now = time(NULL); + struct Curl_llist_node *e; + struct Curl_llist_node *n; + size_t blen = 0; + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; + if(hostname[hlen - 1] == '.') + /* remove the trailing dot */ + --hlen; + + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + size_t ntail; + n = Curl_node_next(e); + if(sts->expires <= now) { + /* remove expired entries */ + Curl_node_remove(&sts->node); + hsts_free(sts); + continue; + } + ntail = strlen(sts->host); + if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { + size_t offs = hlen - ntail; + if((hostname[offs - 1] == '.') && + curl_strnequal(&hostname[offs], sts->host, ntail) && + (ntail > blen)) { + /* save the tail match with the longest tail */ + bestsub = sts; + blen = ntail; + } + } + /* avoid curl_strequal because the hostname is not null-terminated */ + if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) + return sts; + } + } + return bestsub; +} + CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header) { @@ -203,7 +259,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, if(!expires) { /* remove the entry if present verbatim (without subdomain match) */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { Curl_node_remove(&sts->node); hsts_free(sts); @@ -218,7 +274,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, expires += now; /* check if it already exists */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { /* update these fields */ sts->expires = expires; @@ -230,57 +286,6 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, return CURLE_OK; } -/* - * Return TRUE if the given hostname is currently an HSTS one. - * - * The 'subdomain' argument tells the function if subdomain matching should be - * attempted. - */ -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - size_t hlen, bool subdomain) -{ - struct stsentry *bestsub = NULL; - if(h) { - time_t now = time(NULL); - struct Curl_llist_node *e; - struct Curl_llist_node *n; - size_t blen = 0; - - if((hlen > MAX_HSTS_HOSTLEN) || !hlen) - return NULL; - if(hostname[hlen - 1] == '.') - /* remove the trailing dot */ - --hlen; - - for(e = Curl_llist_head(&h->list); e; e = n) { - struct stsentry *sts = Curl_node_elem(e); - size_t ntail; - n = Curl_node_next(e); - if(sts->expires <= now) { - /* remove expired entries */ - Curl_node_remove(&sts->node); - hsts_free(sts); - continue; - } - ntail = strlen(sts->host); - if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { - size_t offs = hlen - ntail; - if((hostname[offs - 1] == '.') && - curl_strnequal(&hostname[offs], sts->host, ntail) && - (ntail > blen)) { - /* save the tail match with the longest tail */ - bestsub = sts; - blen = ntail; - } - } - /* avoid curl_strequal because the hostname is not null-terminated */ - if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) - return sts; - } - } - return bestsub; -} - /* * Send this HSTS entry to the write callback. */ @@ -437,7 +442,7 @@ static CURLcode hsts_add_host_expire(struct hsts *h, if(hostlen) { /* only add it if not already present */ - e = Curl_hsts(h, host, hostlen, subdomain); + e = hsts_check(h, host, hostlen, subdomain); if(!e) result = hsts_create(h, host, hostlen, subdomain, expires); /* 'host' is not necessarily null terminated */ @@ -612,8 +617,8 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) { - return !!Curl_hsts(h, dest->hostname, - strlen(dest->hostname), TRUE); + return !!hsts_check(h, dest->hostname, + strlen(dest->hostname), TRUE); } #if defined(DEBUGBUILD) || defined(UNITTESTS) diff --git a/lib/hsts.h b/lib/hsts.h index 93b998072926..d4c7fe826b13 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -25,6 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" +struct hsts; + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" @@ -54,8 +56,6 @@ struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header); -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - size_t hlen, bool subdomain); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadfile(struct Curl_easy *data, diff --git a/tests/unit/unit1660.c b/tests/unit/unit1660.c index bc68a76c4654..62b4ea009598 100644 --- a/tests/unit/unit1660.c +++ b/tests/unit/unit1660.c @@ -138,7 +138,7 @@ static CURLcode test_unit1660(const char *arg) } chost = headers[i].chost ? headers[i].chost : headers[i].host; - e = Curl_hsts(h, chost, strlen(chost), TRUE); + e = hsts_check(h, chost, strlen(chost), TRUE); showsts(e, chost); } @@ -147,7 +147,7 @@ static CURLcode test_unit1660(const char *arg) /* verify that it is exists for 7 seconds */ chost = "expire.example"; for(i = 100; i < 110; i++) { - e = Curl_hsts(h, chost, strlen(chost), TRUE); + e = hsts_check(h, chost, strlen(chost), TRUE); showsts(e, chost); deltatime++; /* another second passed */ } From 481c9d46f1405723260a6633bfc1eee9ada2631f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 17:09:36 +0200 Subject: [PATCH 0029/1062] hostip: convert Curl_resolv_unix to static resolv_unix It was only used within this file Closes #21508 --- lib/hostip.c | 57 ++++++++++++++++++++++++++-------------------------- lib/hostip.h | 7 ------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/lib/hostip.c b/lib/hostip.c index 7b85f9ff8105..9fd7c75a73d8 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -935,6 +935,33 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, #endif /* USE_ALARM_TIMEOUT */ +#ifdef USE_UNIX_SOCKETS +static CURLcode 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, 0, &addr, NULL, 0); + return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} +#endif /* USE_UNIX_SOCKETS */ + /* * Curl_resolv() is the main name resolve function within libcurl. It resolves * a name and returns a pointer to the entry in the 'entry' argument. This @@ -975,8 +1002,7 @@ CURLcode Curl_resolv(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS if(peer->unix_socket) - return Curl_resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, - pdns); + return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); #else if(peer->unix_socket) return hostip_resolv_failed(data, peer->hostname, for_proxy); @@ -1110,30 +1136,3 @@ void Curl_resolv_destroy_all(struct Curl_easy *data) } #endif /* USE_CURL_ASYNC */ - -#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, 0, &addr, NULL, 0); - return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; -} -#endif /* USE_UNIX_SOCKETS */ diff --git a/lib/hostip.h b/lib/hostip.h index 2ba586ce9722..45370ec48e0c 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -184,11 +184,4 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, uint8_t transport); #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 */ From 2c81cf620e559b6c448dcfd8ef389f214746b533 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 15:02:32 +0200 Subject: [PATCH 0030/1062] multi: make multi_runsingle use sub functions for states The state machine now calls dedicated sub functions for each state, to reduce the size and complexity. Closes #21506 --- lib/multi.c | 438 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 254 insertions(+), 184 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 7520253d702c..5e5c3194de3c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2000,9 +2000,9 @@ static CURLcode mspeed_check(struct Curl_easy *data) return CURLE_OK; } -static CURLMcode state_performing(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_performing(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { char *newurl = NULL; bool retry = FALSE; @@ -2139,9 +2139,9 @@ static CURLMcode state_performing(struct Curl_easy *data, return mresult; } -static CURLMcode state_do(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_do(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; @@ -2276,8 +2276,8 @@ static CURLMcode state_do(struct Curl_easy *data, return mresult; } -static CURLMcode state_ratelimiting(struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_ratelimiting(struct Curl_easy *data, + CURLcode *resultp) { CURLcode result = CURLE_OK; CURLMcode mresult = CURLM_OK; @@ -2301,9 +2301,9 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, return mresult; } -static CURLMcode state_connect(struct Curl_multi *multi, - struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_connect(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode *resultp) { /* Connect. We want to get a connection identifier filled in. This state can be entered from SETUP and from PENDING. */ @@ -2449,16 +2449,240 @@ static void handle_completed(struct Curl_multi *multi, multi_assess_wakeup(multi); } -static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_easy *data, - struct Curl_sigpipe_ctx *sigpipe_ctx) +static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) +{ + *result = Curl_pretransfer(data); + if(*result) + return CURLM_OK; + + /* after init, go SETUP */ + multistate(data, MSTATE_SETUP); + Curl_pgrsTime(data, TIMER_STARTOP); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_setup(struct Curl_easy *data) +{ + Curl_pgrsTime(data, TIMER_STARTSINGLE); + if(data->set.timeout) + Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + if(data->set.connecttimeout) + /* Since a connection might go to pending and back to CONNECT several + times before it actually takes off, we need to set the timeout once + in SETUP before we enter CONNECT the first time. */ + Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + + multistate(data, MSTATE_CONNECT); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_connecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) { bool connected; + + if(!data->conn) { + DEBUGASSERT(0); + *result = CURLE_FAILED_INIT; + return CURLM_OK; + } + if(!Curl_xfer_recv_is_paused(data)) { + *result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + if(connected && !(*result)) { + 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); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", *result); + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; + } + } + return CURLM_OK; +} + +static CURLMcode multistate_protoconnect(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + bool protocol_connected = FALSE; + + if(!(*result) && data->conn->bits.reuse) { + /* ftp seems to hang when protoconnect on reused connection since we + * handle PROTOCONNECT in general inside the filters, it seems wrong to + * restart this on a reused connection. + */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + if(!(*result)) + *result = protocol_connect(data, &protocol_connected); + if(!(*result) && !protocol_connected) { + /* switch to waiting state */ + multistate(data, MSTATE_PROTOCONNECTING); + return CURLM_CALL_MULTI_PERFORM; + } + else if(!(*result)) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; +} + +static CURLMcode multistate_protoconnecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool protocol_connected = FALSE; + + /* protocol-specific connect phase */ + *result = protocol_connecting(data, &protocol_connected); + if(!(*result) && protocol_connected) { + /* after the connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool dophase_done = FALSE; + + /* we continue DOING until the DO phase is complete */ + DEBUGASSERT(data->conn); + *result = protocol_doing(data, &dophase_done); + if(!(*result)) { + if(dophase_done) { + /* after DO, go DO_DONE or DO_MORE */ + multistate(data, data->conn->bits.do_more ? + MSTATE_DOING_MORE : MSTATE_DID); + return CURLM_CALL_MULTI_PERFORM; + } /* dophase_done */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing_more(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + int control; + + /* + * When we are connected, DOING MORE and then go DID + */ + DEBUGASSERT(data->conn); + *result = multi_do_more(data, &control); + + if(!(*result)) { + if(control) { + /* if positive, advance to DO_DONE + if negative, go back to DOING */ + multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); + return CURLM_CALL_MULTI_PERFORM; + } + /* else + stay in DO_MORE */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_did(struct Curl_multi *multi, + struct Curl_easy *data) +{ + DEBUGASSERT(data->conn); + if(data->conn->bits.multiplex) + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* multiplexed */ + + /* Only perform the transfer if there is a good socket to work with. + Having both BAD is a signal to skip immediately to DONE */ + if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || + CONN_SOCK_IDX_VALID(data->conn->send_idx)) + multistate(data, MSTATE_PERFORMING); + else { +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch && + ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { + data->wildcard->state = CURLWC_DONE; + } +#endif + multistate(data, MSTATE_DONE); + } + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *result) +{ + if(data->conn) { + CURLcode res; + + /* post-transfer command */ + res = multi_done(data, *result, FALSE); + + /* allow a previously set error code take precedence */ + if(!(*result)) + *result = res; + } + +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch) { + if(data->wildcard->state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with MSTATE_INIT */ + multistate(data, MSTATE_INIT); + return CURLM_CALL_MULTI_PERFORM; + } + } +#endif + /* after we have DONE what we are supposed to do, go COMPLETED, and + it does not matter what the multi_done() returned! */ + multistate(data, MSTATE_COMPLETED); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) +{ CURLMcode mresult; CURLcode result = CURLE_OK; - int control; if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; @@ -2517,217 +2741,63 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_INIT: /* Transitional state. init this transfer. A handle never comes back to this state. */ - result = Curl_pretransfer(data); - if(result) - break; - - /* after init, go SETUP */ - multistate(data, MSTATE_SETUP); - Curl_pgrsTime(data, TIMER_STARTOP); - FALLTHROUGH(); + mresult = multistate_init(data, &result); + break; case MSTATE_SETUP: /* Transitional state. Setup things for a new transfer. The handle can come back to this state on a redirect. */ - Curl_pgrsTime(data, TIMER_STARTSINGLE); - if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - if(data->set.connecttimeout) - /* Since a connection might go to pending and back to CONNECT several - times before it actually takes off, we need to set the timeout once - in SETUP before we enter CONNECT the first time. */ - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - - multistate(data, MSTATE_CONNECT); - FALLTHROUGH(); + mresult = multistate_setup(data); + break; case MSTATE_CONNECT: - mresult = state_connect(multi, data, &result); + mresult = multistate_connect(multi, data, &result); break; case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - 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 && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - mresult = CURLM_CALL_MULTI_PERFORM; - multistate(data, MSTATE_PROTOCONNECT); - } - else if(result) { - /* failure detected */ - CURL_TRC_M(data, "connect failed -> %d", result); - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - break; - } - } + mresult = multistate_connecting(data, &stream_error, &result); break; case MSTATE_PROTOCONNECT: - if(!result && data->conn->bits.reuse) { - /* ftp seems to hang when protoconnect on reused connection since we - * handle PROTOCONNECT in general inside the filers, it seems wrong to - * restart this on a reused connection. - */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - break; - } - if(!result) - result = protocol_connect(data, &protocol_connected); - if(!result && !protocol_connected) { - /* switch to waiting state */ - multistate(data, MSTATE_PROTOCONNECTING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(!result) { - /* protocol connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnect(data, &stream_error, &result); break; case MSTATE_PROTOCONNECTING: /* protocol-specific connect phase */ - result = protocol_connecting(data, &protocol_connected); - if(!result && protocol_connected) { - /* after the connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(result) { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnecting(data, &stream_error, &result); break; case MSTATE_DO: - mresult = state_do(data, &stream_error, &result); + mresult = multistate_do(data, &stream_error, &result); break; case MSTATE_DOING: /* we continue DOING until the DO phase is complete */ - DEBUGASSERT(data->conn); - result = protocol_doing(data, &dophase_done); - if(!result) { - if(dophase_done) { - /* after DO, go DO_DONE or DO_MORE */ - multistate(data, data->conn->bits.do_more ? - MSTATE_DOING_MORE : MSTATE_DID); - mresult = CURLM_CALL_MULTI_PERFORM; - } /* dophase_done */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing(data, &stream_error, &result); break; case MSTATE_DOING_MORE: /* * When we are connected, DOING MORE and then go DID */ - DEBUGASSERT(data->conn); - result = multi_do_more(data, &control); - - if(!result) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ - multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* else - stay in DO_MORE */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing_more(data, &stream_error, &result); break; case MSTATE_DID: - DEBUGASSERT(data->conn); - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* multiplexed */ - - /* Only perform the transfer if there is a good socket to work with. - Having both BAD is a signal to skip immediately to DONE */ - if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || - CONN_SOCK_IDX_VALID(data->conn->send_idx)) - multistate(data, MSTATE_PERFORMING); - else { -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch && - ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard->state = CURLWC_DONE; - } -#endif - multistate(data, MSTATE_DONE); - } - mresult = CURLM_CALL_MULTI_PERFORM; + mresult = multistate_did(multi, data); break; case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - mresult = state_ratelimiting(data, &result); + mresult = multistate_ratelimiting(data, &result); break; case MSTATE_PERFORMING: - mresult = state_performing(data, &stream_error, &result); + mresult = multistate_performing(data, &stream_error, &result); break; case MSTATE_DONE: - /* this state is highly transient, so run another loop after this */ - mresult = CURLM_CALL_MULTI_PERFORM; - - if(data->conn) { - CURLcode res; - - /* post-transfer command */ - res = multi_done(data, result, FALSE); - - /* allow a previously set error code take precedence */ - if(!result) - result = res; - } - -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch) { - if(data->wildcard->state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again - with MSTATE_INIT */ - multistate(data, MSTATE_INIT); - break; - } - } -#endif - /* after we have DONE what we are supposed to do, go COMPLETED, and - it does not matter what the multi_done() returned! */ - multistate(data, MSTATE_COMPLETED); + mresult = multistate_done(data, &result); break; case MSTATE_COMPLETED: From e0df43b9d35a2ccacfb61a60534fa2f51a6a9468 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 May 2026 18:39:12 +0200 Subject: [PATCH 0031/1062] protocol: introduce typedef for the do_more() function Instead of using magic values -1, 0 and -1 using enum. Closes #21509 --- lib/ftp.c | 27 +++++++++++++++------------ lib/multi.c | 18 +++++++++--------- lib/protocol.h | 14 ++++++++++++-- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/lib/ftp.c b/lib/ftp.c index 4f537db2b26b..f06f8ef7ca16 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2281,10 +2281,10 @@ static CURLcode ftp_statemach(struct Curl_easy *data, * This function shall be called when the second FTP (data) connection is * connected. * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * 'more' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK * (which is for when PASV is being sent to retry a failed EPSV). */ -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +static CURLcode ftp_do_more(struct Curl_easy *data, domore *more) { struct connectdata *conn = data->conn; struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); @@ -2299,7 +2299,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc || !ftp) return CURLE_FAILED_INIT; - *completep = 0; /* default to stay in the state */ + *more = DOMORE_INCOMPLETE; /* default to stay in the state */ /* if the second connection has been set up, try to connect it fully * to the remote host. This may not complete at this time, for several @@ -2317,7 +2317,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result || (!connected && !is_eptr && !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { if(result && !is_eptr && (ftpc->count1 == 0)) { - *completep = -1; /* go back to DOING please */ + *more = DOMORE_GOBACK; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ return ftp_epsv_disable(data, ftpc, conn); } @@ -2330,7 +2330,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) They are only done to kickstart the do_more state */ result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; /* if we got an error or if we do not wait for a data connection return immediately */ @@ -2340,7 +2341,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) /* if we reach the end of the FTP state machine here, *complete will be TRUE but so is ftpc->wait_data_conn, which says we need to wait for the data connection and therefore we are not actually complete */ - *completep = 0; + *more = DOMORE_INCOMPLETE; } if(ftp->transfer <= PPTRANSFER_INFO) { @@ -2362,8 +2363,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result) return result; - *completep = 1; /* this state is now complete when the server has - connected back to us */ + *more = DOMORE_DONE; /* this state is now complete when the server has + connected back to us */ } else { result = ftp_check_ctrl_on_data_wait(data, ftpc); @@ -2382,7 +2383,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * deemed necessary and directly sent `STORE name`. If this was * then complete, but we are still waiting on the data connection, * the transfer has not been initiated yet. */ - *completep = (int)(ftpc->wait_data_conn ? 0 : complete); + *more = (!ftpc->wait_data_conn && complete) ? + DOMORE_DONE : DOMORE_INCOMPLETE; } else { /* download */ @@ -2425,7 +2427,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; } return result; } @@ -2435,7 +2438,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ - *completep = 1; + *more = DOMORE_DONE; CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), (int)result); } @@ -2450,7 +2453,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) { if(connected) { - int completed; + domore completed; CURLcode result = ftp_do_more(data, &completed); if(result) { diff --git a/lib/multi.c b/lib/multi.c index 5e5c3194de3c..be32740a7097 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1773,16 +1773,16 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) * stage DO state which (wrongly) was introduced to support FTP's second * connection. * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to - * DOING state there is more work to do! + * 'complete' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK + * (to DOING state when there is more work to do) */ -static CURLcode multi_do_more(struct Curl_easy *data, int *complete) +static CURLcode multi_do_more(struct Curl_easy *data, domore *complete) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - *complete = 0; + *complete = DOMORE_INCOMPLETE; if(conn->scheme->run->do_more) result = conn->scheme->run->do_more(data, complete); @@ -2596,7 +2596,7 @@ static CURLMcode multistate_doing_more(struct Curl_easy *data, bool *stream_error, CURLcode *result) { - int control; + domore control; /* * When we are connected, DOING MORE and then go DID @@ -2605,10 +2605,10 @@ static CURLMcode multistate_doing_more(struct Curl_easy *data, *result = multi_do_more(data, &control); if(!(*result)) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ - multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); + if(control != DOMORE_INCOMPLETE) { + /* if DONE, advance to DO_DONE + if GOBACK, go back to DOING */ + multistate(data, control == DOMORE_DONE ? MSTATE_DID : MSTATE_DOING); return CURLM_CALL_MULTI_PERFORM; } /* else diff --git a/lib/protocol.h b/lib/protocol.h index fc2c844db708..8ae2155ee6e6 100644 --- a/lib/protocol.h +++ b/lib/protocol.h @@ -105,6 +105,12 @@ typedef enum { FOLLOW_REDIR /* a full true redirect */ } followtype; +typedef enum { + DOMORE_GOBACK = -1, + DOMORE_INCOMPLETE = 0, + DOMORE_DONE = 1 +} domore; + /* * Specific protocol handler, an implementation of one or more URI schemes. */ @@ -120,9 +126,13 @@ struct Curl_protocol { /* If the curl_do() function is better made in two halves, this * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. + * for doing the FTP stuff after the PASV/PORT command. The second + * argument is an output parameter that MUST be set to one of the + * DOMORE_* values: DOMORE_INCOMPLETE if more do_more work remains, + * DOMORE_DONE when the second phase is complete, or DOMORE_GOBACK + * to return to the regular DO/DOING handling. */ - CURLcode (*do_more)(struct Curl_easy *, int *); + CURLcode (*do_more)(struct Curl_easy *, domore *); /* This function *MAY* be set to a protocol-dependent function that is run * after the connect() and everything is done, as a step in the connection. From 21687202d957453dd147e835c357598c02a52b29 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 6 May 2026 08:50:44 +0200 Subject: [PATCH 0032/1062] tool_formparse: polish error message + make two functions static Closes #21510 --- src/tool_formparse.c | 13 +++++++------ src/tool_formparse.h | 4 ---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 4db2dce96ba2..19c0e1aa6666 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -192,8 +192,8 @@ void tool_mime_free(struct tool_mime *mime) } /* Mime part callbacks for stdin. */ -size_t tool_mime_stdin_read(char *buffer, - size_t size, size_t nitems, void *arg) +static size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg) { struct tool_mime *sip = (struct tool_mime *)arg; curl_off_t bytesleft; @@ -217,7 +217,8 @@ size_t tool_mime_stdin_read(char *buffer, if(ferror(stdin)) { char errbuf[STRERROR_LEN]; /* Show error only once. */ - warnf("stdin: %s", curlx_strerror(errno, errbuf, sizeof(errbuf))); + warnf("Failed to read from stdin: %s", + curlx_strerror(errno, errbuf, sizeof(errbuf))); return CURL_READFUNC_ABORT; } } @@ -226,7 +227,7 @@ size_t tool_mime_stdin_read(char *buffer, return nitems; } -int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) +static int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) { struct tool_mime *sip = (struct tool_mime *)instream; @@ -294,8 +295,8 @@ static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, FALLTHROUGH(); case TOOLMIME_STDINDATA: result = curl_mime_data_cb(part, m->size, - (curl_read_callback)tool_mime_stdin_read, - (curl_seek_callback)tool_mime_stdin_seek, + tool_mime_stdin_read, + tool_mime_stdin_seek, NULL, m); break; diff --git a/src/tool_formparse.h b/src/tool_formparse.h index d2222330eef9..9df5cb7b7a8f 100644 --- a/src/tool_formparse.h +++ b/src/tool_formparse.h @@ -57,10 +57,6 @@ struct tool_mime { curl_off_t curpos; /* Stdin current read position. */ }; -size_t tool_mime_stdin_read(char *buffer, - size_t size, size_t nitems, void *arg); -int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence); - int formparse(const char *input, struct tool_mime **mimeroot, struct tool_mime **mimecurrent, From 06839bda7662b9f24a3c26223f0767e9e094926e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 6 May 2026 09:19:12 +0200 Subject: [PATCH 0033/1062] RELEASE-NOTES: synced Also bump pending version to 8.21.0 --- RELEASE-NOTES | 26 ++++++++++++++++++++------ include/curl/curlver.h | 8 ++++---- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 11e2a62d8358..ebcf7a1eb37d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,11 +1,11 @@ -curl and libcurl 8.20.1 +curl and libcurl 8.21.0 Public curl releases: 275 Command line options: 273 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1463 - Contributors: 3664 + Authors: 1464 + Contributors: 3665 This release includes the following changes: @@ -13,7 +13,14 @@ This release includes the following changes: This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] + o gtls: fix some typos [15] + o lib: two minor typos [16] + o libcurl-easy.md: minor clarifications [19] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o setopt: changing the proxy port is also a proxy change [23] + o show-headers.md: mention bold headers and --no-styled-output [17] + o tool_formparse.c: fix two minor comment typos [25] + o tool_formparse: polish error message + make two functions static [1] o user-agent.md: mention double quotes too [3] This release includes the following known bugs: @@ -36,12 +43,19 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Daniel Stenberg, Jeremy Nicoll, Raymond Steen, Stefan Eissing, - Viktor Szakats - (5 contributors) + Daniel Stenberg, dependabot[bot], Jeremy Nicoll, Raymond Steen, + Sollace on github, Stefan Eissing, Viktor Szakats + (7 contributors) References to bug reports and discussions on issues: + [1] = https://curl.se/bug/?i=21510 [2] = https://curl.se/bug/?i=21476 [3] = https://curl.se/mail/archive-2026-04/0029.html [7] = https://hackerone.com/reports/3702718 + [15] = https://curl.se/bug/?i=21498 + [16] = https://curl.se/bug/?i=21496 + [17] = https://curl.se/bug/?i=21495 + [19] = https://curl.se/bug/?i=21491 + [23] = https://curl.se/bug/?i=21485 + [25] = https://curl.se/bug/?i=21480 diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 231adf743a93..f93ca6fd0326 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.20.1-DEV" +#define LIBCURL_VERSION "8.21.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 20 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 21 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always follows this syntax: @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081401 +#define LIBCURL_VERSION_NUM 0x081500 /* * This is the date and time when the full source package was created. The From 455bebc2c76223a1be26042f6d2393715c0df0cd Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 6 May 2026 09:24:50 +0200 Subject: [PATCH 0034/1062] peer: fix compare of hostname for uds Unix domain socket paths need to be compared case-senstive, in contrast to DNS hostnames. Follow-up to bc40e09f63889a8bc14fa8f7221921 Pointed out by Codex Security Closes #21511 --- lib/peer.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/peer.c b/lib/peer.c index 52b40a5da7ad..49669e4e0e9f 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -318,15 +318,24 @@ bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) Curl_peer_same_destination(p1, p2)); } +static bool peer_same_hostname(struct Curl_peer *p1, struct Curl_peer *p2) +{ + /* UNIX domain socket paths must be compared case-sensitive, + * as many filesystem are like that. */ + return (p1->unix_socket == p2->unix_socket) && + (p1->abstract_uds == p2->abstract_uds) && + (p1->ipv6 == p2->ipv6) && + (p1->unix_socket ? + !strcmp(p1->hostname, p2->hostname) : + curl_strequal(p1->hostname, p2->hostname)); +} + bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) { return (p1 == p2) || (p1 && p2 && (p1->port == p2->port) && - curl_strequal(p1->hostname, p2->hostname) && - (p1->ipv6 == p2->ipv6) && - (p1->unix_socket == p2->unix_socket) && - (p1->abstract_uds == p2->abstract_uds) && + peer_same_hostname(p1, p2) && (p1->scopeid == p2->scopeid) && (p1->scopeid || curl_strequal(p1->zoneid, p2->zoneid))); } From 478e280278dea959bf727fdacd5d33acf0d85d2e Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Mon, 4 May 2026 20:56:51 -0700 Subject: [PATCH 0035/1062] tests: fix unit1636 with --disable-progress-meter Closes #21500 --- tests/unit/unit1636.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/unit/unit1636.c b/tests/unit/unit1636.c index cb50879efe60..adb048a87297 100644 --- a/tests/unit/unit1636.c +++ b/tests/unit/unit1636.c @@ -23,6 +23,7 @@ ***************************************************************************/ #include "unitcheck.h" +#ifndef CURL_DISABLE_PROGRESS_METER static CURLcode test_unit1636(const char *arg) { UNITTEST_BEGIN_SIMPLE @@ -71,3 +72,11 @@ static CURLcode test_unit1636(const char *arg) } UNITTEST_END(curl_global_cleanup()) } + +#else /* CURL_DISABLE_PROGRESS_METER */ +static CURLcode test_unit1636(const char *arg) +{ + (void)arg; + return CURLE_OK; +} +#endif From 67bf021e97bbebadade7e40217a4967042ac6a07 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 6 May 2026 23:59:22 +0200 Subject: [PATCH 0036/1062] mbedtls: null terminate the private key blob Unfortunately, mbedtls_pk_parse_key() requires the data to be null-terminated if the data is PEM encoded (even when provided the exact length), so this function needs to make a copy that has one. Reported-by: Elise Vance Closes #21515 --- lib/vtls/mbedtls.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 4396c703ace6..9cd890a1c05d 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -699,11 +699,17 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, } else { const struct curl_blob *ssl_key_blob = ssl_config->key_blob; - const unsigned char *key_data = - (const unsigned char *)ssl_key_blob->data; const char *passwd = ssl_config->key_passwd; + /* Unfortunately, mbedtls_pk_parse_key() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). */ + unsigned char *newblob = curlx_memdup0(ssl_key_blob->data, + ssl_key_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0); if(ret == 0 && @@ -715,7 +721,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0, mbedtls_ctr_drbg_random, @@ -724,6 +730,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #endif + curlx_free(newblob); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); From a946fbb5e60d6c4ca3fab5cd5cf041ea9ca23e10 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 6 May 2026 23:40:25 +0200 Subject: [PATCH 0037/1062] setopt: gate a few proxy TLS options by checking backend support The same way the corresponding non-proxy options are checked. Closes #21514 --- lib/setopt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 330595876ae9..59d3c3f616b2 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1788,13 +1788,16 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * Set CRL file info for SSL connection for proxy. Specify filename of the * CRL to check certificates revocation */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT: /* - * Set Issuer certificate file - * to check certificates issuer + * Set Issuer certificate file to check certificates issuer */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_CAPATH: /* * Set CA path info for SSL connection proxy. Specify directory name of the @@ -2838,7 +2841,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, /* * Blob that holds Issuer certificate to check certificates issuer */ - return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT_BLOB)) + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + return CURLE_NOT_BUILT_IN; #endif case CURLOPT_SSLKEY_BLOB: /* From 74bc655bdfec8d7966b965d3c72f5e314a1d2b74 Mon Sep 17 00:00:00 2001 From: parasol-aser Date: Thu, 7 May 2026 03:53:34 +0000 Subject: [PATCH 0038/1062] tool1622: assert width and exact format boundaries Convert the silent "was too long!" diagnostics in the timebuf and max5data width loops into fail_unless assertions, so a regression in output width fails the unit test directly instead of only printing. Add small exact-output tables that probe format-transition boundaries not necessarily hit by the geometric value sweep: the 99999/100000 suffix kick-in for max5data, and the 6d/01h, 51m, 136y, and >99999y roll points for timebuf. Closes #21516 --- tests/tunit/tool1622.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/tests/tunit/tool1622.c b/tests/tunit/tool1622.c index ec585983babf..6844cf0ec50c 100644 --- a/tests/tunit/tool1622.c +++ b/tests/tunit/tool1622.c @@ -40,14 +40,34 @@ static CURLcode test_tool1622(const char *arg) 1099445657078333, 0 /* end of list */ }; + struct exactcase { + curl_off_t value; + const char *output; + }; + static const struct exactcase timecases[] = { + { 0, " " }, + { 1, "00:00:01" }, + { 524287, " 6d 01h" }, + { 134217727, " 51m 23d" }, + { 4294967295, " 136y" }, + { 4398046511103, " >99999y" }, + { 0, NULL } + }; + static const struct exactcase datacases[] = { + { 0, " 0" }, + { 99999, "99999" }, + { 100000, "97.6k" }, + { 131072, " 128k" }, + { 12645826, "12.0M" }, + { 1099445657078333, " 999T" }, + { 0, NULL } + }; puts("timebuf"); for(i = 0, secs = 0; i < 63; i++) { timebuf(buffer, sizeof(buffer), secs); curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 8) { - curl_mprintf("^^ was too long!\n"); - } + fail_unless(strlen(buffer) == 8, "timebuf output width"); secs *= 2; secs++; } @@ -55,9 +75,7 @@ static CURLcode test_tool1622(const char *arg) for(i = 0, secs = 0; i < 63; i++) { max5data(secs, buffer, sizeof(buffer)); curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 5) { - curl_mprintf("^^ was too long!\n"); - } + fail_unless(strlen(buffer) == 5, "max5data output width"); secs *= 2; secs++; } @@ -65,9 +83,15 @@ static CURLcode test_tool1622(const char *arg) secs = check[i]; max5data(secs, buffer, sizeof(buffer)); curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 5) { - curl_mprintf("^^ was too long!\n"); - } + fail_unless(strlen(buffer) == 5, "max5data check output width"); + } + for(i = 0; timecases[i].output; i++) { + timebuf(buffer, sizeof(buffer), timecases[i].value); + fail_unless(!strcmp(buffer, timecases[i].output), timecases[i].output); + } + for(i = 0; datacases[i].output; i++) { + max5data(datacases[i].value, buffer, sizeof(buffer)); + fail_unless(!strcmp(buffer, datacases[i].output), datacases[i].output); } UNITTEST_END_SIMPLE From 98d818cf2b216227d12c2fa30e128e18686a573b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 08:33:46 +0200 Subject: [PATCH 0039/1062] tool_formparse: tool2curlparts is no longer recursive It could otherwise trigger a stack overflow in extreme cases Reported-by: Andrew Nesbit Closes #21518 --- src/tool_formparse.c | 126 +++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 19c0e1aa6666..f74cb77433e0 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -251,71 +251,103 @@ static int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) /* Translate an internal mime tree into a libcurl mime tree. */ +#define MAX_FORMPARTS 100000 /* arbitrarily picked */ + static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, curl_mime *mime) { CURLcode result = CURLE_OK; - curl_mimepart *part = NULL; - curl_mime *submime = NULL; - const char *filename = NULL; + struct tool_mime *curr; + struct tool_mime **nodes = NULL; + int count; + int i; + + if(!m) + return CURLE_OK; + + for(curr = m, count = 0; curr; curr = curr->prev) { + if(count > MAX_FORMPARTS) + return CURLE_BAD_FUNCTION_ARGUMENT; + count++; + } - if(m) { - result = tool2curlparts(curl, m->prev, mime); - if(!result) { - part = curl_mime_addpart(mime); - if(!part) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) { - filename = m->filename; - switch(m->kind) { - case TOOLMIME_PARTS: - result = tool2curlmime(curl, m, &submime); - if(!result) { - result = curl_mime_subparts(part, submime); - if(result) - curl_mime_free(submime); - } - break; + nodes = curlx_malloc(sizeof(struct tool_mime *) * count); + if(!nodes) + return CURLE_OUT_OF_MEMORY; - case TOOLMIME_DATA: - result = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); - break; + /* populate array from the end to the beginning */ + curr = m; + for(i = count - 1; i >= 0; i--) { + nodes[i] = curr; + curr = curr->prev; + } - case TOOLMIME_FILE: - case TOOLMIME_FILEDATA: - result = curl_mime_filedata(part, m->data); - if(!result && m->kind == TOOLMIME_FILEDATA && !filename) - result = curl_mime_filename(part, NULL); - break; + for(i = 0; i < count; i++) { + struct tool_mime *node = nodes[i]; + curl_mimepart *part = NULL; + curl_mime *submime = NULL; + const char *filename = node->filename; - case TOOLMIME_STDIN: - if(!filename) - filename = "-"; - FALLTHROUGH(); - case TOOLMIME_STDINDATA: - result = curl_mime_data_cb(part, m->size, - tool_mime_stdin_read, - tool_mime_stdin_seek, - NULL, m); - break; + part = curl_mime_addpart(mime); + if(!part) { + result = CURLE_OUT_OF_MEMORY; + break; + } - default: - /* Other cases not possible in this context. */ - break; + switch(node->kind) { + case TOOLMIME_PARTS: + result = tool2curlmime(curl, node, &submime); + if(!result) { + result = curl_mime_subparts(part, submime); + if(result) + curl_mime_free(submime); } + break; + + case TOOLMIME_DATA: + result = curl_mime_data(part, node->data, CURL_ZERO_TERMINATED); + break; + + case TOOLMIME_FILE: + case TOOLMIME_FILEDATA: + result = curl_mime_filedata(part, node->data); + if(!result && node->kind == TOOLMIME_FILEDATA && !filename) + result = curl_mime_filename(part, NULL); + break; + + case TOOLMIME_STDIN: + if(!filename) + filename = "-"; + FALLTHROUGH(); + case TOOLMIME_STDINDATA: + result = curl_mime_data_cb(part, node->size, + tool_mime_stdin_read, + tool_mime_stdin_seek, + NULL, node); + break; + + default: + /* Other cases not possible in this context. */ + break; } + + /* Common part configuration */ if(!result && filename) result = curl_mime_filename(part, filename); if(!result) - result = curl_mime_type(part, m->type); + result = curl_mime_type(part, node->type); if(!result) - result = curl_mime_headers(part, m->headers, 0); + result = curl_mime_headers(part, node->headers, 0); if(!result) - result = curl_mime_encoder(part, m->encoder); + result = curl_mime_encoder(part, node->encoder); if(!result) - result = curl_mime_name(part, m->name); + result = curl_mime_name(part, node->name); + + if(result) + break; } + + curlx_free(nodes); return result; } From 94729ce4e41a446f2c197772fc408adf89834140 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 08:19:36 +0200 Subject: [PATCH 0040/1062] CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections Closes #21517 --- docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md index 77ef0e06f33f..1cfb53de6012 100644 --- a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md +++ b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md @@ -33,6 +33,10 @@ send this header. This option is primarily useful when sending test requests to a service that expects this header. +Note that the HAProxy protocol message is only is sent over a freshly setup +connection. A subsequent transfer that reuses a previous connection does not +send it again. + Most applications do not need this option. # DEFAULT From 3e9817cd1bb6aa53d3d3bf10572bb245d064870c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 09:04:55 +0200 Subject: [PATCH 0041/1062] url: remove ssh_config_matches The CURLOPT_SSH_HOST_PUBLIC_KEY_* options are documented to verify the host at connect time and not for connection reuse. Once the SSH host has been deemed okay, it remains okay as long as the connection survives. In addition: this function currently always returned TRUE since the pointers have been NULLed in the SSH backend code before this function is called. Follow-up to c31fcf2decfbf1259cc1f31 Reported-by: Andrew Nesbit Closes #21519 --- lib/url.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/url.c b/lib/url.c index 912e47175b33..c8986f0e5586 100644 --- a/lib/url.c +++ b/lib/url.c @@ -682,19 +682,6 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, return result; } -#ifdef USE_SSH -static bool ssh_config_matches(struct connectdata *one, - struct connectdata *two) -{ - struct ssh_conn *sshc1, *sshc2; - - sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); - sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); - return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && - Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); -} -#endif - struct url_conn_match { struct connectdata *found; struct Curl_easy *data; @@ -947,12 +934,6 @@ static bool url_match_proto_config(struct connectdata *conn, if(!url_match_http_version(conn, m)) return FALSE; -#ifdef USE_SSH - if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_SSH) { - if(!ssh_config_matches(m->needle, conn)) - return FALSE; - } -#endif #ifndef CURL_DISABLE_FTP else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) { if(!ftp_conns_match(m->needle, conn)) From 71a5725563f21bfd52a4f47312a603f1f69f5609 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 6 May 2026 09:49:14 +0200 Subject: [PATCH 0042/1062] ftp: remove 2 Curl_resolv_blocking() calls They are no longer needed with the new peers and dns filter. Connection setup will take care of the resoling and connecting. Closes #21512 --- lib/cf-dns.c | 23 +++++----------- lib/cf-dns.h | 3 +-- lib/connect.c | 3 +-- lib/connect.h | 3 +-- lib/ftp.c | 72 ++++----------------------------------------------- lib/url.c | 3 +-- 6 files changed, 16 insertions(+), 91 deletions(-) diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 6044868164ba..b75b5620ebc7 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -53,8 +53,7 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, uint8_t dns_queries, uint8_t transport, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { struct cf_dns_ctx *ctx; @@ -67,8 +66,6 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, ctx->transport = transport; ctx->for_proxy = for_proxy; ctx->complete_resolve = complete_resolve; - ctx->dns = Curl_dns_entry_link(data, dns); - ctx->started = !!ctx->dns; CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", peer->hostname, peer->port, ctx->transport, ctx->dns_queries); @@ -383,8 +380,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, uint8_t dns_queries, uint8_t transport, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { struct Curl_cfilter *cf = NULL; struct cf_dns_ctx *ctx; @@ -392,7 +388,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, (void)data; ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, - for_proxy, complete_resolve, dns); + for_proxy, complete_resolve); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -408,18 +404,13 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, } /* 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. - */ + * The filter will resolve the peer on the first connect attempt. */ CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns) + uint8_t transport) { struct Curl_cfilter *cf = NULL; bool for_proxy = FALSE; @@ -433,7 +424,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, #endif result = cf_dns_create(&cf, data, peer, dns_queries, transport, - for_proxy, FALSE, dns); + for_proxy, FALSE); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -458,7 +449,7 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, CURLcode result; result = cf_dns_create(&cf, data, peer, dns_queries, transport, - FALSE, complete_resolve, NULL); + FALSE, complete_resolve); if(result) return result; diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 12767b005c02..f6902b8f7b2f 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -36,8 +36,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, int sockindex, struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns); + uint8_t transport); CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, diff --git a/lib/connect.c b/lib/connect.c index 2aa22c766058..c36a7e0381d8 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -576,7 +576,6 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode) { CURLcode result = CURLE_OK; @@ -614,7 +613,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, dns_queries |= CURL_DNSQ_HTTPS; #endif result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, - conn->transport_wanted, dns); + conn->transport_wanted); DEBUGASSERT(conn->cfilter[sockindex]); out: return result; diff --git a/lib/connect.h b/lib/connect.h index 380b2fc6116b..8aa130e8865c 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -116,12 +116,11 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, /** * Setup the cfilters at `sockindex` in connection `conn`. * If no filter chain is installed yet, inspects the configuration - * in `data` and `conn? to install a suitable filter chain. + * in `data` and `conn` to install a suitable filter chain. */ CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode); /* Set conn to allow multiplexing. */ diff --git a/lib/ftp.c b/lib/ftp.c index f06f8ef7ca16..ccc171837c01 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2059,7 +2059,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, { struct connectdata *conn = data->conn; CURLcode result; - struct Curl_dns_entry *dns = NULL; const struct pingpong *pp = &ftpc->pp; char *newhost = NULL; unsigned short newport = 0; @@ -2146,63 +2145,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, return CURLE_FTP_WEIRD_PASV_REPLY; } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - /* This connection uses a proxy and we need to connect to the proxy again - * here. We do not want to rely on a former host lookup that might have - * expired now, instead we remake the lookup here and now! */ - struct ip_quadruple ipquad; - bool is_ipv6; - const struct Curl_peer *dest = conn->bits.socksproxy ? - conn->socks_proxy.peer : conn->http_proxy.peer; - - if(!dest) { - result = CURLE_FAILED_INIT; - goto error; - } - - result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, - &is_ipv6, &ipquad); - if(result) - goto error; - - (void)Curl_resolv_blocking( - data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, - dest->hostname, dest->port, Curl_conn_get_transport(data, conn), - &dns); - - if(!dns) { - failf(data, "cannot resolve proxy host %s:%hu", - dest->hostname, dest->port); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - } - else -#endif - { - /* normal, direct, ftp connection */ - DEBUGASSERT(newhost); - - /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { - curlx_free(newhost); - result = ftp_control_addr_dup(data, &newhost); - if(result) - goto error; - } - - (void)Curl_resolv_blocking( - data, Curl_resolv_dns_queries(data, conn->ip_version), - newhost, newport, Curl_conn_get_transport(data, conn), &dns); - - if(!dns) { - failf(data, "cannot resolve new host %s:%hu", newhost, newport); - result = CURLE_FTP_CANT_GET_HOST; - goto error; - } - } - DEBUGASSERT(newhost); Curl_peer_unlink(&conn->origin2); result = Curl_peer_create(data, conn->scheme, newhost, newport, @@ -2221,7 +2163,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, goto error; } - result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); @@ -2241,13 +2183,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, #ifdef CURLVERBOSE if(data->set.verbose) { - /* Dump information about this second connection when we have issued a PASV - * command before and thus we have connected to a possibly new IP address. - */ - char buf[256]; - Curl_printable_address(dns->addr, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", - conn->origin2->hostname, buf, conn->origin2->port); + /* Dump information about this second connection when we have issued + * a PASV command. */ + infof(data, "Connecting to %s port %d", + conn->origin2->hostname, conn->origin2->port); } #endif @@ -2255,7 +2194,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ error: - Curl_dns_entry_unlink(data, &dns); curlx_free(newhost); return result; } diff --git a/lib/url.c b/lib/url.c index c8986f0e5586..b98f85bbd3ed 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3002,8 +3002,7 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) *pconnected = TRUE; } else { - result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, - CURL_CF_SSL_DEFAULT); + result = Curl_conn_setup(data, conn, FIRSTSOCKET, CURL_CF_SSL_DEFAULT); if(!result) result = Curl_headers_init(data); CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); From fdd27a538c4d69c4bb5030b3f9694183cf0077da Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 6 May 2026 13:44:16 +0200 Subject: [PATCH 0043/1062] auth: cleanups - rename `req->proxyuserpwd` to `req->hd_proxy_auth` - rename `req->userpwd` to `req->hd_auth` - rename parameter `proxytunnel` to `is_connect` for Curl_http_output_auth() - move path+query concatenation into Curl_http_output_auth(), saving an alloc when no auth is in play - rename `H1_HD_USER_AUTH` into `H1_HD_AUTH` Closes #21513 --- lib/cf-h1-proxy.c | 6 +- lib/cf-h2-proxy.c | 2 +- lib/http.c | 135 +++++++++++++++++++------------------------ lib/http.h | 10 ++-- lib/http_aws_sigv4.c | 4 +- lib/http_digest.c | 4 +- lib/http_negotiate.c | 8 +-- lib/http_ntlm.c | 4 +- lib/http_proxy.c | 6 +- lib/request.c | 12 ++-- lib/request.h | 4 +- lib/rtsp.c | 18 +++--- 12 files changed, 100 insertions(+), 113 deletions(-) diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index c5de52c5f4a1..0f1c392d4847 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -174,7 +174,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request after we have connected. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } @@ -461,7 +461,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(!nread) { if(data->set.proxyauth && data->state.authproxy.avail && - data->req.proxyuserpwd) { + data->req.hd_proxy_auth) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -702,7 +702,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = H1_CONNECT(cf, data, ts); if(result) goto out; - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); out: *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 8938d149a2f6..a0c5b143215f 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -154,7 +154,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request after we have connected. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } diff --git a/lib/http.c b/lib/http.c index 6d483b70744d..edca1dc1eaf5 100644 --- a/lib/http.c +++ b/lib/http.c @@ -254,7 +254,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) { size_t size = 0; char *authorization = NULL; - char **userp; + char **p_hd; const char *user; const char *pwd; CURLcode result; @@ -264,7 +264,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->req.proxyuserpwd; + p_hd = &data->req.hd_proxy_auth; user = data->state.aptr.proxyuser; pwd = data->state.aptr.proxypasswd; #else @@ -272,7 +272,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) #endif } else { - userp = &data->req.userpwd; + p_hd = &data->req.hd_auth; user = data->state.aptr.user; pwd = data->state.aptr.passwd; } @@ -291,12 +291,12 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) goto fail; } - curlx_free(*userp); - *userp = curl_maprintf("%sAuthorization: Basic %s\r\n", - proxy ? "Proxy-" : "", - authorization); + curlx_free(*p_hd); + *p_hd = curl_maprintf("%sAuthorization: Basic %s\r\n", + proxy ? "Proxy-" : "", + authorization); curlx_free(authorization); - if(!*userp) { + if(!*p_hd) { result = CURLE_OUT_OF_MEMORY; goto fail; } @@ -320,7 +320,7 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; - userp = &data->req.userpwd; + userp = &data->req.hd_auth; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", data->set.str[STRING_BEARER]); @@ -760,53 +760,48 @@ static CURLcode output_auth_headers(struct Curl_easy *data, return result; } -/** - * Curl_http_output_auth() setups the authentication headers for the - * host/proxy and the correct authentication - * method. data->state.authdone is set to TRUE when authentication is - * done. - * - * @param conn all information about the current connection - * @param request pointer to the request keyword - * @param path pointer to the requested path; should include query part - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" - * - * @returns CURLcode - */ CURLcode Curl_http_output_auth(struct Curl_easy *data, struct connectdata *conn, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel) /* TRUE if this is - the request setting up - the proxy tunnel */ + const char *query, + bool is_connect) { CURLcode result = CURLE_OK; struct auth *authhost; struct auth *authproxy; + const char *path_and_query = path; + char *tmp_str = NULL; DEBUGASSERT(data); - authhost = &data->state.authhost; authproxy = &data->state.authproxy; if( #ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || + (!conn->bits.httpproxy || !conn->bits.proxy_user_passwd) && #endif - data->state.aptr.user || + !data->state.aptr.user && #ifdef USE_SPNEGO - authhost->want & CURLAUTH_NEGOTIATE || - authproxy->want & CURLAUTH_NEGOTIATE || + !(authhost->want & CURLAUTH_NEGOTIATE) && + !(authproxy->want & CURLAUTH_NEGOTIATE) && #endif - data->set.str[STRING_BEARER]) - /* continue please */; - else { + !data->set.str[STRING_BEARER]) { + /* no authentication with no user or password */ authhost->done = TRUE; authproxy->done = TRUE; - return CURLE_OK; /* no authentication with no user or password */ + result = CURLE_OK; + goto out; + } + + if(query) { + tmp_str = curl_maprintf("%s?%s", path, query); + if(!tmp_str) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + path_and_query = tmp_str; } if(authhost->want && !authhost->picked) @@ -823,15 +818,15 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ - if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == (curl_bit)proxytunnel)) { - result = output_auth_headers(data, conn, authproxy, request, path, TRUE); + if(conn->bits.httpproxy && (!conn->bits.tunnel_proxy || is_connect)) { + result = output_auth_headers(data, conn, authproxy, request, + path_and_query, TRUE); if(result) - return result; + goto out; } else #else - (void)proxytunnel; + (void)is_connect; #endif /* CURL_DISABLE_PROXY */ /* we have no proxy so let's pretend we are done authenticating with it */ @@ -844,7 +839,8 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, || conn->bits.netrc #endif ) - result = output_auth_headers(data, conn, authhost, request, path, FALSE); + result = output_auth_headers(data, conn, authhost, request, + path_and_query, FALSE); else authhost->done = TRUE; @@ -859,27 +855,31 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, else data->req.authneg = FALSE; +out: + curlx_free(tmp_str); return result; } -#else +#else /* !CURL_DISABLE_HTTP_AUTH */ /* when disabled */ CURLcode Curl_http_output_auth(struct Curl_easy *data, struct connectdata *conn, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel) + const char *query, + bool is_connect) { (void)data; (void)conn; (void)request; (void)httpreq; (void)path; - (void)proxytunnel; + (void)query; + (void)is_connect; return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_HTTP_AUTH, else */ #if defined(USE_SPNEGO) || defined(USE_NTLM) || \ !defined(CURL_DISABLE_DIGEST_AUTH) || \ @@ -2059,8 +2059,8 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) } else { /* Use the hostname as present in the URL if it was IPv6. */ - char *host = (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->origin->hostname; + char *host = (conn->origin->user_hostname[0] == '[') ? + conn->origin->user_hostname : conn->origin->hostname; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && (conn->origin->port == PORT_HTTPS)) || @@ -2834,7 +2834,7 @@ typedef enum { #ifndef CURL_DISABLE_PROXY H1_HD_PROXY_AUTH, #endif - H1_HD_USER_AUTH, + H1_HD_AUTH, H1_HD_RANGE, H1_HD_USER_AGENT, H1_HD_ACCEPT, @@ -2889,14 +2889,14 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->req.proxyuserpwd) - result = curlx_dyn_add(req, data->req.proxyuserpwd); + if(data->req.hd_proxy_auth) + result = curlx_dyn_add(req, data->req.hd_proxy_auth); break; #endif - case H1_HD_USER_AUTH: - if(data->req.userpwd) - result = curlx_dyn_add(req, data->req.userpwd); + case H1_HD_AUTH: + if(data->req.hd_auth) + result = curlx_dyn_add(req, data->req.hd_auth); break; case H1_HD_RANGE: @@ -3054,29 +3054,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* select host to send */ result = http_set_aptr_host(data); - if(!result) { - /* setup the authentication headers, how that method and host are known */ - char *pq = NULL; - if(data->state.up.query) { - pq = curl_maprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } + /* setup the authentication headers, how that method and host are known */ + if(!result) result = Curl_http_output_auth(data, data->conn, method, httpreq, - (pq ? pq : data->state.up.path), FALSE); - curlx_free(pq); - } - if(result) - goto out; - - result = http_useragent(data); - if(result) - goto out; - + data->state.up.path, + data->state.up.query, FALSE); + if(!result) + result = http_useragent(data); /* Setup input reader, resume information and ranges */ - result = set_reader(data, httpreq); + if(!result) + result = set_reader(data, httpreq); if(!result) result = http_resume(data, httpreq); if(!result) diff --git a/lib/http.h b/lib/http.h index 6e33c00e9219..9c25471d3330 100644 --- a/lib/http.h +++ b/lib/http.h @@ -180,8 +180,9 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, * @param request pointer to the request keyword * @param httpreq is the request type * @param path pointer to the requested path - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" + * @param query pointer to the requested query or NULL + * @param is_connect boolean if this is a CONNECT request + * (where httpreq is HTTPREQ_GET since there is no HTTPREQ_CONNECT) * * @returns CURLcode */ @@ -190,9 +191,8 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel); /* TRUE if this is - the request setting up - the proxy tunnel */ + const char *query, + bool is_connect); /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index cb99c6d45ef2..5761acae5fe1 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -1113,8 +1113,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], curlx_str(&provider0), curlx_strlen(&provider0)); - curlx_free(data->req.userpwd); - data->req.userpwd = auth_headers; + curlx_free(data->req.hd_auth); + data->req.hd_auth = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK; diff --git a/lib/http_digest.c b/lib/http_digest.c index b7007071e77e..55e27052d9b3 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -91,7 +91,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->req.proxyuserpwd; + allocuserpwd = &data->req.hd_proxy_auth; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; authp = &data->state.authproxy; @@ -99,7 +99,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, } else { digest = &data->state.digest; - allocuserpwd = &data->req.userpwd; + allocuserpwd = &data->req.hd_auth; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; authp = &data->state.authhost; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 8cced878219e..b037bb2ec904 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -217,13 +217,13 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(proxy) { #ifndef CURL_DISABLE_PROXY - curlx_free(data->req.proxyuserpwd); - data->req.proxyuserpwd = userp; + curlx_free(data->req.hd_proxy_auth); + data->req.hd_proxy_auth = userp; #endif } else { - curlx_free(data->req.userpwd); - data->req.userpwd = userp; + curlx_free(data->req.hd_auth); + data->req.hd_auth = userp; } curlx_free(base64); diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 9c234a8e7dc2..0240251a5f6a 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -139,7 +139,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->req.proxyuserpwd; + allocuserpwd = &data->req.hd_proxy_auth; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? @@ -152,7 +152,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #endif } else { - allocuserpwd = &data->req.userpwd; + allocuserpwd = &data->req.hd_auth; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; service = data->set.str[STRING_SERVICE_NAME] ? diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 361f1f3287ef..fd87c1db1918 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -196,7 +196,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, - req->authority, TRUE); + req->authority, NULL, TRUE); if(result) goto out; @@ -208,9 +208,9 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(data->req.proxyuserpwd) { + if(data->req.hd_proxy_auth) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->req.proxyuserpwd); + data->req.hd_proxy_auth); if(result) goto out; } diff --git a/lib/request.c b/lib/request.c index c414383dc068..c231a63eaa38 100644 --- a/lib/request.c +++ b/lib/request.c @@ -65,9 +65,9 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->httpversion = 0; req->sendbuf_hds_len = 0; - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif result = Curl_client_start(data); @@ -115,9 +115,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) struct curltime t0 = { 0, 0 }; curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif #ifndef CURL_DISABLE_COOKIES curlx_safefree(req->cookiehost); @@ -175,9 +175,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); diff --git a/lib/request.h b/lib/request.h index 6948d79be763..e67865a98433 100644 --- a/lib/request.h +++ b/lib/request.h @@ -114,9 +114,9 @@ struct SingleRequest { wanted */ uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ - char *userpwd; /* auth header */ + char *hd_auth; /* Authorization header, full HTTP/1.x line */ #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; /* proxy auth header */ + char *hd_proxy_auth; /* Proxy-Authorization header, full HTTP/1.x line */ #endif #ifndef CURL_DISABLE_COOKIES char *cookiehost; diff --git a/lib/rtsp.c b/lib/rtsp.c index 78cb6847b5bd..8ba168cb5b7b 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -288,8 +288,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) const char *p_stream_uri = NULL; const char *p_transport = NULL; const char *p_uagent = NULL; - const char *p_proxyuserpwd = NULL; - const char *p_userpwd = NULL; + const char *p_hd_proxy_auth = NULL; + const char *p_hd_auth = NULL; *done = TRUE; if(!rtsp) @@ -442,14 +442,14 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* setup the authentication headers */ result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, - p_stream_uri, FALSE); + p_stream_uri, NULL, FALSE); if(result) goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->req.proxyuserpwd; + p_hd_proxy_auth = data->req.hd_proxy_auth; #endif - p_userpwd = data->req.userpwd; + p_hd_auth = data->req.hd_auth; /* Referrer */ curlx_safefree(data->state.aptr.ref); @@ -520,8 +520,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) "%s" /* range */ "%s" /* referrer */ "%s" /* user-agent */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ + "%s" /* hd_proxy_auth */ + "%s" /* hd_auth */ , p_transport ? p_transport : "", p_accept ? p_accept : "", @@ -529,8 +529,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) p_range ? p_range : "", p_referrer ? p_referrer : "", p_uagent ? p_uagent : "", - p_proxyuserpwd ? p_proxyuserpwd : "", - p_userpwd ? p_userpwd : ""); + p_hd_proxy_auth ? p_hd_proxy_auth : "", + p_hd_auth ? p_hd_auth : ""); if(result) goto out; From ef3b7903aa7cc45cf012a3f222f102414e4c5037 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 7 May 2026 10:00:10 +0200 Subject: [PATCH 0044/1062] ftp: remove bits.ftp_use_control_ssl It's not needed since we can check the connection for SSL use. Closes #21521 --- lib/ftp.c | 9 ++++----- lib/urldata.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/ftp.c b/lib/ftp.c index ccc171837c01..691578699b01 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2905,7 +2905,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(data->conn->bits.ftp_use_control_ssl) { + if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { /* PBSZ = PROTECTION BUFFER SIZE. The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: @@ -3117,7 +3117,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, if(ftpcode == 230) { /* 230 User logged in - already! Take as 220 if TLS required. */ if(data->set.use_ssl <= CURLUSESSL_TRY || - conn->bits.ftp_use_control_ssl) + Curl_conn_is_ssl(conn, FIRSTSOCKET)) return ftp_state_user_resp(data, ftpc, ftpcode); } else if(ftpcode != 220) { @@ -3126,7 +3126,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, return CURLE_WEIRD_SERVER_REPLY; } - if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { + if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* We do not have an SSL/TLS control connection yet, but FTPS is requested. Try an FTPS connection now */ @@ -3204,10 +3204,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, return CURLE_USE_SSL_FAILED; } } + /* BLOCKING */ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); if(!result) { conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ - conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ result = ftp_state_user(data, ftpc, conn); } } @@ -3527,7 +3527,6 @@ static CURLcode ftp_connect(struct Curl_easy *data, result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) return result; - conn->bits.ftp_use_control_ssl = TRUE; } Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ diff --git a/lib/urldata.h b/lib/urldata.h index 8dec816a16bc..c06865feeed3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -275,7 +275,6 @@ struct ConnectBits { EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ - BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ #endif #ifndef CURL_DISABLE_NETRC BIT(netrc); /* name+password provided by netrc */ From a86efdd7ca5433de9231e650f18247de8319ad16 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 7 May 2026 10:30:07 +0200 Subject: [PATCH 0045/1062] url: fix connection reuse for starttls protocols When a connection is tested for reuse in a transfer that *may* upgrade to TLS (commonly via STARTTLS), the SSL configuration must match the existing connection. Reported-by: Andrew Nesbit Closes #21522 --- lib/url.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/url.c b/lib/url.c index b98f85bbd3ed..ba662d6a0ba9 100644 --- a/lib/url.c +++ b/lib/url.c @@ -691,7 +691,11 @@ struct url_conn_match { BIT(want_proxy_ntlm_http); BIT(want_nego_http); BIT(want_proxy_nego_http); - BIT(req_tls); /* require TLS use from a clear-text start */ + BIT(may_tls); /* May upgrade clear-text connection to TLS, can only reuse + * connections that have matching TLS configuration. + * Always TRUE if `req_tls` is TRUE. */ + BIT(require_tls); /* Requires TLS use from a clear-text start, can only + * reuse connections that have TLS. */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -824,7 +828,7 @@ static bool url_match_ssl_use(struct connectdata *conn, (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } - else if(m->req_tls) + else if(m->require_tls) /* a clear-text STARTTLS protocol with required TLS */ return FALSE; return TRUE; @@ -1005,8 +1009,8 @@ static bool url_match_destination(struct connectdata *conn, static bool url_match_ssl_config(struct connectdata *conn, struct url_conn_match *m) { - /* If talking TLS, conn needs to use the same SSL options. */ - if((m->needle->scheme->flags & PROTOPT_SSL) && + /* If talking/upgrading to TLS, conn needs to use the same SSL options. */ + if(((m->needle->scheme->flags & PROTOPT_SSL) || m->may_tls) && !Curl_ssl_conn_config_match(m->data, conn, FALSE)) { DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T " has different SSL parameters, cannot reuse", @@ -1278,7 +1282,8 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif - match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.may_tls = data->set.use_ssl > CURLUSESSL_NONE; /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ From df315692d722ca0fd0dc93d0e43f29cc6975d728 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 09:44:28 +0200 Subject: [PATCH 0046/1062] ftp: simplify ftp_done Closes #21520 --- lib/ftp.c | 99 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 30 deletions(-) diff --git a/lib/ftp.c b/lib/ftp.c index 691578699b01..17cedb335398 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -3595,30 +3595,10 @@ static CURLcode ftp_sendquote(struct Curl_easy *data, return CURLE_OK; } -/*********************************************************************** - * - * ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, - bool premature) +static CURLcode ftp_done_status(struct connectdata *conn, + struct ftp_conn *ftpc, CURLcode status, + bool premature) { - struct connectdata *conn = data->conn; - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp; - size_t nread; - int ftpcode; - CURLcode result = CURLE_OK; - - if(!ftp || !ftpc) - return CURLE_OK; - - pp = &ftpc->pp; switch(status) { case CURLE_BAD_DOWNLOAD_RESUME: case CURLE_FTP_WEIRD_PASV_REPLY: @@ -3647,10 +3627,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the current path, as this connection is going */ connclose(conn, "FTP ended with bad error code"); - result = status; /* use the already set error code */ - break; + return status; /* use the already set error code */ } + return CURLE_OK; +} +static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc) +{ if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, TRUE); @@ -3660,7 +3643,12 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } ftpc->known_filesize = -1; } +} +static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ @@ -3694,13 +3682,17 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(ftpc->prevpath) infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); } +} - /* shut down the socket to inform the server we are done */ - +static CURLcode ftp_done_secondary_socket(struct Curl_easy *data, + struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ - result = Curl_pp_sendf(data, pp, "%s", "ABOR"); + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR"); if(result) { failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); @@ -3711,16 +3703,27 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, close_secondarysocket(data, ftpc); } + return result; +} + +static CURLcode ftp_done_control_reply(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ + struct connectdata *conn = data->conn; + size_t nread; + int ftpcode; if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && - pp->pending_resp && !premature) { + ftpc->pp.pending_resp && !premature) { /* * Let's see what the server says about the transfer we performed, but * lower the timeout as sometimes this connection has died while the data * has been transferred. This happens when doing through NATs etc that * abandon old silent connections. */ - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */ result = getftpresponse(data, &nread, &ftpcode); if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { @@ -3757,7 +3760,14 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } } + return result; +} +static CURLcode ftp_done_check_partial(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ if(result || premature) /* the response code from the transfer showed an error already so no use checking further */ @@ -3791,6 +3801,35 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, result = CURLE_FTP_COULDNT_RETR_FILE; } } + return result; +} + +/*********************************************************************** + * + * ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + CURLcode result; + + if(!ftp || !ftpc) + return CURLE_OK; + + result = ftp_done_status(data->conn, ftpc, status, premature); + + ftp_done_wildcard(data, ftpc); + ftp_done_path(data, ftpc, result); + result = ftp_done_secondary_socket(data, ftpc, result); + result = ftp_done_control_reply(data, ftpc, ftp, result, premature); + result = ftp_done_check_partial(data, ftpc, ftp, result, premature); /* clear these for next connection */ ftp->transfer = PPTRANSFER_BODY; From 9249aad4c210e2f5690a95ca2421defc83e00771 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 18:02:35 +0200 Subject: [PATCH 0047/1062] ldap: fix minor leak on write callback error The 'ber' pointer could remain allocated in the exit path if the write callback returned error for one of the Curl_client_write() calls. Reported-by: Andrew Nesbit Closes #21530 --- lib/ldap.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/lib/ldap.c b/lib/ldap.c index 236e0204084f..9c689c24c6fe 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -256,6 +256,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) char *passwd = NULL; struct ip_quadruple ipquad; bool is_ipv6; + BerElement *ber = NULL; *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", @@ -427,7 +428,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) for(entryIterator = ldap_first_entry(server, ldapmsg); entryIterator; entryIterator = ldap_next_entry(server, entryIterator), num++) { - BerElement *ber = NULL; #ifdef USE_WIN32_LDAP TCHAR *attribute; #else @@ -477,11 +477,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef USE_WIN32_LDAP char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { - if(ber) - ber_free(ber, 0); - result = CURLE_OUT_OF_MEMORY; - goto quit; } #else @@ -497,9 +493,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -508,9 +501,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -519,9 +509,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -536,9 +523,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -550,9 +534,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } } @@ -565,9 +546,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } } @@ -577,9 +555,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } } @@ -597,11 +572,15 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - if(ber) + if(ber) { ber_free(ber, 0); + ber = NULL; + } } quit: + if(ber) + ber_free(ber, 0); if(ldapmsg) { ldap_msgfree(ldapmsg); LDAP_TRACE(("Received %d entries\n", num)); From 3ce10063f191e3d0a2dac3daf997b4a0aaf28ac1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 17:45:48 +0200 Subject: [PATCH 0048/1062] tool_urlglob: avoid overflow at end of range Due to how the range span globbing code works, a range that ends with 9223372036854775807 (the maximum signed 63 bit value) cannot be used as it triggers an integer overflow. Verified in test 2092 Reported-by: Andrew Nesbit Closes #21529 --- src/tool_urlglob.c | 6 ++++-- tests/data/Makefile.am | 2 +- tests/data/test2092 | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/data/test2092 diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index ad1c8087da44..d2249980e2bf 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -324,8 +324,10 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, /* the pattern is not well-formed */ return globerror(glob, "bad range", *posp, CURLE_URL_MALFORMAT); - /* typecasting to ints are fine here since we make sure above that we - are within 31 bits */ + if((CURL_OFF_T_MAX - step_n) < max_n) + return globerror(glob, "range end/step overflow", *posp, + CURLE_URL_MALFORMAT); + pat->c.num.idx = pat->c.num.min = min_n; pat->c.num.max = max_n; pat->c.num.step = step_n; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 0abf6a0998b9..85ea4bcd1db4 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -256,7 +256,7 @@ test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ -test2088 test2089 test2090 test2091 \ +test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ diff --git a/tests/data/test2092 b/tests/data/test2092 new file mode 100644 index 000000000000..6cbf8563fb7a --- /dev/null +++ b/tests/data/test2092 @@ -0,0 +1,28 @@ + + + + +globbing + + + +# Client-side + + +http + + +glob range that ends with 9223372036854775807 + + +"%HOSTIP:%HTTPPORT/[0-1][9223372036854775806-9223372036854775807]/%TESTNUMBER" + + + +# Verify data after the test has been "shot" + + +3 + + + From cda0268593071b833e113751c46513d35f8f4767 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 23:35:40 +0200 Subject: [PATCH 0049/1062] x509asn1: fix operator order in do_pubkey Check the range before reading data, as it would otherwise read one byte too many. Reported-by: Andrew Nesbit Closes #21533 --- lib/vtls/x509asn1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index 788dfb278ade..2ab74cadf6d5 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -1013,7 +1013,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, return 1; /* Compute key length. */ - for(q = elem.beg; !*q && q < elem.end; q++) + for(q = elem.beg; q < elem.end && !*q; q++) ; len = ((elem.end - q) * 8); if(len) { From b174b8b326622574dd49fce71589f414f6af937f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 May 2026 23:07:54 +0200 Subject: [PATCH 0050/1062] ECH: cleanups - passing an unknown string to CURLOPT_ECH now returns error To properly allow applications to spot if they pass in a typo or something to libcurl. - CURLECH_DISABLE is now a plain zero internally, not a dedicated bit which simplifies checks for when ECH is enabled - Dropped the CURLECH_CLA_CFG bit, and just check STRING_ECH_CONFIG - Turn grease/enable/hard into three different numerical values, no bitmask needed - Convert the struct field 'tls_ech' from an int to a byte. Closes #21532 --- lib/setopt.c | 73 +++++++++++++++++++++---------------- lib/urldata.h | 6 +-- lib/vtls/openssl.c | 22 +++++------ lib/vtls/rustls.c | 12 +++--- lib/vtls/vtls.h | 16 ++++---- lib/vtls/wolfssl.c | 15 ++++---- tests/libtest/mk-lib1521.pl | 1 + 7 files changed, 76 insertions(+), 69 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 59d3c3f616b2..f481614dc180 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1858,6 +1858,45 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) } #endif +#ifdef USE_ECH +static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) +{ + struct UserDefined *s = &data->set; + CURLcode result = CURLE_OK; + + if(!ptr || !strcmp(ptr, "false")) + s->tls_ech = CURLECH_DISABLE; + else { + size_t plen = strlen(ptr); + if(plen > CURL_MAX_INPUT_LENGTH) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + if(!strcmp(ptr, "grease")) + s->tls_ech = CURLECH_GREASE; + else if(!strcmp(ptr, "true")) + s->tls_ech = CURLECH_ENABLE; + else if(!strcmp(ptr, "hard")) + s->tls_ech = CURLECH_HARD; + else if(plen > 4 && !strncmp(ptr, "ecl:", 4)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); + } + else if(plen > 3 && !strncmp(ptr, "pn:", 3)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); + } + else + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + } + return result; +} +#else +#define setopt_ech(x,y) CURLE_NOT_BUILT_IN +#endif + static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, char *ptr) { @@ -2495,38 +2534,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_altsvc_load(data->asi, ptr); break; #endif /* !CURL_DISABLE_ALTSVC */ -#ifdef USE_ECH - case CURLOPT_ECH: { - size_t plen = 0; - - if(!ptr) { - s->tls_ech = CURLECH_DISABLE; - break; - } - plen = strlen(ptr); - if(plen > CURL_MAX_INPUT_LENGTH) { - s->tls_ech = CURLECH_DISABLE; - return CURLE_BAD_FUNCTION_ARGUMENT; - } - /* set tls_ech flag value, preserving CLA_CFG bit */ - if(!strcmp(ptr, "false")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_DISABLE; - else if(!strcmp(ptr, "grease")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_GREASE; - else if(!strcmp(ptr, "true")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_ENABLE; - else if(!strcmp(ptr, "hard")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_HARD; - else if(plen > 5 && !strncmp(ptr, "ecl:", 4)) { - result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); - if(!result) - s->tls_ech |= CURLECH_CLA_CFG; - } - else if(plen > 4 && !strncmp(ptr, "pn:", 3)) - result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); - break; - } -#endif + case CURLOPT_ECH: + return setopt_ech(data, ptr); default: return CURLE_UNKNOWN_OPTION; } diff --git a/lib/urldata.h b/lib/urldata.h index c06865feeed3..7fff77c2b3cd 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1157,9 +1157,6 @@ struct UserDefined { struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif uint32_t maxconnects; /* Max idle connections in the connection cache */ -#ifdef USE_ECH - int tls_ech; /* TLS ECH configuration */ -#endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ uint16_t expect_100_timeout; /* in milliseconds */ @@ -1174,6 +1171,9 @@ struct UserDefined { #ifndef CURL_DISABLE_TFTP uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif +#ifdef USE_ECH + uint8_t tls_ech; /* TLS ECH configuration */ +#endif #ifndef CURL_DISABLE_NETRC uint8_t use_netrc; /* enum CURL_NETRC_OPTION values */ #endif diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 30b5c1e2e98c..0e9796f009a4 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3430,9 +3430,9 @@ bool Curl_ossl_need_httpsrr(struct Curl_easy *data) { if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -3450,7 +3450,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, if(!CURLECH_ENABLED(data)) return CURLE_OK; - if(data->set.tls_ech & CURLECH_GREASE) { + if(data->set.tls_ech == CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); #ifdef HAVE_BORINGSSL_LIKE SSL_set_enable_ech_grease(octx->ssl, 1); @@ -3458,7 +3458,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); #endif } - else if(data->set.tls_ech & CURLECH_CLA_CFG) { + else if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { #ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; @@ -3471,12 +3471,12 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, result = curlx_base64_decode(b64, &ech_config, &ech_config_len); if(result || !ech_config) { infof(data, "ECH: cannot base64 decode ECHConfig from command line"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return result; } if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { curlx_free(ech_config); return CURLE_SSL_CONNECT_ERROR; } @@ -3492,7 +3492,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else @@ -3511,7 +3511,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, infof(data, "ECH: ECHConfig from HTTPS RR"); if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { infof(data, "ECH: SSL_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -3521,7 +3521,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } } @@ -4335,7 +4335,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, 0); } - if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech & CURLECH_HARD)) { + if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech == CURLECH_HARD)) { infof(data, "ECH: ech-hard failed"); return CURLE_SSL_CONNECT_ERROR; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 8c56ede7fcf8..24b8597045d6 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -915,9 +915,9 @@ static bool cr_ech_need_httpsrr(struct Curl_easy *data) { if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -957,7 +957,7 @@ init_config_builder_ech(struct Curl_easy *data, return CURLE_OK; } - if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { const char *b64 = data->set.str[STRING_ECH_CONFIG]; size_t decode_result; if(!b64) { @@ -997,7 +997,7 @@ init_config_builder_ech(struct Curl_easy *data, } cleanup: /* if we base64 decoded, we can free now */ - if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { curlx_free(ech_config); } if(dns) { @@ -1074,7 +1074,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, #ifdef USE_ECH if(CURLECH_ENABLED(data)) { result = init_config_builder_ech(data, cf, config_builder); - if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { + if((result != CURLE_OK) && (data->set.tls_ech == CURLECH_HARD)) { rustls_client_config_builder_free(config_builder); return result; } diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 54933169545f..484696dffeb1 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -49,15 +49,13 @@ struct dynbuf; #define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -/* CURLECH_ bits for the tls_ech option */ -#define CURLECH_DISABLE (1 << 0) -#define CURLECH_GREASE (1 << 1) -#define CURLECH_ENABLE (1 << 2) -#define CURLECH_HARD (1 << 3) -#define CURLECH_CLA_CFG (1 << 4) - -#define CURLECH_ENABLED(data) \ - ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) +/* CURLECH_ values for the tls_ech option */ +#define CURLECH_DISABLE 0 +#define CURLECH_GREASE 1 +#define CURLECH_ENABLE 2 +#define CURLECH_HARD 3 + +#define CURLECH_ENABLED(data) ((data)->set.tls_ech) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 7de03b36d50b..59574c9b6a78 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1273,15 +1273,14 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, infof(data, "ECH: GREASE is done by default by" " wolfSSL: no need to ask"); } - if(data->set.tls_ech & CURLECH_CLA_CFG && - data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { char *b64val = data->set.str[STRING_ECH_CONFIG]; word32 b64len = 0; b64len = (word32)strlen(b64val); if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len) != WOLFSSL_SUCCESS) { - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -1301,7 +1300,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != WOLFSSL_SUCCESS) { infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1312,7 +1311,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1492,9 +1491,9 @@ bool Curl_wssl_need_httpsrr(struct Curl_easy *data) #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; #else (void)data; diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl index f3d0c088e86d..2e7a01835ab6 100755 --- a/tests/libtest/mk-lib1521.pl +++ b/tests/libtest/mk-lib1521.pl @@ -42,6 +42,7 @@ 'CURLOPT_DNS_LOCAL_IP4', 'CURLOPT_DNS_LOCAL_IP6', 'CURLOPT_DNS_SERVERS', + 'CURLOPT_ECH', 'CURLOPT_PROXY_TLSAUTH_TYPE', 'CURLOPT_SSLENGINE', 'CURLOPT_TLSAUTH_TYPE', From d656ff945865060150fab5f0c215b0079c37cc60 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 8 May 2026 13:13:20 +0200 Subject: [PATCH 0051/1062] CURLOPT_ECH.md: simplify the description language It no longer requires "a special build" of OpenSSL, just OpenSSL 4+. Emphasize the experimental part a little clearer. Drop the caveat for wolfSSL from the main description. Closes #21536 --- docs/libcurl/opts/CURLOPT_ECH.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_ECH.md b/docs/libcurl/opts/CURLOPT_ECH.md index e409d58cece4..99d3ddaefc33 100644 --- a/docs/libcurl/opts/CURLOPT_ECH.md +++ b/docs/libcurl/opts/CURLOPT_ECH.md @@ -29,14 +29,10 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ECH, char *config); # DESCRIPTION -ECH is only compatible with TLSv1.3. - -This experimental feature requires a special build of OpenSSL, as ECH is not -yet supported in OpenSSL releases. In contrast ECH is supported by the latest -BoringSSL, wolfSSL and Rustls-ffi releases. +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. -There is also a known issue with using wolfSSL which does not support ECH when -the HelloRetryRequest mechanism is used. +ECH is only compatible with TLSv1.3. Pass a string that specifies configuration details for ECH. In all cases, if ECH is attempted, it may fail for various reasons. The keywords supported are: From 1698a3f85799769d9333e5281738bd015a02709e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 14:24:33 +0000 Subject: [PATCH 0052/1062] GHA: update google/boringssl to v0.20260508.0 Closes #21537 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 63d60c119e3c..a94e052c741b 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -46,7 +46,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 1.71.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260413.0 + BORINGSSL_VERSION: 0.20260508.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8b39455a352b..9889c35cb911 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -46,7 +46,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 1.71.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260413.0 + BORINGSSL_VERSION: 0.20260508.0 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com OPENSSL_VERSION: 4.0.0 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com From 0f6af820c226b8b9da98e3c5dbce43d4fc309521 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 04:45:54 +0000 Subject: [PATCH 0053/1062] Dockerfile: update debian:bookworm-slim Docker digest to 67b30a6 Closes #21539 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 67027b9402e8..c6021752b081 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252 +FROM debian:bookworm-slim@sha256:67b30a61dc87758f0caf819646104f29ecbda97d920aaf5edc834128ac8493d3 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From b9449408284cf9c86a7db86a34967c29de137a00 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 9 May 2026 15:27:11 +0200 Subject: [PATCH 0054/1062] cookie: simplify strstore(), remove outdated comment Closes #21541 --- lib/cookie.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/cookie.c b/lib/cookie.c index 0f822ea7cf20..57dccf4ce9fb 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -250,19 +250,11 @@ static char *sanitize_cookie_path(const char *cookie_path, size_t len) /* * strstore * - * A thin wrapper around strdup which ensures that any memory allocated at - * *str will be freed before the string allocated by strdup is stored there. - * The intended usecase is repeated assignments to the same variable during - * parsing in a last-wins scenario. The caller is responsible for checking - * for OOM errors. + * A thin wrapper around curlx_memdup0(). */ static CURLcode strstore(char **str, const char *newstr, size_t len) { DEBUGASSERT(str); - if(!len) { - len++; - newstr = ""; - } *str = curlx_memdup0(newstr, len); if(!*str) return CURLE_OUT_OF_MEMORY; From ea75ccc53b84b5f1a9b5e21670693b82c43a827a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 10 May 2026 15:13:59 +0200 Subject: [PATCH 0055/1062] schannel_verify: avoid out of blob access The code would previously read one byte past the provided CURLOPT_CAINFO_BLOB if the blob ends exactly with -----BEGIN CERTIFICATE----- Reported-by: Andrew Nesbit Closes #21543 --- lib/vtls/schannel_verify.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 47c52af280ee..bcea2c8c81cb 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -92,11 +92,6 @@ struct cert_chain_engine_config_win7 { HCERTSTORE hExclusiveTrustedPeople; }; -static int is_cr_or_lf(char c) -{ - return c == '\r' || c == '\n'; -} - /* Search the substring needle,needlelen into string haystack,haystacklen * Strings do not need to be terminated by a '\0'. * Similar of macOS/Linux memmem (not available on Visual Studio). @@ -134,10 +129,11 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, while(more_certs && (current_ca_file_ptr < ca_buffer_limit)) { const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, - ca_buffer_limit-current_ca_file_ptr, + ca_buffer_limit - + current_ca_file_ptr - 1, BEGIN_CERT, begin_cert_len); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { + if(!begin_cert_ptr || !ISNEWLINE(begin_cert_ptr[begin_cert_len])) { more_certs = 0; } else { From 67ce67284258bbce5403dca70ec78161e199bc1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 00:33:47 +0000 Subject: [PATCH 0056/1062] GHA: update awslabs/aws-lc to v1.73.0 Closes #21542 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a94e052c741b..216cafd6643f 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -44,7 +44,7 @@ env: # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.1 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.71.0 + AWSLC_VERSION: 1.73.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260508.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9889c35cb911..1a7621f25f43 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -44,7 +44,7 @@ env: MBEDTLS_PREV_VERSION: 3.6.5 MBEDTLS_PREV_SHA256: 4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.71.0 + AWSLC_VERSION: 1.73.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260508.0 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com From 6f1dfab6a29242525cdc7b48f5ee49269b9cc316 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 May 2026 00:18:53 +0200 Subject: [PATCH 0057/1062] ftp: avoid accessing EPSV response one byte past the NULL If the response is just a single "(". Reported-by: Andrew Nesbit Closes #21545 --- lib/ftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ftp.c b/lib/ftp.c index 17cedb335398..3f55f68d8287 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2073,7 +2073,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ptr++; /* |||12345| */ sep = ptr[0]; - if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + if(sep && (ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { const char *p = &ptr[3]; curl_off_t num; if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) { From ed3cd8b0463844056e039f283f8e43e2795f269a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Apr 2026 19:09:28 +0200 Subject: [PATCH 0058/1062] cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config When detecting these dependencies via CMake Config and their main imported target is undefined, automatically assume and use their static imported target instead. Adopting from vcpkg downstream, where it's done for nghttp3 and ngtcp2, but not for nghttp2. Refs: https://github.com/microsoft/vcpkg/blob/773e092a82fc3b4c3e73ee7b049a5e119fa45898/ports/curl/dependencies.patch https://github.com/microsoft/vcpkg/commit/70b941a5d2443e79eeab62507acb41bd22201277 Downstream-patch-by: Kai Pastor Closes #21470 --- CMake/FindNGHTTP2.cmake | 2 +- CMake/FindNGHTTP3.cmake | 2 +- CMake/FindNGTCP2.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMake/FindNGHTTP2.cmake b/CMake/FindNGHTTP2.cmake index f93113f404e1..bca7cf8a8181 100644 --- a/CMake/FindNGHTTP2.cmake +++ b/CMake/FindNGHTTP2.cmake @@ -61,7 +61,7 @@ if(_nghttp2_FOUND) elseif(nghttp2_CONFIG) set(NGHTTP2_FOUND TRUE) set(NGHTTP2_VERSION ${nghttp2_VERSION}) - if(NGHTTP2_USE_STATIC_LIBS) + if(NGHTTP2_USE_STATIC_LIBS OR NOT TARGET nghttp2::nghttp2) set(_nghttp2_LIBRARIES nghttp2::nghttp2_static) else() set(_nghttp2_LIBRARIES nghttp2::nghttp2) diff --git a/CMake/FindNGHTTP3.cmake b/CMake/FindNGHTTP3.cmake index 427c139f21c4..ed671b7a4929 100644 --- a/CMake/FindNGHTTP3.cmake +++ b/CMake/FindNGHTTP3.cmake @@ -61,7 +61,7 @@ if(_nghttp3_FOUND) elseif(nghttp3_CONFIG) set(NGHTTP3_FOUND TRUE) set(NGHTTP3_VERSION ${nghttp3_VERSION}) - if(NGHTTP3_USE_STATIC_LIBS) + if(NGHTTP3_USE_STATIC_LIBS OR NOT TARGET nghttp3::nghttp3) set(_nghttp3_LIBRARIES nghttp3::nghttp3_static) else() set(_nghttp3_LIBRARIES nghttp3::nghttp3) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index e4929163777c..70dae14b8663 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -105,7 +105,7 @@ if(_ngtcp2_FOUND) elseif(ngtcp2_CONFIG) set(NGTCP2_FOUND TRUE) set(NGTCP2_VERSION ${ngtcp2_VERSION}) - if(NGTCP2_USE_STATIC_LIBS) + if(NGTCP2_USE_STATIC_LIBS OR NOT TARGET ngtcp2::ngtcp2) set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static) else() set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower}) From 48150707948cc1390c816934691524bdfde6c4af Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 1 May 2026 13:25:49 +0200 Subject: [PATCH 0059/1062] tidy-up: sort TLS backends, distros, alphabetically Also: - replace stray [Rr]ustls-ffi with Rustls for consistency. - add AWS-LC to a couple of lists where missing. Closes #21481 --- .github/workflows/http3-linux.yml | 378 +++++++++--------- .github/workflows/linux.yml | 146 +++---- CMakeLists.txt | 26 +- docs/CIPHERS.md | 8 +- docs/CURLDOWN.md | 2 +- docs/ECH.md | 14 +- docs/FAQ.md | 8 +- docs/INSTALL.md | 4 +- docs/cmdline-opts/ca-native.md | 2 +- docs/cmdline-opts/tls-earlydata.md | 4 +- docs/libcurl/curl_global_sslset.md | 10 +- .../libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md | 6 +- docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md | 12 +- lib/dllmain.c | 2 +- lib/ldap.c | 2 +- lib/vquic/curl_ngtcp2.c | 6 +- lib/vtls/openssl.c | 16 +- lib/vtls/openssl.h | 6 +- m4/curl-openssl.m4 | 20 +- tests/libtest/lib1587.c | 2 +- tests/runtests.pl | 2 +- 21 files changed, 338 insertions(+), 338 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 216cafd6643f..b4939e811806 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -36,13 +36,6 @@ env: CURL_CI: github CURL_TEST_MIN: 1850 DO_NOT_TRACK: '1' - # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.0 - # manually bumped - OPENSSL_PREV_VERSION: 3.6.2 - OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f - # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.3.1 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 1.73.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com @@ -51,6 +44,15 @@ env: NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com GNUTLS_VERSION: 3.8.11 + # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com + LIBRESSL_VERSION: 4.3.1 + # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com + OPENSSL_VERSION: 4.0.0 + # manually bumped + OPENSSL_PREV_VERSION: 3.6.2 + OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f + # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com + QUICHE_VERSION: 0.24.7 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com @@ -59,8 +61,6 @@ env: NGTCP2_VERSION: 1.22.1 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 - # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.24.7 jobs: build-cache: @@ -68,33 +68,6 @@ jobs: runs-on: ubuntu-latest steps: - - name: 'cache openssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-http3-no-deprecated - env: - cache-name: cache-openssl-http3-no-deprecated - with: - path: ~/openssl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - - - name: 'cache openssl-prev' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-prev-http3-no-deprecated - env: - cache-name: cache-openssl-prev-http3-no-deprecated - with: - path: ~/openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - - - name: 'cache libressl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-libressl - env: - cache-name: cache-libressl - with: - path: ~/libressl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - - name: 'cache awslc' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc @@ -131,6 +104,33 @@ jobs: path: ~/gnutls/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} + - name: 'cache libressl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-libressl + env: + cache-name: cache-libressl + with: + path: ~/libressl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} + + - name: 'cache openssl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-http3-no-deprecated + env: + cache-name: cache-openssl-http3-no-deprecated + with: + path: ~/openssl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} + + - name: 'cache openssl-prev' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-prev-http3-no-deprecated + env: + cache-name: cache-openssl-prev-http3-no-deprecated + with: + path: ~/openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} + - name: 'cache wolfssl' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl @@ -159,15 +159,6 @@ jobs: key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" - - name: 'cache ngtcp2 openssl-prev' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-ngtcp2-openssl-prev - env: - cache-name: cache-ngtcp2-openssl-prev - with: - path: ~/ngtcp2-openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - - name: 'cache ngtcp2 boringssl' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-boringssl @@ -177,6 +168,15 @@ jobs: path: ~/ngtcp2-boringssl/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} + - name: 'cache ngtcp2 openssl-prev' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-ngtcp2-openssl-prev + env: + cache-name: cache-ngtcp2-openssl-prev + with: + path: ~/ngtcp2-openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + - name: 'cache nghttp2' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 @@ -189,18 +189,18 @@ jobs: - id: settings if: >- - ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' || - steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' || - steps.cache-libressl.outputs.cache-hit != 'true' || - steps.cache-awslc.outputs.cache-hit != 'true' || + ${{ steps.cache-awslc.outputs.cache-hit != 'true' || steps.cache-boringssl.outputs.cache-hit != 'true' || steps.cache-nettle.outputs.cache-hit != 'true' || steps.cache-gnutls.outputs.cache-hit != 'true' || + steps.cache-libressl.outputs.cache-hit != 'true' || + steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' || + steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' || steps.cache-wolfssl.outputs.cache-hit != 'true' || steps.cache-nghttp3.outputs.cache-hit != 'true' || - steps.cache-ngtcp2.outputs.cache-hit != 'true' || - steps.cache-ngtcp2-openssl-prev.outputs.cache-hit != 'true' || steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' || + steps.cache-ngtcp2-openssl-prev.outputs.cache-hit != 'true' || + steps.cache-ngtcp2.outputs.cache-hit != 'true' || steps.cache-nghttp2.outputs.cache-hit != 'true' }} run: echo 'needs-build=true' >> "$GITHUB_OUTPUT" @@ -221,40 +221,6 @@ jobs: echo 'CC=gcc-12' >> "$GITHUB_ENV" echo 'CXX=g++-12' >> "$GITHUB_ENV" - - name: 'build openssl' - if: ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' }} - run: | - cd ~ - git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl - cd openssl - ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated - make - make -j1 install_sw - - - name: 'build openssl-prev' - if: ${{ steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' }} - run: | - cd ~ - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin - sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin - cd "openssl-${OPENSSL_PREV_VERSION}" - ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated - make - make -j1 install_sw - - - name: 'build libressl' - if: ${{ steps.cache-libressl.outputs.cache-hit != 'true' }} - run: | - cd ~ - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin - sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin - cd "libressl-${LIBRESSL_VERSION}" - cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl/build - cmake --build . - cmake --install . - - name: 'build awslc' if: ${{ steps.cache-awslc.outputs.cache-hit != 'true' }} run: | @@ -309,6 +275,40 @@ jobs: --disable-guile --disable-doc --disable-tests --disable-tools make install + - name: 'build libressl' + if: ${{ steps.cache-libressl.outputs.cache-hit != 'true' }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin + cd "libressl-${LIBRESSL_VERSION}" + cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl/build + cmake --build . + cmake --install . + + - name: 'build openssl' + if: ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' }} + run: | + cd ~ + git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl + cd openssl + ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated + make + make -j1 install_sw + + - name: 'build openssl-prev' + if: ${{ steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin + cd "openssl-${OPENSSL_PREV_VERSION}" + ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated + make + make -j1 install_sw + - name: 'build wolfssl' if: ${{ steps.cache-wolfssl.outputs.cache-hit != 'true' }} run: | @@ -412,57 +412,6 @@ jobs: fail-fast: false matrix: build: - - name: 'openssl' - tflags: '--min=1700' - LDFLAGS: -Wl,-rpath,/home/runner/openssl/build/lib - PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - configure: >- - --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export - - - name: 'openssl' - install_steps: skipall - PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_NGTCP2=ON - -DCURL_DISABLE_LDAP=ON - -DUSE_ECH=ON - -DCMAKE_UNITY_BUILD=ON - - - name: 'openssl-prev' - install_steps: skipall - LDFLAGS: -Wl,-rpath,/home/runner/openssl-prev/build/lib - PKG_CONFIG_PATH: "\ - /home/runner/openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp3/build/lib/pkgconfig:\ - /home/runner/nghttp2-openssl-prev/build/lib/pkgconfig" - configure: >- - --with-openssl=/home/runner/openssl-prev/build --with-ngtcp2=/home/runner/ngtcp2-openssl-prev/build --enable-ssls-export - - - name: 'openssl-prev' - tflags: '--min=1700' - PKG_CONFIG_PATH: "\ - /home/runner/openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp3/build/lib/pkgconfig:\ - /home/runner/ngtcp2-openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp2/build/lib/pkgconfig" - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DUSE_NGTCP2=ON - -DCURL_DISABLE_LDAP=ON - - - name: 'libressl' - install_steps: skipall - LDFLAGS: -Wl,-rpath,/home/runner/libressl/build/lib - PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - # Intentionally using '--with-ngtcp2=' to test this way of configuration, in addition to bare '--with-ngtcp2' + 'PKG_CONFIG_PATH' in other jobs. - configure: >- - --with-openssl=/home/runner/libressl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export - --enable-unity - - - name: 'libressl' - PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON - - name: 'awslc' install_steps: skipall LDFLAGS: -Wl,-rpath,/home/runner/awslc/build/lib @@ -515,22 +464,56 @@ jobs: -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD=ON - - name: 'wolfssl' - install_packages: libssh2-1-dev + - name: 'libressl' install_steps: skipall - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl/build/lib - PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + LDFLAGS: -Wl,-rpath,/home/runner/libressl/build/lib + PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + # Intentionally using '--with-ngtcp2=' to test this way of configuration, in addition to bare '--with-ngtcp2' + 'PKG_CONFIG_PATH' in other jobs. configure: >- - --with-wolfssl=/home/runner/wolfssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --with-libssh2 --enable-ssls-export + --with-openssl=/home/runner/libressl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export --enable-unity - - name: 'wolfssl' - install_packages: libssh2-1-dev - tflags: '--min=1900' - PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + - name: 'libressl' + PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- - -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON + + - name: 'openssl' + tflags: '--min=1700' + LDFLAGS: -Wl,-rpath,/home/runner/openssl/build/lib + PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + configure: >- + --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export + + - name: 'openssl' + install_steps: skipall + PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + generate: >- + -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_NGTCP2=ON + -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON + -DCMAKE_UNITY_BUILD=ON + + - name: 'openssl-prev' + install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/openssl-prev/build/lib + PKG_CONFIG_PATH: "\ + /home/runner/openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/nghttp2-openssl-prev/build/lib/pkgconfig" + configure: >- + --with-openssl=/home/runner/openssl-prev/build --with-ngtcp2=/home/runner/ngtcp2-openssl-prev/build --enable-ssls-export + + - name: 'openssl-prev' + tflags: '--min=1700' + PKG_CONFIG_PATH: "\ + /home/runner/openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/ngtcp2-openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp2/build/lib/pkgconfig" + generate: >- + -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DUSE_NGTCP2=ON + -DCURL_DISABLE_LDAP=ON - name: 'quiche' install_steps: skipall @@ -549,6 +532,23 @@ jobs: -DUSE_QUICHE=ON -DCURL_CA_FALLBACK=ON + - name: 'wolfssl' + install_packages: libssh2-1-dev + install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl/build/lib + PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + configure: >- + --with-wolfssl=/home/runner/wolfssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --with-libssh2 --enable-ssls-export + --enable-unity + + - name: 'wolfssl' + install_packages: libssh2-1-dev + tflags: '--min=1900' + PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + generate: >- + -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON + -DUSE_ECH=ON + steps: - name: 'install prereqs' timeout-minutes: 2 @@ -570,38 +570,6 @@ jobs: echo 'CC=gcc-12' >> "$GITHUB_ENV" echo 'CXX=g++-12' >> "$GITHUB_ENV" - - name: 'cache openssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-http3-no-deprecated - env: - cache-name: cache-openssl-http3-no-deprecated - with: - path: ~/openssl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - fail-on-cache-miss: true - - - name: 'cache openssl-prev' - if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-prev-http3-no-deprecated - env: - cache-name: cache-openssl-prev-http3-no-deprecated - with: - path: ~/openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - fail-on-cache-miss: true - - - name: 'cache libressl' - if: ${{ contains(matrix.build.name, 'libressl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-libressl - env: - cache-name: cache-libressl - with: - path: ~/libressl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - fail-on-cache-miss: true - - name: 'cache awslc' if: ${{ contains(matrix.build.name, 'awslc') }} uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -646,6 +614,38 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} fail-on-cache-miss: true + - name: 'cache libressl' + if: ${{ contains(matrix.build.name, 'libressl') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-libressl + env: + cache-name: cache-libressl + with: + path: ~/libressl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} + fail-on-cache-miss: true + + - name: 'cache openssl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-http3-no-deprecated + env: + cache-name: cache-openssl-http3-no-deprecated + with: + path: ~/openssl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} + fail-on-cache-miss: true + + - name: 'cache openssl-prev' + if: ${{ contains(matrix.build.name, 'openssl-prev') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-prev-http3-no-deprecated + env: + cache-name: cache-openssl-prev-http3-no-deprecated + with: + path: ~/openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache wolfssl' if: ${{ contains(matrix.build.name, 'wolfssl') }} uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -678,17 +678,6 @@ jobs: ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" fail-on-cache-miss: true - - name: 'cache ngtcp2 openssl-prev' - if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-ngtcp2-openssl-prev - env: - cache-name: cache-ngtcp2-openssl-prev - with: - path: ~/ngtcp2-openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - fail-on-cache-miss: true - - name: 'cache ngtcp2 boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -700,6 +689,17 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} fail-on-cache-miss: true + - name: 'cache ngtcp2 openssl-prev' + if: ${{ contains(matrix.build.name, 'openssl-prev') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-ngtcp2-openssl-prev + env: + cache-name: cache-ngtcp2-openssl-prev + with: + path: ~/ngtcp2-openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache nghttp2' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1a7621f25f43..14623ac0c1ac 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,29 +34,29 @@ env: CURL_CI: github CURL_TEST_MIN: 1660 DO_NOT_TRACK: '1' + # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com + AWSLC_VERSION: 1.73.0 + # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com + BORINGSSL_VERSION: 0.20260508.0 + # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com + FIL_C_VERSION: 0.678 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.1 - # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com MBEDTLS_VERSION: 4.0.0 # manually bumped MBEDTLS_PREV_VERSION: 3.6.5 MBEDTLS_PREV_SHA256: 4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8 - # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.73.0 - # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260508.0 + # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com + NGHTTP2_VERSION: 1.69.0 + # handled in renovate.json + OPENLDAP_VERSION: 2.6.10 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com OPENSSL_VERSION: 4.0.0 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com RUSTLS_VERSION: 0.15.3 - # handled in renovate.json - OPENLDAP_VERSION: 2.6.10 - # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com - NGHTTP2_VERSION: 1.69.0 - # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.678 + # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com + WOLFSSL_VERSION: 5.9.1 jobs: linux: @@ -72,6 +72,20 @@ jobs: fail-fast: false matrix: build: + - name: 'awslc' + install_steps: awslc pytest + LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib + configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm + + - name: 'awslc' + install_packages: libidn2-dev + install_steps: awslc + generate: -DOPENSSL_ROOT_DIR=/home/runner/awslc -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF -DCURL_DROP_UNUSED=ON -DCURL_PATCHSTAMP=test-patch -DCURL_ENABLE_NTLM=ON + + - name: 'boringssl' + install_steps: boringssl pytest + generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + - name: 'libressl krb5' image: ubuntu-24.04-arm install_packages: libidn2-dev libnghttp2-dev libldap-dev libkrb5-dev @@ -101,27 +115,17 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib configure: --with-openssl=/home/runner/libressl --enable-debug - - name: 'wolfssl-all' - image: ubuntu-24.04-arm - install_steps: wolfssl-all-arm - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-all/lib - configure: --with-wolfssl=/home/runner/wolfssl-all --enable-ech --enable-debug - - - name: 'wolfssl-opensslextra valgrind 1' - image: ubuntu-24.04-arm - install_packages: valgrind - install_steps: wolfssl-opensslextra-arm - tflags: '--min=815 1 to 1000' - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - - - name: 'wolfssl-opensslextra valgrind 2' - image: ubuntu-24.04-arm - install_packages: valgrind - install_steps: wolfssl-opensslextra-arm - tflags: '--min=835 1001 to 9999' - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + - name: 'libressl Fil-C' + install_steps: filc libressl-filc nghttp2-filc pytest + tflags: '!776' # adds 1-9 minutes to the test run step, and fails consistently + CC: /home/runner/filc/build/bin/filcc + PKG_CONFIG_PATH: /home/runner/nghttp2/lib/pkgconfig + generate: >- + -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_DISABLE_TYPECHECK=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_LIBPSL=OFF + -DCURL_ZLIB=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + -DCURL_DISABLE_LDAP=ON -DUSE_LIBIDN2=OFF -DCURL_USE_LIBSSH2=OFF + -DCURL_ENABLE_NTLM=ON - name: 'mbedtls gss valgrind 1' image: ubuntu-24.04-arm @@ -167,19 +171,44 @@ jobs: -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON - - name: 'awslc' - install_steps: awslc pytest - LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib - configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm + - name: 'rustls valgrind 1' + install_packages: libnghttp2-dev libldap-dev valgrind + install_steps: rust rustls + tflags: '--min=820 1 to 1000' + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - name: 'awslc' - install_packages: libidn2-dev - install_steps: awslc - generate: -DOPENSSL_ROOT_DIR=/home/runner/awslc -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF -DCURL_DROP_UNUSED=ON -DCURL_PATCHSTAMP=test-patch -DCURL_ENABLE_NTLM=ON + - name: 'rustls valgrind 2' + install_packages: libnghttp2-dev libldap-dev valgrind + install_steps: rust rustls + tflags: '--min=830 1001 to 9999' + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - name: 'boringssl' - install_steps: boringssl pytest - generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + - name: 'rustls' + install_packages: libnghttp2-dev libldap-dev + install_steps: rust rustls skiprun pytest + configure: --with-rustls --enable-ech --enable-debug + + - name: 'wolfssl-all' + image: ubuntu-24.04-arm + install_steps: wolfssl-all-arm + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-all/lib + configure: --with-wolfssl=/home/runner/wolfssl-all --enable-ech --enable-debug + + - name: 'wolfssl-opensslextra valgrind 1' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=815 1 to 1000' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + + - name: 'wolfssl-opensslextra valgrind 2' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=835 1001 to 9999' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - name: 'openssl default' install_steps: pytest @@ -287,18 +316,6 @@ jobs: tflags: '--min=500' configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity - - name: 'libressl Fil-C' - install_steps: filc libressl-filc nghttp2-filc pytest - tflags: '!776' # adds 1-9 minutes to the test run step, and fails consistently - CC: /home/runner/filc/build/bin/filcc - PKG_CONFIG_PATH: /home/runner/nghttp2/lib/pkgconfig - generate: >- - -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_DISABLE_TYPECHECK=ON - -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_LIBPSL=OFF - -DCURL_ZLIB=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF - -DCURL_DISABLE_LDAP=ON -DUSE_LIBIDN2=OFF -DCURL_USE_LIBSSH2=OFF - -DCURL_ENABLE_NTLM=ON - - name: 'clang-tidy' install_packages: clang-20 clang-tidy-20 libssl-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev install_steps: skiprun mbedtls-latest-intel rustls wolfssl-opensslextra-intel @@ -412,23 +429,6 @@ jobs: configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl tflags: '-n --test-duphandle' - - name: 'rustls valgrind 1' - install_packages: libnghttp2-dev libldap-dev valgrind - install_steps: rust rustls - tflags: '--min=820 1 to 1000' - generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - - name: 'rustls valgrind 2' - install_packages: libnghttp2-dev libldap-dev valgrind - install_steps: rust rustls - tflags: '--min=830 1001 to 9999' - generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - - name: 'rustls' - install_packages: libnghttp2-dev libldap-dev - install_steps: rust rustls skiprun pytest - configure: --with-rustls --enable-ech --enable-debug - - name: 'IntelC openssl' install_packages: libssl-dev install_steps: intelc diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dd9c7aa7027..506dfeb30567 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -835,26 +835,26 @@ if(CURL_USE_OPENSSL) cmake_push_check_state() list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - if(NOT DEFINED HAVE_BORINGSSL) - check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) - endif() if(NOT DEFINED HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() + if(NOT DEFINED HAVE_BORINGSSL) + check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) + endif() if(NOT DEFINED HAVE_LIBRESSL) check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" HAVE_LIBRESSL) endif() cmake_pop_check_state() - if(HAVE_BORINGSSL OR HAVE_AWSLC) - if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads + if(HAVE_AWSLC OR HAVE_BORINGSSL) + if(NOT MSVC AND NOT ANDROID) # AWS-LC/BoringSSL MSVC builds use native Windows threads find_package(Threads) if(CMAKE_USE_PTHREADS_INIT) set(HAVE_THREADS_POSIX_BORINGSSL 1) list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads) elseif(OPENSSL_USE_STATIC_LIBS) - message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.") + message(WARNING "AWS-LC/BoringSSL requires POSIX Threads.") endif() endif() if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -863,17 +863,17 @@ if(CURL_USE_OPENSSL) endif() endif() - if(HAVE_BORINGSSL) + if(USE_AMISSL) + set(_openssl "AmiSSL") + elseif(HAVE_AWSLC) + set(_openssl "AWS-LC") + elseif(HAVE_BORINGSSL) if(BORINGSSL_VERSION) set(CURL_BORINGSSL_VERSION "\"${BORINGSSL_VERSION}\"") endif() set(_openssl "BoringSSL") - elseif(HAVE_AWSLC) - set(_openssl "AWS-LC") elseif(HAVE_LIBRESSL) set(_openssl "LibreSSL") - elseif(USE_AMISSL) - set(_openssl "AmiSSL") else() set(_openssl "OpenSSL") endif() @@ -1097,7 +1097,7 @@ if(USE_ECH) set(HAVE_ECH 1) endif() if(NOT HAVE_ECH) - message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL/rustls-ffi") + message(FATAL_ERROR "ECH support missing in AWS-LC/BoringSSL/OpenSSL/Rustls/wolfSSL") else() message(STATUS "ECH enabled") # ECH wants HTTPSRR @@ -1105,7 +1105,7 @@ if(USE_ECH) message(STATUS "HTTPSRR enabled") endif() else() - message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC, wolfSSL or rustls-ffi") + message(FATAL_ERROR "ECH requires ECH-enabled AWS-LC, BoringSSL, OpenSSL, Rustls or wolfSSL") endif() endif() diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 060d3da94983..9606f2d79566 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -96,10 +96,10 @@ are NULL ciphers, offering no encryption whatsoever.) ### TLS 1.2 (1.1, 1.0) cipher suites -Setting TLS 1.2 cipher suites is supported by curl with OpenSSL, LibreSSL, -BoringSSL, mbedTLS (curl 8.8.0+), wolfSSL (curl 7.53.0+). Schannel does not -support setting cipher suites directly, but does support setting algorithms -(curl 7.61.0+), see Schannel notes below. +Setting TLS 1.2 cipher suites is supported by curl with AWS-LC, BoringSSL, +LibreSSL, mbedTLS (curl 8.8.0+), OpenSSL, wolfSSL (curl 7.53.0+). Schannel +does not support setting cipher suites directly, but does support setting +algorithms (curl 7.61.0+), see Schannel notes below. For TLS 1.2 cipher suites there are multiple naming schemes, the two most used are with OpenSSL names (e.g. `ECDHE-RSA-AES128-GCM-SHA256`) and IANA names diff --git a/docs/CURLDOWN.md b/docs/CURLDOWN.md index ce19b5f5d606..c804eae746b2 100644 --- a/docs/CURLDOWN.md +++ b/docs/CURLDOWN.md @@ -97,7 +97,7 @@ option. The available TLS backends are: - `GnuTLS` - `mbedTLS` -- `OpenSSL` (also covers BoringSSL, LibreSSL, quictls, AWS-LC and AmiSSL) +- `OpenSSL` (also covers AmiSSL, AWS-LC, BoringSSL, LibreSSL and quictls) - `rustls` - `Schannel` - `wolfSSL` diff --git a/docs/ECH.md b/docs/ECH.md index 6314abb5f357..8a0153209d8c 100644 --- a/docs/ECH.md +++ b/docs/ECH.md @@ -8,8 +8,8 @@ SPDX-License-Identifier: curl We have added support for ECH to curl. It can use HTTPS RRs published in the DNS if curl uses DoH, or else can accept the relevant ECHConfigList values -from the command line. This works with OpenSSL, wolfSSL, BoringSSL, AWS-LC -or rustls-ffi as the TLS provider. +from the command line. This works with AWS-LC, BoringSSL, OpenSSL, Rustls or +wolfSSL as the TLS provider. This feature is EXPERIMENTAL. DO NOT USE IN PRODUCTION. @@ -153,7 +153,7 @@ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo ``` At that point, you could copy the base64 encoded value above and try again. -For now, this only works for the OpenSSL and BoringSSL/AWS-LC builds. +For now, this only works for the OpenSSL and AWS-LC/BoringSSL builds. ## Default settings @@ -338,11 +338,11 @@ WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution. make ``` -The BoringSSL/AWS-LC APIs are fairly similar to those in our ECH-enabled +The AWS-LC/BoringSSL APIs are fairly similar to those in our ECH-enabled OpenSSL fork, so code changes are also in `lib/vtls/openssl.c`, protected via `#ifdef OPENSSL_IS_BORINGSSL` and are mostly obvious API variations. -The BoringSSL/AWS-LC APIs however do not support the `--ech pn:` command +The AWS-LC/BoringSSL APIs however do not support the `--ech pn:` command line variant as of now. ## wolfSSL build @@ -405,7 +405,7 @@ Then there are some functional code changes: The lack of support for `--ech false` is because wolfSSL has decided to always at least GREASE if built to support ECH. In other words, GREASE is a compile time choice for wolfSSL, but a runtime choice for OpenSSL or -BoringSSL/AWS-LC. (Both are reasonable.) +AWS-LC/BoringSSL. (Both are reasonable.) ## Additional notes @@ -471,7 +471,7 @@ get the HTTPS RR and pass the ECHConfigList from that on the command line, if needed, or one can access the value from command line output in verbose more and then reuse that in another invocation. -Both our OpenSSL fork and BoringSSL/AWS-LC have APIs for both controlling GREASE +Both our OpenSSL fork and AWS-LC/BoringSSL have APIs for both controlling GREASE and accessing and logging `retry_configs`, it seems wolfSSL has neither. ### Testing ECH diff --git a/docs/FAQ.md b/docs/FAQ.md index 05f7eda38299..7748d1bd2c49 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -294,10 +294,10 @@ curl has been written to use a generic SSL function layer internally, and that SSL functionality can then be provided by one out of many different SSL backends. -curl can be built to use one of the following SSL alternatives: OpenSSL, -LibreSSL, BoringSSL, AWS-LC, GnuTLS, wolfSSL, mbedTLS, Schannel (native -Windows) or Rustls. They all have their pros and cons, and we maintain [a TLS -library comparison](https://curl.se/docs/ssl-compared.html). +curl can be built to use one of the following SSL alternatives: AWS-LC, +BoringSSL, GnuTLS, LibreSSL, OpenSSL, mbedTLS, Rustls, Schannel (native +Windows), or wolfSSL. They all have their pros and cons, and we maintain +[a TLS library comparison](https://curl.se/docs/ssl-compared.html). ## How do I upgrade curl.exe in Windows? diff --git a/docs/INSTALL.md b/docs/INSTALL.md index db743e0554e8..467aa64c058f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -146,7 +146,7 @@ These options are provided to select the TLS backend to use. - AmiSSL: `--with-amissl` - GnuTLS: `--with-gnutls`. - mbedTLS: `--with-mbedtls` -- OpenSSL: `--with-openssl` (also for BoringSSL, AWS-LC, LibreSSL, and quictls) +- OpenSSL: `--with-openssl` (also for AWS-LC, BoringSSL, LibreSSL, and quictls) - Rustls: `--with-rustls` - Schannel: `--with-schannel` - wolfSSL: `--with-wolfssl` @@ -486,7 +486,7 @@ install `libssl.a` and `libcrypto.a` to `$TOOLCHAIN/sysroot/usr/lib` and copy for Android using OpenSSL like this: ```sh -# For OpenSSL/BoringSSL. In general, you need to the SSL/TLS layer's transitive +# For BoringSSL/OpenSSL. In general, you need to the SSL/TLS layer's transitive # dependencies if you are linking statically. LIBS='-lssl -lcrypto -lc++' ./configure --host aarch64-linux-android --with-pic --disable-shared --with-openssl="$TOOLCHAIN/sysroot/usr" diff --git a/docs/cmdline-opts/ca-native.md b/docs/cmdline-opts/ca-native.md index 4a887df558a6..67fdf8c3acef 100644 --- a/docs/cmdline-opts/ca-native.md +++ b/docs/cmdline-opts/ca-native.md @@ -24,7 +24,7 @@ Use the operating system's native CA store for certificate verification. This option is independent of other CA certificate locations set at run time or build time. Those locations are searched in addition to the native CA store. -This option works with OpenSSL and its forks (LibreSSL, BoringSSL, etc) on +This option works with OpenSSL and its forks (BoringSSL, LibreSSL, etc) on Windows (Added in 7.71.0) and on Apple OS when libcurl is built with Apple SecTrust enabled. (Added in 8.17.0) diff --git a/docs/cmdline-opts/tls-earlydata.md b/docs/cmdline-opts/tls-earlydata.md index 8e344758be5e..22a7abd3c376 100644 --- a/docs/cmdline-opts/tls-earlydata.md +++ b/docs/cmdline-opts/tls-earlydata.md @@ -20,8 +20,8 @@ Example: Enable the use of TLSv1.3 early data, also known as '0RTT' where possible. This has security implications for the requests sent that way. -This option can be used when curl is built to use GnuTLS, wolfSSL, quictls and -OpenSSL as a TLS provider (but not BoringSSL, AWS-LC, or Rustls). +This option can be used when curl is built to use GnuTLS, OpenSSL, quictls and +wolfSSL as a TLS provider (but not AWS-LC, BoringSSL, or Rustls). If a server supports this TLSv1.3 feature, and to what extent, is announced as part of the TLS "session" sent back to curl. Until curl has seen such diff --git a/docs/libcurl/curl_global_sslset.md b/docs/libcurl/curl_global_sslset.md index 8ef0ca99923b..8218d355e27a 100644 --- a/docs/libcurl/curl_global_sslset.md +++ b/docs/libcurl/curl_global_sslset.md @@ -70,11 +70,11 @@ SSL backend names (case-insensitive): GnuTLS, mbedTLS, OpenSSL, Rustls, Schannel, wolfSSL The name "OpenSSL" is used for all versions of OpenSSL and its associated -forks/flavors in this function. OpenSSL, BoringSSL, LibreSSL, quictls and -AmiSSL are all supported by libcurl, but in the eyes of curl_global_sslset(3) -they are all called "OpenSSL". They all mostly provide the same API. -curl_version_info(3) can return more specific info about the exact OpenSSL -flavor and version number in use. +forks/flavors in this function. AmiSSL, AWS-LC, BoringSSL, LibreSSL, OpenSSL +and quictls are all supported by libcurl, but in the eyes of +curl_global_sslset(3) they are all called "OpenSSL". They all mostly provide +the same API. curl_version_info(3) can return more specific info about the +exact OpenSSL flavor and version number in use. # struct diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md index ab0e366b0d8c..e7c596d15ac5 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md @@ -58,7 +58,7 @@ Tells libcurl to not accept "partial" certificate chains, which it otherwise does by default. This option fails the certificate verification if the chain ends with an intermediate certificate and not with a root cert. -Works with OpenSSL and its forks (LibreSSL, BoringSSL, etc). (Added in 7.68.0) +Works with OpenSSL and its forks (BoringSSL, LibreSSL, etc). (Added in 7.68.0) Works with Schannel if the user specified certificates to verify the peer. (Added in 8.15.0) @@ -78,9 +78,9 @@ verification. This option is independent of other CA certificate locations set at run time or build time. Those locations are searched in addition to the native CA store. -Works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), +Works with wolfSSL on Windows, Linux (Debian, Fedora, Gentoo, RHEL, Ubuntu), macOS, Android and iOS (added in 8.3.0); with GnuTLS (added in 8.5.0) and with -OpenSSL and its forks (LibreSSL, BoringSSL, etc) on Windows (Added in 7.71.0). +OpenSSL and its forks (BoringSSL, LibreSSL, etc) on Windows (Added in 7.71.0). ## CURLSSLOPT_AUTO_CLIENT_CERT diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 2fdf8ee15f57..1314ae0e8d4c 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -56,7 +56,7 @@ Tells libcurl to not accept "partial" certificate chains, which it otherwise does by default. This option fails the certificate verification if the chain ends with an intermediate certificate and not with a root cert. -Works with OpenSSL and its forks (LibreSSL, BoringSSL, etc). (Added in 7.68.0) +Works with OpenSSL and its forks (BoringSSL, LibreSSL, etc). (Added in 7.68.0) Works with Schannel if the user specified certificates to verify the peer. (Added in 8.15.0) @@ -76,9 +76,9 @@ verification. This option is independent of other CA certificate locations set at run time or build time. Those locations are searched in addition to the native CA store. -Works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), +Works with wolfSSL on Windows, Linux (Debian, Fedora, Gentoo, RHEL, Ubuntu), macOS, Android and iOS (added in 8.3.0); with GnuTLS (added in 8.5.0) and with -OpenSSL and its forks (LibreSSL, BoringSSL, etc) on Windows (Added in 7.71.0). +OpenSSL and its forks (BoringSSL, LibreSSL, etc) on Windows (Added in 7.71.0). This works with Rustls on Windows, macOS, Android and iOS. On Linux it is equivalent to using the Mozilla CA certificate bundle. When used with Rustls @@ -98,13 +98,13 @@ could be a privacy violation and unexpected. ## CURLSSLOPT_EARLYDATA Tell libcurl to try sending application data as TLS1.3 early data. This option -is supported for GnuTLS, wolfSSL, quictls and OpenSSL (but not BoringSSL -or AWS-LC). It works on TCP and QUIC connections using ngtcp2. +is supported for GnuTLS, OpenSSL, quictls and wolfSSL (but not AWS-LC or +BoringSSL). It works on TCP and QUIC connections using ngtcp2. This option works on a best effort basis, in cases when it was not possible to send early data the request is resent normally post-handshake. This option does not work when using QUIC. -(Added in 8.11.0 for GnuTLS and 8.13.0 for wolfSSL, quictls and OpenSSL) +(Added in 8.11.0 for GnuTLS and 8.13.0 for OpenSSL, quictls and wolfSSL) # DEFAULT diff --git a/lib/dllmain.c b/lib/dllmain.c index f715b6d30161..5aa6565010d8 100644 --- a/lib/dllmain.c +++ b/lib/dllmain.c @@ -31,7 +31,7 @@ #if defined(_WIN32) && !defined(CURL_STATICLIB) #if defined(USE_OPENSSL) && \ - !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \ + !defined(OPENSSL_IS_AWSLC) && !defined(OPENSSL_IS_BORINGSSL) && \ !defined(LIBRESSL_VERSION_NUMBER) #define PREVENT_OPENSSL_MEMLEAK #endif diff --git a/lib/ldap.c b/lib/ldap.c index 9c689c24c6fe..3705754476d1 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -45,7 +45,7 @@ #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ # include -/* Undefine indirect symbols conflicting with BoringSSL/AWS-LC. */ +/* Undefine indirect symbols conflicting with AWS-LC/BoringSSL. */ # undef X509_NAME # undef X509_EXTENSIONS # undef PKCS7_ISSUER_AND_SERIAL diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 8cf3886d2241..2f5cae511699 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -29,7 +29,7 @@ #ifdef USE_OPENSSL #include -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #include #elif defined(OPENSSL_QUIC_API2) #include @@ -2484,7 +2484,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, struct curl_tls_ctx *ctx = user_data; #ifdef USE_OPENSSL -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); @@ -2497,7 +2497,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); return CURLE_FAILED_INIT; } -#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ +#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ if(Curl_ssl_scache_use(cf, data)) { /* Enable the session cache because it is a prerequisite for the * "new session" callback. Use the "external storage" mode to prevent diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 0e9796f009a4..0178acfe5930 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -127,9 +127,9 @@ #endif /* Whether SSL_CTX_set_ciphersuites is available. - * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no * LibreSSL: supported since 3.4.1 (released 2021-10-14) + * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) */ #if (!defined(LIBRESSL_VERSION_NUMBER) || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -142,9 +142,9 @@ #endif /* Whether SSL_CTX_set1_sigalgs_list is available - * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) * BoringSSL: supported since 0.20240913.0 (commit 826ce15) * LibreSSL: no + * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_SSL_CTX_SET1_SIGALGS @@ -152,10 +152,10 @@ #ifdef LIBRESSL_VERSION_NUMBER #define OSSL_PACKAGE "LibreSSL" -#elif defined(OPENSSL_IS_BORINGSSL) -#define OSSL_PACKAGE "BoringSSL" #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" +#elif defined(OPENSSL_IS_BORINGSSL) +#define OSSL_PACKAGE "BoringSSL" #elif defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \ !defined(OPENSSL_QUIC_API2) #define OSSL_PACKAGE "quictls" @@ -4219,7 +4219,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on - OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ + OpenSSL version above v1.1.1, not AWS-LC, BoringSSL, or LibreSSL */ else if((lib == ERR_LIB_SSL) && (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { /* If client certificate is required, communicate the @@ -5408,6 +5408,9 @@ size_t Curl_ossl_version(char *buffer, size_t size) *p = '_'; } return count; +#elif defined(OPENSSL_IS_AWSLC) + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); #elif defined(OPENSSL_IS_BORINGSSL) #ifdef CURL_BORINGSSL_VERSION return curl_msnprintf(buffer, size, "%s/%s", @@ -5415,9 +5418,6 @@ size_t Curl_ossl_version(char *buffer, size_t size) #else return curl_msnprintf(buffer, size, OSSL_PACKAGE); #endif -#elif defined(OPENSSL_IS_AWSLC) - return curl_msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); #else /* OpenSSL 3+ */ return curl_msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 61d4a1757e25..717058c6573e 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -33,7 +33,7 @@ * , , or something else, does this: * #define X509_NAME ((LPCSTR)7) * - * In BoringSSL/AWC-LC's there is: + * In AWC-LC/BoringSSL's there is: * typedef struct X509_name_st X509_NAME; * etc. * @@ -74,7 +74,7 @@ #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ #endif -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #define HAVE_BORINGSSL_LIKE #endif @@ -86,9 +86,9 @@ /* * Whether SSL_CTX_set_keylog_callback is available. - * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. + * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_KEYLOG_CALLBACK diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 256037b19a76..d0f2f261ed17 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -231,36 +231,36 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" = "1"; then dnl These can only exist if OpenSSL exists - AC_MSG_CHECKING([for BoringSSL]) + AC_MSG_CHECKING([for AWS-LC]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ - #ifndef OPENSSL_IS_BORINGSSL - #error not boringssl + #ifndef OPENSSL_IS_AWSLC + #error not AWS-LC #endif ]]) ],[ AC_MSG_RESULT([yes]) - ssl_msg="BoringSSL" - OPENSSL_IS_BORINGSSL=1 + ssl_msg="AWS-LC" + OPENSSL_IS_AWSLC=1 ],[ AC_MSG_RESULT([no]) ]) - AC_MSG_CHECKING([for AWS-LC]) + AC_MSG_CHECKING([for BoringSSL]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ - #ifndef OPENSSL_IS_AWSLC - #error not AWS-LC + #ifndef OPENSSL_IS_BORINGSSL + #error not BoringSSL #endif ]]) ],[ AC_MSG_RESULT([yes]) - ssl_msg="AWS-LC" - OPENSSL_IS_AWSLC=1 + ssl_msg="BoringSSL" + OPENSSL_IS_BORINGSSL=1 ],[ AC_MSG_RESULT([no]) ]) diff --git a/tests/libtest/lib1587.c b/tests/libtest/lib1587.c index ad66c003f0a1..a0739cb22153 100644 --- a/tests/libtest/lib1587.c +++ b/tests/libtest/lib1587.c @@ -32,7 +32,7 @@ #include #ifdef HAVE_BORINGSSL_LIKE -/* BoringSSL and AWS-LC */ +/* AWS-LC and BoringSSL */ typedef uint32_t opt1587; #else typedef uint64_t opt1587; diff --git a/tests/runtests.pl b/tests/runtests.pl index 050875dc5b35..b0230330c80f 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -589,7 +589,7 @@ sub checksystemfeatures { $feature{"wolfssl"} = 1; $feature{"SSLpinning"} = 1; } - elsif($libcurl =~ /\s(BoringSSL|AWS-LC)\b/i) { + elsif($libcurl =~ /\s(AWS-LC|BoringSSL)\b/i) { # OpenSSL compatible API $feature{"OpenSSL"} = 1; $feature{"SSLpinning"} = 1; From 10d4b34e5c962a0b17836b0bc7483ce3b7c518cc Mon Sep 17 00:00:00 2001 From: Kai Pastor Date: Fri, 8 May 2026 17:21:52 +0200 Subject: [PATCH 0060/1062] cmake: fix zstd CMake config name They install `zstdConfig.cmake`, https://github.com/facebook/zstd/blob/885c79ba4ae8345e006f61bc97b270d4cf7ff076/build/cmake/CMakeModules/ZstdPackage.cmake#L33-L38. With the `Config.cmake` pattern, this is a case-sensitive package name, `zstd`. Follow-up to 8fce3e17e6cb310cd6dbe38ff14869b8fe5827d2 #20814 Closes #21538 --- CMake/FindZstd.cmake | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 8dc620a1009f..176645d97bab 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -53,12 +53,12 @@ if(NOT DEFINED ZSTD_INCLUDE_DIR AND pkg_check_modules(_zstd ${_zstd_pc_requires}) endif() if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) - find_package(Zstd CONFIG QUIET) + find_package(zstd CONFIG QUIET) # Skip using if older than v1.4.5 - if(Zstd_CONFIG AND + if(zstd_CONFIG AND NOT TARGET zstd::libzstd_static AND NOT TARGET zstd::libzstd_shared) - unset(Zstd_CONFIG) + unset(zstd_CONFIG) endif() endif() endif() @@ -74,9 +74,10 @@ if(_zstd_FOUND) set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}") endif() message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") -elseif(Zstd_CONFIG) +elseif(zstd_CONFIG) + set(Zstd_FOUND TRUE) set(ZSTD_FOUND TRUE) - set(ZSTD_VERSION ${Zstd_VERSION}) + set(ZSTD_VERSION ${zstd_VERSION}) if(ZSTD_USE_STATIC_LIBS) set(_zstd_LIBRARIES zstd::libzstd_static) elseif(TARGET zstd::libzstd) @@ -84,7 +85,7 @@ elseif(Zstd_CONFIG) else() set(_zstd_LIBRARIES zstd::libzstd_shared) endif() - message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") + message(STATUS "Found Zstd (via CMake Config): ${zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") else() find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") if(ZSTD_USE_STATIC_LIBS) From 37b2403f48959c632c8dd1dac688087237410ea2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 2 May 2026 22:50:10 +0200 Subject: [PATCH 0061/1062] lib: drop support for CURLAUTH_DIGEST_IE This bit was used to do Digest authentication like Internet Explorer before version 7 (released on October 18, 2006). Presumably no one uses this anymore and since it is hard to use and does broken auth, starting in 8.21.0 this bit does nothing (except setting the actual Digest bit). Closes #21486 --- docs/libcurl/opts/CURLOPT_HTTPAUTH.md | 9 +++---- docs/libcurl/symbols-in-versions | 2 +- lib/http_digest.c | 35 +++------------------------ lib/setopt.c | 10 +------- lib/urldata.h | 2 -- 5 files changed, 9 insertions(+), 49 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md index e05c84183a76..692178c72e53 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md +++ b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md @@ -54,11 +54,8 @@ regular old-fashioned Basic method. ## CURLAUTH_DIGEST_IE -HTTP Digest authentication with an IE flavor. Digest authentication is defined -in RFC 2617 and is a more secure way to do authentication over public networks -than the regular old-fashioned Basic method. The IE flavor means that -libcurl uses a special "quirk" that IE is known to have used before version 7 -and that some servers require the client to use. +The IE-specific Digest authentication behavior is no longer supported. +This bit is kept for compatibility and is treated as CURLAUTH_DIGEST. ## CURLAUTH_BEARER @@ -159,6 +156,8 @@ CURLAUTH_BEARER was added in 7.61.0 CURLAUTH_AWS_SIGV4 was added in 7.74.0 +CURLAUTH_DIGEST_IE does nothing since 8.21.0 + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 0d775fa65594..6516f7823de8 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -205,7 +205,7 @@ CURLAUTH_AWS_SIGV4 7.75.0 CURLAUTH_BASIC 7.10.6 CURLAUTH_BEARER 7.61.0 CURLAUTH_DIGEST 7.10.6 -CURLAUTH_DIGEST_IE 7.19.3 +CURLAUTH_DIGEST_IE 7.19.3 8.21.0 CURLAUTH_GSSAPI 7.55.0 CURLAUTH_GSSNEGOTIATE 7.10.6 7.38.0 CURLAUTH_NEGOTIATE 7.38.0 diff --git a/lib/http_digest.c b/lib/http_digest.c index 55e27052d9b3..e87fb362ed66 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -68,8 +68,6 @@ CURLcode Curl_output_digest(struct Curl_easy *data, const unsigned char *uripath) { CURLcode result; - unsigned char *path = NULL; - const char *tmp = NULL; char *response; size_t len; bool have_chlg; @@ -125,36 +123,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - /* IE browsers < v7 cut off the URI part at the query part when they - evaluate the MD5 and some (IIS?) servers work with them so we may need to - do the Digest IE-style. Note that the different ways cause different MD5 - sums to get sent. - - Apache servers can be set to do the Digest IE-style automatically using - the BrowserMatch feature: - https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie - - Further details on Digest implementation differences: - https://web.archive.org/web/2009/fngtps.com/2006/09/http-authentication - */ - - if(authp->iestyle) { - tmp = strchr((const char *)uripath, '?'); - if(tmp) { - size_t urilen = tmp - (const char *)uripath; - /* typecast is fine here since the value is always less than 32 bits */ - path = (unsigned char *)curl_maprintf("%.*s", (int)urilen, uripath); - } - } - if(!tmp) - path = (unsigned char *)curlx_strdup((const char *)uripath); - - if(!path) - return CURLE_OUT_OF_MEMORY; - - result = Curl_auth_create_digest_http_message(data, userp, passwdp, request, - path, digest, &response, &len); - curlx_free(path); + result = Curl_auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + &response, &len); if(result) return result; diff --git a/lib/setopt.c b/lib/setopt.c index f481614dc180..24d0c42bcf5d 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -240,17 +240,9 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, if(auth != CURLAUTH_NONE) { int bitcheck = 0; bool authbits = FALSE; - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - bool iestyle = !!(auth & CURLAUTH_DIGEST_IE); - if(proxy) - data->state.authproxy.iestyle = iestyle; - else - data->state.authhost.iestyle = iestyle; - if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* drop the legacy bit */ } /* switch off bits we cannot support */ diff --git a/lib/urldata.h b/lib/urldata.h index 7fff77c2b3cd..335e61c4a39d 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -586,8 +586,6 @@ struct auth { actual request */ BIT(multipass); /* TRUE if this is not yet authenticated but within the auth multipass negotiation */ - BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it - should be RFC compliant */ }; #ifdef USE_NGHTTP2 From daf6f541cc1fc4f5d17989d317464764d1bd7cd7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 May 2026 14:51:03 +0200 Subject: [PATCH 0062/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ebcf7a1eb37d..f639feea28c5 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,24 +4,44 @@ curl and libcurl 8.21.0 Command line options: 273 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1464 - Contributors: 3665 + Authors: 1465 + Contributors: 3668 This release includes the following changes: + o lib: drop support for CURLAUTH_DIGEST_IE [4] This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] + o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] + o cmake: fix zstd CMake config name [5] + o cookie: simplify strstore(), remove outdated comment [12] + o CURLOPT_ECH.md: simplify the description language [18] + o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] + o ECH: cleanups [20] + o ftp: avoid accessing EPSV response one byte past the NULL [9] + o ftp: remove 2 Curl_resolv_blocking() calls [30] + o ftp: remove bits.ftp_use_control_ssl [28] o gtls: fix some typos [15] + o ldap: fix minor leak on write callback error [24] o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] + o mbedtls: null terminate the private key blob [36] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o schannel_verify: avoid out of blob access [11] o setopt: changing the proxy port is also a proxy change [23] + o setopt: gate a few proxy TLS options by checking backend support [35] o show-headers.md: mention bold headers and --no-styled-output [17] + o tests: fix unit1636 with --disable-progress-meter [37] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] + o tool_formparse: tool2curlparts is no longer recursive [33] + o tool_urlglob: avoid overflow at end of range [22] + o url: fix connection reuse for starttls protocols [27] + o url: remove ssh_config_matches [31] o user-agent.md: mention double quotes too [3] + o x509asn1: fix operator order in do_pubkey [21] This release includes the following known bugs: @@ -43,19 +63,40 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Daniel Stenberg, dependabot[bot], Jeremy Nicoll, Raymond Steen, + Andrew Nesbit, Dan Fandrich, Daniel Stenberg, dependabot[bot], Elise Vance, + Jeremy Nicoll, Kai Pastor, parasol-aser, Raymond Steen, renovate[bot], Sollace on github, Stefan Eissing, Viktor Szakats - (7 contributors) + (13 contributors) References to bug reports and discussions on issues: [1] = https://curl.se/bug/?i=21510 [2] = https://curl.se/bug/?i=21476 [3] = https://curl.se/mail/archive-2026-04/0029.html + [4] = https://curl.se/bug/?i=21486 + [5] = https://curl.se/bug/?i=21538 [7] = https://hackerone.com/reports/3702718 + [8] = https://curl.se/bug/?i=21470 + [9] = https://curl.se/bug/?i=21545 + [11] = https://curl.se/bug/?i=21543 + [12] = https://curl.se/bug/?i=21541 [15] = https://curl.se/bug/?i=21498 [16] = https://curl.se/bug/?i=21496 [17] = https://curl.se/bug/?i=21495 + [18] = https://curl.se/bug/?i=21536 [19] = https://curl.se/bug/?i=21491 + [20] = https://curl.se/bug/?i=21532 + [21] = https://curl.se/bug/?i=21533 + [22] = https://curl.se/bug/?i=21529 [23] = https://curl.se/bug/?i=21485 + [24] = https://curl.se/bug/?i=21530 [25] = https://curl.se/bug/?i=21480 + [27] = https://curl.se/bug/?i=21522 + [28] = https://curl.se/bug/?i=21521 + [30] = https://curl.se/bug/?i=21512 + [31] = https://curl.se/bug/?i=21519 + [32] = https://curl.se/bug/?i=21517 + [33] = https://curl.se/bug/?i=21518 + [35] = https://curl.se/bug/?i=21514 + [36] = https://curl.se/bug/?i=21515 + [37] = https://curl.se/bug/?i=21500 From e0e56e9ae434552bd6ac5570ed91483188d75788 Mon Sep 17 00:00:00 2001 From: amitbidlan Date: Mon, 11 May 2026 22:39:53 +0900 Subject: [PATCH 0063/1062] hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE These macros are leftovers from when DNS caching was moved out of hostip.c into its own source file. Both are still defined and used in lib/dnscache.c; the copies in lib/hostip.c are unreferenced. Detected with clang -Wunused-macros. Follow-up to 96d5b5c688 Closes #21550 --- lib/hostip.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/hostip.c b/lib/hostip.c index 9fd7c75a73d8..e18c86298463 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -70,10 +70,6 @@ #define USE_ALARM_TIMEOUT #endif -#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ - -#define MAX_DNS_CACHE_SIZE 29999 - #define RESOLV_FAIL(for_proxy) \ ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST) From e8ce697973ba7c7c8667c48ab6cd8509c77c37e1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 11 May 2026 10:50:36 +0200 Subject: [PATCH 0064/1062] idn: replace header guards with forward declaration Follow-up to bc40e09f63889a8bc14fa8f7221921eb5b4a559e #21472 Closes #21551 --- lib/idn.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/idn.h b/lib/idn.h index b0ac981ba893..c73b870a7080 100644 --- a/lib/idn.h +++ b/lib/idn.h @@ -25,19 +25,16 @@ ***************************************************************************/ struct Curl_str; +struct hostname; bool Curl_is_ASCII_name(const char *hostname); bool Curl_is_ASCII_str(struct Curl_str *s); -#ifdef HEADER_CURL_URLDATA_H /* HACK */ CURLcode Curl_idnconvert_hostname(struct hostname *host); -#endif #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #define USE_IDN -#ifdef HEADER_CURL_URLDATA_H /* HACK */ void Curl_free_idnconverted_hostname(struct hostname *host); -#endif CURLcode Curl_idn_decode(const char *input, char **output); CURLcode Curl_idn_encode(const char *puny, char **output); #else From 7d546e52b21c94e1d4f6669d2d4d64f79bff0d7b Mon Sep 17 00:00:00 2001 From: Kai Pastor Date: Sat, 9 May 2026 07:23:37 +0200 Subject: [PATCH 0065/1062] cmake: export/forward `NGTCP2_CRYPTO_BACKEND` Exporting the component name as passed in is somewhat boring. OTOH it is convenient for reuse. - FindNGTCP2: export crypto backend in `NGTCP2_CRYPTO_BACKEND`. - pass `COMPONENTS` `NGTCP2_CRYPTO_BACKEND` in `curl-config.cmake`. - FindNGTCP2: fix to skip Config detection when optional `COMPONENTS` is not passed. Co-authored-by: Viktor Szakats Reported-by: x-xiang on github Fixes #21523 Follow-up to 8fce3e17e6cb310cd6dbe38ff14869b8fe5827d2 #20814 Closes #21540 --- CMake/FindNGTCP2.cmake | 21 +++++++++++---------- CMake/curl-config.in.cmake | 2 +- CMakeLists.txt | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index 70dae14b8663..bf2f48877749 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -49,27 +49,28 @@ # # - `NGTCP2_FOUND`: System has ngtcp2. # - `NGTCP2_VERSION`: Version of ngtcp2. +# - `NGTCP2_CRYPTO_BACKEND`: Name of the crypto library component. (Empty if COMPONENTS was not used.) # - `CURL::ngtcp2`: ngtcp2 library target. +set(NGTCP2_CRYPTO_BACKEND "") if(NGTCP2_FIND_COMPONENTS) - set(_ngtcp2_crypto_backend "") foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS) if(_component MATCHES "^(BoringSSL|GnuTLS|LibreSSL|ossl|quictls|wolfSSL)") - if(_ngtcp2_crypto_backend) + if(NGTCP2_CRYPTO_BACKEND) message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") endif() - set(_ngtcp2_crypto_backend ${_component}) + set(NGTCP2_CRYPTO_BACKEND ${_component}) endif() endforeach() - if(_ngtcp2_crypto_backend) - string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_lower) - string(TOUPPER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_upper) + if(NGTCP2_CRYPTO_BACKEND) + string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_lower) + string(TOUPPER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_upper) endif() endif() set(_ngtcp2_pc_requires "libngtcp2") -if(_ngtcp2_crypto_backend) +if(NGTCP2_CRYPTO_BACKEND) list(APPEND _ngtcp2_pc_requires "lib${_crypto_library_lower}") endif() @@ -81,7 +82,7 @@ if(NOT DEFINED NGTCP2_INCLUDE_DIR AND pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) set(_tried_pkgconfig TRUE) endif() - if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG) + if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG AND NGTCP2_CRYPTO_BACKEND) find_package(ngtcp2 CONFIG QUIET) # Skip using it if the crypto library target is not available if(ngtcp2_CONFIG AND @@ -130,7 +131,7 @@ else() unset(_version_str) endif() - if(_ngtcp2_crypto_backend) + if(NGTCP2_CRYPTO_BACKEND) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) cmake_path(GET NGTCP2_LIBRARY PARENT_PATH _ngtcp2_library_dir) else() @@ -145,7 +146,7 @@ else() endif() if(${_crypto_library_upper}_LIBRARY) - set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) + set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE) set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library_upper}_LIBRARY}) endif() endif() diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake index 317477197f51..1c0eec36ed81 100644 --- a/CMake/curl-config.in.cmake +++ b/CMake/curl-config.in.cmake @@ -122,7 +122,7 @@ if("@USE_NGHTTP3@") list(APPEND _curl_libs CURL::nghttp3) endif() if("@USE_NGTCP2@") - find_dependency(NGTCP2 MODULE) + find_dependency(NGTCP2 MODULE COMPONENTS @NGTCP2_CRYPTO_BACKEND@) list(APPEND _curl_libs CURL::ngtcp2) endif() if("@USE_GNUTLS@") diff --git a/CMakeLists.txt b/CMakeLists.txt index 506dfeb30567..f8d7a0b86e86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2374,7 +2374,7 @@ if(NOT CURL_DISABLE_INSTALL) # USE_MBEDTLS # USE_NGHTTP2 # USE_NGHTTP3 - # USE_NGTCP2 + # USE_NGTCP2 NGTCP2_CRYPTO_BACKEND # USE_OPENSSL OPENSSL_VERSION_MAJOR # USE_QUICHE # USE_RUSTLS From 4d82423dd323978153fa7c4d21d7030643dd8efa Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 18 Mar 2026 13:24:07 +0100 Subject: [PATCH 0066/1062] delta: harden external command invocations By moving operations Perl-native (from shell and external commands), and passing arguments individually to external commands. Pointed out by Codex Security Closes #21104 --- scripts/delta | 104 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 38 deletions(-) diff --git a/scripts/delta b/scripts/delta index 02b0232c47c2..7a1793dbb36a 100755 --- a/scripts/delta +++ b/scripts/delta @@ -34,8 +34,20 @@ use strict; use warnings; use POSIX; +use IPC::Open3; use Time::Piece; +sub cmd { + my @out; + my $as_string = shift if($_[0] eq '$'); # return as string (vs. list of lines) + my $hideerr = shift if($_[0] eq '2>'); # 2>/dev/null + my $pid = open3(my $in, my $out, $hideerr ? '>/dev/null' : '>&STDERR', @_); + close $in; + push @out, <$out>; + waitpid($pid, 0); + return $as_string ? join('', @out) : @out; +} + my $start = $ARGV[0] || ''; if($start eq "-h") { @@ -43,76 +55,92 @@ if($start eq "-h") { exit; } elsif($start eq "") { - $start = `git tag --sort=taggerdate | grep "^curl-" | tail -1`; + my @tags = cmd('git', 'tag', '--sort=taggerdate', '--no-column', '--list', 'curl-*'); + $start = $tags[-1]; # latest tag chomp $start; } -my $commits = `git log --oneline $start.. | wc -l`; -my $committers = `git shortlog -s $start.. | wc -l`; -my $bcommitters = `git shortlog -s $start | wc -l`; +my $commits = cmd('$', 'git', 'rev-list', '--count', "$start.."); +my $committers = cmd('git', 'shortlog', '-s', "$start.."); +my $bcommitters = cmd('git', 'shortlog', '-s', $start); -my $acommits = `git log --oneline | wc -l`; -my $acommitters = `git shortlog -s | wc -l`; +my $acommits = cmd('$', 'git', 'rev-list', '--count', 'HEAD'); +my $acommitters = cmd('git', 'shortlog', '-s', 'HEAD'); # delta from now compared to before my $ncommitters = $acommitters - $bcommitters; # number of contributors right now -my $acontribs = `./scripts/contrithanks.sh stdout | wc -l`; +my $acontribs = cmd('./scripts/contrithanks.sh', 'stdout'); # number when the tag was set -my $bcontribs = `git show $start:docs/THANKS | grep -c '^[^ ]'`; +my $bcontribs = cmd('$', 'git', 'grep', '-h', '-c', '^[^ ]', "$start:docs/THANKS"); # delta my $contribs = $acontribs - $bcontribs; # number of setops: sub setopts { - my ($f)=@_; - open(H, $f); - my $opts; - while() { - if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { - $opts++; + my $mode = shift; + my $opts = 0; + if(open(H, $mode, @_)) { + while() { + if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { + $opts++; + } } + close(H); + } + else { + die join(' ', @_) . ": $!\n"; } - close(H); return $opts; } -my $asetopts = setopts("/dev/null | grep -c '{ *"....--'`; -my $noptions=$aoptions - $boptions; +my $aoptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', 'src/tool_listhelp.c'); +my $boptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', "$start:src/tool_listhelp.c"); +my $noptions = $aoptions - $boptions; # current local branch -my $branch=`git rev-parse --abbrev-ref HEAD 2>/dev/null`; +my $branch = cmd('$', '2>', 'git', 'rev-parse', '--abbrev-ref', 'HEAD'); chomp $branch; # Number of files in git -my $afiles=`git ls-files | wc -l`; -my $deletes=`git diff-tree --diff-filter=A -r --summary origin/$branch $start 2>/dev/null | wc -l`; -my $creates=`git diff-tree --diff-filter=D -r --summary origin/$branch $start 2>/dev/null | wc -l`; +my $afiles = cmd('git', 'ls-files'); +my $deletes = cmd('2>', 'git', 'diff-tree', '--diff-filter=A', '-r', '--summary', "origin/$branch", $start); +my $creates = cmd('2>', 'git', 'diff-tree', '--diff-filter=D', '-r', '--summary', "origin/$branch", $start); # Time since that tag -my $tagged=`git for-each-ref --format="%(refname:short) | %(taggerdate:unix)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # Unix timestamp -my $taggednice=`git for-each-ref --format="%(refname:short) | %(creatordate)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # human readable time +sub tagstamp { + my $col = shift; + foreach my $line (@_) { + if(index($line, "$start ") == 0) { + my @cols = split(/\|/, $line); + return $cols[$col - 1]; + } + } +} +my @tagged = cmd('git', 'for-each-ref', '--format=%(refname:short) | %(taggerdate:unix) | %(creatordate)', 'refs/tags/*'); +my $tagged = tagstamp(2, @tagged); # Unix timestamp +my $taggednice = tagstamp(3, @tagged); # human readable time chomp $taggednice; -my $now=POSIX::strftime("%s", localtime()); -my $elapsed=$now - $tagged; # number of seconds since tag -my $total=$now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; -my $totalhttpget=$now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; +my $now = POSIX::strftime("%s", localtime()); +my $elapsed = $now - $tagged; # number of seconds since tag +my $total = $now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; +my $totalhttpget = $now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; # Number of public functions in libcurl -my $apublic=`git grep ^CURL_EXTERN -- include/curl | wc -l`; -my $bpublic=`git grep ^CURL_EXTERN $start -- include/curl | wc -l`; +my $apublic = cmd('git', 'grep', '^CURL_EXTERN', '--', 'include/curl'); +my $bpublic = cmd('git', 'grep', '^CURL_EXTERN', $start, '--', 'include/curl'); my $public = $apublic - $bpublic; # diffstat my ($fileschanged, $insertions, $deletions); -my $diffstat=`git diff --stat $start.. | tail -1`; +my @diffstat = cmd('2>', 'git', 'diff', '--stat', "$start.."); +my $diffstat = $diffstat[-1]; if($diffstat =~ /^ *(\d+) files changed, (\d+) insertions\(\+\), (\d+)/) { - ($fileschanged, $insertions, $deletions)=($1, $2, $3); + ($fileschanged, $insertions, $deletions) = ($1, $2, $3); } # Changes/bug-fixes currently logged @@ -123,16 +151,16 @@ open(F, ") { if($_ =~ /following changes:/) { - $mode=1; + $mode = 1; } elsif($_ =~ /following bugfixes:/) { - $mode=2; + $mode = 2; } elsif($_ =~ /known bugs:/) { - $mode=3; + $mode = 3; } elsif($_ =~ /like these:/) { - $mode=4; + $mode = 4; } if($_ =~ /^ o /) { if($mode == 1) { From 185e67e1fa32e36b019fb0b5918e0e17a3edd087 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Mon, 11 May 2026 15:06:57 -0500 Subject: [PATCH 0067/1062] docs: fix --follow doc typo Let the singular ~~object~~ subject "option" agree with the verb "set". Closes #21553 --- docs/cmdline-opts/follow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cmdline-opts/follow.md b/docs/cmdline-opts/follow.md index 096324f1654b..b34e30c693d0 100644 --- a/docs/cmdline-opts/follow.md +++ b/docs/cmdline-opts/follow.md @@ -24,7 +24,7 @@ set with --request when following redirects as the HTTP specification says. The method string set with --request is used in subsequent requests for the status codes 307 or 308, but may be reset to GET for 301, 302 and 303. -This is subtly different than --location, as that option always set the custom +This is subtly different than --location, as that option always sets the custom method in all subsequent requests independent of response code. Restrict which protocols a redirect is accepted to follow with --proto-redir. From cfadbaa133504d47ece989486fde944d076e0222 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Thu, 30 Apr 2026 22:53:20 +0800 Subject: [PATCH 0068/1062] gnutls: allow building with nettle 4.0 Closes #21169 --- lib/curl_sha512_256.c | 7 ++++++- lib/md5.c | 5 +++++ lib/sha256.c | 7 ++++++- lib/vtls/gtls.c | 5 +++++ m4/curl-gnutls.m4 | 4 ++-- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 73b959c91df0..5c3bcdf800ca 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -75,7 +75,8 @@ #endif #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) -# include +# include +# include # ifdef SHA512_256_DIGEST_SIZE # define USE_GNUTLS_SHA512_256 1 # endif @@ -281,8 +282,12 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; +#if NETTLE_VERSION_MAJOR >= 4 + sha512_256_digest(ctx, (uint8_t *)digest); +#else sha512_256_digest(ctx, (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest); +#endif return CURLE_OK; } diff --git a/lib/md5.c b/lib/md5.c index 4dd0d7c27859..9d339becfa80 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -47,6 +47,7 @@ #ifdef USE_GNUTLS #include +#include typedef struct md5_ctx my_md5_ctx; @@ -64,7 +65,11 @@ static void my_md5_update(void *ctx, static void my_md5_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md5_digest(ctx, digest); +#else md5_digest(ctx, 16, digest); +#endif } #elif defined(USE_OPENSSL) && \ diff --git a/lib/sha256.c b/lib/sha256.c index eeeec6c6967e..6211d04cd008 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -113,7 +113,8 @@ static void my_sha256_final(unsigned char *digest, void *in) } #elif defined(USE_GNUTLS) -#include +#include +#include typedef struct sha256_ctx my_sha256_ctx; @@ -132,7 +133,11 @@ static void my_sha256_update(void *ctx, static void my_sha256_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + sha256_digest(ctx, digest); +#else sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); +#endif } #elif defined(USE_MBEDTLS) && \ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index e60e5a5ecc70..db62c7577441 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "urldata.h" #include "curl_trc.h" @@ -2268,7 +2269,11 @@ static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ struct sha256_ctx SHA256pw; sha256_init(&SHA256pw); sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); +#if NETTLE_VERSION_MAJOR >= 4 + sha256_digest(&SHA256pw, sha256sum); +#else sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); +#endif return CURLE_OK; } diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index b8ee3f5780c9..222386e0d9a7 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -145,7 +145,7 @@ dnl if test "$GNUTLS_ENABLED" = "1"; then USE_GNUTLS_NETTLE= dnl First check if we can detect either crypto library via transitive linking - AC_CHECK_LIB(gnutls, nettle_MD5Init, [ USE_GNUTLS_NETTLE=1 ]) + AC_CHECK_LIB(gnutls, nettle_md5_init, [ USE_GNUTLS_NETTLE=1 ]) dnl If not, try linking directly to both of them to see if they are available if test -z "$USE_GNUTLS_NETTLE"; then @@ -174,7 +174,7 @@ if test "$GNUTLS_ENABLED" = "1"; then CPPFLAGS="$CPPFLAGS $addcflags" fi - AC_CHECK_LIB(nettle, nettle_MD5Init, + AC_CHECK_LIB(nettle, nettle_md5_init, [ USE_GNUTLS_NETTLE=1 ], From b582a936dd207fe2f2d1346208e5411686548891 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 04:02:36 +0200 Subject: [PATCH 0069/1062] GHA/linux: build local wolfSSL opensslextra with `--enable-ed25519` For use with RFC 9421 HTTP Message Signatures support. Ref: https://github.com/curl/curl/pull/21239/files#r3222322908 Ref: #21239 Closes #21555 --- .github/workflows/linux.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 14623ac0c1ac..4a58278d92cf 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -596,7 +596,8 @@ jobs: sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-all --enable-tls13 --enable-harden --enable-all \ + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-all \ + --enable-tls13 --enable-harden --enable-all \ --disable-benchmark --disable-crypttests --disable-examples make install @@ -618,7 +619,8 @@ jobs: sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ + --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ --disable-benchmark --disable-crypttests --disable-examples make install @@ -640,7 +642,8 @@ jobs: sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ + --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ --disable-benchmark --disable-crypttests --disable-examples make install From 01f08dc4eb20a19aa60230653715c8b839619cbb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 04:50:09 +0200 Subject: [PATCH 0070/1062] gnutls: fix more nettle 4+ compatibility issues - disable DES with nettle 4. It no longer supports it. ``` lib/curl_ntlm_core.c:67:12: fatal error: 'nettle/des.h' file not found 67 | # include | ^~~~~~~~~~~~~~ ``` - fix MD4 support with nettle 4. ``` lib/md4.c:178:36: error: too many arguments to function call, expected 2, have 3 178 | md4_digest(ctx, MD4_DIGEST_SIZE, digest); | ~~~~~~~~~~ ^~~~~~ ``` - fix unused argument compiler warning: ``` lib/vtls/gtls.c:2267:39: error: unused parameter 'sha256len' [clang-diagnostic-unused-parameter,-warnings-as-errors] 2267 | size_t sha256len) | ^ ``` Ref: https://github.com/curl/curl/actions/runs/25710321195/job/75488970143?pr=21557 - GHA/macos: stop enabling NTLM in the GnuTLS job. It no longer builds due to missing DES support in nettle 4. ``` lib/curl_ntlm_core.c:90:4: error: "cannot compile NTLM support without a crypto library with DES." 90 | # error "cannot compile NTLM support without a crypto library with DES." | ^ ``` Ref: https://github.com/curl/curl/actions/runs/25710321195/job/75488970170?pr=21557 Follow-up to cfadbaa133504d47ece989486fde944d076e0222 #21169 Closes #21557 --- .github/workflows/macos.yml | 2 +- lib/curl_ntlm_core.c | 9 ++++++++- lib/md4.c | 5 +++++ lib/vtls/gtls.c | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index edc877b38342..3cda27766ada 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -354,7 +354,7 @@ jobs: generate: >- -DENABLE_DEBUG=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=OFF -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 - -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON -DCURL_ENABLE_NTLM=ON + -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON - name: 'aws-lc +analyzer' compiler: gcc-15 diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 4b2007bbad89..447ff64aeb8b 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -49,6 +49,13 @@ in NTLM type-3 messages. */ +#ifdef USE_GNUTLS +#include +#if NETTLE_VERSION_MAJOR < 4 +#define HAVE_GNUTLS_DES +#endif +#endif + #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) # include @@ -63,7 +70,7 @@ # include # define USE_WOLFSSL_DES -#elif defined(USE_GNUTLS) +#elif defined(HAVE_GNUTLS_DES) # include # define USE_CURL_DES_SET_ODD_PARITY #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) diff --git a/lib/md4.c b/lib/md4.c index 0213483ad30c..e030ffac30c2 100644 --- a/lib/md4.c +++ b/lib/md4.c @@ -158,6 +158,7 @@ static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) #elif defined(USE_GNUTLS) #include +#include typedef struct md4_ctx my_md4_ctx; @@ -175,7 +176,11 @@ static void my_md4_update(my_md4_ctx *ctx, static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md4_digest(ctx, digest); +#else md4_digest(ctx, MD4_DIGEST_SIZE, digest); +#endif } #else diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index db62c7577441..fa4d6c42cc38 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -2270,6 +2270,7 @@ static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ sha256_init(&SHA256pw); sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); #if NETTLE_VERSION_MAJOR >= 4 + (void)sha256len; sha256_digest(&SHA256pw, sha256sum); #else sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); From 2a2104f3cff44bb28bb570a093be52bbeeed8f23 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 11 May 2026 14:56:04 +0200 Subject: [PATCH 0071/1062] event: fix wakeup consumption The events on a multi wakeup socketpair were only consumed via curl_multi_poll()/curl_multi_wait() but not in event based processing on a curl_multi_socket() call. That led to busy loops as reported in Fixes #21547 Reported-by: Earnestly on github Closes #21549 --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/multi.c b/lib/multi.c index be32740a7097..5e84133f13fd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2703,6 +2703,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { +#ifdef ENABLE_WAKEUP + /* Consume any pending wakeup signals before processing. + * This is necessary for event based processing. See #21547 */ + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); +#endif #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); #endif From cb0636980bd6b667ff6a8370210186295e24cb80 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 22 Apr 2026 11:38:02 +0200 Subject: [PATCH 0072/1062] tool_urlglob: add named globs Idea-by: Bastian Jesuiter Verified by test 2408 - 2411 Closes #21409 --- docs/cmdline-opts/_GLOBBING.md | 13 +++ docs/cmdline-opts/output.md | 11 ++ src/tool_operate.c | 7 +- src/tool_urlglob.c | 188 +++++++++++++++++++++++---------- src/tool_urlglob.h | 4 + tests/data/Makefile.am | 1 + tests/data/test2408 | 92 ++++++++++++++++ tests/data/test2409 | 92 ++++++++++++++++ tests/data/test2410 | 37 +++++++ tests/data/test2411 | 37 +++++++ tests/data/test75 | 2 +- tests/data/test759 | 2 +- tests/data/test761 | 4 +- 13 files changed, 432 insertions(+), 58 deletions(-) create mode 100644 tests/data/test2408 create mode 100644 tests/data/test2409 create mode 100644 tests/data/test2410 create mode 100644 tests/data/test2411 diff --git a/docs/cmdline-opts/_GLOBBING.md b/docs/cmdline-opts/_GLOBBING.md index 37c8d430693d..b801adb4d1f9 100644 --- a/docs/cmdline-opts/_GLOBBING.md +++ b/docs/cmdline-opts/_GLOBBING.md @@ -39,4 +39,17 @@ probably have to put the full URL within double quotes to avoid the shell from interfering with it. This also goes for other characters treated special, like for example '&', '?' and '*'. +The separate globbing components can be referenced in the --output option to +allow pieces to be reused in the target filename. + +Starting in curl 8.21.0, the separate globbing parts can be named and +referenced by their names. The case sensitive alphanumeric name is set +enclosed within angle brackets after the opening character. Examples: + + https://fun.example/{one,two,three}.jpg + + ftp://ftp.example.com/file[1-100].txt + +Setting the same glob name twice is an error. + Switch off globbing with --globoff. diff --git a/docs/cmdline-opts/output.md b/docs/cmdline-opts/output.md index 0c4f7f9facdd..c1d823e3244d 100644 --- a/docs/cmdline-opts/output.md +++ b/docs/cmdline-opts/output.md @@ -69,3 +69,14 @@ override curl's internal binary output in terminal prevention: Note that the binary output may be caused by the response being compressed, in which case you may want to use the --compressed option. + +Starting in curl 8.21.0, the separate globbing parts can be named and +referenced by their names. The case sensitive alphanumeric name is set +enclosed within angle brackets after the opening character. Examples: + + curl "https://fun.example/{one,two}.jpg" -o "save-#" + + curl "ftp://ftp.example/file[1-100].txt" \ + -o "save-#.txt" + +Referencing a named glob that is not set, causes an error. diff --git a/src/tool_operate.c b/src/tool_operate.c index 3ad30ca4c4e8..62d40afd55e3 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1067,7 +1067,12 @@ static CURLcode setup_outfile(struct OperationConfig *config, } else if(result) { /* bad globbing */ - warnf("bad output glob"); + if(state->urlglob.error) { + glob_show_error(&state->urlglob, u->outfile, tool_stderr, result); + config->synthetic_error = TRUE; + } + else + warnf("bad output glob"); return result; } if(!*per->outfile) { diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index d2249980e2bf..305efdeeccb6 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -56,6 +56,7 @@ static CURLcode glob_fixed(struct URLGlob *glob, char *fixed, size_t len) pat->c.set.palloc = 1; pat->c.set.size = 1; + pat->name = NULL; /* unnamed */ return CURLE_OK; } @@ -89,7 +90,7 @@ static int multiply(curl_off_t *amount, curl_off_t with) static CURLcode glob_set(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, - int globindex) + int globindex, const struct Curl_str *name) { /* processes a set expression with the point behind the opening '{' ','-separated elements are collected until the next closing '}' @@ -103,6 +104,7 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, size_t size = 0; char **elem = NULL; size_t palloc = 0; /* start with this */ + DEBUGASSERT(name); while(!done) { switch(*pattern) { @@ -198,6 +200,15 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, pat->c.set.size = size; pat->c.set.idx = 0; pat->c.set.palloc = palloc; + if(curlx_strlen(name)) { + pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name)); + if(!pat->name) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } + else + pat->name = NULL; /* no name */ return CURLE_OK; error: @@ -212,7 +223,7 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, static CURLcode glob_range(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, - int globindex) + int globindex, const struct Curl_str *name) { /* processes a range expression with the point behind the opening '[' - char range: e.g. "a-z]", "B-Q]" @@ -224,8 +235,10 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, const char *pattern = *patternp; const char *c; + DEBUGASSERT(name); pat = &glob->pattern[glob->pnum]; pat->globindex = globindex; + pat->name = NULL; /* no name (so far) */ if(ISALPHA(*pattern)) { /* character range detected */ @@ -340,6 +353,11 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, return globerror(glob, "bad range specification", *posp, CURLE_URL_MALFORMAT); + if(curlx_strlen(name)) { + pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name)); + if(!pat->name) + return CURLE_OUT_OF_MEMORY; + } *patternp = pattern; return CURLE_OK; } @@ -407,12 +425,31 @@ static CURLcode add_glob(struct URLGlob *glob, size_t pos) return CURLE_OK; } +/* returns the named glob pattern (case sensitively) if it exists, otherwise + NULL +*/ +static struct URLPattern *glob_find_name(struct URLGlob *glob, + struct Curl_str *name) +{ + size_t i; + /* find the correct glob entry */ + for(i = 0; i < glob->pnum; i++) { + if(glob->pattern[i].name && + curlx_str_cmp(name, glob->pattern[i].name)) + return &glob->pattern[i]; + } + return NULL; /* no match */ +} + +#define MAX_GLOBNAME_LEN 64 + static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, size_t pos, curl_off_t *amount) { /* processes a literal string component of a URL special characters '{' and '[' branch to set/range processing functions */ + const char *ipattern = pattern; /* start position */ CURLcode result = CURLE_OK; int globindex = 0; /* count "actual" globs */ @@ -464,21 +501,39 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, curlx_dyn_reset(&glob->buf); } else { + struct Curl_str name; if(!*pattern) /* done */ break; - else if(*pattern == '{') { - /* process set pattern */ + else if((*pattern == '{') || (*pattern == '[')) { + bool set = (*pattern == '{'); + const char *start; pattern++; pos++; - result = glob_set(glob, &pattern, &pos, amount, globindex++); - if(!result) - result = add_glob(glob, pos); - } - else if(*pattern == '[') { - /* process range pattern */ - pattern++; - pos++; - result = glob_range(glob, &pattern, &pos, amount, globindex++); + start = pattern; + /* fetch the name, if provided */ + if(curlx_str_single(&pattern, '<') || + curlx_str_until(&pattern, &name, MAX_GLOBNAME_LEN, '>') || + curlx_str_single(&pattern, '>')) { + /* Not a proper name. This is not reporting errors on syntax errors + on purpose: it means that if there is an existing use case that + uses what looks like a broken named-glob syntax (now introduced) + we let that function like before. */ + curlx_str_init(&name); + pattern = start; /* reset any partial patch */ + } + else { + /* check that the name is not already used */ + struct URLPattern *p = glob_find_name(glob, &name); + if(p) + return globerror(glob, "Duplicate glob name", 2 + start - ipattern, + CURLE_URL_MALFORMAT); + } + if(set) + result = glob_set(glob, &pattern, &pos, amount, globindex++, &name); + else + result = glob_range(glob, &pattern, &pos, amount, globindex++, + &name); + if(!result) result = add_glob(glob, pos); } @@ -492,6 +547,26 @@ bool glob_inuse(struct URLGlob *glob) return glob->palloc ? TRUE : FALSE; } +/* a glob error has been confirmed, this outputs details about it to the set + error stream */ +void glob_show_error(struct URLGlob *glob, const char *url, FILE *error, + CURLcode result) +{ + char text[512]; + const char *t; + if(glob->pos) { + curl_msnprintf(text, sizeof(text), "%s in position %zu:\n%s\n%*s^", + glob->error, + glob->pos, url, (int)glob->pos - 1, " "); + t = text; + } + else + t = glob->error; + + /* send error description to the error-stream */ + curl_mfprintf(error, "curl: (%d) %s\n", result, t); +} + CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, FILE *error) { @@ -511,21 +586,8 @@ CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, result = glob_parse(glob, url, 1, &amount); if(result) { - if(error && glob->error) { - char text[512]; - const char *t; - if(glob->pos) { - curl_msnprintf(text, sizeof(text), "%s in URL position %zu:\n%s\n%*s^", - glob->error, - glob->pos, url, (int)glob->pos - 1, " "); - t = text; - } - else - t = glob->error; - - /* send error description to the error-stream */ - curl_mfprintf(error, "curl: (%d) %s\n", result, t); - } + if(error && glob->error) + glob_show_error(glob, url, error, result); *urlnum = 1; return result; } @@ -547,6 +609,7 @@ void glob_cleanup(struct URLGlob *glob) curlx_safefree(glob->pattern[i].c.set.elem[elem]); curlx_safefree(glob->pattern[i].c.set.elem); } + curlx_safefree(glob->pattern[i].name); } curlx_safefree(glob->pattern); glob->palloc = 0; @@ -643,6 +706,7 @@ CURLcode glob_match_url(char **output, const char *filename, struct URLGlob *glob, SANITIZEcode *sc) { struct dynbuf dyn; + const char *ifilename = filename; *output = NULL; *sc = SANITIZE_ERR_OK; @@ -650,11 +714,11 @@ CURLcode glob_match_url(char **output, const char *filename, while(*filename) { CURLcode result = CURLE_OK; + struct URLPattern *pat = NULL; if(*filename == '#' && ISDIGIT(filename[1])) { - const char *ptr = filename; + /* a numbered glob reference */ + const char *ptr = filename++; curl_off_t num; - struct URLPattern *pat = NULL; - filename++; if(!curlx_str_number(&filename, &num, glob->pnum) && num) { size_t i; num--; /* make it zero based */ @@ -666,31 +730,49 @@ CURLcode glob_match_url(char **output, const char *filename, } } } - - if(pat) { - switch(pat->type) { - case GLOB_SET: - if(pat->c.set.elem) - result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]); - break; - case GLOB_ASCII: { - char letter = (char)pat->c.ascii.letter; - result = curlx_dyn_addn(&dyn, &letter, 1); - break; - } - case GLOB_NUM: - result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T, - pat->c.num.npad, pat->c.num.idx); - break; - default: - DEBUGASSERT(0); + if(!pat) + filename = ptr; + } + else if(*filename == '#' && (filename[1] == '<')) { + /* a named glob reference */ + struct Curl_str name; + const char *ptr = filename; + filename += 2; /* pass both leading bytes */ + if(!curlx_str_until(&filename, &name, MAX_GLOBNAME_LEN, '>') && + !curlx_str_single(&filename, '>')) { + /* find the correct glob entry */ + pat = glob_find_name(glob, &name); + if(!pat) { + /* when the name is given correctly, it needs to be an existing glob + name, which makes this an error */ curlx_dyn_free(&dyn); - return CURLE_FAILED_INIT; + return globerror(glob, "no glob exists with this name", + filename - ifilename, CURLE_BAD_FUNCTION_ARGUMENT); } } - else - /* #[num] out of range, use the #[num] in the output */ - result = curlx_dyn_addn(&dyn, ptr, filename - ptr); + if(!pat) + filename = ptr; + } + if(pat) { + switch(pat->type) { + case GLOB_SET: + if(pat->c.set.elem) + result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]); + break; + case GLOB_ASCII: { + char letter = (char)pat->c.ascii.letter; + result = curlx_dyn_addn(&dyn, &letter, 1); + break; + } + case GLOB_NUM: + result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T, + pat->c.num.npad, pat->c.num.idx); + break; + default: + DEBUGASSERT(0); + curlx_dyn_free(&dyn); + return CURLE_FAILED_INIT; + } } else result = curlx_dyn_addn(&dyn, filename++, 1); diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h index ad0f144fd232..abc279de73db 100644 --- a/src/tool_urlglob.h +++ b/src/tool_urlglob.h @@ -33,6 +33,7 @@ typedef enum { struct URLPattern { globtype type; + char *name; /* if not NULL */ int globindex; /* the number of this particular glob or -1 if not used within {} or [] */ union { @@ -71,6 +72,9 @@ struct URLGlob { size_t pos; /* column position of error or 0 */ }; +void glob_show_error(struct URLGlob *glob, const char *url, FILE *error, + CURLcode result); + CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, FILE *error); CURLcode glob_next_url(char **globbed, struct URLGlob *glob); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 85ea4bcd1db4..12f6bfbc0cc7 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -265,6 +265,7 @@ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ +test2408 test2409 test2410 test2411 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ diff --git a/tests/data/test2408 b/tests/data/test2408 new file mode 100644 index 000000000000..edc83d48a4ec --- /dev/null +++ b/tests/data/test2408 @@ -0,0 +1,92 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + +# Client-side + + +http + + +multiple requests using named {} globs in URL + + +"%HOSTIP:%HTTPPORT/{%LTtest%GT%TESTNUMBER,%TESTNUMBER0002,%TESTNUMBER0003}" -o "%LOGDIR/dump-#%LTtest%GT" + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0003 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + + diff --git a/tests/data/test2409 b/tests/data/test2409 new file mode 100644 index 000000000000..4c9e9c57a175 --- /dev/null +++ b/tests/data/test2409 @@ -0,0 +1,92 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + +# Client-side + + +http + + +multiple requests using named [] globs in URL + + +"%HOSTIP:%HTTPPORT/hello[%LTtest%GT7-9]" -o "%LOGDIR/dump-#%LTtest%GT" + + + +# Verify data after the test has been "shot" + + +GET /hello7 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /hello8 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /hello9 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + + diff --git a/tests/data/test2410 b/tests/data/test2410 new file mode 100644 index 000000000000..fdec8c2e5d58 --- /dev/null +++ b/tests/data/test2410 @@ -0,0 +1,37 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + +# Client-side + + +http + + +duplicate named glob + + +"%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTtest%GTC,D}" -o "%LOGDIR/dump" + + + +# Verify data after the test has been "shot" + + +curl: (3) Duplicate glob name in position 30: +%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTtest%GTC,D} + ^ + + +3 + + + diff --git a/tests/data/test2411 b/tests/data/test2411 new file mode 100644 index 000000000000..5f45ac6ad8cf --- /dev/null +++ b/tests/data/test2411 @@ -0,0 +1,37 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + +# Client-side + + +http + + +reference a named glob not set + + +"%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTmoo%GTC,D}" -o "somewhere/#%LTfoo%GT" + + + +# Verify data after the test has been "shot" + + +curl: (43) no glob exists with this name in position 16: +somewhere/#%LTfoo%GT + ^ + + +43 + + + diff --git a/tests/data/test75 b/tests/data/test75 index 40c0d3b4dea4..0ba5e782c55e 100644 --- a/tests/data/test75 +++ b/tests/data/test75 @@ -31,7 +31,7 @@ HTTP, urlglob retrieval with bad range 3 -curl: (3) bad range in URL position 47: +curl: (3) bad range in position 47: http://a-site-never-accessed.example.org/[2-1] ^ diff --git a/tests/data/test759 b/tests/data/test759 index 9c67e30f2d94..7ad896bbccce 100644 --- a/tests/data/test759 +++ b/tests/data/test759 @@ -18,7 +18,7 @@ glob '{,' # Verify data after the test has been "shot" -# curl: (3) unmatched brace in URL position 1: +# curl: (3) unmatched brace in position 1: 3 diff --git a/tests/data/test761 b/tests/data/test761 index eec55e297fca..2772a8f78fa8 100644 --- a/tests/data/test761 +++ b/tests/data/test761 @@ -22,8 +22,8 @@ http://testingthis/%repeat[201 x {a}b]% 3 -curl: (3) too many {} sets in URL position 403: -http://testingthis/%repeat[113 x {a}b]%{a +curl: (3) too many {} sets in position 403: +http://testingthis/%repeat[114 x {a}b]%{a From 7eb0b30934d1e54229646293b088e625a8b7b214 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 May 2026 10:03:02 +0200 Subject: [PATCH 0073/1062] tool_urlglob: make globbing error reported for correct position Reported by Codex Security Closes #21561 --- src/tool_urlglob.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index 305efdeeccb6..72893fe66185 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -99,7 +99,6 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, bool done = FALSE; const char *pattern = *patternp; const char *opattern = pattern; - size_t opos = *posp - 1; CURLcode result = CURLE_OK; size_t size = 0; char **elem = NULL; @@ -109,7 +108,7 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, while(!done) { switch(*pattern) { case '\0': /* URL ended while set was still open */ - result = globerror(glob, "unmatched brace", opos, CURLE_URL_MALFORMAT); + result = globerror(glob, "unmatched brace", *posp, CURLE_URL_MALFORMAT); goto error; case '{': @@ -527,6 +526,7 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, if(p) return globerror(glob, "Duplicate glob name", 2 + start - ipattern, CURLE_URL_MALFORMAT); + pos += (pattern - start); } if(set) result = glob_set(glob, &pattern, &pos, amount, globindex++, &name); From 2256162fa748216f6ffec9a50ed5e199d47341a1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 May 2026 09:20:31 +0200 Subject: [PATCH 0074/1062] tftp: stricter option name checks Previously, the use of checkprefix() alone allowed the code to match not only on "blksize" but also (mistakenly) on "blksizeFOO" etc. Reported-by: Andrew Nesbit Closes #21560 --- lib/tftp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/tftp.c b/lib/tftp.c index 6cc672d447bf..a088cd90466e 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -266,16 +266,19 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, while(tmp < ptr + len) { const char *option, *value; + size_t olen; tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); if(!tmp) { failf(data, "Malformed ACK packet, rejecting"); return CURLE_TFTP_ILLEGAL; } + olen = strlen(option); infof(data, "got option=(%s) value=(%s)", option, value); - if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { + if((strlen(TFTP_OPTION_BLKSIZE) == olen) && + checkprefix(TFTP_OPTION_BLKSIZE, option)) { curl_off_t blksize; if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) { failf(data, "%s (%d)", "blksize is larger than max supported", @@ -304,7 +307,8 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } - else if(checkprefix(TFTP_OPTION_TSIZE, option)) { + else if((strlen(TFTP_OPTION_TSIZE) == olen) && + checkprefix(TFTP_OPTION_TSIZE, option)) { curl_off_t tsize = 0; /* tsize should be ignored on upload: Who cares about the size of the remote file? */ From cc6777d939976b2f322dcbe5ae76ef28c6b4632d Mon Sep 17 00:00:00 2001 From: "Song X. Gao" <39278329+xsgao-github@users.noreply.github.com> Date: Mon, 11 May 2026 12:45:15 -0400 Subject: [PATCH 0075/1062] spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI Make CURLOPT_GSSAPI_DELEGATION effective on Windows builds that use SSPI (instead of a native GSS-API implementation), so Kerberos delegation can be requested during SPNEGO/Negotiate authentication. Closes #21528 --- lib/setopt.c | 2 +- lib/urldata.h | 5 +-- lib/vauth/spnego_sspi.c | 24 ++++++----- tests/data/Makefile.am | 2 +- tests/data/test3302 | 19 +++++++++ tests/unit/Makefile.inc | 2 +- tests/unit/unit3302.c | 89 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 tests/data/test3302 create mode 100644 tests/unit/unit3302.c diff --git a/lib/setopt.c b/lib/setopt.c index 24d0c42bcf5d..61d87be06fcf 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1288,7 +1288,7 @@ static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, case CURLOPT_ALTSVC_CTRL: return Curl_altsvc_ctrl(data, arg); #endif -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_GSSAPI_DELEGATION: s->gssapi_delegation = (unsigned char)arg & (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); diff --git a/lib/urldata.h b/lib/urldata.h index 335e61c4a39d..2889e936a4ae 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1191,9 +1191,8 @@ struct UserDefined { uint8_t ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ uint8_t upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ -#ifdef HAVE_GSSAPI - /* GSS-API credential delegation, see the documentation of - CURLOPT_GSSAPI_DELEGATION */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS-API/SSPI credential delegation, see CURLOPT_GSSAPI_DELEGATION */ uint8_t gssapi_delegation; #endif uint8_t http_follow_mode; /* follow HTTP redirects */ diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 1baf59320a37..eeae02148416 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -223,15 +223,21 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(nego->token_max); /* Generate our challenge-response message */ - nego->status = - Curl_pSecFn->InitializeSecurityContext(nego->credentials, - chlg ? nego->context : NULL, - nego->spn, - ISC_REQ_CONFIDENTIALITY, - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, - 0, nego->context, - &resp_desc, &attrs, NULL); + { + DWORD sspi_flags = ISC_REQ_CONFIDENTIALITY; + if(data->set.gssapi_delegation & (CURLGSSAPI_DELEGATION_FLAG | + CURLGSSAPI_DELEGATION_POLICY_FLAG)) + sspi_flags |= ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH; + nego->status = + Curl_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : NULL, + nego->spn, + sspi_flags, + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, + 0, nego->context, + &resp_desc, &attrs, NULL); + } /* Free the decoded challenge as it is not required anymore */ curlx_free(chlg); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 12f6bfbc0cc7..8dcf2d360c94 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -287,7 +287,7 @@ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 \ \ -test3300 test3301 \ +test3300 test3301 test3302 \ \ test4000 test4001 diff --git a/tests/data/test3302 b/tests/data/test3302 new file mode 100644 index 000000000000..35ccfc79af00 --- /dev/null +++ b/tests/data/test3302 @@ -0,0 +1,19 @@ + + + + +unittest +CURLOPT_GSSAPI_DELEGATION + + + +# Client-side + + +unittest + + +CURLOPT_GSSAPI_DELEGATION stores flags in data->set on GSS-API and SSPI builds + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index 102c15f3b288..f0ce3d4eefaa 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -47,4 +47,4 @@ TESTS_C = \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ - unit3300.c unit3301.c + unit3300.c unit3301.c unit3302.c diff --git a/tests/unit/unit3302.c b/tests/unit/unit3302.c new file mode 100644 index 000000000000..6c0abed90caa --- /dev/null +++ b/tests/unit/unit3302.c @@ -0,0 +1,89 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" +#include "urldata.h" + +static CURLcode test_unit3302(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + struct Curl_easy *easy; + CURLcode result; + + curl_global_init(CURL_GLOBAL_ALL); + easy = curl_easy_init(); + if(!easy) { + curl_global_cleanup(); + goto unit_test_abort; /* OOM during setup, not a test failure */ + } + + /* CURLGSSAPI_DELEGATION_FLAG must be stored */ + result = curl_easy_setopt(easy, CURLOPT_GSSAPI_DELEGATION, + CURLGSSAPI_DELEGATION_FLAG); + fail_unless(result == CURLE_OK, + "setopt DELEGATION_FLAG returned error"); + fail_unless(easy->set.gssapi_delegation == CURLGSSAPI_DELEGATION_FLAG, + "DELEGATION_FLAG not stored in data->set"); + + /* CURLGSSAPI_DELEGATION_POLICY_FLAG must be stored */ + result = curl_easy_setopt(easy, CURLOPT_GSSAPI_DELEGATION, + CURLGSSAPI_DELEGATION_POLICY_FLAG); + fail_unless(result == CURLE_OK, + "setopt DELEGATION_POLICY_FLAG returned error"); + fail_unless(easy->set.gssapi_delegation == CURLGSSAPI_DELEGATION_POLICY_FLAG, + "DELEGATION_POLICY_FLAG not stored in data->set"); + + /* both flags together */ + result = curl_easy_setopt(easy, CURLOPT_GSSAPI_DELEGATION, + CURLGSSAPI_DELEGATION_FLAG | + CURLGSSAPI_DELEGATION_POLICY_FLAG); + fail_unless(result == CURLE_OK, + "setopt both flags returned error"); + fail_unless(easy->set.gssapi_delegation == + (CURLGSSAPI_DELEGATION_FLAG | CURLGSSAPI_DELEGATION_POLICY_FLAG), + "both delegation flags not stored in data->set"); + + /* CURLGSSAPI_DELEGATION_NONE must clear the field */ + result = curl_easy_setopt(easy, CURLOPT_GSSAPI_DELEGATION, + CURLGSSAPI_DELEGATION_NONE); + fail_unless(result == CURLE_OK, + "setopt DELEGATION_NONE returned error"); + fail_unless(easy->set.gssapi_delegation == 0, + "gssapi_delegation not cleared by DELEGATION_NONE"); + + /* unknown bits must be masked off */ + result = curl_easy_setopt(easy, CURLOPT_GSSAPI_DELEGATION, 0xFFL); + fail_unless(result == CURLE_OK, + "setopt 0xFF returned error"); + fail_unless(easy->set.gssapi_delegation == + (CURLGSSAPI_DELEGATION_FLAG | CURLGSSAPI_DELEGATION_POLICY_FLAG), + "unknown bits not masked off"); + + curl_easy_cleanup(easy); + curl_global_cleanup(); +#endif /* HAVE_GSSAPI || USE_WINDOWS_SSPI */ + + UNITTEST_END_SIMPLE +} From cb4395b4031679c91f0a53286dc584d5f53d7b33 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 May 2026 11:56:16 +0200 Subject: [PATCH 0076/1062] rtsp: bump buf after rtsp_filter_rtp() Reported-by: Andrew Nesbit Closes #21563 --- lib/rtsp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rtsp.c b/lib/rtsp.c index 8ba168cb5b7b..b39b8a740b76 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -887,6 +887,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed); if(result) goto out; + buf += consumed; blen -= consumed; } } From 82216163b11574f7d2dee155d75314ddbebd612c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 13:42:12 +0200 Subject: [PATCH 0077/1062] curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos Also rename macro to resemble other backends. Reported by Codex Security Fixes #21562 Follow-up to 01f08dc4eb20a19aa60230653715c8b839619cbb #21557 Closes #21566 --- lib/curl_ntlm_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 447ff64aeb8b..e6aab7e39e96 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -52,7 +52,7 @@ #ifdef USE_GNUTLS #include #if NETTLE_VERSION_MAJOR < 4 -#define HAVE_GNUTLS_DES +#define USE_GNUTLS_DES #endif #endif @@ -70,7 +70,7 @@ # include # define USE_WOLFSSL_DES -#elif defined(HAVE_GNUTLS_DES) +#elif defined(USE_GNUTLS_DES) # include # define USE_CURL_DES_SET_ODD_PARITY #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) @@ -182,7 +182,7 @@ static void setup_des_key(const unsigned char *key_56, Des *des) wc_Des_SetKey(des, key, NULL, 0); } -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -321,7 +321,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE); setup_des_key(keys + 14, &des); wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE); -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(keys, &des); des_encrypt(&des, 8, results, plaintext); @@ -374,7 +374,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE); setup_des_key(pw + 7, &des); wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE); -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(pw, &des); des_encrypt(&des, 8, lmbuffer, magic); From a32a2b0b77c190981e1198c6b89b92430e231c37 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 May 2026 12:49:30 +0200 Subject: [PATCH 0078/1062] GHA: (re-)enable SMB in a few builds Closes #21564 --- .github/workflows/linux.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4a58278d92cf..4a9fdcc28b34 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -75,7 +75,7 @@ jobs: - name: 'awslc' install_steps: awslc pytest LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib - configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm + configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm --enable-smb - name: 'awslc' install_packages: libidn2-dev @@ -266,7 +266,7 @@ jobs: - name: 'openssl !ipv6 !--libcurl !--digest-auth' image: ubuntu-24.04-arm - configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth --enable-ntlm + configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth --enable-ntlm --enable-smb - name: 'curl_global_init_mem debug valgrind' image: ubuntu-24.04-arm @@ -275,7 +275,7 @@ jobs: configure: >- --enable-init-mem-debug --with-openssl --disable-debug --enable-unity - --enable-ntlm + --enable-ntlm --enable-smb - name: 'openssl https-only' image: ubuntu-24.04-arm @@ -420,7 +420,7 @@ jobs: - name: 'event-based' install_packages: libssh-dev - configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl --enable-ntlm + configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl --enable-ntlm --enable-smb tflags: '-n --test-event --min=1420' - name: 'duphandle' From 8f71d0fde515aa4c68002477356c35bd79927729 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 11 May 2026 14:25:52 +0200 Subject: [PATCH 0079/1062] creds: hold credentials Authorizdation credentials are kept in `struct Curl_creds`. This contains: * `user`: the username, maybe the empty string * `passwd`: the password, maybe the empty string * `sasl_authzid`: the SASL authz value, maybe the empty string * `oauth_bearer`: the OAUTH bearer token, maybe the empty string * `source`: where the credentials from from * `refcount`: a reference counter to link/unkink creds A `creds` with all values empty is equivalent to NULL, e.g. no `creds` instance. With reference counting, `creds` can be linked/unlinked in several places. See docs/internals/CREDENTIALS.md for use. Closes #21548 --- docs/Makefile.am | 1 + docs/internals/CREDENTIALS.md | 75 ++++ lib/Makefile.inc | 2 + lib/connect.c | 3 +- lib/creds.c | 183 ++++++++++ lib/creds.h | 86 +++++ lib/curl_sasl.c | 110 +++--- lib/ftp.c | 5 +- lib/http.c | 118 +++--- lib/http_aws_sigv4.c | 6 +- lib/http_digest.c | 20 +- lib/http_negotiate.c | 32 +- lib/http_ntlm.c | 34 +- lib/imap.c | 7 +- lib/ldap.c | 11 +- lib/mqtt.c | 15 +- lib/netrc.c | 95 +++-- lib/netrc.h | 7 +- lib/openldap.c | 10 +- lib/pop3.c | 16 +- lib/rtsp.c | 6 - lib/smb.c | 20 +- lib/socks.c | 38 +- lib/socks.h | 4 +- lib/telnet.c | 7 +- lib/transfer.c | 42 +-- lib/transfer.h | 2 - lib/url.c | 671 +++++++++++++++------------------- lib/urldata.h | 45 +-- lib/vauth/cleartext.c | 19 +- lib/vauth/cram.c | 11 +- lib/vauth/digest.c | 22 +- lib/vauth/digest_sspi.c | 55 +-- lib/vauth/gsasl.c | 7 +- lib/vauth/krb5_gssapi.c | 6 +- lib/vauth/krb5_sspi.c | 8 +- lib/vauth/ntlm.c | 11 +- lib/vauth/ntlm_sspi.c | 14 +- lib/vauth/oauth2.c | 18 +- lib/vauth/spnego_gssapi.c | 6 +- lib/vauth/spnego_sspi.c | 8 +- lib/vauth/vauth.c | 18 +- lib/vauth/vauth.h | 37 +- lib/vssh/libssh.c | 13 +- lib/vssh/libssh2.c | 36 +- tests/libtest/lib1978.c | 1 - tests/unit/unit1304.c | 139 +++---- 47 files changed, 1138 insertions(+), 962 deletions(-) create mode 100644 docs/internals/CREDENTIALS.md create mode 100644 lib/creds.c create mode 100644 lib/creds.h diff --git a/docs/Makefile.am b/docs/Makefile.am index 01c223ccb397..fdc4511b5cab 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -53,6 +53,7 @@ INTERNALDOCS = \ internals/CLIENT-WRITERS.md \ internals/CODE_STYLE.md \ internals/CONNECTION-FILTERS.md \ + internals/CREDENTIALS.md \ internals/CURLX.md \ internals/DYNBUF.md \ internals/HASH.md \ diff --git a/docs/internals/CREDENTIALS.md b/docs/internals/CREDENTIALS.md new file mode 100644 index 000000000000..95c12cb47cc1 --- /dev/null +++ b/docs/internals/CREDENTIALS.md @@ -0,0 +1,75 @@ + + +# curl `creds` + +Authorization credentials are kept in `struct Curl_creds`. This contains: + +* `user`: the username, maybe the empty string +* `passwd`: the password, maybe the empty string +* `sasl_authzid`: the SASL `authz` value, maybe the empty string +* `oauth_bearer`: the OAUTH bearer token, maybe the empty string +* `source`: where the credentials from +* `refcount`: a reference counter to link/unlink `creds` + +A `creds` with all values empty is equivalent to NULL, e.g. no `creds` +instance. With reference counting, `creds` can be linked in several places. + +Two `creds` are the same if all values are equal apart from `source` +and `refcount`. The comparison of strings is done via `Curl_timestrcmp()` +to prevent side channel attacks. + +## `creds` locations + +Credentials are kept in three places: + +* `data->state.creds`: the credentials to use for the transfer in talking + to the `origin` (see PEERS) +* `conn->creds`: the credentials tied to a connection (more below) +* `conn->*_proxy.creds`: credentials used to talk to the `conn->*_proxy.peer` + +### `data->state.creds` + +This `creds` instance is created when the transfer starts looking for a +suitable connection. For an `easy_perform()` this may happen several times +if, for example, http redirects are followed. + +When an `easy_perform()` starts, the transfer's `data->state.initial_origin` +peer is cleared. When creating the connection, `conn->origin` is calculated +(e.g. who the request talks to). If `data->state.initial_origin` is not +set, the first `conn->origin` is linked there. Now `libcurl` knows where +the transfer initially talked to on all possible subsequent requests. + +Credential information from `CURLOPT_*` settings is only applicable for the +initial origin. Any followup request going to another origin must not +use it. Therefore `data->state.creds` is *only* created from `CURLOPT_*` +when current origin and initial origin match. + +Without credentials from `CURLOPT_*`, the URL is inspected for user and +password and `netrc` is consulted as well (when built in). + +### `conn->creds` + +Once `data->state.creds` is known, the connection credentials are +determined. For protocols that tie authorization to everything send +on a connection (protocols without flag `PROTOPT_CREDSPERREQUEST`), +`conn->creds` is linked to `data->state.creds`. Only connections +carrying the same credentials may be reused. + +Protocol with flag `PROTOPT_CREDSPERREQUEST` leave `conn->creds` empty, +as connections for such protocols may be reused with different +credentials. + +That being said, there are authentication schemes like `NTLM` and +`NEGOTIATE` that tie credentials to a connection. Those do set `conn->creds` +once they start to operate, preventing connection reuse from then on +for transfers with different credentials. + +### `conn->*_proxy.creds` + +Those are set during connection setup from the `CURLOPT_*` values. They +do not require any "initial origin" handling as the origin of a proxy +does not change for a transfer. diff --git a/lib/Makefile.inc b/lib/Makefile.inc index f1b0ef8f0d93..2c7259af0dd4 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -161,6 +161,7 @@ LIB_CFILES = \ connect.c \ content_encoding.c \ cookie.c \ + creds.c \ cshutdn.c \ curl_addrinfo.c \ curl_endian.c \ @@ -293,6 +294,7 @@ LIB_HFILES = \ connect.h \ content_encoding.h \ cookie.h \ + creds.h \ curl_addrinfo.h \ curl_ctype.h \ curl_endian.h \ diff --git a/lib/connect.c b/lib/connect.c index c36a7e0381d8..e0f93d3c46e0 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -387,8 +387,7 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, result = Curl_cf_socks_proxy_insert_after( cf, data, dest, cf->conn->ip_version, cf->conn->socks_proxy.proxytype, - cf->conn->socks_proxy.user, - cf->conn->socks_proxy.passwd); + cf->conn->socks_proxy.creds); CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", dest->hostname, dest->port, result); diff --git a/lib/creds.c b/lib/creds.c new file mode 100644 index 000000000000..fe8693a97ef5 --- /dev/null +++ b/lib/creds.c @@ -0,0 +1,183 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include /* for offsetof() */ + +#include "creds.h" +#include "curl_trc.h" +#include "strcase.h" +#include "urldata.h" + + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *sasl_authzid, + const char *oauth_bearer, + uint8_t source, + struct Curl_creds **pcreds) +{ + struct Curl_creds *creds = NULL; + size_t ulen = user ? strlen(user) : 0; + size_t plen = passwd ? strlen(passwd) : 0; + size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; + size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; + char *s, *buf; + CURLcode result = CURLE_OK; + + Curl_creds_unlink(pcreds); + + /* Everything empty/NULL, this is the NULL credential */ + if(!ulen && !plen && !salen && !olen) + goto out; + + if((ulen > CURL_MAX_INPUT_LENGTH) || + (plen > CURL_MAX_INPUT_LENGTH) || + (salen > CURL_MAX_INPUT_LENGTH) || + (olen > CURL_MAX_INPUT_LENGTH)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* NUL terminator for user already part of struct */ + creds = curlx_calloc(1, sizeof(*creds) + + ulen + plen + 1 + salen + 1 + olen + 1); + if(!creds) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + creds->refcount = 1; + creds->source = source; + /* Some compilers try to be too smart about our dynamic struct size */ + buf = ((char *)creds) + offsetof(struct Curl_creds, buf); + creds->user = s = buf; + if(ulen) + memcpy(s, CURL_UNCONST(user), ulen + 1); + creds->passwd = s = buf + ulen + 1; + if(plen) + memcpy(s, CURL_UNCONST(passwd), plen + 1); + creds->sasl_authzid = s = buf + ulen + 1 + plen + 1; + if(salen) + memcpy(s, CURL_UNCONST(sasl_authzid), salen + 1); + creds->oauth_bearer = s = buf + ulen + 1 + plen + 1 + salen + 1; + if(olen) + memcpy(s, CURL_UNCONST(oauth_bearer), olen + 1); + +out: + if(!result) + *pcreds = creds; + else + Curl_creds_unlink(&creds); + return result; +} + +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out) +{ + struct Curl_creds *creds_out = NULL; + CURLcode result; + + if(!user || !user[0]) + user = Curl_creds_user(creds_in); + if(!passwd || !passwd[0]) + passwd = Curl_creds_passwd(creds_in); + result = Curl_creds_create(user, passwd, + Curl_creds_sasl_authzid(creds_in), + Curl_creds_oauth_bearer(creds_in), + source, &creds_out); + Curl_creds_link(pcreds_out, creds_out); + Curl_creds_unlink(&creds_out); + return result; +} + +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src) +{ + if(*pdest != src) { + Curl_creds_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_creds_unlink(struct Curl_creds **pcreds) +{ + if(*pcreds) { + struct Curl_creds *creds = *pcreds; + + DEBUGASSERT(creds->refcount); + *pcreds = NULL; + if(creds->refcount) + creds->refcount--; + if(!creds->refcount) { + curlx_free(creds); + } + } +} + +bool Curl_creds_same_user(struct Curl_creds *creds, const char *user) +{ + return creds && !Curl_timestrcmp(creds->user, user); +} + +bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd) +{ + return creds && !Curl_timestrcmp(creds->passwd, passwd); +} + +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return (c1 == c2) || + (c1 && c2 && + !Curl_timestrcmp(c1->user, c2->user) && + !Curl_timestrcmp(c1->passwd, c2->passwd) && + !Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) && + !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer)); +} + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg) +{ + if(creds) { + CURL_TRC_M(data, "%s: user=%s, passwd=%s, " + "sasl_authzid=%s, oauth_bearer=%s, source=%d", + msg, + Curl_creds_user(creds), + Curl_creds_has_passwd(creds) ? "***" : "", + Curl_creds_sasl_authzid(creds), + Curl_creds_oauth_bearer(creds), + creds->source); + } + else + CURL_TRC_M(data, "%s: -", msg); +} + +#endif diff --git a/lib/creds.h b/lib/creds.h new file mode 100644 index 000000000000..2eb5998cc85a --- /dev/null +++ b/lib/creds.h @@ -0,0 +1,86 @@ +#ifndef HEADER_CURL_CREDS_H +#define HEADER_CURL_CREDS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; + +#define CREDS_NONE 0 /* used for default username/passwd */ +#define CREDS_URL 1 /* username/passwd from URL */ +#define CREDS_OPTION 2 /* username/passwd set with a CURLOPT_ */ +#define CREDS_NETRC 3 /* username/passwd found in netrc */ + +struct Curl_creds { + const char *user; /* non-NULL, maybe empty string */ + const char *passwd; /* non-NULL, maybe empty string */ + const char *sasl_authzid; /* non-NULL, maybe empty string */ + const char *oauth_bearer; /* non-NULL, maybe empty string */ + uint32_t refcount; + uint8_t source; /* CREDS_* value */ + char buf[1]; +}; + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *sasl_authzid, + const char *oauth_bearer, + uint8_t source, + struct Curl_creds **pcreds); + +/* Create credentials by overriding `user` and/or `passwd` in `creds_in` */ +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out); + +/* Unlink any creds in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src); + +/* Drop a reference, creds may be passed as NULL */ +void Curl_creds_unlink(struct Curl_creds **pcreds); + +/* TRUE if both creds are NULL or have same username and password. */ +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); +bool Curl_creds_same_user(struct Curl_creds *creds, const char *user); +bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd); + + +/* Provides properties for creds or, if creds is NULL, the empty string */ +#define Curl_creds_has_user(c) ((c) && (c)->user[0]) +#define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) +#define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) +#define Curl_creds_user(c) ((c)? (c)->user : "") +#define Curl_creds_passwd(c) ((c)? (c)->passwd : "") +#define Curl_creds_sasl_authzid(c) ((c)? (c)->sasl_authzid : "") +#define Curl_creds_oauth_bearer(c) ((c)? (c)->oauth_bearer : "") + + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg); +#endif + +#endif /* HEADER_CURL_CREDS_H */ diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 00eff8e6a999..6c955446fea8 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -276,7 +276,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) { /* Have credentials been provided? */ - if(data->conn->user[0]) + if(data->conn->creds) return TRUE; /* EXTERNAL can authenticate without a username and/or password */ @@ -299,13 +299,15 @@ struct sasl_ctx { static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) { - if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { + if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && + !Curl_creds_has_passwd(sctx->conn->creds)) { sctx->mech = SASL_MECH_STRING_EXTERNAL; sctx->state1 = SASL_EXTERNAL; sctx->sasl->authused = SASL_MECH_EXTERNAL; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_external_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_external_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -316,7 +318,7 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) { if((sctx->enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && - Curl_auth_user_contains_domain(sctx->conn->user)) { + Curl_auth_user_contains_domain(sctx->conn->creds)) { const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : sctx->sasl->params->service; @@ -330,8 +332,7 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn); sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_gssapi_user_message(data, sctx->conn->creds, service, sctx->conn->origin->hostname, (bool)sctx->sasl->mutual_auth, @@ -375,8 +376,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) Curl_bufref_init(&nullmsg); sctx->state1 = SASL_GSASL; sctx->state2 = SASL_GSASL; - sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user, - sctx->conn->passwd, gsasl); + sctx->result = Curl_auth_gsasl_start(data, sctx->conn->creds, gsasl); if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); return TRUE; @@ -427,9 +427,7 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE); sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_ntlm_type1_message(data, sctx->conn->creds, service, hostname, ntlm, &sctx->resp); } @@ -441,11 +439,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + if(Curl_creds_has_oauth_bearer(data->state.creds) && + (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { const char *hostname; int port; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -457,9 +452,8 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_oauth_bearer_message(sctx->conn->user, - hostname, port, - oauth_bearer, &sctx->resp); + Curl_auth_create_oauth_bearer_message(sctx->conn->creds, + hostname, port, &sctx->resp); return TRUE; } return FALSE; @@ -467,19 +461,15 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { + if(Curl_creds_has_oauth_bearer(sctx->conn->creds) && + (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { sctx->mech = SASL_MECH_STRING_XOAUTH2; sctx->state1 = SASL_OAUTH2; sctx->sasl->authused = SASL_MECH_XOAUTH2; if(sctx->sasl->force_ir || data->set.sasl_ir) - sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user, - oauth_bearer, - &sctx->resp); + sctx->result = Curl_auth_create_xoauth_bearer_message( + sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -494,9 +484,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_plain_message(sctx->conn->sasl_authzid, - sctx->conn->user, sctx->conn->passwd, - &sctx->resp); + Curl_auth_create_plain_message(sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -511,7 +499,8 @@ static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) sctx->sasl->authused = SASL_MECH_LOGIN; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_login_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_login_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -606,7 +595,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, data->set.str[STRING_SERVICE_NAME] : sasl->params->service; #endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -634,18 +622,17 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, &resp); + result = Curl_auth_create_plain_message(conn->creds, &resp); break; case SASL_LOGIN: - Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(Curl_creds_user(conn->creds), &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - Curl_auth_create_login_message(conn->passwd, &resp); + Curl_auth_create_login_message(Curl_creds_passwd(conn->creds), &resp); break; case SASL_EXTERNAL: - Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(Curl_creds_user(conn->creds), &resp); break; #ifdef USE_GSASL case SASL_GSASL: @@ -663,15 +650,15 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, - conn->passwd, &resp); + result = Curl_auth_create_cram_md5_message(&serverdata, conn->creds, + &resp); break; case SASL_DIGESTMD5: result = get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_create_digest_md5_message(data, &serverdata, - conn->user, conn->passwd, - service, &resp); + conn->creds, service, + &resp); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) newstate = SASL_DIGESTMD5_RESP; break; @@ -685,8 +672,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, /* Create the type-1 message */ struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, + Curl_auth_create_ntlm_type1_message(data, conn->creds, service, hostname, ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; @@ -700,9 +686,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!result) result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); if(!result) - result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, ntlm, - &resp); + result = Curl_auth_create_ntlm_type3_message(data, conn->creds, + ntlm, &resp); break; } #endif @@ -711,7 +696,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_GSSAPI: { struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, + Curl_auth_create_gssapi_user_message(data, conn->creds, service, conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); @@ -727,7 +712,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, else if(sasl->mutual_auth) { /* Decode the user token challenge and create the optional response message */ - result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, + result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, (bool)sasl->mutual_auth, &serverdata, @@ -736,10 +721,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else /* Decode the security challenge and create the response message */ - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; case SASL_GSSAPI_NO_DATA: @@ -750,10 +734,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!krb5) result = CURLE_OUT_OF_MEMORY; else - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; #endif @@ -761,18 +744,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_OAUTH2: /* Create the authorization message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { - result = Curl_auth_create_oauth_bearer_message(conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->creds, hostname, port, - oauth_bearer, &resp); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ newstate = SASL_OAUTH2_RESP; } else - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, + result = Curl_auth_create_xoauth_bearer_message(conn->creds, &resp); break; @@ -862,7 +843,7 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, else { if(param_missing) infof(data, "SASL: %s is missing %s", mname, param_missing); - if(!data->conn->user[0]) + if(!Curl_creds_has_user(data->conn->creds)) infof(data, "SASL: %s is missing username", mname); } } @@ -904,7 +885,8 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) "auth mechanisms"); else { infof(data, "SASL: no auth mechanism offered could be selected"); - if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0]) + if((enabledmechs & SASL_MECH_EXTERNAL) && + Curl_creds_has_passwd(data->conn->creds)) infof(data, "SASL: auth EXTERNAL not chosen with password"); sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs, CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL); @@ -919,10 +901,10 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs, CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL); sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); } #endif /* CURLVERBOSE */ diff --git a/lib/ftp.c b/lib/ftp.c index 3f55f68d8287..68c1a46ca5dc 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -743,7 +743,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) { ftpc->ftp_trying_alternative = FALSE; ftp_state(data, ftpc, FTP_USER); @@ -2941,7 +2941,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ - result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd); + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", + Curl_creds_passwd(data->conn->creds)); if(!result) ftp_state(data, ftpc, FTP_PASS); } diff --git a/lib/http.c b/lib/http.c index edca1dc1eaf5..9cb8b17b347c 100644 --- a/lib/http.c +++ b/lib/http.c @@ -250,14 +250,14 @@ char *Curl_copy_header_value(const char *header) * * Returns CURLcode. */ -static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) +static CURLcode http_output_basic(struct Curl_easy *data, + struct connectdata *conn, bool proxy) { size_t size = 0; char *authorization = NULL; char **p_hd; - const char *user; - const char *pwd; CURLcode result; + struct Curl_creds *creds = NULL; char *out; /* credentials are unique per transfer for HTTP, do not use the ones for the @@ -265,19 +265,23 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY p_hd = &data->req.hd_proxy_auth; - user = data->state.aptr.proxyuser; - pwd = data->state.aptr.proxypasswd; + creds = conn->http_proxy.creds; #else + (void)conn; return CURLE_NOT_BUILT_IN; #endif } else { p_hd = &data->req.hd_auth; - user = data->state.aptr.user; - pwd = data->state.aptr.passwd; + creds = data->state.creds; } - out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + if(!creds) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + + out = curl_maprintf("%s:%s", creds->user, creds->passwd); if(!out) return CURLE_OUT_OF_MEMORY; @@ -320,10 +324,11 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; + DEBUGASSERT(Curl_creds_has_oauth_bearer(data->state.creds)); userp = &data->req.hd_auth; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); + Curl_creds_oauth_bearer(data->state.creds)); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -527,10 +532,10 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) * Either we are not authenticating, or we are supposed to be authenticating * something else. This is an error. */ - if((httpcode == 401) && !data->state.aptr.user) + if((httpcode == 401) && !data->state.creds) return TRUE; #ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + if((httpcode == 407) && !data->conn->http_proxy.creds) return TRUE; #endif @@ -551,7 +556,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) CURLcode result = CURLE_OK; unsigned long authmask = ~0UL; - if(!data->set.str[STRING_BEARER]) + if(!Curl_creds_has_oauth_bearer(data->state.creds)) authmask &= (unsigned long)~CURLAUTH_BEARER; if(100 <= data->req.httpcode && data->req.httpcode <= 199) @@ -561,7 +566,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authproblem) return data->set.http_fail_on_error ? CURLE_HTTP_RETURNED_ERROR : CURLE_OK; - if((data->state.aptr.user || data->set.str[STRING_BEARER]) && + if(data->state.creds && ((data->req.httpcode == 401) || (data->req.authneg && data->req.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost, authmask); @@ -578,7 +583,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) } } #ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_user_passwd && + if(conn->http_proxy.creds && ((data->req.httpcode == 407) || (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, @@ -694,14 +699,14 @@ static CURLcode output_auth_headers(struct Curl_easy *data, /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && + (proxy && conn->http_proxy.creds && !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && + (!proxy && data->state.creds && !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; - result = http_output_basic(data, proxy); + result = http_output_basic(data, conn, proxy); if(result) return result; } @@ -714,8 +719,7 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if(!proxy && data->set.str[STRING_BEARER] && - Curl_auth_allowed_to_host(data) && + if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); @@ -737,15 +741,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data, data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, - proxy ? (data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : "") : - (data->state.aptr.user ? - data->state.aptr.user : "")); + proxy ? (conn->http_proxy.creds ? + conn->http_proxy.creds->user : "") : + (data->state.creds ? + data->state.creds->user : "")); #else (void)proxy; infof(data, "Server auth using %s with user '%s'", - auth, data->state.aptr.user ? - data->state.aptr.user : ""); + auth, data->state.creds ? + data->state.creds->user : ""); #endif authstatus->multipass = !authstatus->done; } @@ -780,14 +784,13 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, if( #ifndef CURL_DISABLE_PROXY - (!conn->bits.httpproxy || !conn->bits.proxy_user_passwd) && + (!conn->bits.httpproxy || !conn->http_proxy.creds) && #endif - !data->state.aptr.user && #ifdef USE_SPNEGO !(authhost->want & CURLAUTH_NEGOTIATE) && !(authproxy->want & CURLAUTH_NEGOTIATE) && #endif - !data->set.str[STRING_BEARER]) { + !data->state.creds) { /* no authentication with no user or password */ authhost->done = TRUE; authproxy->done = TRUE; @@ -832,13 +835,9 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, with it */ authproxy->done = TRUE; - /* To prevent the user+password to get sent to other than the original host - due to a location-follow */ - if(Curl_auth_allowed_to_host(data) -#ifndef CURL_DISABLE_NETRC - || conn->bits.netrc -#endif - ) + /* Either we have credentials for the origin we talk to or + performing authentication is allowed here */ + if(data->state.creds || Curl_auth_allowed_to_host(data)) result = output_auth_headers(data, conn, authhost, request, path_and_query, FALSE); else @@ -1227,8 +1226,6 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return CURLE_OUT_OF_MEMORY; } else { - bool same_origin; - CURLcode result; CURLU *u = curl_url(); if(!u) return CURLE_OUT_OF_MEMORY; @@ -1242,29 +1239,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return Curl_uc_to_curlcode(uc); } - same_origin = Curl_url_same_origin(u, data->state.uh); - curl_url_cleanup(u); - #ifndef CURL_DISABLE_DIGEST_AUTH - if(!same_origin) - Curl_auth_digest_cleanup(&data->state.digest); -#endif - - if((!same_origin && !data->set.allow_auth_to_other_hosts) || - !data->set.str[STRING_USERNAME]) { - result = Curl_reset_userpwd(data); - if(result) { - curlx_free(follow_url); - return result; - } - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); - } - result = Curl_reset_proxypwd(data); - if(result) { - curlx_free(follow_url); - return result; + { + bool same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); } +#else + curl_url_cleanup(u); +#endif } DEBUGASSERT(follow_url); @@ -2005,9 +1989,6 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr; - if(!data->state.this_is_a_follow) - Curl_peer_link(&data->state.first_origin, conn->origin); - curlx_safefree(aptr->host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); @@ -2015,7 +1996,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || - Curl_peer_equal(data->state.first_origin, conn->origin))) { + Curl_peer_equal(data->state.initial_origin, conn->origin))) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2138,6 +2119,19 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } + else if(data->state.creds && (data->state.creds->source != CREDS_URL)) { + /* credentials not from the URL need to be set */ + uc = curl_url_set(h, CURLUPART_USER, + data->state.creds->user, CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(h, CURLUPART_PASSWORD, + data->state.creds->passwd, CURLU_URLENCODE); + if(uc) { + curl_url_cleanup(h); + return Curl_uc_to_curlcode(uc); + } + } + /* Extract the URL to use in the request. */ uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); if(uc) { diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 5761acae5fe1..f77f7a088c07 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -848,7 +848,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) char *request_type = NULL; char *credential_scope = NULL; char *str_to_sign = NULL; - const char *user = data->state.aptr.user ? data->state.aptr.user : ""; + const char *user = Curl_creds_user(data->state.creds); + const char *passwd = Curl_creds_passwd(data->state.creds); char *secret = NULL; unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; @@ -1068,8 +1069,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) str_to_sign); secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + curlx_str(&provider0), passwd); if(!secret) goto fail; /* make provider0 part done uppercase */ diff --git a/lib/http_digest.c b/lib/http_digest.c index e87fb362ed66..d20e16562354 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -77,8 +77,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, char **allocuserpwd; /* Point to the name and password for this */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; /* Point to the correct struct with this */ struct digestdata *digest; @@ -90,28 +89,19 @@ CURLcode Curl_output_digest(struct Curl_easy *data, #else digest = &data->state.proxydigest; allocuserpwd = &data->req.hd_proxy_auth; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + creds = data->conn->http_proxy.creds; authp = &data->state.authproxy; #endif } else { digest = &data->state.digest; allocuserpwd = &data->req.hd_auth; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + creds = data->state.creds; authp = &data->state.authhost; } curlx_safefree(*allocuserpwd); - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI have_chlg = !!digest->input_token; #else @@ -123,8 +113,8 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - result = Curl_auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, + result = Curl_auth_create_digest_http_message(data, creds, request, + uripath, digest, &response, &len); if(result) return result; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index b037bb2ec904..d987b8b9d1d4 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -40,8 +40,10 @@ static void http_auth_nego_reset(struct connectdata *conn, { if(proxy) conn->proxy_negotiate_state = GSS_AUTHNONE; - else + else { conn->http_negotiate_state = GSS_AUTHNONE; + Curl_creds_unlink(&conn->creds); + } if(neg_ctx) Curl_auth_cleanup_spnego(neg_ctx); } @@ -53,8 +55,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, size_t len; /* Point to the username, password, service and host */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; const char *service; const char *host; @@ -64,8 +65,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = conn->http_proxy.user; - passwdp = conn->http_proxy.passwd; + creds = conn->http_proxy.creds; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.peer->hostname; @@ -75,8 +75,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif } else { - userp = conn->user; - passwdp = conn->passwd; + creds = data->state.creds; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->origin->hostname; @@ -87,13 +86,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(!neg_ctx) return CURLE_OUT_OF_MEMORY; - /* Not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - /* Obtain the input token, if any */ header += strlen("Negotiate"); curlx_str_passblanks(&header); @@ -135,7 +127,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, + result = Curl_auth_decode_spnego_message(data, creds, service, host, header, neg_ctx); #ifdef GSS_C_CHANNEL_BOUND_FLAG @@ -145,6 +137,16 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(result) http_auth_nego_reset(conn, neg_ctx, proxy); + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_creds_link(&conn->creds, creds); + } + return result; } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 0240251a5f6a..1a02a0fd867a 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -122,9 +122,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) server, which is for a plain host or for an HTTP proxy */ char **allocuserpwd; - /* point to the username, password, service and host */ - const char *userp; - const char *passwdp; + /* point to credentials, service and host */ + struct Curl_creds *creds = NULL; const char *service = NULL; const char *hostname = NULL; @@ -140,8 +139,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY allocuserpwd = &data->req.hd_proxy_auth; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + creds = conn->http_proxy.creds; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; hostname = conn->http_proxy.peer->hostname; @@ -153,26 +151,19 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) } else { allocuserpwd = &data->req.hd_auth; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + creds = data->state.creds; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; hostname = conn->origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; } + ntlm = Curl_auth_ntlm_get(conn, proxy); if(!ntlm) return CURLE_OUT_OF_MEMORY; authp->done = FALSE; - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ @@ -195,8 +186,16 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) switch(*state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ - /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_creds_link(&conn->creds, creds); + } + result = Curl_auth_create_ntlm_type1_message(data, creds, service, hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); @@ -215,8 +214,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ - result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, - ntlm, &ntlmmsg); + result = Curl_auth_create_ntlm_type3_message(data, creds, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); diff --git a/lib/imap.c b/lib/imap.c index 5ef2a2cb2189..7c73255e960b 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -597,15 +597,15 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { imap_state(data, imapc, IMAP_STOP); return result; } /* Make sure the username and password are in the correct atom format */ - user = imap_atom(conn->user, FALSE); - passwd = imap_atom(conn->passwd, FALSE); + user = imap_atom(Curl_creds_user(conn->creds), FALSE); + passwd = imap_atom(Curl_creds_passwd(conn->creds), FALSE); /* Send the LOGIN command */ result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "", @@ -712,7 +712,6 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, /* Calculate the SASL login details */ result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, &progress); - if(!result) { if(progress == SASL_INPROGRESS) imap_state(data, imapc, IMAP_AUTHENTICATE); diff --git a/lib/ldap.c b/lib/ldap.c index 3705754476d1..f476da4ea00d 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -252,8 +252,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *host = NULL; #endif - char *user = NULL; - char *passwd = NULL; + const char *user = Curl_creds_has_user(data->state.creds) ? + data->state.creds->user : NULL; + const char *passwd = Curl_creds_has_passwd(data->state.creds) ? + data->state.creds->passwd : NULL; struct ip_quadruple ipquad; bool is_ipv6; BerElement *ber = NULL; @@ -295,11 +297,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) host = conn->origin->hostname; #endif - if(data->state.aptr.user) { - user = conn->user; - passwd = conn->passwd; - } - #ifdef USE_WIN32_LDAP if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); diff --git a/lib/mqtt.c b/lib/mqtt.c index d28a25bb50dd..113790945416 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -280,11 +280,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data) char *packet = NULL; /* extracting username from request */ - const char *username = data->state.aptr.user ? data->state.aptr.user : ""; - const size_t ulen = strlen(username); - /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : ""; - const size_t plen = strlen(passwd); + struct Curl_creds *creds = data->state.creds; + const size_t ulen = creds ? strlen(creds->user) : 0; + const size_t plen = creds ? strlen(creds->passwd) : 0; const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 + /* The plus 2s below are for the MSB and LSB describing the length of the string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ @@ -326,7 +324,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) if(ulen) { start_pwd += 2; - rc = add_user(username, ulen, + rc = add_user(creds->user, ulen, (unsigned char *)packet, start_user, remain_pos); if(rc) { failf(data, "Username too long: [%zu]", ulen); @@ -337,7 +335,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) /* if passwd was provided, add it to the packet */ if(plen) { - rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); + rc = add_passwd(creds->passwd, plen, packet, start_pwd, remain_pos); if(rc) { failf(data, "Password too long: [%zu]", plen); result = CURLE_WEIRD_SERVER_REPLY; @@ -351,8 +349,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) end: if(packet) curlx_free(packet); - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); + Curl_creds_unlink(&data->state.creds); return result; } diff --git a/lib/netrc.c b/lib/netrc.c index 72d8feee7d94..76fd5541ce07 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -36,6 +36,7 @@ #endif #include "netrc.h" +#include "creds.h" #include "strcase.h" #include "curl_get_line.h" #include "curlx/fopen.h" @@ -108,6 +109,7 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) /* bundled parser state to keep function signatures compact */ struct netrc_state { + struct Curl_creds *existing; char *login; char *password; enum host_lookup_state state; @@ -116,7 +118,6 @@ struct netrc_state { unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ bool our_login; bool done; - bool specific_login; }; /* @@ -250,8 +251,7 @@ static void netrc_new_machine(struct netrc_state *ns) ns->found = 0; ns->our_login = FALSE; curlx_safefree(ns->password); - if(!ns->specific_login) - curlx_safefree(ns->login); + curlx_safefree(ns->login); } /* @@ -263,8 +263,8 @@ static void netrc_new_machine(struct netrc_state *ns) static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) { if(ns->keyword == LOGIN) { - if(ns->specific_login) - ns->our_login = !Curl_timestrcmp(ns->login, tok); + if(Curl_creds_has_user(ns->existing)) + ns->our_login = !Curl_timestrcmp(ns->existing->user, tok); else { ns->our_login = TRUE; curlx_free(ns->login); @@ -289,15 +289,15 @@ static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) ns->keyword = PASSWORD; else if(curl_strequal("machine", tok)) { /* a new machine here */ + bool specific_login = Curl_creds_has_user(ns->existing); - if(ns->found & FOUND_PASSWORD && + if((ns->found & FOUND_PASSWORD) && /* a password was provided for this host */ - - ((!ns->specific_login || ns->our_login) || - /* either there was no specific login to search for, or this - is the specific one we wanted */ - (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { - /* or we look for a specific login, but that was not specified */ + (!specific_login || ns->our_login || + /* and found a login that is suitable + (either matched specific one or simply present) */ + (specific_login && !(ns->found & FOUND_LOGIN)))) { + /* or we look for a specific login, but no login was not specified */ ns->done = TRUE; return NETRC_OK; @@ -361,38 +361,48 @@ static NETRCcode netrc_handle_token(struct netrc_state *ns, * resources on error. */ static NETRCcode netrc_finalize(struct netrc_state *ns, - char **loginp, - char **passwordp, - struct store_netrc *store) + struct store_netrc *store, + struct Curl_creds **pcreds) { NETRCcode retcode = ns->retcode; if(!retcode) { if(!ns->password && ns->our_login) { /* success without a password, set a blank one */ ns->password = curlx_strdup(""); - if(!ns->password) + if(!ns->password) { retcode = NETRC_OUT_OF_MEMORY; + goto out; + } } - else if(!ns->login && !ns->password) + else if(!ns->login && !ns->password) { /* a default with no credentials */ retcode = NETRC_NO_MATCH; + goto out; + } } - if(!retcode) { - /* success */ - if(!ns->specific_login) - *loginp = ns->login; - /* netrc_finalize() can return a password even when specific_login is set + if(!retcode) { + /* success + netrc_finalize() can return a password even when specific_login is set but our_login is false (e.g., host matched but the requested login never matched). See test 685. */ - *passwordp = ns->password; + const char *login = Curl_creds_has_user(ns->existing) ? + ns->existing->user : ns->login; + /* success without a password, set a blank one */ + const char *passwd = ns->password ? ns->password : ""; + + if(Curl_creds_create(login, passwd, NULL, NULL, CREDS_NETRC, pcreds)) { + retcode = NETRC_OUT_OF_MEMORY; + goto out; + } } - else { + +out: + curlx_free(ns->login); + curlx_free(ns->password); + if(retcode) { curlx_dyn_free(&store->filebuf); store->loaded = FALSE; - if(!ns->specific_login) - curlx_free(ns->login); - curlx_free(ns->password); } return retcode; } @@ -402,21 +412,20 @@ static NETRCcode netrc_finalize(struct netrc_state *ns, */ static NETRCcode parsenetrc(struct store_netrc *store, const char *host, - char **loginp, - char **passwordp, - const char *netrcfile) + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds) { const char *netrcbuffer; struct dynbuf token; struct dynbuf *filebuf = &store->filebuf; struct netrc_state ns; + DEBUGASSERT(!existing || !Curl_creds_has_passwd(existing)); memset(&ns, 0, sizeof(ns)); ns.retcode = NETRC_NO_MATCH; - ns.login = *loginp; - ns.specific_login = !!ns.login; + ns.existing = existing; - DEBUGASSERT(!*passwordp); curlx_dyn_init(&token, MAX_NETRC_TOKEN); if(!store->loaded) { @@ -466,7 +475,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, out: curlx_dyn_free(&token); - return netrc_finalize(&ns, loginp, passwordp, store); + return netrc_finalize(&ns, store, pcreds); } const char *Curl_netrc_strerror(NETRCcode ret) @@ -493,12 +502,14 @@ const char *Curl_netrc_strerror(NETRCcode ret) * in. */ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile) + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; char *filealloc = NULL; + Curl_creds_unlink(pcreds); if(!netrcfile) { char *home = NULL; char *homea = NULL; @@ -543,10 +554,11 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { curlx_free(homea); - return NETRC_OUT_OF_MEMORY; + retcode = NETRC_OUT_OF_MEMORY; + goto out; } } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, existing, filealloc, pcreds); curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -556,14 +568,17 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, curlx_free(homea); return NETRC_OUT_OF_MEMORY; } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, existing, filealloc, pcreds); curlx_free(filealloc); } #endif curlx_free(homea); } else - retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); + retcode = parsenetrc(store, host, existing, netrcfile, pcreds); +out: + if(retcode) + Curl_creds_unlink(pcreds); return retcode; } diff --git a/lib/netrc.h b/lib/netrc.h index 90318c2bd645..92dd4d47c9e6 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -29,6 +29,8 @@ #include "curlx/dynbuf.h" +struct Curl_creds; + struct store_netrc { struct dynbuf filebuf; char *filename; @@ -49,8 +51,9 @@ void Curl_netrc_init(struct store_netrc *store); void Curl_netrc_cleanup(struct store_netrc *store); NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile); + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds); /* Assume: (*passwordp)[0]=0, host[0] != 0. * If (*loginp)[0] = 0, search for login and password within a machine * section in the netrc. diff --git a/lib/openldap.c b/lib/openldap.c index 30e4bcc7521c..1ed72c1ea816 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -345,9 +345,9 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(data->state.aptr.user) { - binddn = conn->user; - passwd.bv_val = conn->passwd; + if(data->state.creds) { + binddn = Curl_creds_user(conn->creds); + passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds)); passwd.bv_len = strlen(passwd.bv_val); } @@ -355,7 +355,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) NULL, NULL, &li->msgid); if(rc != LDAP_SUCCESS) return oldap_map_error(rc, - data->state.aptr.user ? + data->state.creds ? CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND); oldap_state(data, li, newstate); return CURLE_OK; @@ -911,7 +911,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) else if(ssl_installed(conn)) { if(li->sasl.prefmech != SASL_AUTH_NONE) result = oldap_perform_mechs(data); - else if(data->state.aptr.user) + else if(data->state.creds) result = oldap_perform_bind(data, OLDAP_BIND); else { /* Version 3 supported: no bind required */ diff --git a/lib/pop3.c b/lib/pop3.c index 317c04bbe363..b7bbd765b916 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -527,7 +527,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -535,7 +535,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Send the USER command */ result = Curl_pp_sendf(data, &pop3c->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) pop3_state(data, POP3_USER); @@ -564,7 +564,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -578,8 +578,8 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp, curlx_uztoui(strlen(pop3c->apoptimestamp))); - Curl_MD5_update(ctxt, (const unsigned char *)conn->passwd, - curlx_uztoui(strlen(conn->passwd))); + Curl_MD5_update(ctxt, (const unsigned char *)Curl_creds_passwd(conn->creds), + curlx_uztoui(strlen(Curl_creds_passwd(conn->creds)))); /* Finalise the digest */ Curl_MD5_final(ctxt, digest); @@ -588,7 +588,8 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]); - result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); + result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", + Curl_creds_user(conn->creds), secret); if(!result) pop3_state(data, POP3_APOP); @@ -1038,7 +1039,8 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, } else /* Send the PASS command */ - result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", conn->passwd); + result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", + Curl_creds_passwd(conn->creds)); if(!result) pop3_state(data, POP3_PASS); diff --git a/lib/rtsp.c b/lib/rtsp.c index b39b8a740b76..60c09cbb4b0e 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -301,12 +301,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; - /* Setup the first_* fields to allow auth details get sent - to this origin */ - - if(!data->state.first_origin) - Curl_peer_link(&data->state.first_origin, conn->origin); - /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. diff --git a/lib/smb.c b/lib/smb.c index 8a75b2dbae27..a660f053ebb8 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -61,7 +61,7 @@ enum smb_conn_state { /* SMB connection data, kept at connection */ struct smb_conn { enum smb_conn_state state; - char *user; + const char *user; char *domain; char *share; unsigned char challenge[8]; @@ -468,13 +468,14 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN); char *slash; + const char *user = Curl_creds_user(conn->creds); (void)done; if(!smbc) return CURLE_FAILED_INIT; /* Check we have a username and password to authenticate with */ - if(!data->state.aptr.user) + if(!Curl_creds_has_user(data->state.creds)) return CURLE_LOGIN_DENIED; /* Initialize the connection state */ @@ -487,19 +488,19 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* Parse the username, domain, and password */ - slash = strchr(conn->user, '/'); + slash = strchr(user, '/'); if(!slash) - slash = strchr(conn->user, '\\'); + slash = strchr(user, '\\'); if(slash) { smbc->user = slash + 1; - smbc->domain = curlx_strdup(conn->user); + smbc->domain = curlx_strdup(user); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; - smbc->domain[slash - conn->user] = 0; + smbc->domain[slash - user] = 0; } else { - smbc->user = conn->user; + smbc->user = user; smbc->domain = curlx_strdup(conn->origin->hostname); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; @@ -670,6 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) unsigned char nt_hash[21]; unsigned char nt[24]; size_t byte_count; + const char *passwd = Curl_creds_passwd(conn->creds); if(!smbc || !req) return CURLE_FAILED_INIT; @@ -680,9 +682,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data) if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); + Curl_ntlm_core_mk_lm_hash(passwd, lm_hash); Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); - Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); + Curl_ntlm_core_mk_nt_hash(passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); diff --git a/lib/socks.c b/lib/socks.c index ed60063ab5d6..2d8a4f3ab6a9 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -99,8 +99,7 @@ struct socks_ctx { enum socks_state_t state; struct bufq iobuf; struct Curl_peer *dest; - const char *user; - const char *passwd; + struct Curl_creds *creds; CURLproxycode presult; uint32_t resolv_id; uint8_t ip_version; @@ -287,8 +286,8 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, CURLcode result; size_t nwritten; - if(sx->user) { - size_t plen = strlen(sx->user); + if(sx->creds) { + size_t plen = strlen(sx->creds->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -297,7 +296,7 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -603,7 +602,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->user = NULL; + Curl_creds_unlink(&sx->creds); req[0] = 5; /* version */ nauths = 1; @@ -614,7 +613,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->user) { + if(sx->creds) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -687,9 +686,9 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, unsigned char buf[2]; CURLcode result; - if(sx->user && sx->passwd) { - ulen = strlen(sx->user); - plen = strlen(sx->passwd); + if(sx->creds) { + ulen = strlen(sx->creds->user); + plen = strlen(sx->creds->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -714,7 +713,8 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, + &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -723,7 +723,8 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, + &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -1185,6 +1186,7 @@ static void socks_proxy_ctx_free(struct socks_ctx *ctx) { if(ctx) { Curl_peer_unlink(&ctx->dest); + Curl_creds_unlink(&ctx->creds); Curl_bufq_free(&ctx->iobuf); curlx_free(ctx); } @@ -1259,10 +1261,8 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, out: *done = (bool)cf->connected; - if(*done || result) { - ctx->user = NULL; - ctx->passwd = NULL; - } + if(*done || result) + Curl_creds_unlink(&ctx->creds); return result; } @@ -1361,8 +1361,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd) + struct Curl_creds *creds) { struct Curl_cfilter *cf; struct socks_ctx *ctx; @@ -1391,8 +1390,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; - ctx->user = user; - ctx->passwd = passwd; + Curl_creds_link(&ctx->creds, creds); Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, BUFQ_OPT_SOFT_LIMIT); diff --git a/lib/socks.h b/lib/socks.h index e17b761f1c81..fca10c833258 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -28,6 +28,7 @@ #ifndef CURL_DISABLE_PROXY struct Curl_peer; +struct Curl_creds; /* * Helper read-from-socket functions. Does the same as Curl_read() but it @@ -58,8 +59,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd); + struct Curl_creds *creds); extern struct Curl_cftype Curl_cft_socks_proxy; diff --git a/lib/telnet.c b/lib/telnet.c index c5ce9c2c97e3..d1faec87a87d 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -840,13 +840,14 @@ static CURLcode check_telnet_options(struct Curl_easy *data, /* Add the username as an environment variable if it was given on the command line */ - if(data->state.aptr.user) { + if(data->state.creds) { char buffer[256]; - if(str_is_nonascii(data->conn->user)) { + if(str_is_nonascii(Curl_creds_user(data->conn->creds))) { DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } - curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + curl_msnprintf(buffer, sizeof(buffer), "USER,%s", + Curl_creds_user(data->conn->creds)); beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); diff --git a/lib/transfer.c b/lib/transfer.c index fd1a903dabc8..721ad8d9cec7 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -438,40 +438,6 @@ void Curl_init_CONNECT(struct Curl_easy *data) data->state.upload = (data->state.httpreq == HTTPREQ_PUT); } -/* - * Restore the user credentials to those set in options. - */ -CURLcode Curl_reset_userpwd(struct Curl_easy *data) -{ - CURLcode result; - if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); - return result; -} - -/* - * Restore the proxy credentials to those set in options. - */ -CURLcode Curl_reset_proxypwd(struct Curl_easy *data) -{ -#ifndef CURL_DISABLE_PROXY - CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); - return result; -#else - (void)data; - return CURLE_OK; -#endif -} - /* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass @@ -524,6 +490,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #endif data->state.httpreq = data->set.method; + /* initial transfer request coming up, forget the initial origin + * from a previous perform() on this handle. */ + Curl_peer_unlink(&data->state.initial_origin); data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ @@ -625,11 +594,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } - if(!result) - result = Curl_reset_userpwd(data); - if(!result) - result = Curl_reset_proxypwd(data); - data->req.headerbytecount = 0; Curl_headers_cleanup(data); return result; diff --git a/lib/transfer.h b/lib/transfer.h index b29e70b9ec12..41ec0357f681 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -31,8 +31,6 @@ char *Curl_checkheaders(const struct Curl_easy *data, void Curl_init_CONNECT(struct Curl_easy *data); -CURLcode Curl_reset_userpwd(struct Curl_easy *data); -CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data); diff --git a/lib/url.c b/lib/url.c index ba662d6a0ba9..298e5478a2a1 100644 --- a/lib/url.c +++ b/lib/url.c @@ -249,7 +249,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - Curl_peer_unlink(&data->state.first_origin); + Curl_peer_unlink(&data->state.initial_origin); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -281,6 +281,7 @@ CURLcode Curl_close(struct Curl_easy **datap) DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); + Curl_creds_unlink(&data->state.creds); curlx_safefree(data->state.aptr.uagent); curlx_safefree(data->state.aptr.accept_encoding); curlx_safefree(data->state.aptr.rangeline); @@ -292,12 +293,6 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_RTSP curlx_safefree(data->state.aptr.rtsp_transport); #endif - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); -#ifndef CURL_DISABLE_PROXY - curlx_safefree(data->state.aptr.proxyuser); - curlx_safefree(data->state.aptr.proxypasswd); -#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); @@ -511,18 +506,13 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) } #ifndef CURL_DISABLE_PROXY - curlx_safefree(conn->http_proxy.user); - curlx_safefree(conn->socks_proxy.user); - curlx_safefree(conn->http_proxy.passwd); - curlx_safefree(conn->socks_proxy.passwd); Curl_peer_unlink(&conn->http_proxy.peer); Curl_peer_unlink(&conn->socks_proxy.peer); + Curl_creds_unlink(&conn->http_proxy.creds); + Curl_creds_unlink(&conn->socks_proxy.creds); #endif - curlx_safefree(conn->user); - curlx_safefree(conn->passwd); - curlx_safefree(conn->sasl_authzid); + Curl_creds_unlink(&conn->creds); curlx_safefree(conn->options); - curlx_safefree(conn->oauth_bearer); curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); @@ -567,11 +557,8 @@ static bool proxy_info_matches(const struct proxy_info *data, const struct proxy_info *needle) { if((data->proxytype == needle->proxytype) && - Curl_peer_same_destination(data->peer, needle->peer)) { - - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; + Curl_peer_same_destination(data->peer, needle->peer) && + Curl_creds_same(data->creds, needle->creds)) { return TRUE; } return FALSE; @@ -950,16 +937,11 @@ static bool url_match_proto_config(struct connectdata *conn, static bool url_match_auth(struct connectdata *conn, struct url_conn_match *m) { - if(!(m->needle->scheme->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we are using the same name and password as well */ - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd) || - Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) || - Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) { - /* one of them was different */ + if(!Curl_creds_same(m->needle->creds, conn->creds)) { + if(m->needle->creds) + return FALSE; + if(!Curl_creds_same(m->data->state.creds, conn->creds)) return FALSE; - } } #ifdef HAVE_GSSAPI /* GSS delegation differences do not actually affect every connection @@ -1030,8 +1012,7 @@ static bool url_match_auth_ntlm(struct connectdata *conn, possible. (Especially we must not reuse the same connection if partway through a handshake!) */ if(m->want_ntlm_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) { + if(!Curl_creds_same(m->data->state.creds, conn->creds)) { /* we prefer a credential match, but this is at least a connection that can be reused and "upgraded" to NTLM if it does not have any auth ongoing. */ @@ -1056,13 +1037,10 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(m->want_proxy_ntlm_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { @@ -1102,8 +1080,7 @@ static bool url_match_auth_nego(struct connectdata *conn, already authenticating with the right credentials. If not, keep looking so that we can reuse Negotiate connections if possible. */ if(m->want_nego_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) + if(!Curl_creds_same(m->needle->creds, conn->creds)) return FALSE; } else if(conn->http_negotiate_state != GSS_AUTHNONE) { @@ -1116,13 +1093,10 @@ static bool url_match_auth_nego(struct connectdata *conn, if(m->want_proxy_nego_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { @@ -1265,7 +1239,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NTLM) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1277,7 +1251,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_nego_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1339,7 +1313,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.socksproxy = TRUE; } - conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser; conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; #endif /* CURL_DISABLE_PROXY */ @@ -1452,13 +1425,76 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, #define hsts_upgrade(x, y, z, a, b) CURLE_OK #endif +static CURLcode url_set_data_creds(struct Curl_easy *data, + struct connectdata *conn, + CURLU *uh) +{ + CURLcode result = CURLE_OK; + + /* We reset any existing credentials on the transfer. Then + * set the CURLOPT_* credentials ONLY IF the origin is the initial one. */ + Curl_creds_unlink(&data->state.creds); + if((data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD] || + data->set.str[STRING_SASL_AUTHZID] || + data->set.str[STRING_BEARER]) && + (data->set.allow_auth_to_other_hosts || + Curl_peer_same_destination(data->state.initial_origin, conn->origin))) { + result = Curl_creds_create(data->set.str[STRING_USERNAME], + data->set.str[STRING_PASSWORD], + data->set.str[STRING_SASL_AUTHZID], + data->set.str[STRING_BEARER], + CREDS_OPTION, &data->state.creds); + if(result) + return result; + } + + /* Extract credentials from the URL only if there are none OR + * if no CURLOPT_USER was set. */ + if(!data->state.creds || !Curl_creds_has_user(data->state.creds)) { + char *udecoded = NULL; + char *pdecoded = NULL; + CURLUcode uc; + + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(uc && (uc != CURLUE_NO_USER)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(uc && (uc != CURLUE_NO_PASSWORD)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + if(data->state.up.user) { + result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, + conn->scheme->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + } + if(!result && data->state.up.password) { + result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, + conn->scheme->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + } + if(!result) + result = Curl_creds_merge(udecoded, pdecoded, data->state.creds, + CREDS_URL, &data->state.creds); +out: + curlx_free(udecoded); + curlx_free(pdecoded); + if(result) + failf(data, "error extracting credentials from URL"); + } + return result; +} + /* * Parse URL and fill in the relevant members of the connection struct. */ static CURLcode parseurlandfillconn(struct Curl_easy *data, struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; CURLU *uh; CURLUcode uc; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); @@ -1472,8 +1508,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uh = data->state.uh = curl_url_dup(data->set.uh); else uh = data->state.uh = curl_url(); - if(!uh) - return CURLE_OUT_OF_MEMORY; + if(!uh) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } /* Calculate the *real* URL this transfer uses, applying defaults * where information is missing. */ @@ -1482,8 +1520,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, char *url = curl_maprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], Curl_bufref_ptr(&data->state.url)); - if(!url) - return CURLE_OUT_OF_MEMORY; + if(!url) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } Curl_bufref_set(&data->state.url, url, 0, curl_free); } @@ -1497,13 +1537,16 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); if(uc) { failf(data, "URL rejected: %s", curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); + result = Curl_uc_to_curlcode(uc); + goto out; } /* after it was parsed, get the generated normalized version */ uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } Curl_bufref_set(&data->state.url, newurl, 0, curl_free); } @@ -1515,86 +1558,64 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, result = Curl_peer_from_url(uh, data, port_override, scope_id, &data->state.up, &conn->origin); if(result) - return result; + goto out; result = hsts_upgrade(data, conn, uh, port_override, scope_id); if(result) - return result; + goto out; /* now that the origin is fixed, check and set the connection scheme */ result = url_set_conn_scheme(data, conn, conn->origin->scheme); if(result) - return result; + goto out; - /* - * username and password set with their own options override the credentials - * possibly set in the URL, but netrc does not. - */ - if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->passwd = decoded; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; - data->state.creds_from = CREDS_URL; - } - else if(uc != CURLUE_NO_PASSWORD) - return Curl_uc_to_curlcode(uc); - } + /* When the transfers initial_origin is not set, this is the initial + * request. Remember this starting point. This is used to + * select credentials. */ + if(!data->state.initial_origin) + Curl_peer_link(&data->state.initial_origin, conn->origin); - if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { - /* we do not use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->user = decoded; - result = Curl_setstropt(&data->state.aptr.user, decoded); - data->state.creds_from = CREDS_URL; - } - else if(uc != CURLUE_NO_USER) - return Curl_uc_to_curlcode(uc); - if(result) - return result; - } + result = url_set_data_creds(data, conn, uh); + if(result) + goto out; uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, CURLU_URLDECODE); if(!uc) { conn->options = curlx_strdup(data->state.up.options); - if(!conn->options) - return CURLE_OUT_OF_MEMORY; + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(uc != CURLUE_NO_OPTIONS) { + result = Curl_uc_to_curlcode(uc); + goto out; } - else if(uc != CURLUE_NO_OPTIONS) - return Curl_uc_to_curlcode(uc); uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); - if(uc && (uc != CURLUE_NO_QUERY)) - return CURLE_OUT_OF_MEMORY; + if(uc && (uc != CURLUE_NO_QUERY)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef USE_IPV6 /* Fill in the conn parts that do not use authority, yet. */ conn->scope_id = conn->origin->scopeid; #endif - return CURLE_OK; +#ifdef CURLVERBOSE + Curl_creds_trace(data, data->state.creds, "transfer credentials"); +#endif + +out: + return result; } /* @@ -1770,14 +1791,15 @@ static char *url_detect_proxy(struct Curl_easy *data, * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, const char *proxy, - uint8_t proxytype) + const char *proxy, + bool for_pre_proxy, + struct proxy_info *proxyinfo) { char *proxyuser = NULL; char *proxypasswd = NULL; - struct proxy_info *proxyinfo = NULL; CURLcode result = CURLE_OK; - struct Curl_peer *peer = NULL; + /* Set the start proxy type for url scheme guessing */ + uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; CURLU *uhp = curl_url(); CURLUcode uc; @@ -1797,7 +1819,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, } result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, - &peer, &proxytype); + &proxyinfo->peer, &proxytype); if(result) goto error; @@ -1806,22 +1828,21 @@ static CURLcode parse_proxy(struct Curl_easy *data, case CURLPROXY_HTTP_1_0: case CURLPROXY_HTTPS: case CURLPROXY_HTTPS2: - proxyinfo = &conn->http_proxy; + if(for_pre_proxy) { + failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - proxyinfo = &conn->socks_proxy; break; default: - break; - } - - if(!proxyinfo) { - failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; } /* Is there a username and password given in this proxy URL? */ @@ -1837,35 +1858,29 @@ static CURLcode parse_proxy(struct Curl_easy *data, } if(proxyuser || proxypasswd) { - curlx_free(proxyinfo->user); - proxyinfo->user = proxyuser; - result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); - proxyuser = NULL; + result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, + CREDS_URL, &proxyinfo->creds); if(result) goto error; - curlx_safefree(proxyinfo->passwd); - if(!proxypasswd) { - proxypasswd = curlx_strdup(""); - if(!proxypasswd) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - proxyinfo->passwd = proxypasswd; - result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); - proxypasswd = NULL; - if(result) - goto error; - conn->bits.proxy_user_passwd = TRUE; /* enable it */ } + else if(!for_pre_proxy && + (data->set.str[STRING_PROXYUSERNAME] || + data->set.str[STRING_PROXYPASSWORD])) { + /* No user/passwd in URL, if this is not a pre-proxy, the + * CURLOPT_PROXY* settings apply. */ + result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], + data->set.str[STRING_PROXYPASSWORD], + NULL, NULL, + CREDS_OPTION, &proxyinfo->creds); + } + else + Curl_creds_unlink(&proxyinfo->creds); - Curl_peer_link(&proxyinfo->peer, peer); proxyinfo->proxytype = proxytype; error: curlx_free(proxyuser); curlx_free(proxypasswd); - Curl_peer_unlink(&peer); curl_url_cleanup(uhp); #ifdef DEBUGBUILD if(!result) { @@ -1876,46 +1891,14 @@ static CURLcode parse_proxy(struct Curl_easy *data, return result; } -/* - * Extract the user and password from the authentication string - */ -static CURLcode parse_proxy_auth(struct Curl_easy *data, - struct connectdata *conn) -{ - const char *proxyuser = data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : ""; - const char *proxypasswd = data->state.aptr.proxypasswd ? - data->state.aptr.proxypasswd : ""; - CURLcode result = CURLE_OUT_OF_MEMORY; - - conn->http_proxy.user = curlx_strdup(proxyuser); - if(conn->http_proxy.user) { - conn->http_proxy.passwd = curlx_strdup(proxypasswd); - if(conn->http_proxy.passwd) - result = CURLE_OK; - else - curlx_safefree(conn->http_proxy.user); - } - return result; -} - static CURLcode url_set_conn_proxies(struct Curl_easy *data, struct connectdata *conn) { char *proxy = NULL; - char *socksproxy = NULL; + char *pre_proxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result) - goto out; - } - /************************************************************* * Detect what (if any) proxy to use *************************************************************/ @@ -1930,9 +1913,9 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, } if(data->set.str[STRING_PRE_PROXY]) { - socksproxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); + pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); /* if global socks proxy is set, this is it */ - if(!socksproxy) { + if(!pre_proxy) { failf(data, "memory shortage"); result = CURLE_OUT_OF_MEMORY; goto out; @@ -1954,10 +1937,10 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { curlx_safefree(proxy); - curlx_safefree(socksproxy); + curlx_safefree(pre_proxy); } #ifndef CURL_DISABLE_HTTP - else if(!proxy && !socksproxy) + else if(!proxy && !pre_proxy) /* if the host is not in the noproxy list, detect proxy. */ proxy = url_detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ @@ -1968,12 +1951,12 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, or if the protocol does not work with network */ proxy = NULL; } - if(socksproxy && (!*socksproxy || + if(pre_proxy && (!*pre_proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(socksproxy); /* Do not bother with an empty socks proxy string - or if the protocol does not work with - network */ - socksproxy = NULL; + curlx_free(pre_proxy); /* Do not bother with an empty socks proxy string + or if the protocol does not work with + network */ + pre_proxy = NULL; } /*********************************************************************** @@ -1981,21 +1964,37 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, * name, proxy type and port number, so that we can reuse an existing * connection that may exist registered to the same proxy host. ***********************************************************************/ - if(proxy || socksproxy) { - if(proxy) { - result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); - curlx_safefree(proxy); /* parse_proxy copies the proxy string */ + if(proxy || pre_proxy) { + if(pre_proxy) { + result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); if(result) goto out; } - if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, - conn->socks_proxy.proxytype); - /* parse_proxy copies the socks proxy string */ - curlx_safefree(socksproxy); + if(proxy) { + result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); if(result) goto out; + switch(conn->http_proxy.proxytype) { + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + /* Whoops, it's not a HTTP proxy */ + if(conn->socks_proxy.peer) { + /* and we already have a SOCKS pre-proxy. Cannot have both */ + failf(data, "Having a SOCKS pre-proxy and proxy is not " + "supported with \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + /* switch */ + conn->socks_proxy = conn->http_proxy; + memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); + break; + default: + break; + } } if(conn->http_proxy.peer) { @@ -2013,34 +2012,14 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, /* if not converting to HTTP over the proxy, enforce tunneling */ conn->bits.tunnel_proxy = TRUE; } - conn->bits.httpproxy = TRUE; #endif } - else { - conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ - conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ - } - - if(conn->socks_proxy.peer) { - if(!conn->http_proxy.peer) { - /* once a socks proxy */ - if(!conn->socks_proxy.user) { - conn->socks_proxy.user = conn->http_proxy.user; - conn->http_proxy.user = NULL; - curlx_free(conn->socks_proxy.passwd); - conn->socks_proxy.passwd = conn->http_proxy.passwd; - conn->http_proxy.passwd = NULL; - } - } - conn->bits.socksproxy = TRUE; - } else - conn->bits.socksproxy = FALSE; /* not a socks proxy */ - } - else { - conn->bits.socksproxy = FALSE; - conn->bits.httpproxy = FALSE; + conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ } + + conn->bits.socksproxy = !!conn->socks_proxy.peer; + conn->bits.httpproxy = !!conn->http_proxy.peer; conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; if(!conn->bits.proxy) { @@ -2048,7 +2027,6 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, conn->bits.proxy = FALSE; conn->bits.httpproxy = FALSE; conn->bits.socksproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; conn->bits.tunnel_proxy = FALSE; /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need to signal that CURLPROXY_HTTPS is not used for this connection */ @@ -2057,7 +2035,7 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, out: - curlx_free(socksproxy); + curlx_free(pre_proxy); curlx_free(proxy); return result; } @@ -2180,41 +2158,71 @@ static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn) { CURLUcode uc; - char **userp = &conn->user; - char **passwdp = &conn->passwd; char **optionsp = &conn->options; +#ifndef CURL_DISABLE_NETRC + struct Curl_creds *ncreds_in = NULL; + struct Curl_creds *ncreds_out = NULL; +#endif + CURLcode result = CURLE_OK; + bool creds_changed = FALSE; if(data->set.str[STRING_OPTIONS]) { curlx_free(*optionsp); *optionsp = curlx_strdup(data->set.str[STRING_OPTIONS]); - if(!*optionsp) - return CURLE_OUT_OF_MEMORY; + if(!*optionsp) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } #ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - curlx_safefree(*userp); - curlx_safefree(*passwdp); - } - conn->bits.netrc = FALSE; - if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { - bool url_provided = FALSE; - - if(data->state.aptr.user && - (data->state.creds_from != CREDS_NETRC)) { - /* there was a username with a length in the URL. Use the URL decoded - version */ - userp = &data->state.aptr.user; - url_provided = TRUE; + if(data->set.use_netrc) { + /* Determine how to react on already existing credentials */ + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + Curl_creds_unlink(&conn->creds); } - if(!*passwdp) { - NETRCcode ret = Curl_parsenetrc(&data->state.netrc, - conn->origin->hostname, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - if(ret == NETRC_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; + if(data->state.creds) { + switch(data->state.creds->source) { + case CREDS_OPTION: + /* we never override credentials set via CURLOPT_* */ + goto out; + case CREDS_URL: + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + /* use the URL user to search netrc */ + result = Curl_creds_create( + data->state.creds->user, NULL, NULL, NULL, CREDS_URL, &ncreds_in); + if(result) + goto out; + } + else if(data->state.creds) { + /* only search when something is still missing */ + Curl_creds_link(&ncreds_in, data->state.creds); + } + break; + default: + /* ignore credentials from other sources */ + break; + } + } + + /* Only search in netrc when the creds are not already complete */ + if(!Curl_creds_has_passwd(ncreds_in)) { + NETRCcode ret; + + CURL_TRC_M(data, "netrc: find credentials for %s, user %s", + conn->origin->hostname, + Curl_creds_has_user(ncreds_in) ? ncreds_in->user : "*"); + ret = Curl_parsenetrc(&data->state.netrc, + conn->origin->hostname, + ncreds_in, + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } else if(ret && ((ret == NETRC_NO_MATCH) || (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { infof(data, "Could not find host %s in the %s file; using defaults", @@ -2225,113 +2233,76 @@ static CURLcode override_login(struct Curl_easy *data, else if(ret) { const char *m = Curl_netrc_strerror(ret); failf(data, ".netrc error: %s", m); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + goto out; } - else { + else if(ncreds_out) { if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { /* if the protocol cannot handle control codes in credentials, make sure there are none */ - if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { failf(data, "control code detected in .netrc credentials"); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + goto out; } } - /* set bits.netrc TRUE to remember that we got the name from a .netrc - file, so that it is safe to use even if we followed a Location: to a - different host or similar. */ - conn->bits.netrc = TRUE; + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + conn->origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + data->state.creds, CREDS_NETRC, + &data->state.creds); + if(result) + goto out; + creds_changed = TRUE; } - } - if(url_provided) { - curlx_free(conn->user); - conn->user = curlx_strdup(*userp); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - /* no user was set but a password, set a blank user */ - if(!*userp && *passwdp) { - *userp = curlx_strdup(""); - if(!*userp) - return CURLE_OUT_OF_MEMORY; + else + DEBUGASSERT(0); } } + #endif - /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result; - if(data->state.aptr.user != *userp) { - /* nothing to do then */ - result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); + if(creds_changed) { + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(data->state.creds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(data->state.creds), CURLU_URLENCODE); if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = curlx_strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } - if(*passwdp) { - CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - if(data->state.aptr.passwd) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - data->state.aptr.passwd, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*passwdp) { - *passwdp = curlx_strdup(data->state.aptr.passwd); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; - } + result = Curl_uc_to_curlcode(uc); } - return CURLE_OK; +out: +#ifndef CURL_DISABLE_NETRC + Curl_creds_unlink(&ncreds_in); + Curl_creds_unlink(&ncreds_out); +#endif + return result; } /* * Set the login details so they are available in the connection */ -static CURLcode set_login(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode url_set_conn_login(struct Curl_easy *data, + struct connectdata *conn) { - CURLcode result = CURLE_OK; - const char *setuser = CURL_DEFAULT_USER; - const char *setpasswd = CURL_DEFAULT_PASSWORD; - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) - ; - else { - setuser = ""; - setpasswd = ""; - } - /* Store the default user */ - if(!conn->user) { - conn->user = curlx_strdup(setuser); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) { + if(data->state.creds) + Curl_creds_link(&conn->creds, data->state.creds); + else + return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD, + NULL, NULL, CREDS_NONE, &conn->creds); } - - /* Store the default password */ - if(!conn->passwd) { - conn->passwd = curlx_strdup(setpasswd); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; + else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { + /* for protocols that do not handle credentials per request, + * the connection credentials are set by the initial transfer. */ + Curl_creds_link(&conn->creds, data->state.creds); } - return result; + return CURLE_OK; } /* @@ -2549,33 +2520,15 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, /* get the user+password information from the needle since it may * be new for this request even when we reuse conn */ - if(needle->user) { + if(needle->creds) { /* use the new username and password though */ - curlx_free(conn->user); - curlx_free(conn->passwd); - conn->user = needle->user; - conn->passwd = needle->passwd; - needle->user = NULL; - needle->passwd = NULL; + Curl_creds_link(&conn->creds, needle->creds); } #ifndef CURL_DISABLE_PROXY - conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy username and proxy password though */ - curlx_free(conn->http_proxy.user); - curlx_free(conn->socks_proxy.user); - curlx_free(conn->http_proxy.passwd); - curlx_free(conn->socks_proxy.passwd); - conn->http_proxy.user = needle->http_proxy.user; - conn->socks_proxy.user = needle->socks_proxy.user; - conn->http_proxy.passwd = needle->http_proxy.passwd; - conn->socks_proxy.passwd = needle->socks_proxy.passwd; - needle->http_proxy.user = NULL; - needle->socks_proxy.user = NULL; - needle->http_proxy.passwd = NULL; - needle->socks_proxy.passwd = NULL; - } + /* use the new proxy username and proxy password though */ + Curl_creds_link(&conn->http_proxy.creds, needle->http_proxy.creds); + Curl_creds_link(&conn->socks_proxy.creds, needle->socks_proxy.creds); #endif /* Finding a connection for reuse in the cpool matches, among other @@ -2686,29 +2639,13 @@ static CURLcode url_create_needle(struct Curl_easy *data, } #endif /* CURL_DISABLE_PROXY */ - if(data->set.str[STRING_SASL_AUTHZID]) { - needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!needle->sasl_authzid) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_BEARER]) { - needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!needle->oauth_bearer) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - /* Check for overridden login details and set them accordingly so that they are known when protocol->setup_connection is called! */ result = override_login(data, needle); if(result) goto out; - result = set_login(data, needle); /* default credentials */ + result = url_set_conn_login(data, needle); /* default credentials */ if(result) goto out; diff --git a/lib/urldata.h b/lib/urldata.h index 2889e936a4ae..6ad4666280d3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -55,6 +55,7 @@ #include "asyn.h" #include "cookie.h" +#include "creds.h" #include "psl.h" #include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ @@ -206,10 +207,9 @@ struct digestdata { BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* copy of user/passwd used to make the identity for http_context. - either may be NULL. */ - char *user; - char *passwd; + /* linked credentials used to make the identity for http_context. + may be NULL. */ + struct Curl_creds *creds; #else char *nonce; char *cnonce; @@ -249,7 +249,6 @@ struct ConnectBits { #ifndef CURL_DISABLE_PROXY BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ - BIT(proxy_user_passwd); /* user+password for the proxy? */ BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. This is implicit when SSL-protocols are used through proxies, but can also be enabled explicitly by @@ -275,9 +274,6 @@ struct ConnectBits { EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ -#endif -#ifndef CURL_DISABLE_NETRC - BIT(netrc); /* name+password provided by netrc */ #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ @@ -328,9 +324,8 @@ struct ip_quadruple { struct proxy_info { struct Curl_peer *peer; /* proxy to this peer */ + struct Curl_creds *creds; /* use these credentials, maybe NULL */ uint8_t proxytype; /* what kind of proxy that is in use */ - char *user; /* proxy username string, allocated */ - char *passwd; /* proxy password string, allocated */ }; /* @@ -370,11 +365,8 @@ struct connectdata { struct proxy_info socks_proxy; struct proxy_info http_proxy; #endif - char *user; /* username string, allocated */ - char *passwd; /* password string, allocated */ + struct Curl_creds *creds; /* When connection itself is tied to credentials */ char *options; /* options string, allocated */ - char *sasl_authzid; /* authorization identity string, allocated */ - char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ @@ -652,11 +644,6 @@ struct urlpieces { char *query; }; -#define CREDS_NONE 0 -#define CREDS_URL 1 /* from URL */ -#define CREDS_OPTION 2 /* set with a CURLOPT_ */ -#define CREDS_NETRC 3 /* found in netrc */ - struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ @@ -672,10 +659,10 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* origin of the first (not followed) request. - if set, this is the origin we sent authorization to, none else. - Used to make Location: following not keep sending user+password. */ - struct Curl_peer *first_origin; + /* Origin of the initial (e.g. not followed) request of a transfer. + Credentials from CURLOPT_* are only valid for this origin. + Always set once a transfer starts searching for connections. */ + struct Curl_peer *initial_origin; int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ @@ -765,6 +752,8 @@ struct UrlState { struct store_netrc netrc; #endif + struct Curl_creds *creds; /* Credentials for the origin only */ + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { @@ -776,14 +765,6 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif - - /* transfer credentials */ - char *user; - char *passwd; -#ifndef CURL_DISABLE_PROXY - char *proxyuser; - char *proxypasswd; -#endif } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; @@ -793,8 +774,6 @@ struct UrlState { CONN_MAX_RETRIES */ uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - unsigned int creds_from:2; /* where is the server credentials originating - from, see the CREDS_* defines above */ /* when curl_easy_perform() is called, the multi handle is "owned" by the easy handle so curl_easy_cleanup() on such an easy handle will diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c index 7976adec9c8f..ca0c9967ff9c 100644 --- a/lib/vauth/cleartext.c +++ b/lib/vauth/cleartext.c @@ -40,24 +40,21 @@ * * Parameters: * - * authzid [in] - The authorization identity. - * authcid [in] - The authentication identity. + * creds [in] - The credentials. * passwd [in] - The password. * out [out] - The result storage. * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out) { size_t len; char *auth; - size_t zlen = (authzid == NULL ? 0 : strlen(authzid)); - size_t clen = strlen(authcid); - size_t plen = strlen(passwd); + size_t zlen = strlen(Curl_creds_sasl_authzid(creds)); + size_t clen = strlen(Curl_creds_user(creds)); + size_t plen = strlen(Curl_creds_passwd(creds)); if((zlen > CURL_MAX_INPUT_LENGTH) || (clen > CURL_MAX_INPUT_LENGTH) || (plen > CURL_MAX_INPUT_LENGTH)) @@ -65,8 +62,10 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, len = zlen + clen + plen + 2; - auth = curl_maprintf("%s%c%s%c%s", authzid ? authzid : "", '\0', - authcid, '\0', passwd); + auth = curl_maprintf("%s%c%s%c%s", + Curl_creds_sasl_authzid(creds), '\0', + Curl_creds_user(creds), '\0', + Curl_creds_passwd(creds)); if(!auth) return CURLE_OUT_OF_MEMORY; Curl_bufref_set(out, auth, len, curl_free); diff --git a/lib/vauth/cram.c b/lib/vauth/cram.c index d3f2d137b779..023c31b58b67 100644 --- a/lib/vauth/cram.c +++ b/lib/vauth/cram.c @@ -47,18 +47,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out) { struct HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char *response; + const char *user = Curl_creds_user(creds); + const char *passwd = Curl_creds_passwd(creds); /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, - (const unsigned char *)passwdp, - curlx_uztoui(strlen(passwdp))); + (const unsigned char *)passwd, + curlx_uztoui(strlen(passwd))); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -73,7 +74,7 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, /* Generate the response */ response = curl_maprintf( "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - userp, digest[0], digest[1], digest[2], digest[3], digest[4], + user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); if(!response) diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 3c6f6e8e9559..f7080e2ece86 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -332,13 +332,14 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out) { size_t i; struct MD5_context *ctxt; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; @@ -666,8 +667,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The username. - * passwdp [in] - The user's password. + * creds [in] - The credentials * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. @@ -679,8 +679,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, */ static CURLcode auth_create_digest_http_message( struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -689,6 +688,8 @@ static CURLcode auth_create_digest_http_message( CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); unsigned char hashbuf[32]; /* 32 bytes/256 bits */ unsigned char request_digest[65]; unsigned char ha1[65]; /* 64 digits and 1 zero byte */ @@ -986,29 +987,28 @@ static CURLcode auth_create_digest_http_message( * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { if(digest->algo <= ALGO_MD5SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_md5_to_ascii, Curl_md5it); if(digest->algo <= ALGO_SHA256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, Curl_sha256it); #ifdef CURL_HAVE_SHA512_256 if(digest->algo <= ALGO_SHA512_256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index f351a76986e2..31dfebfa5126 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -28,6 +28,7 @@ #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) +#include "creds.h" #include "vauth/vauth.h" #include "vauth/digest.h" #include "curlx/multibyte.h" @@ -83,8 +84,7 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out) { @@ -137,9 +137,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &identity); if(result) { curlx_free(spn); curlx_free(output_token); @@ -381,8 +382,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -421,16 +421,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* If the user/passwd that was used to make the identity for http_context has changed then delete that context. */ - if((userp && !digest->user) || (!userp && digest->user) || - (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || - (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || - (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { + if(!Curl_creds_same(creds, digest->creds)) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } if(digest->http_context) { @@ -473,13 +469,13 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, unsigned long attrs; TCHAR *spn; - /* free the copy of user/passwd used to make the previous identity */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + /* free the credentials used to make the previous identity */ + Curl_creds_unlink(&digest->creds); - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - if(Curl_create_sspi_identity(userp, passwdp, &identity)) { + if(Curl_create_sspi_identity(creds->user, creds->passwd, + &identity)) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -499,26 +495,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Use the current Windows user */ p_identity = NULL; - if(userp) { - digest->user = curlx_strdup(userp); - - if(!digest->user) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - return CURLE_OUT_OF_MEMORY; - } - } - - if(passwdp) { - digest->passwd = curlx_strdup(passwdp); - - if(!digest->passwd) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - curlx_safefree(digest->user); - return CURLE_OUT_OF_MEMORY; - } - } + if(creds) + Curl_creds_link(&digest->creds, creds); /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, @@ -649,8 +627,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } /* Free the copy of user/passwd used to make the identity for http_context */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 958f4ffab746..3ea77eecd1b4 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -54,15 +54,14 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, } CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl) { #if GSASL_VERSION_NUMBER >= 0x010b00 int res; res = #endif - gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); + gsasl_property_set(gsasl->client, GSASL_AUTHID, creds->user); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting AUTHID failed: %s", gsasl_strerror(res)); @@ -73,7 +72,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, #if GSASL_VERSION_NUMBER >= 0x010b00 res = #endif - gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); + gsasl_property_set(gsasl->client, GSASL_PASSWORD, creds->passwd); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting PASSWORD failed: %s", gsasl_strerror(res)); diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index 64c735be582a..ad2c04facff8 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -74,8 +74,7 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -90,8 +89,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - (void)userp; - (void)passwdp; + (void)creds; if(!krb5->spn) { gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index e7491be022f8..dfac639bf9ca 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -79,8 +79,7 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -128,9 +127,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &krb5->identity); if(result) return result; diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 1e485ed34d6f..bd914f3c8ce9 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -421,8 +421,7 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -453,8 +452,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size_t domoff = hostoff + hostlen; /* This is 0: remember that host and domain are empty */ (void)data; - (void)userp; - (void)passwdp; + (void)creds; (void)service; (void)host; @@ -542,8 +540,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -579,6 +576,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* The fixed hostname we provide, in order to not leak our real local host name. Copy the name used by Firefox. */ static const char host[] = "WORKSTATION"; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); const char *user; const char *domain = ""; size_t hostoff = 0; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 4c41eb21f4e6..5fe78a622d7e 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -76,8 +76,7 @@ bool Curl_auth_is_ntlm_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -111,11 +110,12 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, if(!ntlm->output_token) return CURLE_OUT_OF_MEMORY; - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { CURLcode result; /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &ntlm->identity); if(result) return result; @@ -227,8 +227,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -240,8 +239,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SECURITY_STATUS status; unsigned long attrs; - (void)passwdp; - (void)userp; + (void)creds; /* Setup the type-2 "input" security buffer */ type_2_desc.ulVersion = SECBUFFER_VERSION; diff --git a/lib/vauth/oauth2.c b/lib/vauth/oauth2.c index 4541d1d551bb..f597114638a5 100644 --- a/lib/vauth/oauth2.c +++ b/lib/vauth/oauth2.c @@ -47,21 +47,22 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out) { char *oauth; /* Generate the message */ if(port == 0 || port == 80) - oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, - bearer); + oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), host, + Curl_creds_oauth_bearer(creds)); else oauth = curl_maprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", - user, host, port, bearer); + Curl_creds_user(creds), host, port, + Curl_creds_oauth_bearer(creds)); if(!oauth) return CURLE_OUT_OF_MEMORY; @@ -83,12 +84,13 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out) { /* Generate the message */ - char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), + Curl_creds_oauth_bearer(creds)); if(!xoauth) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 38bb4c1422c4..631480fa7691 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -70,8 +70,7 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, @@ -90,8 +89,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct gss_channel_bindings_struct chan; #endif - (void)user; - (void)password; + (void)creds; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index eeae02148416..ba4c4186a000 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -78,8 +78,7 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, @@ -133,9 +132,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(user, password, &nego->identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &nego->identity); if(result) return result; diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index 81c29cd497d4..76de85cb2844 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "vauth/vauth.h" +#include "creds.h" #include "curlx/multibyte.h" #include "url.h" @@ -111,15 +112,16 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, * * Returns TRUE on success; otherwise FALSE. */ -bool Curl_auth_user_contains_domain(const char *user) +bool Curl_auth_user_contains_domain(struct Curl_creds *creds) { bool valid = FALSE; - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Check we have a domain name or UPN present */ - const char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(creds->user, "\\/@"); - valid = (p != NULL && p > user && p < user + strlen(user) - 1); + valid = (p != NULL) && (p > creds->user) && + (p < (creds->user + strlen(creds->user) - 1)); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else @@ -133,14 +135,12 @@ bool Curl_auth_user_contains_domain(const char *user) /* * Curl_auth_allowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. + * "sensitive data" can be sent to the connection's origin. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) { - return !data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_origin && - Curl_peer_equal(data->state.first_origin, data->conn->origin)); + return data->set.allow_auth_to_other_hosts || + Curl_peer_equal(data->state.initial_origin, data->conn->origin); } #ifdef USE_NTLM diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 279da60be2cb..cdd64a1cfbd6 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -30,6 +30,7 @@ #include "urldata.h" struct Curl_easy; +struct Curl_creds; struct connectdata; #ifndef CURL_DISABLE_DIGEST_AUTH @@ -69,12 +70,10 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, #endif /* This is used to test if the user contains a Windows domain name */ -bool Curl_auth_user_contains_domain(const char *user); +bool Curl_auth_user_contains_domain(struct Curl_creds *creds); /* This is used to generate a PLAIN cleartext message */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out); /* This is used to generate a LOGIN cleartext message */ @@ -86,8 +85,7 @@ void Curl_auth_create_external_message(const char *user, struct bufref *out); #ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out); /* This is used to evaluate if DIGEST is supported */ @@ -96,8 +94,7 @@ bool Curl_auth_is_digest_supported(void); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out); @@ -107,8 +104,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* This is used to generate an HTTP DIGEST response message */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -140,8 +136,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, struct gsasldata *gsasl); /* This is used to start a gsasl method */ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl); /* This is used to process and generate a new SASL token */ @@ -197,8 +192,7 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -211,8 +205,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, /* This is used to generate a base64 encoded NTLM type-3 message */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out); @@ -221,15 +214,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #endif /* USE_NTLM */ /* This is used to generate a base64 encoded OAuth 2.0 message */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out); /* This is used to generate a base64 encoded XOAuth 2.0 message */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out); #ifdef USE_KERBEROS5 @@ -260,8 +251,7 @@ bool Curl_auth_is_gssapi_supported(void); /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token message */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -330,8 +320,7 @@ Curl_auth_nego_get(struct connectdata *conn, bool proxy); /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index c6a6e0cfdfbe..49f9d3f93d2a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -633,7 +633,8 @@ static int myssh_auth_interactive(struct connectdata *conn, if(nprompts != 1) return SSH_ERROR; - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, + Curl_creds_passwd(conn->creds)); if(rc < 0) return SSH_ERROR; @@ -920,7 +921,8 @@ static int myssh_in_AUTH_PASS_INIT(struct Curl_easy *data, static int myssh_in_AUTH_PASS(struct Curl_easy *data, struct ssh_conn *sshc) { - int rc = ssh_userauth_password(sshc->ssh_session, NULL, data->conn->passwd); + int rc = ssh_userauth_password(sshc->ssh_session, NULL, + Curl_creds_passwd(data->conn->creds)); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; else if(rc == SSH_AUTH_SUCCESS) { @@ -2571,9 +2573,10 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - if(conn->user && conn->user[0] != '\0') { - infof(data, "User: %s", conn->user); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user); + if(Curl_creds_has_user(conn->creds)) { + infof(data, "User: %s", conn->creds->user); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, + conn->creds->user); if(rc != SSH_OK) { failf(data, "Could not set user"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 1f36934f6dda..118bc594f641 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -148,11 +148,12 @@ static void kbd_callback(const char *name, int name_len, #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; + const char *passwd = Curl_creds_passwd(conn->creds); /* this function must allocate memory that can be freed by libssh2, which uses the LIBSSH2_FREE_FUNC callback */ - responses[0].text = Curl_cstrdup(conn->passwd); + responses[0].text = Curl_cstrdup(passwd); responses[0].length = - responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + responses[0].text == NULL ? 0 : curlx_uztoui(strlen(passwd)); } (void)prompts; } /* kbd_callback */ @@ -1496,9 +1497,9 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, * Therefore always specify it here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); sshc->authlist = libssh2_userauth_list(sshc->ssh_session, - conn->user, - curlx_uztoui(strlen(conn->user))); + user, curlx_uztoui(strlen(user))); if(!sshc->authlist) { int rc; @@ -1527,11 +1528,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, /* The function below checks if the files exists, no need to stat() here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, + curlx_uztoui(strlen(user)), sshc->rsa_pub, sshc->rsa, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -1579,11 +1580,13 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, struct ssh_conn *sshc) { struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); + const char *passwd = Curl_creds_passwd(conn->creds); int rc = - libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - curlx_uztoui(strlen(conn->user)), - conn->passwd, - curlx_uztoui(strlen(conn->passwd)), + libssh2_userauth_password_ex(sshc->ssh_session, user, + curlx_uztoui(strlen(user)), + passwd, + curlx_uztoui(strlen(passwd)), NULL); if(rc == LIBSSH2_ERROR_EAGAIN) { return CURLE_AGAIN; @@ -1680,7 +1683,7 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, if(rc == 0) { struct connectdata *conn = data->conn; - rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + rc = libssh2_agent_userauth(sshc->ssh_agent, Curl_creds_user(conn->creds), sshc->sshagent_identity); if(rc < 0) { @@ -1727,11 +1730,10 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, { /* Authentication failed. Continue with keyboard-interactive now. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, curlx_uztoui(strlen(user)), &kbd_callback); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -3452,9 +3454,9 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(!sshc) return CURLE_FAILED_INIT; - infof(data, "User: '%s'", conn->user); + infof(data, "User: '%s'", Curl_creds_user(conn->creds)); #ifdef CURL_LIBSSH2_DEBUG - infof(data, "Password: %s", conn->passwd); + infof(data, "Password: %s", Curl_creds_passwd(conn->creds)); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index 4f3ef11fd064..c13c5e863c88 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -46,7 +46,6 @@ static CURLcode test_lib1978(const char *URL) test_setopt(curl, CURLOPT_INFILESIZE, 0L); test_setopt(curl, CURLOPT_VERBOSE, 1L); test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); test_setopt(curl, CURLOPT_HEADER, 0L); test_setopt(curl, CURLOPT_URL, URL); diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index 72b2ab5a32f6..d66fe796e952 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -25,17 +25,37 @@ #ifndef CURL_DISABLE_NETRC #include "netrc.h" +#include "creds.h" -static void t1304_stop(char **password, char **login) +static void t1304_stop(struct Curl_creds **pc1, struct Curl_creds **pc2) { - curlx_safefree(*password); - curlx_safefree(*login); + Curl_creds_unlink(pc1); + Curl_creds_unlink(pc2); +} + +static bool t1304_set_creds(const char *user, const char *passwd, + struct Curl_creds **pcreds) +{ + Curl_creds_unlink(pcreds); + if(user || passwd) + return !Curl_creds_create(user, passwd, NULL, NULL, CREDS_NONE, pcreds); + else + return TRUE; +} + +static bool t1304_no_user(struct Curl_creds *creds) +{ + return !creds || !creds->user[0]; +} + +static bool t1304_no_passwd(struct Curl_creds *creds) +{ + return !creds || !creds->passwd[0]; } static CURLcode test_unit1304(const char *arg) { - char *login = NULL; - char *password = NULL; + struct Curl_creds *cr_out = NULL, *cr_in = NULL; UNITTEST_BEGIN_SIMPLE @@ -46,126 +66,119 @@ static CURLcode test_unit1304(const char *arg) * Test a non existent host in our netrc file. */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", &login, &password, arg); - fail_unless(result == 1, "Host not found should return 1"); - abort_unless(password == NULL, "password did not return NULL!"); - abort_unless(login == NULL, "user did not return NULL!"); + result = Curl_parsenetrc(&store, "test.example.com", NULL, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(cr_out == NULL, "creds did not return NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login in our netrc file. */ - login = curlx_strdup("me"); + fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login and host in our netrc file. */ - login = curlx_strdup("me"); + fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", &login, &password, arg); - fail_unless(result == 1, "Host not found should return 1"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "test.example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login (substring of an existing one) in our * netrc file. */ - login = curlx_strdup("admi"); /* spellchecker:disable-line */ + fail_unless(t1304_set_creds( + "admi", NULL, &cr_in), "err set creds"); /* spellchecker:disable-line */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login (superstring of an existing one) * in our netrc file. */ - login = curlx_strdup("adminn"); + fail_unless(t1304_set_creds("adminn", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test for the first existing host in our netrc file * with login[0] = 0. */ - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "passwd", 6) == 0, + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, + "login should be 'admin'"); Curl_netrc_cleanup(&store); /* * Test for the first existing host in our netrc file * with login[0] != 0. */ - curlx_free(password); - curlx_free(login); - password = NULL; - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "passwd", 6) == 0, + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, + "login should be 'admin'"); Curl_netrc_cleanup(&store); /* * Test for the second existing host in our netrc file * with login[0] = 0. */ - curlx_free(password); - password = NULL; - curlx_free(login); - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "none", 4) == 0, "password should be 'none'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, + "password should be 'none'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, + "login should be 'none'"); Curl_netrc_cleanup(&store); /* * Test for the second existing host in our netrc file * with login[0] != 0. */ - curlx_free(password); - password = NULL; - curlx_free(login); - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "none", 4) == 0, "password should be 'none'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, + "password should be 'none'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, + "login should be 'none'"); Curl_netrc_cleanup(&store); - UNITTEST_END(t1304_stop(&password, &login)) + UNITTEST_END(t1304_stop(&cr_in, &cr_out)) } #else From 2538dc04e3a57d718de8676bd7dc86f643fd06b0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 15:56:11 +0200 Subject: [PATCH 0080/1062] curl_ntlm_core: propagate DES `CryptEncrypt()` error Spotted by GitHub Code Quality Closes #21569 --- lib/curl_ntlm_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index e6aab7e39e96..9f8db0328fd7 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -251,6 +251,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, char key[8]; } blob; DWORD len = 8; + BOOL res; /* Acquire the crypto provider */ if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, @@ -280,12 +281,12 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, memcpy(out, in, 8); /* Perform the encryption */ - CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); + res = CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); CryptDestroyKey(hkey); CryptReleaseContext(hprov, 0); - return TRUE; + return res; } #endif /* crypto backends */ From 02dca1eb868fa8b4ef82bfe530ad47ddd8c6d7b0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 15:54:06 +0200 Subject: [PATCH 0081/1062] src: fix comment typos Found by GitHub Code Quality Closes #21570 --- lib/curl_ntlm_core.c | 2 +- lib/multi.c | 4 ++-- lib/tftp.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 9f8db0328fd7..e774d282f703 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -633,7 +633,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, * * ntlmv2hash [in] - The NTLMv2 hash (16 bytes) * challenge_client [in] - The client nonce (8 bytes) - * challenge_client [in] - The server challenge (8 bytes) + * challenge_server [in] - The server challenge (8 bytes) * lmresp [out] - The LMv2 response (24 bytes) * * Returns CURLE_OK on success. diff --git a/lib/multi.c b/lib/multi.c index 5e84133f13fd..53b23641eb93 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -404,7 +404,7 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a removed easy right away. + * not have to reuse the `mid` from a removed easy right away. * Since uint_tbl and uint_bset are memory efficient, * regard less than 25% free as insufficient. * (for low capacities, e.g. multi_easy, 4 or less). */ @@ -420,7 +420,7 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, new_size = max_capacity; /* can not be larger than this */ } else { - /* make it a 64 multiple, since our bitsets frow by that and + /* make it a 64 multiple, since our bitsets grow by that and * small (easy_multi) grows to at least 64 on first resize. */ new_size = (((used + min_unused) + 63) / 64) * 64; } diff --git a/lib/tftp.c b/lib/tftp.c index a088cd90466e..7aaf882d9b5e 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -675,7 +675,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } if(data->state.upload) { - /* If we are uploading, send an WRQ */ + /* If we are uploading, send a WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); if(data->state.infilesize != -1) Curl_pgrsSetUploadSize(data, data->state.infilesize); @@ -740,7 +740,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } } - /* the typecase for the 3rd argument is mostly for systems that do + /* the typecast for the 3rd argument is mostly for systems that do not have a size_t argument, like older unixes that want an 'int' */ #ifdef __AMIGA__ #define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) From 287b082c63864213dabf68ae2b6dea08e3c60b35 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 May 2026 16:01:41 +0200 Subject: [PATCH 0082/1062] tool_urlglob: better 'Duplicate glob name' position This now points to where the duplicate name ends, not where it starts. Also fixes test 2410 to use a fixed hostname so that the error position remains the same. Reported-by: Viktor Szakats Fixes #21567 Closes #21568 --- src/tool_urlglob.c | 2 +- tests/data/test2410 | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index 72893fe66185..a0dbb0bb6f2e 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -524,7 +524,7 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, /* check that the name is not already used */ struct URLPattern *p = glob_find_name(glob, &name); if(p) - return globerror(glob, "Duplicate glob name", 2 + start - ipattern, + return globerror(glob, "Duplicate glob name", pattern - ipattern, CURLE_URL_MALFORMAT); pos += (pattern - start); } diff --git a/tests/data/test2410 b/tests/data/test2410 index fdec8c2e5d58..ce83263e532d 100644 --- a/tests/data/test2410 +++ b/tests/data/test2410 @@ -12,23 +12,20 @@ globbing # Client-side - -http - duplicate named glob -"%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTtest%GTC,D}" -o "%LOGDIR/dump" +"https://dummy.example/{%LTtest%GTA,B}{%LTtest%GTC,D}" -o "%LOGDIR/dump" # Verify data after the test has been "shot" -curl: (3) Duplicate glob name in position 30: -%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTtest%GTC,D} - ^ +curl: (3) Duplicate glob name in position 40: +https://dummy.example/{%LTtest%GTA,B}{%LTtest%GTC,D} + ^ 3 From a3618d166db0ec522c8ed0892177d8470dd4df46 Mon Sep 17 00:00:00 2001 From: Andrei Rybak Date: Tue, 12 May 2026 18:02:21 +0200 Subject: [PATCH 0083/1062] VULN-DISCLOSURE-POLICY.md: remove mention of bug bounty reward As a follow-up to commits ca7ef4b817 ("BUG-BOUNTY.md: we stop the bug-bounty end of Jan 2026", 2026-01-22) and ed7bf43a08 ("BUG-BOUNTY.md: minor rephrase to say there is no bug bounty", 2026-03-10), remove a leftover mention of the reward for vulnerability reports, that no longer exists, in file `VULN-DISCLOSURE-POLICY.md`. Fixes #21571 Reported-by: Alan De Smet Closes #21574 --- docs/VULN-DISCLOSURE-POLICY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index abc7ef2c0a96..1ce3f4e26d4d 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -248,8 +248,8 @@ already do much worse harm and the problem is not really in curl. ## Debug & Experiments Vulnerabilities in features which are off by default (in the build) and -documented as experimental, or exist only in debug mode, are not eligible for a -reward and we do not consider them security problems. +documented as experimental, or exist only in debug mode, are not considered +security problems. The same applies to scripts and software which are not installed by default through the make install rule. From 54d5de53051c0580ea9dcd8fd1c7029b942e23fe Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 18:24:19 +0200 Subject: [PATCH 0084/1062] THANKS-filter: update Source: https://github.com/andrew Closes #21577 --- RELEASE-NOTES | 2 +- docs/THANKS-filter | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index f639feea28c5..de57813a6853 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -63,7 +63,7 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Andrew Nesbit, Dan Fandrich, Daniel Stenberg, dependabot[bot], Elise Vance, + Andrew Nesbitt, Dan Fandrich, Daniel Stenberg, dependabot[bot], Elise Vance, Jeremy Nicoll, Kai Pastor, parasol-aser, Raymond Steen, renovate[bot], Sollace on github, Stefan Eissing, Viktor Szakats (13 contributors) diff --git a/docs/THANKS-filter b/docs/THANKS-filter index cc964a49b2a9..cd99f569b14d 100644 --- a/docs/THANKS-filter +++ b/docs/THANKS-filter @@ -162,3 +162,4 @@ s/Maksim Sciepanienka/Maksim Ściepanienka/ s/Qriist.*/Qriist on github/ s/Viktor Szakatas/Viktor Szakats/ s/Val S\./Valerie Snyder/ +s/Andrew Nesbit$/Andrew Nesbitt/ From eca309c2a1fd60b8bf6b1ca19008ac601e99d40e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 18:19:26 +0200 Subject: [PATCH 0085/1062] ldap: fix to not leak `attribute` on OOM (WinLDAP) Reported-by: Andrew Nesbitt Closes #21576 --- lib/ldap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ldap.c b/lib/ldap.c index f476da4ea00d..0f9e7821719b 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -474,6 +474,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef USE_WIN32_LDAP char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { + ldap_memfree(attribute); result = CURLE_OUT_OF_MEMORY; goto quit; } From 89f38c168cf5e898d099302b8ed0cec0f82c0415 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 08:58:04 +0200 Subject: [PATCH 0086/1062] CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers It was not really clear, but it has worked like this since 8.4.0 which now is a while. Closes #21582 --- docs/libcurl/opts/CURLOPT_MAXFILESIZE.md | 19 ++++++++------- .../libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md | 23 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md index 17c4c9f4e176..a1a0d8496ccd 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md @@ -32,17 +32,16 @@ value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is returned. Passing a zero *size* disables this, and passing a negative *size* yields a *CURLE_BAD_FUNCTION_ARGUMENT*. -The file size is not always known prior to the download start, and for such -transfers this option has no effect - even if the file transfer eventually -ends up being larger than this given limit. - If you want a limit above 2GB, use CURLOPT_MAXFILESIZE_LARGE(3). -Since 8.4.0, this option also stops ongoing transfers if they reach this -threshold. +If the size is known to be too big before the transfer starts, libcurl +aborts before starting the transfer. If it is instead found to be too big +while the transfer is in progress, libcurl aborts the transfer once the +received bytes exceed the limit. -Since 8.20.0, this option also stops ongoing transfers that would reach this -threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). +Since 8.20.0, this option also aborts ongoing transfers once the +decompressed bytes exceed this threshold due to automatic decompression using +CURLOPT_ACCEPT_ENCODING(3). # DEFAULT @@ -68,6 +67,10 @@ int main(void) # %AVAILABILITY% +# HISTORY + +Before curl 8.4.0, the limit was not applied to transfers in progress. + # RETURN VALUE curl_easy_setopt(3) returns a CURLcode indicating success or error. diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md index 791b25862f7e..929bae67f2c1 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md @@ -29,18 +29,15 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAXFILESIZE_LARGE, # DESCRIPTION -Pass a curl_off_t as parameter. This specifies the maximum accepted *size* -(in bytes) of a file to download. If the file requested is found larger than -this value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is -returned. Passing a zero *size* disables this, and passing a negative *size* -yields a *CURLE_BAD_FUNCTION_ARGUMENT*. +Pass a curl_off_t as parameter. This specifies the maximum accepted *size* (in +bytes) of a file to download. If the file requested is found larger than this +value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is returned. +Passing a zero *size* disables this, and passing a negative *size* yields a +*CURLE_BAD_FUNCTION_ARGUMENT*. -The file size is not always known prior to the download start, and for such -transfers this option has no effect - even if the file transfer eventually -ends up being larger than this given limit. - -Since 8.4.0, this option also stops ongoing transfers if they reach this -threshold. +If the size is known to exceed the limit before the transfer starts, libcurl +aborts before starting the transfer. If the transfer instead exceeds the limit +while it is in progress, libcurl aborts it at that point. Since 8.20.0, this option also stops ongoing transfers that would reach this threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). @@ -70,6 +67,10 @@ int main(void) # %AVAILABILITY% +# HISTORY + +Before curl 8.4.0, the limit was not applied to transfers in progress. + # RETURN VALUE curl_easy_setopt(3) returns a CURLcode indicating success or error. From 2238f0921cb00b33958470e30dff6326ea6d5c65 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 22 Apr 2026 00:52:16 +0200 Subject: [PATCH 0087/1062] curl: named globs in output file name for upload glob references Use parts of text from the upload filename field when that uses globbing by giving it a name the same way we do it for URL globs. For example, if you upload three files to a HTTP URL and want to save the corresponding responses in separate files: curl -T 'file{1,2,3}' https://upload.example/ -o 'response-#' Verified by test 2014 Closes #21407 --- docs/cmdline-opts/output.md | 22 +++++--- docs/cmdline-opts/upload-file.md | 26 ++++++--- src/tool_operate.c | 8 ++- src/tool_urlglob.c | 9 ++- src/tool_urlglob.h | 3 +- tests/data/Makefile.am | 2 +- tests/data/test2014 | 97 ++++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 tests/data/test2014 diff --git a/docs/cmdline-opts/output.md b/docs/cmdline-opts/output.md index c1d823e3244d..b2d196d038da 100644 --- a/docs/cmdline-opts/output.md +++ b/docs/cmdline-opts/output.md @@ -23,10 +23,10 @@ Example: # `--output` -Write output to the given file instead of stdout. If you are using globbing to -fetch multiple documents, you should quote the URL and you can use `#` -followed by a number in the filename. That variable is then replaced with the -current string for the URL being fetched. Like in: +Write output to the given file instead of stdout. If you are using globbing in +the URL to fetch multiple documents, you should quote the URL and you can use +`#` followed by a number in the filename. That variable gets replaced with the +current glob text. Like in: curl "http://{one,two}.example.com" -o "file_#1.txt" @@ -70,9 +70,9 @@ override curl's internal binary output in terminal prevention: Note that the binary output may be caused by the response being compressed, in which case you may want to use the --compressed option. -Starting in curl 8.21.0, the separate globbing parts can be named and -referenced by their names. The case sensitive alphanumeric name is set -enclosed within angle brackets after the opening character. Examples: +Since curl 8.21.0, the separate globbing parts can be named and referenced by +their names. The case sensitive alphanumeric name is set enclosed within angle +brackets after the opening character. Examples: curl "https://fun.example/{one,two}.jpg" -o "save-#" @@ -80,3 +80,11 @@ enclosed within angle brackets after the opening character. Examples: -o "save-#.txt" Referencing a named glob that is not set, causes an error. + +Since curl 8.21.0, you can use parts of the upload filename when it uses +globbing by setting a glob name and referencing it the same way you reference +named URL globs. For example, if you upload three files to a single fixed HTTP +URL and want to save the corresponding responses in separate files: + + curl -T 'file{1,2,3}' \ + https://upload.example/ -o 'response-#' diff --git a/docs/cmdline-opts/upload-file.md b/docs/cmdline-opts/upload-file.md index 5a2842e58ad8..1988d8afb728 100644 --- a/docs/cmdline-opts/upload-file.md +++ b/docs/cmdline-opts/upload-file.md @@ -26,13 +26,13 @@ Upload the specified local file to the remote URL. If there is no file part in the specified URL, curl appends the local file name to the end of the URL before the operation starts. You must use a -trailing slash (/) on the last directory to prove to curl that there is no +trailing slash (`/`) on the last directory to prove to curl that there is no filename or curl thinks that your last directory name is the remote filename to use. When putting the local filename at the end of the URL, curl ignores what is on -the left side of any slash (/) or backslash (\\) used in the filename and only -appends what is on the right side of the rightmost such character. +the left side of any slash (`/`) or backslash (`\\`) used in the filename and +only appends what is on the right side of the rightmost such character. Use the filename `-` (a single dash) to use stdin instead of a given file. Alternately, the filename `.` (a single period) may be specified instead of @@ -45,9 +45,19 @@ You can specify one --upload-file for each URL on the command line. Each --upload-file + URL pair specifies what to upload and to where. curl also supports globbing of the --upload-file argument, meaning that you can upload multiple files to a single URL by using the same URL globbing style supported -in the URL. +in the URL. Example: -When uploading to an SMTP server: the uploaded data is assumed to be RFC 5322 -formatted. It has to feature the necessary set of headers and mail body -formatted correctly by the user as curl does not transcode nor encode it -further in any way. + curl --upload-file 'file{1,2,3}' ftp://ftp.example/ + +Since curl 8.21.0, you can use parts of the upload filename when it uses +globbing by setting a glob name and referencing that in the same way you +reference named URL globs. For example, if you upload three files to a single +fixed HTTP URL and want to save the corresponding responses in separate files: + + curl -T 'file{1,2,3}' \ + https://upload.example/ -o 'response-#' + +When uploading to an SMTP server (aka "sending email"): the uploaded data is +assumed to be RFC 5322 formatted. It has to feature the necessary set of +headers and mail body formatted correctly by the user as curl does not +transcode nor encode it further in any way. diff --git a/src/tool_operate.c b/src/tool_operate.c index 62d40afd55e3..c5ac095cb910 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1052,11 +1052,13 @@ static CURLcode setup_outfile(struct OperationConfig *config, return result; } } - else if(glob_inuse(&state->urlglob)) { - /* fill '#1' ... '#9' terms from URL pattern */ + else if(glob_inuse(&state->urlglob) || glob_inuse(&state->inglob)) { + /* expand '#1' ... '#9' references from URL pattern and named references + from the upload file glob */ SANITIZEcode sc; CURLcode result = - glob_match_url(&per->outfile, u->outfile, &state->urlglob, &sc); + glob_match_url(&per->outfile, u->outfile, &state->urlglob, + glob_inuse(&state->inglob) ? &state->inglob : NULL, &sc); if(sc) { if(sc == SANITIZE_ERR_OUT_OF_MEMORY) diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index a0dbb0bb6f2e..dd7a6a9d8ef0 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -703,7 +703,8 @@ CURLcode glob_next_url(char **globbed, struct URLGlob *glob) #define MAX_OUTPUT_GLOB_LENGTH (1024 * 1024) CURLcode glob_match_url(char **output, const char *filename, - struct URLGlob *glob, SANITIZEcode *sc) + struct URLGlob *glob, struct URLGlob *glob2, + SANITIZEcode *sc) { struct dynbuf dyn; const char *ifilename = filename; @@ -741,7 +742,11 @@ CURLcode glob_match_url(char **output, const char *filename, if(!curlx_str_until(&filename, &name, MAX_GLOBNAME_LEN, '>') && !curlx_str_single(&filename, '>')) { /* find the correct glob entry */ - pat = glob_find_name(glob, &name); + if(glob_inuse(glob)) + pat = glob_find_name(glob, &name); + if(!pat && glob2 && glob_inuse(glob2)) + /* scan the second glob list if there is one */ + pat = glob_find_name(glob2, &name); if(!pat) { /* when the name is given correctly, it needs to be an existing glob name, which makes this an error */ diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h index abc279de73db..e891258aa46e 100644 --- a/src/tool_urlglob.h +++ b/src/tool_urlglob.h @@ -79,7 +79,8 @@ CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, FILE *error); CURLcode glob_next_url(char **globbed, struct URLGlob *glob); CURLcode glob_match_url(char **output, const char *filename, - struct URLGlob *glob, SANITIZEcode *sc); + struct URLGlob *glob, struct URLGlob *glob2, + SANITIZEcode *sc); void glob_cleanup(struct URLGlob *glob); bool glob_inuse(struct URLGlob *glob); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 8dcf2d360c94..b63c9c06a0c9 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -245,7 +245,7 @@ test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ -test2008 test2009 test2010 test2011 test2012 test2013 \ +test2008 test2009 test2010 test2011 test2012 test2013 test2014 \ \ test2023 \ test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ diff --git a/tests/data/test2014 b/tests/data/test2014 new file mode 100644 index 000000000000..a7fb078db635 --- /dev/null +++ b/tests/data/test2014 @@ -0,0 +1,97 @@ + + + + +HTTP +HTTP PUT + + + +# Server-side + + +HTTP/1.1 200 OK swsbounce +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Accept-Ranges: bytes +Content-Length: 6 +Content-Type: text/html + +-foo- + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 20 +Content-Type: text/html + +the second response + + + +# Client-side + + +http + + +upload with glob, output name based on upload glob + + +-T '%LOGDIR/upload{%LThej%GT1,2}' http://%HOSTIP:%HTTPPORT/%TESTNUMBER --silent '--output=%LOGDIR/out-#%LThej%GT' + + + +first! + + + +second + + + + +# Verify data after the test has been "shot" + + +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 7 + +first! +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 7 + +second + + +%EMPTY + + + +HTTP/1.1 200 OK swsbounce +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Accept-Ranges: bytes +Content-Length: 6 +Content-Type: text/html + +-foo- + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 20 +Content-Type: text/html + +the second response + + + + From b2476a07128fc1e83a0b322fe6eb9dfa761db53d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 12:41:51 +0200 Subject: [PATCH 0088/1062] tool_urlglob: check glob use before access As this function can now be invoked with only the second glob "active", it must avoid accessing the first one if not in use. Follow-up to 2238f0921cb00b3395847 Spotted by Codex Security Closes #21586 --- src/tool_urlglob.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index dd7a6a9d8ef0..ee894d79ddeb 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -716,7 +716,7 @@ CURLcode glob_match_url(char **output, const char *filename, while(*filename) { CURLcode result = CURLE_OK; struct URLPattern *pat = NULL; - if(*filename == '#' && ISDIGIT(filename[1])) { + if(glob_inuse(glob) && *filename == '#' && ISDIGIT(filename[1])) { /* a numbered glob reference */ const char *ptr = filename++; curl_off_t num; From 5e99b73cf441d9c369768b9cd48b5389b9a2503d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 13 May 2026 12:02:48 +0200 Subject: [PATCH 0089/1062] creds: add sasl service name The SASL service name, used in authentication, is part of curl's credentials when authenticating to a server/proxy. Make it part of `struct Curl_creds`. Change code to use `creds` to obtain a service name. By tying creds used to the connection, connection reuse is also only allowed when the service name matches. Closes #21585 --- lib/creds.c | 30 +++++++++++++++++++----------- lib/creds.h | 11 +++++++---- lib/curl_sasl.c | 24 +++++++++--------------- lib/http_negotiate.c | 9 ++------- lib/http_ntlm.c | 11 +++-------- lib/imap.c | 2 +- lib/netrc.c | 3 ++- lib/openldap.c | 2 +- lib/pop3.c | 2 +- lib/socks.c | 2 +- lib/socks.h | 3 ++- lib/socks_gssapi.c | 10 +++++----- lib/socks_sspi.c | 10 ++++++---- lib/url.c | 13 +++++++++---- lib/vauth/digest.c | 4 +++- lib/vauth/digest_sspi.c | 4 +++- lib/vauth/krb5_gssapi.c | 6 +++--- lib/vauth/krb5_sspi.c | 4 +++- lib/vauth/ntlm.c | 4 +++- lib/vauth/ntlm_sspi.c | 4 +++- lib/vauth/spnego_gssapi.c | 4 +++- lib/vauth/spnego_sspi.c | 4 +++- lib/vauth/vauth.h | 8 ++++---- tests/unit/unit1304.c | 3 ++- 24 files changed, 98 insertions(+), 79 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index fe8693a97ef5..4767527ed945 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -33,36 +33,39 @@ CURLcode Curl_creds_create(const char *user, const char *passwd, - const char *sasl_authzid, const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, uint8_t source, struct Curl_creds **pcreds) { struct Curl_creds *creds = NULL; size_t ulen = user ? strlen(user) : 0; size_t plen = passwd ? strlen(passwd) : 0; - size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; + size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; + size_t sslen = sasl_service ? strlen(sasl_service) : 0; char *s, *buf; CURLcode result = CURLE_OK; Curl_creds_unlink(pcreds); /* Everything empty/NULL, this is the NULL credential */ - if(!ulen && !plen && !salen && !olen) + if(!ulen && !plen && !olen && !salen && !sslen) goto out; if((ulen > CURL_MAX_INPUT_LENGTH) || (plen > CURL_MAX_INPUT_LENGTH) || + (olen > CURL_MAX_INPUT_LENGTH) || (salen > CURL_MAX_INPUT_LENGTH) || - (olen > CURL_MAX_INPUT_LENGTH)) { + (sslen > CURL_MAX_INPUT_LENGTH)) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } /* NUL terminator for user already part of struct */ creds = curlx_calloc(1, sizeof(*creds) + - ulen + plen + 1 + salen + 1 + olen + 1); + ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1); if(!creds) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -78,12 +81,15 @@ CURLcode Curl_creds_create(const char *user, creds->passwd = s = buf + ulen + 1; if(plen) memcpy(s, CURL_UNCONST(passwd), plen + 1); - creds->sasl_authzid = s = buf + ulen + 1 + plen + 1; - if(salen) - memcpy(s, CURL_UNCONST(sasl_authzid), salen + 1); - creds->oauth_bearer = s = buf + ulen + 1 + plen + 1 + salen + 1; + creds->oauth_bearer = s = buf + ulen + 1 + plen + 1; if(olen) memcpy(s, CURL_UNCONST(oauth_bearer), olen + 1); + creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1; + if(salen) + memcpy(s, CURL_UNCONST(sasl_authzid), salen + 1); + creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1; + if(sslen) + memcpy(s, CURL_UNCONST(sasl_service), sslen + 1); out: if(!result) @@ -107,8 +113,9 @@ CURLcode Curl_creds_merge(const char *user, if(!passwd || !passwd[0]) passwd = Curl_creds_passwd(creds_in); result = Curl_creds_create(user, passwd, - Curl_creds_sasl_authzid(creds_in), Curl_creds_oauth_bearer(creds_in), + Curl_creds_sasl_authzid(creds_in), + Curl_creds_sasl_service(creds_in), source, &creds_out); Curl_creds_link(pcreds_out, creds_out); Curl_creds_unlink(&creds_out); @@ -158,8 +165,9 @@ bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) (c1 && c2 && !Curl_timestrcmp(c1->user, c2->user) && !Curl_timestrcmp(c1->passwd, c2->passwd) && + !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer) && !Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) && - !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer)); + !Curl_timestrcmp(c1->sasl_service, c2->sasl_service)); } #ifdef CURLVERBOSE diff --git a/lib/creds.h b/lib/creds.h index 2eb5998cc85a..7f50d3bd8cc7 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -34,8 +34,9 @@ struct Curl_easy; struct Curl_creds { const char *user; /* non-NULL, maybe empty string */ const char *passwd; /* non-NULL, maybe empty string */ - const char *sasl_authzid; /* non-NULL, maybe empty string */ const char *oauth_bearer; /* non-NULL, maybe empty string */ + const char *sasl_authzid; /* non-NULL, maybe empty string */ + const char *sasl_service; /* non-NULL, maybe empty string */ uint32_t refcount; uint8_t source; /* CREDS_* value */ char buf[1]; @@ -43,8 +44,9 @@ struct Curl_creds { CURLcode Curl_creds_create(const char *user, const char *passwd, - const char *sasl_authzid, const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, uint8_t source, struct Curl_creds **pcreds); @@ -72,11 +74,12 @@ bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd); #define Curl_creds_has_user(c) ((c) && (c)->user[0]) #define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) #define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) +#define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) #define Curl_creds_user(c) ((c)? (c)->user : "") #define Curl_creds_passwd(c) ((c)? (c)->passwd : "") -#define Curl_creds_sasl_authzid(c) ((c)? (c)->sasl_authzid : "") #define Curl_creds_oauth_bearer(c) ((c)? (c)->oauth_bearer : "") - +#define Curl_creds_sasl_authzid(c) ((c)? (c)->sasl_authzid : "") +#define Curl_creds_sasl_service(c) ((c)? (c)->sasl_service : "") #ifdef CURLVERBOSE void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 6c955446fea8..d8c088dda2ff 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -319,9 +319,8 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) if((sctx->enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && Curl_auth_user_contains_domain(sctx->conn->creds)) { - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sctx->sasl->params->service; + const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; sctx->sasl->mutual_auth = FALSE; sctx->mech = SASL_MECH_STRING_GSSAPI; @@ -412,9 +411,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) { if((sctx->enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sctx->sasl->params->service; + const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; const char *hostname; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); @@ -589,12 +587,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; const char *hostname; int port; -#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \ - !defined(CURL_DISABLE_DIGEST_AUTH) - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; -#endif struct bufref serverdata; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -657,7 +649,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, result = get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_create_digest_md5_message(data, &serverdata, - conn->creds, service, + conn->creds, + sasl->params->service, &resp); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) newstate = SASL_DIGESTMD5_RESP; @@ -673,7 +666,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); result = !ntlm ? CURLE_OUT_OF_MEMORY : Curl_auth_create_ntlm_type1_message(data, conn->creds, - service, hostname, + sasl->params->service, hostname, ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; break; @@ -697,7 +690,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : Curl_auth_create_gssapi_user_message(data, conn->creds, - service, conn->origin->hostname, + sasl->params->service, + conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); newstate = SASL_GSSAPI_TOKEN; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index d987b8b9d1d4..5a05ab1412fa 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -54,9 +54,8 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, CURLcode result; size_t len; - /* Point to the username, password, service and host */ + /* Point to credentials and host */ struct Curl_creds *creds = NULL; - const char *service; const char *host; /* Point to the correct struct with this */ @@ -66,8 +65,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(proxy) { #ifndef CURL_DISABLE_PROXY creds = conn->http_proxy.creds; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.peer->hostname; state = conn->proxy_negotiate_state; #else @@ -76,8 +73,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, } else { creds = data->state.creds; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->origin->hostname; state = conn->http_negotiate_state; } @@ -127,7 +122,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, creds, service, + result = Curl_auth_decode_spnego_message(data, creds, "HTTP", host, header, neg_ctx); #ifdef GSS_C_CHANNEL_BOUND_FLAG diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 1a02a0fd867a..05c2f2faf8a6 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -122,9 +122,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) server, which is for a plain host or for an HTTP proxy */ char **allocuserpwd; - /* point to credentials, service and host */ + /* point to credentials and host */ struct Curl_creds *creds = NULL; - const char *service = NULL; const char *hostname = NULL; /* point to the correct struct with this */ @@ -140,8 +139,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #ifndef CURL_DISABLE_PROXY allocuserpwd = &data->req.hd_proxy_auth; creds = conn->http_proxy.creds; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; hostname = conn->http_proxy.peer->hostname; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; @@ -152,8 +149,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) else { allocuserpwd = &data->req.hd_auth; creds = data->state.creds; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; hostname = conn->origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; @@ -185,7 +180,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) switch(*state) { case NTLMSTATE_TYPE1: - default: /* for the weird cases we (re)start here */ + default: /* for the weird cases we (re)start here */ if(!proxy) { /* Start it up. From this time onwards, the connection is tied * tp the credentials used. */ @@ -195,7 +190,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) } Curl_creds_link(&conn->creds, creds); } - result = Curl_auth_create_ntlm_type1_message(data, creds, service, + result = Curl_auth_create_ntlm_type1_message(data, creds, "HTTP", hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); diff --git a/lib/imap.c b/lib/imap.c index 7c73255e960b..0a4cb5b7b72f 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -597,7 +597,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.creds) { + if(!conn->creds) { imap_state(data, imapc, IMAP_STOP); return result; diff --git a/lib/netrc.c b/lib/netrc.c index 76fd5541ce07..eb67f2505ec9 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -391,7 +391,8 @@ static NETRCcode netrc_finalize(struct netrc_state *ns, /* success without a password, set a blank one */ const char *passwd = ns->password ? ns->password : ""; - if(Curl_creds_create(login, passwd, NULL, NULL, CREDS_NETRC, pcreds)) { + if(Curl_creds_create(login, passwd, NULL, NULL, NULL, CREDS_NETRC, + pcreds)) { retcode = NETRC_OUT_OF_MEMORY; goto out; } diff --git a/lib/openldap.c b/lib/openldap.c index 1ed72c1ea816..2696fcdc52e8 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -345,7 +345,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(data->state.creds) { + if(conn->creds) { binddn = Curl_creds_user(conn->creds); passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds)); passwd.bv_len = strlen(passwd.bv_val); diff --git a/lib/pop3.c b/lib/pop3.c index b7bbd765b916..7dbeefb6e7a2 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -527,7 +527,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.creds) { + if(!conn->creds) { pop3_state(data, POP3_STOP); return result; diff --git a/lib/socks.c b/lib/socks.c index 2d8a4f3ab6a9..667e728d9abd 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1079,7 +1079,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_GSSAPI_INIT: { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* GSSAPI stuff done non-blocking */ - CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data); + CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data, sx->creds); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; diff --git a/lib/socks.h b/lib/socks.h index fca10c833258..d8e77c7f13a1 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -47,7 +47,8 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, * This function handles the SOCKS5 GSS-API negotiation and initialization */ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_creds *creds); #endif /* Insert a SOCKS filter after `cf_at` for connecting to `dest`. diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 79359be22327..002c1b6d3727 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -564,19 +564,19 @@ static CURLcode socks5_gss_negotiate_enc(struct Curl_cfilter *cf, } CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_creds *creds) { struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode result; OM_uint32 gss_ret_flags = 0; gss_name_t server = GSS_C_NO_NAME; - const char *serviceptr = - data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - result = socks5_gss_create_service_name(data, conn, serviceptr, &server); + result = socks5_gss_create_service_name(data, conn, service, &server); if(!result) { (void)curlx_nonblock(sock, FALSE); result = socks5_gss_auth_loop(cf, data, &server, &gss_context, diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index cc520a49d0cf..a4cc9796b008 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -58,12 +58,13 @@ static int check_sspi_err(struct Curl_easy *data, /* This is the SSPI-using version of this function */ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_creds *creds, CredHandle *cred_handle, char **service_namep) { struct connectdata *conn = cf->conn; - const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; SECURITY_STATUS status; /* prepare service name */ @@ -473,7 +474,8 @@ static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, } CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_creds *creds) { struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; @@ -489,7 +491,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memset(&sspi_context, 0, sizeof(sspi_context)); names.sUserName = NULL; - result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + result = socks5_sspi_setup(cf, data, creds, &cred_handle, &service_name); if(result) goto error; diff --git a/lib/url.c b/lib/url.c index 298e5478a2a1..5159b25e50ca 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1442,8 +1442,9 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, Curl_peer_same_destination(data->state.initial_origin, conn->origin))) { result = Curl_creds_create(data->set.str[STRING_USERNAME], data->set.str[STRING_PASSWORD], - data->set.str[STRING_SASL_AUTHZID], data->set.str[STRING_BEARER], + data->set.str[STRING_SASL_AUTHZID], + data->set.str[STRING_SERVICE_NAME], CREDS_OPTION, &data->state.creds); if(result) return result; @@ -1859,18 +1860,21 @@ static CURLcode parse_proxy(struct Curl_easy *data, if(proxyuser || proxypasswd) { result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], CREDS_URL, &proxyinfo->creds); if(result) goto error; } else if(!for_pre_proxy && (data->set.str[STRING_PROXYUSERNAME] || - data->set.str[STRING_PROXYPASSWORD])) { + data->set.str[STRING_PROXYPASSWORD] || + data->set.str[STRING_PROXY_SERVICE_NAME])) { /* No user/passwd in URL, if this is not a pre-proxy, the * CURLOPT_PROXY* settings apply. */ result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], data->set.str[STRING_PROXYPASSWORD], NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], CREDS_OPTION, &proxyinfo->creds); } else @@ -2191,7 +2195,8 @@ static CURLcode override_login(struct Curl_easy *data, if(data->set.use_netrc == CURL_NETRC_REQUIRED) { /* use the URL user to search netrc */ result = Curl_creds_create( - data->state.creds->user, NULL, NULL, NULL, CREDS_URL, &ncreds_in); + data->state.creds->user, NULL, NULL, NULL, NULL, CREDS_URL, + &ncreds_in); if(result) goto out; } @@ -2294,7 +2299,7 @@ static CURLcode url_set_conn_login(struct Curl_easy *data, Curl_creds_link(&conn->creds, data->state.creds); else return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD, - NULL, NULL, CREDS_NONE, &conn->creds); + NULL, NULL, NULL, CREDS_NONE, &conn->creds); } else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { /* for protocols that do not handle credentials per request, diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index f7080e2ece86..9843fd8ef71f 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -333,9 +333,11 @@ bool Curl_auth_is_digest_supported(void) CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, struct Curl_creds *creds, - const char *service, + const char *default_service, struct bufref *out) { + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t i; struct MD5_context *ctxt; const char *userp = Curl_creds_user(creds); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 31dfebfa5126..6ca00d799890 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -85,7 +85,7 @@ bool Curl_auth_is_digest_supported(void) CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, struct Curl_creds *creds, - const char *service, + const char *default_service, struct bufref *out) { CURLcode result = CURLE_OK; @@ -103,6 +103,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; /* Ensure we have a valid challenge message */ if(!Curl_bufref_len(chlg)) { diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index ad2c04facff8..738ce9a744a8 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -75,7 +75,7 @@ bool Curl_auth_is_gssapi_supported(void) */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -88,8 +88,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, OM_uint32 unused_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - - (void)creds; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index dfac639bf9ca..506ee759df91 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -80,7 +80,7 @@ bool Curl_auth_is_gssapi_supported(void) */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -96,6 +96,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { /* Generate our SPN */ diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index bd914f3c8ce9..121c6cae561f 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -422,7 +422,7 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -441,6 +441,8 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (*) -> Optional */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t size; char *ntlmbuf; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 5fe78a622d7e..e3ade65c96db 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -77,7 +77,7 @@ bool Curl_auth_is_ntlm_supported(void) */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -87,6 +87,8 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SecBufferDesc type_1_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 631480fa7691..869a27fdc983 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -71,7 +71,7 @@ bool Curl_auth_is_spnego_supported(void) */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -103,6 +103,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index ba4c4186a000..d636dfbed49c 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -79,7 +79,7 @@ bool Curl_auth_is_spnego_supported(void) */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -104,6 +104,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->spn) { /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; nego->spn = Curl_auth_build_spn(service, host, NULL); if(!nego->spn) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index cdd64a1cfbd6..3bbecb8896b6 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -95,7 +95,7 @@ bool Curl_auth_is_digest_supported(void); CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, struct Curl_creds *creds, - const char *service, + const char *default_service, struct bufref *out); /* This is used to decode an HTTP DIGEST challenge message */ @@ -193,7 +193,7 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out); @@ -252,7 +252,7 @@ bool Curl_auth_is_gssapi_supported(void); message */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -321,7 +321,7 @@ Curl_auth_nego_get(struct connectdata *conn, bool proxy); message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct Curl_creds *creds, - const char *service, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego); diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index d66fe796e952..099f39dd916c 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -38,7 +38,8 @@ static bool t1304_set_creds(const char *user, const char *passwd, { Curl_creds_unlink(pcreds); if(user || passwd) - return !Curl_creds_create(user, passwd, NULL, NULL, CREDS_NONE, pcreds); + return !Curl_creds_create(user, passwd, NULL, NULL, NULL, CREDS_NONE, + pcreds); else return TRUE; } From 11df1251e550c5b4b77a4c66bca96cbbc09cdcc4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 09:55:36 +0200 Subject: [PATCH 0090/1062] snpego_sspi: preserve distinction btw policy-only and uncond delegation CURLOPT_GSSAPI_DELEGATION exposes distinct modes: CURLGSSAPI_DELEGATION_POLICY_FLAG is documented as delegating only when OK-AS-DELEGATE policy permits it, while CURLGSSAPI_DELEGATION_FLAG is unconditional. The new SSPI implementation checks for either bit and sets ISC_REQ_DELEGATE, so a caller requesting policy-limited delegation is put on the same SSPI path as unconditional delegation. In addition, curl's existing protection that avoids reusing a connection when the GSS delegation setting differs was guarded only by HAVE_GSSAPI; SSPI-only builds now have an effective delegation option, but the connection's delegation setting was neither copied nor compared. This would cause Windows SSPI Negotiate/Kerberos authentication to delegate credentials contrary to the caller's selected policy or reuse an already-delegated authenticated connection for a transfer that requested no delegation. Follow-up to cc6777d939976b2f322dcbe5a Reported by Codex Security Closes #21583 --- lib/url.c | 8 ++++---- lib/vauth/spnego_sspi.c | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/url.c b/lib/url.c index 5159b25e50ca..bcd8f54aa8aa 100644 --- a/lib/url.c +++ b/lib/url.c @@ -943,9 +943,9 @@ static bool url_match_auth(struct connectdata *conn, if(!Curl_creds_same(m->data->state.creds, conn->creds)) return FALSE; } -#ifdef HAVE_GSSAPI - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS delegation differences do not actually affect every connection and + auth method, but this check takes precaution before efficiency */ if(m->needle->gssapi_delegation != conn->gssapi_delegation) return FALSE; #endif @@ -1340,7 +1340,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; conn->lastused = conn->created; -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) conn->gssapi_delegation = data->set.gssapi_delegation; #endif DEBUGF(infof(data, "alloc connection, bits.close=%d", conn->bits.close)); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index d636dfbed49c..8808631e49a4 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -227,8 +227,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Generate our challenge-response message */ { DWORD sspi_flags = ISC_REQ_CONFIDENTIALITY; - if(data->set.gssapi_delegation & (CURLGSSAPI_DELEGATION_FLAG | - CURLGSSAPI_DELEGATION_POLICY_FLAG)) + if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) sspi_flags |= ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH; nego->status = Curl_pSecFn->InitializeSecurityContext(nego->credentials, From b079595f2e903b820a027a68ea5c4c1e6697038b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 10:35:02 +0200 Subject: [PATCH 0091/1062] url: keep the question mark for empty queries Reported-by: Bill Mill Fixes #21544 Verified by test 1721 Closes #21584 --- lib/url.c | 5 +++-- tests/data/Makefile.am | 2 +- tests/data/test1721 | 45 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1721 diff --git a/lib/url.c b/lib/url.c index bcd8f54aa8aa..ff1a9fd4505e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1543,7 +1543,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } /* after it was parsed, get the generated normalized version */ - uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); + uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY); if(uc) { result = Curl_uc_to_curlcode(uc); goto out; @@ -1600,7 +1600,8 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, goto out; } - uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); + uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, + CURLU_GET_EMPTY); if(uc && (uc != CURLUE_NO_QUERY)) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b63c9c06a0c9..3f520af2539c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -228,7 +228,7 @@ test1680 test1681 test1682 test1683 test1684 test1685 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ -test1720 \ +test1720 test1721 \ \ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ diff --git a/tests/data/test1721 b/tests/data/test1721 new file mode 100644 index 000000000000..1b0e7a88f78d --- /dev/null +++ b/tests/data/test1721 @@ -0,0 +1,45 @@ + + + + +HTTP +HTTP GET + + + + + +HTTP/1.1 200 OK +Content-Length: 6 +Content-Type: text/html + +-foo- + + + + + +http + + +Keep question mark for empty query + + +"http://%HOSTIP:%HTTPPORT/hello?" -w '%output{%LOGDIR/out%TESTNUMBER}%{url_effective}' + + + +# Verify data after the test has been "shot" + + +http://%HOSTIP:%HTTPPORT/hello? + + +GET /hello? HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + From 7f7e4e3e689fd7ae36a690a041fe0e1c5a2ed6d2 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 13 May 2026 14:45:35 +0200 Subject: [PATCH 0092/1062] creds: create on service name only Fix creation of creds object for transfer when only a sasl service name is configured by the application. Follow-up to 5e99b73cf441d9c369768 Closes #21591 --- lib/url.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/url.c b/lib/url.c index ff1a9fd4505e..287aa584fa10 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1436,8 +1436,9 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, Curl_creds_unlink(&data->state.creds); if((data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD] || + data->set.str[STRING_BEARER] || data->set.str[STRING_SASL_AUTHZID] || - data->set.str[STRING_BEARER]) && + data->set.str[STRING_SERVICE_NAME]) && (data->set.allow_auth_to_other_hosts || Curl_peer_same_destination(data->state.initial_origin, conn->origin))) { result = Curl_creds_create(data->set.str[STRING_USERNAME], From 675a9b0189d0b291b63874389374c560d78b911a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 16:06:00 +0200 Subject: [PATCH 0093/1062] urlapi: change more lowercase percent-encoded to uppercase For consistency with other code, prefer uppercase. Verified by test 1628 Reported-by: Fabian Keil URL: https://curl.se/mail/lib-2026-05/0006.html Closes #21592 --- lib/urlapi.c | 8 +++---- tests/data/Makefile.am | 1 + tests/data/test1221 | 2 +- tests/data/test1628 | 53 +++++++++++++++++++++++++++++++++++++++++ tests/libtest/lib1560.c | 4 ++-- 5 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 tests/data/test1628 diff --git a/lib/urlapi.c b/lib/urlapi.c index 103e0a6b57e1..24b8bc244a53 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1912,11 +1912,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return cc2cu(result); p = curlx_dyn_ptr(&enc); while(*p) { - /* make sure percent encoded are lower case */ + /* make sure percent encoded are upper case */ if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && - (ISUPPER(p[1]) || ISUPPER(p[2]))) { - p[1] = Curl_raw_tolower(p[1]); - p[2] = Curl_raw_tolower(p[2]); + (ISLOWER(p[1]) || ISLOWER(p[2]))) { + p[1] = Curl_raw_toupper(p[1]); + p[2] = Curl_raw_toupper(p[2]); p += 3; } else diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 3f520af2539c..7216d0a7c968 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -214,6 +214,7 @@ test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ +test1628 \ \ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ diff --git a/tests/data/test1221 b/tests/data/test1221 index 322a5f878f4c..5ff7add9951c 100644 --- a/tests/data/test1221 +++ b/tests/data/test1221 @@ -44,7 +44,7 @@ content to _?!#$'|%LT%GT # Verify data after the test has been "shot" -POST /%TESTNUMBER?my+name+is+moo%5b%5d%AMPyes=s+i+r%AMPv_alue=content+to+_%3f%21%23%24%27%7c%3c%3e%0a%AMPcontent+to+_%3f%21%23%24%27%7c%3c%3e%0a%AMP%3d%3d HTTP/1.1 +POST /%TESTNUMBER?my+name+is+moo%5B%5D%AMPyes=s+i+r%AMPv_alue=content+to+_%3F%21%23%24%27%7C%3C%3E%0A%AMPcontent+to+_%3F%21%23%24%27%7C%3C%3E%0A%AMP%3D%3D HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test1628 b/tests/data/test1628 new file mode 100644 index 000000000000..d5cf83678760 --- /dev/null +++ b/tests/data/test1628 @@ -0,0 +1,53 @@ + + + + +HTTP +HTTP PUT + + +# Server-side + + +HTTP/1.0 200 OK swsclose +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake + +blablabla + + + + +# Client-side + + +proxy + + +http + + +HTTP PUT from file with weird letters over a HTTP proxy + + +-x http://%HOSTIP:%HTTPPORT http://ssss/ -T %LOGDIR/%TESTNUMBERte[]st.txt + + +a few bytes + + + +# Verify data after the test has been "shot" + + +PUT http://ssss/%TESTNUMBERte%5B%5Dst.txt HTTP/1.1 +Host: ssss +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Content-Length: 12 + +a few bytes + + + diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 6d42b3569478..6c3fff9bc999 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1137,8 +1137,8 @@ static const struct setcase set_parts_list[] = { "https://host:1234/", 0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER}, {"https://host/", - "path=%4A%4B%4C,", - "https://host/%4a%4b%4c", + "path=%4A%4b%4C,", + "https://host/%4A%4B%4C", 0, 0, CURLUE_OK, CURLUE_OK}, {"https://host/mooo?q#f", "path=NULL,query=NULL,fragment=NULL,", From 96dbcf69219a812c9fb44de5ae06ec04ae0af983 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 13 May 2026 14:42:55 +0200 Subject: [PATCH 0094/1062] llist: constify struct pointers Closes #21590 --- lib/llist.c | 20 +++++++++++--------- lib/llist.h | 10 +++++----- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/llist.c b/lib/llist.c index 3ec85e4a2ce2..ce0c59cc50ed 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -200,7 +200,7 @@ void Curl_llist_destroy(struct Curl_llist *list, void *user) /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ -struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) +struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -213,8 +213,8 @@ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list); -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list); +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -223,7 +223,7 @@ UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) #endif /* Curl_llist_count() returns a size_t the number of nodes in the list */ -size_t Curl_llist_count(struct Curl_llist *list) +size_t Curl_llist_count(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -231,7 +231,7 @@ size_t Curl_llist_count(struct Curl_llist *list) } /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n) +void *Curl_node_elem(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -240,7 +240,7 @@ void *Curl_node_elem(struct Curl_llist_node *n) /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ -struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) +struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -253,8 +253,10 @@ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n); -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -262,7 +264,7 @@ UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) } #endif -struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(!n->_list || n->_init == NODEINIT); diff --git a/lib/llist.h b/lib/llist.h index 28e958d5c5e9..de4adc972f47 100644 --- a/lib/llist.h +++ b/lib/llist.h @@ -60,13 +60,13 @@ void Curl_llist_destroy(struct Curl_llist *list, void *user); /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ -struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); +struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list); /* Curl_llist_count() returns a size_t the number of nodes in the list */ -size_t Curl_llist_count(struct Curl_llist *list); +size_t Curl_llist_count(const struct Curl_llist *list); /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n); +void *Curl_node_elem(const struct Curl_llist_node *n); /* Remove the node from the list and return the custom data * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ @@ -74,9 +74,9 @@ void *Curl_node_take_elem(struct Curl_llist_node *e); /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ -struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); +struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n); /* Curl_node_llist() return the list the node is in or NULL. */ -struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n); +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n); #endif /* HEADER_CURL_LLIST_H */ From 616e8ea6d8bce692c3d9b0073ddcfab254ad987c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 13 May 2026 14:28:27 +0200 Subject: [PATCH 0095/1062] cookie: constify struct pointers Closes #21589 --- lib/cookie.c | 18 +++++++++--------- lib/cookie.h | 5 +++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/cookie.c b/lib/cookie.c index 57dccf4ce9fb..13732d927440 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -71,7 +71,7 @@ static void freecookie(struct Cookie *co, bool maintoo) } static bool cookie_tailmatch(const char *cookie_domain, - size_t cookie_domain_len, + const size_t cookie_domain_len, const char *hostname) { size_t hostname_len = strlen(hostname); @@ -369,7 +369,7 @@ static bool invalid_octets(const char *ptr, size_t len) #define COOKIE_PIECES 4 /* the list above */ -static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, +static CURLcode storecookie(struct Cookie *co, const struct Curl_str *cp, const char *path, const char *domain) { CURLcode result; @@ -418,7 +418,7 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, static CURLcode parse_cookie_header( struct Curl_easy *data, struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool *okay, /* if the cookie was fine */ const char *ptr, const char *domain, /* default domain */ @@ -638,7 +638,7 @@ static CURLcode parse_cookie_header( } static CURLcode parse_netscape(struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool *okay, const char *lineptr, bool secure) /* TRUE if connection is over @@ -762,7 +762,7 @@ static CURLcode parse_netscape(struct Cookie *co, } static bool is_public_suffix(struct Curl_easy *data, - struct Cookie *co, + const struct Cookie *co, const char *domain) { #ifdef USE_LIBPSL @@ -811,7 +811,7 @@ static bool is_public_suffix(struct Curl_easy *data, /* returns TRUE when replaced */ static bool replace_existing(struct Curl_easy *data, struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool secure, bool *replacep) { @@ -1229,7 +1229,7 @@ static int cookie_sort_ct(const void *p1, const void *p2) return (c2->creationtime > c1->creationtime) ? 1 : -1; } -bool Curl_secure_context(struct connectdata *conn, const char *host) +bool Curl_secure_context(const struct connectdata *conn, const char *host) { return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || curl_strequal("localhost", host) || @@ -1249,7 +1249,7 @@ bool Curl_secure_context(struct connectdata *conn, const char *host) * 'okay' is TRUE when there is a list returned. */ CURLcode Curl_cookie_getlist(struct Curl_easy *data, - struct connectdata *conn, + const struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) @@ -1553,7 +1553,7 @@ static CURLcode cookie_output(struct Curl_easy *data, return result; } -static struct curl_slist *cookie_list(struct Curl_easy *data) +static struct curl_slist *cookie_list(const struct Curl_easy *data) { struct curl_slist *list = NULL; struct curl_slist *beg; diff --git a/lib/cookie.h b/lib/cookie.h index f66e0ef59182..77e31b7e73e0 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -109,7 +109,7 @@ struct connectdata; * are only used if the header boolean is TRUE. */ -bool Curl_secure_context(struct connectdata *conn, const char *host); +bool Curl_secure_context(const struct connectdata *conn, const char *host); CURLcode Curl_cookie_add(struct Curl_easy *data, struct CookieInfo *ci, bool httpheader, @@ -118,7 +118,8 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, const char *domain, const char *path, bool secure) WARN_UNUSED_RESULT; -CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, +CURLcode Curl_cookie_getlist(struct Curl_easy *data, + const struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; void Curl_cookie_clearall(struct CookieInfo *ci); From e25e497c5ef5a15450ae239eb3d019090c8298c9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 13 May 2026 22:08:03 +0200 Subject: [PATCH 0096/1062] cmake: merge `if` blocks (tidy-up) Closes #21596 --- CMakeLists.txt | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8d7a0b86e86..0573d2f14337 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1653,7 +1653,9 @@ check_function_exists("getrlimit" HAVE_GETRLIMIT) check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setrlimit" HAVE_SETRLIMIT) -if(NOT APPLE) +if(APPLE) + check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) +else() # Apple platforms do not offer pipe2(), but the iPhone Simulator-specific # /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false # detection, omit this feature check for Apple targets. @@ -1664,9 +1666,12 @@ if(NOT WIN32) check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) - check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) - check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) - check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) + + check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h + check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h + check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) + check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) + check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) endif() if(AMIGA) @@ -1677,14 +1682,6 @@ if(NOT _ssl_enabled) check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) endif() -if(APPLE) - check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) -endif() -if(NOT WIN32) - check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h - check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h -endif() - check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR) if(HAVE_FSETXATTR) curl_internal_test(HAVE_FSETXATTR_5) From a36c571984b1c4966e11a7b196b79a3b272cc31b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 13 May 2026 22:50:13 +0200 Subject: [PATCH 0097/1062] pythonlint.sh: make it fail on error, fix ruff warnings in pytest Follow-up to a5542c23e7427b8ea8f6183f503f2935d88d5d65 #21289 Follow-up to 17e8200733a1fd9db148f794d7e1cfb47e491fcd Closes #21597 --- scripts/pythonlint.sh | 2 ++ tests/http/test_21_resolve.py | 4 ++-- tests/http/test_22_httpsrr.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/pythonlint.sh b/scripts/pythonlint.sh index 5c1a3cdcc3fd..a6d6aa2dd4ce 100755 --- a/scripts/pythonlint.sh +++ b/scripts/pythonlint.sh @@ -27,6 +27,8 @@ # locations, or all Python files found in the current directory tree by # default. +set -eu + ruff check --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,\ SIM117,SIM118,TRY400,TRY401,RET503,RET504,UP004,B018,B904,RSE102,RET505,I001 \ diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index b55f066f2d25..8f2937f42f1e 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -30,7 +30,7 @@ from typing import Generator import pytest -from testenv import CurlClient, Env, LocalClient, Dnsd +from testenv import CurlClient, Dnsd, Env, LocalClient log = logging.getLogger(__name__) @@ -132,7 +132,7 @@ def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd): run_env = os.environ.copy() run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' curl = CurlClient(env=env, run_env=run_env, force_resolv=False) - url = f'https://test-dnsd.http.curl.invalid/' + url = 'https://test-dnsd.http.curl.invalid/' r = curl.http_download(urls=[url], with_stats=True) r.check_exit_code(6) # could not resolve host r.check_stats(count=1, http_status=0, exitcode=6) diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 61f5e2e2f73f..51ed3bbd9dec 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -29,7 +29,7 @@ from typing import Generator import pytest -from testenv import CurlClient, Env, Dnsd +from testenv import CurlClient, Dnsd, Env log = logging.getLogger(__name__) From db4a6f286bbcf3576964129bd0d694e13e163f8a Mon Sep 17 00:00:00 2001 From: 0xN3R3K3 <168812089+Naraka1337@users.noreply.github.com> Date: Wed, 13 May 2026 18:34:10 +0300 Subject: [PATCH 0098/1062] docs: fix grammar and wording in FAQ Closes #21593 --- docs/FAQ.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 7748d1bd2c49..19a3cf132171 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -214,10 +214,10 @@ that collection (if reasonably updated) should be deemed to be a lot better than a private curl version. If you want the most recent collection of ca certs that Mozilla Firefox uses, -we recommend that using our online [CA certificate +we recommend using our online [CA certificate service](https://curl.se/docs/caextract.html) setup for this purpose. -## I have a problem who, can I chat with? +## I have a problem, who can I chat with? There is a bunch of friendly people hanging out in the #curl channel on the IRC network libera.chat. If you are polite and nice, chances are good that you @@ -253,7 +253,7 @@ to the curl-library mailing list. We are many subscribers there and there are lots of people who can review patches, comment on them and receive them properly. -Lots of more details are found in the +Many more details are found in the [contribute](https://curl.se/dev/contribute.html) and [internals](https://curl.se/dev/internals.html) documents. @@ -276,7 +276,7 @@ You may find that configure fails to properly detect the entire dependency chain of libraries when you provide static versions of the libraries that configure checks for. -The reason why static libraries is much harder to deal with is that for them +The reason why static libraries are much harder to deal with is that for them we do not get any help but the script itself must know or check what more libraries that are needed (with shared libraries, that dependency chain is handled automatically). This is an error-prone process and one that also tends From 81f950dd90b5b515b957a071c1a574803aafd152 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 13 May 2026 23:28:43 +0200 Subject: [PATCH 0099/1062] strparse: split a multi-line assert into many separate ones This way we can better tell exactly which condition that triggers. Like in fuzzer logs. Closes #21599 --- lib/curlx/strparse.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index 7866b2087ab7..3082a6740adc 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -49,9 +49,14 @@ void curlx_str_trim(struct Curl_str *out, size_t len) int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, char delim) { - const char *s = *linep; + const char *s; size_t len = 0; - DEBUGASSERT(linep && *linep && out && max && delim); + DEBUGASSERT(linep); + DEBUGASSERT(*linep); + DEBUGASSERT(out); + DEBUGASSERT(max); + DEBUGASSERT(delim); + s = *linep; curlx_str_init(out); while(*s && (*s != delim)) { From db5d8886738ca8a335898c497ae4808f65ea7781 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 14:09:50 +0200 Subject: [PATCH 0100/1062] GHA: explicitly `brew update` before `brew install` with Linuxbrew Fixing: ``` ==> Installing openssl@3 dependency: ca-certificates ==> Pouring ca-certificates--2026-05-14.all.bottle.tar.gz Error: undefined method '[]' for nil /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/utils/bottles.rb:127:in 'Utils::Bottles.load_tab' /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/formula_installer.rb:1507:in 'FormulaInstaller#pour' [...] /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/brew.rb:114:in '
' You have disabled automatic updates and have not updated today. Do not report this issue until you've run `brew update` and tried again. Error: Process completed with exit code 1. ``` Ref: https://github.com/curl/curl/actions/runs/25859030402/job/75984082148?pr=21607 Dropping `HOMEBREW_NO_AUTO_UPDATE=1` was not enough to fix it. Closes #21608 --- .github/workflows/checksrc.yml | 13 +++++++------ .github/workflows/codeql.yml | 3 ++- .github/workflows/linux.yml | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index cc20781527c3..c05a48d6a0a4 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -74,7 +74,8 @@ jobs: - name: 'typos' timeout-minutes: 2 run: | - HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install typos-cli + /home/linuxbrew/.linuxbrew/bin/brew update + /home/linuxbrew/.linuxbrew/bin/brew install typos-cli eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" typos --version .github/scripts/typos.sh @@ -110,8 +111,7 @@ jobs: sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo apt-get -o Dpkg::Use-Pty=0 update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - pmccabe + sudo apt-get -o Dpkg::Use-Pty=0 install pmccabe - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -134,8 +134,7 @@ jobs: sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo apt-get -o Dpkg::Use-Pty=0 update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - libxml2-utils + sudo apt-get -o Dpkg::Use-Pty=0 install libxml2-utils - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -151,7 +150,9 @@ jobs: steps: - name: 'install prereqs' timeout-minutes: 2 - run: HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor + run: | + /home/linuxbrew/.linuxbrew/bin/brew update + /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ea8927aaf1a4..0423966c4988 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -80,7 +80,8 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 install \ libpsl-dev libbrotli-dev libidn2-dev libssh2-1-dev libssh-dev \ libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev libwolfssl-dev - HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi + /home/linuxbrew/.linuxbrew/bin/brew update + /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4a9fdcc28b34..6486c2f28b66 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -471,7 +471,8 @@ jobs: ${INSTALL_PACKAGES} \ ${MATRIX_INSTALL_PACKAGES} if [ -n "${INSTALL_PACKAGES_BREW}" ]; then - HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} + /home/linuxbrew/.linuxbrew/bin/brew update + /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} fi # Workaround for ubuntu-24.04-arm images having 0777 for /home/runner, # which breaks the test sshd server used in pytest. From 1a69c3a9c03667895b6ce7aaff8d60c82f9f7fb6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 16:33:23 +0200 Subject: [PATCH 0101/1062] cmake: unfold a line --- lib/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index aae466c677bd..0400971d461d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -110,8 +110,7 @@ if(SHARE_LIB_OBJECT) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") endif() target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) - set_target_properties(${LIB_OBJECT} PROPERTIES - POSITION_INDEPENDENT_CODE ON) + set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") From 27936d411a1e842a1bdd08db96e6600501de24f4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 10:57:34 +0200 Subject: [PATCH 0102/1062] lib: make `__STDC_VERSION__` literals `L` (where missing) --- lib/curl_setup.h | 2 +- lib/curl_sha512_256.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 0d4a8fa5777a..312df9d9c3d2 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1578,7 +1578,7 @@ typedef struct sockaddr_un { /* The code is compiled with C++ compiler. C++ always supports 'inline'. */ # define CURL_INLINE inline /* 'inline' keyword supported */ -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 (and later) supports 'inline' keyword */ # define CURL_INLINE inline /* 'inline' keyword supported */ #elif defined(__GNUC__) && __GNUC__ >= 3 diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 5c3bcdf800ca..02baf88f4d5b 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -301,7 +301,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) -# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 +# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901L # define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline)) # endif # endif From 5a869edb0fa3a79c0b4bf6e389afaa660c7f3bd8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 21:57:02 +0200 Subject: [PATCH 0103/1062] creds: drop redundant `CURL_UNCONST()`s Follow-up to 8f71d0fde515aa4c68002477356c35bd79927729 #21548 Closes #21612 --- lib/creds.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index 4767527ed945..1362f92c7905 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -77,19 +77,19 @@ CURLcode Curl_creds_create(const char *user, buf = ((char *)creds) + offsetof(struct Curl_creds, buf); creds->user = s = buf; if(ulen) - memcpy(s, CURL_UNCONST(user), ulen + 1); + memcpy(s, user, ulen + 1); creds->passwd = s = buf + ulen + 1; if(plen) - memcpy(s, CURL_UNCONST(passwd), plen + 1); + memcpy(s, passwd, plen + 1); creds->oauth_bearer = s = buf + ulen + 1 + plen + 1; if(olen) - memcpy(s, CURL_UNCONST(oauth_bearer), olen + 1); + memcpy(s, oauth_bearer, olen + 1); creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1; if(salen) - memcpy(s, CURL_UNCONST(sasl_authzid), salen + 1); + memcpy(s, sasl_authzid, salen + 1); creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1; if(sslen) - memcpy(s, CURL_UNCONST(sasl_service), sslen + 1); + memcpy(s, sasl_service, sslen + 1); out: if(!result) From cc5eb4aba98361094b4ca2e63ae1bf80b58af9ee Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 00:28:02 +0200 Subject: [PATCH 0104/1062] docs: fix a couple of typos Spotted by GitHub Code Quality Closes #21617 --- docs/FAQ.md | 6 +++--- lib/curl_sha512_256.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 19a3cf132171..7e794976538a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -176,7 +176,7 @@ fix and agree on a time schedule for publication etc. That way we produce a fix in a timely manner before the flaw is announced to the world, reducing the impact the problem risks having on existing users. -Security issues can also be taking to the curl security team by emailing +Security issues can also be taken to the curl security team by emailing security at curl.se (closed list of receivers, mails are not disclosed). ## Where do I buy commercial support for curl? @@ -368,7 +368,7 @@ transfer. Study the `-Q`/`--quote` option. Since curl is used for file transfers, you do not normally use curl to perform FTP commands without transferring anything. Therefore you must always specify a URL to transfer to/from even when doing custom FTP commands, or use `-I` -which implies the *no body*" option sent to libcurl. +which implies the *no body* option sent to libcurl. ## How can I disable the Accept: header? @@ -634,7 +634,7 @@ does for you, you can override those request methods by specifying `-X `curl -X DELETE [URL]`. It is thus pointless to do `curl -XGET [URL]` as GET would be used anyway. In -the same vein it is pointless to do `curl -X POST -d data [URL`. You can make +the same vein it is pointless to do `curl -X POST -d data [URL]`. You can make a fun and somewhat rare request that sends a request-body in a GET request with something like `curl -X GET -d data [URL]`. diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 02baf88f4d5b..0e602c6ae806 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -44,7 +44,7 @@ # define USE_OPENSSL_SHA512_256 1 # define HAS_SHA512_256_IMPLEMENTATION 1 # ifdef __NetBSD__ -/* Some NetBSD versions has a bug in SHA-512/256. +/* Some NetBSD versions have a bug in SHA-512/256. * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 * The problematic versions: * - NetBSD before 9.4 From ef068fc8b7c2392cf1f322aa4420c8e94e767c02 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 00:50:18 +0200 Subject: [PATCH 0105/1062] GHA: pin containers to hash (where missing) Fixing this with zizmor v1.25.0: ``` error[unpinned-images]: unpinned image references --> .github/workflows/linux-old.yml:59:5 59 | container: 'debian:stretch' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ container image is not pinned to a SHA256 hash = help: audit documentation -> https://docs.zizmor.sh/audits/#unpinned-images [...] ``` Ref: https://github.com/curl/curl/actions/runs/25890035949/job/76090925291?pr=21618 Sadly there is no automatic mechanism to bump them.. Also: - replace `debian-stretch` with its slim variant. - bump one of the two Alpine jobs from 3.20 to 3.23.4. Closes #21619 --- .github/workflows/linux-old.yml | 2 +- .github/workflows/linux.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 11644174623b..f0513646fbac 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -56,7 +56,7 @@ jobs: cmake-autotools: name: 'autotools & cmake' runs-on: ubuntu-latest - container: 'debian:stretch' + container: debian:stretch-20220622-slim@sha256:c5cd3ffceeb25b683bf5111ea89bf8049a177e00fb237235d48076a19cc80097 steps: - name: 'install prereqs' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6486c2f28b66..5fbe2eb11305 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -440,15 +440,15 @@ jobs: # https://ftpmirror.infania.net/slackware/slackware64-current/source/n/curl/curl.SlackBuild configure: --enable-debug --without-ssl --with-libssh2 --with-gssapi --enable-ares --without-ca-bundle --with-ca-path=/etc/ssl/certs # Docker Hub image that `container-job` executes in - container: 'andy5995/slackware-build-essential:15.0' + container: andy5995/slackware-build-essential:15.0@sha256:f4f2242999038a2c2deb4e5727187caaae92502a7daf8353068932621e1ec92f - name: 'Alpine MUSL https-rr' configure: --enable-debug --with-ssl --with-libssh2 --with-libidn2 --with-gssapi --enable-ldap --with-libpsl --enable-httpsrr --enable-ares --enable-threaded-resolver - container: 'alpine:3.20' + container: alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 # 3.23.4 - name: 'Alpine MUSL https-rr c-ares' configure: --enable-debug --with-ssl --with-libssh2 --with-libidn2 --with-gssapi --enable-ldap --with-libpsl --enable-httpsrr --enable-ares --disable-threaded-resolver - container: 'alpine:3.20' + container: alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc # 3.20.10 steps: - name: 'install prereqs' From 976eb1d50d56dcb1fe55a65ebe095d5012627f7e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 02:10:24 +0200 Subject: [PATCH 0106/1062] windows: update MS SDK versions in comments To make them more accurate. Also: - show Visual Studio version, where missing. - ease the formatting. - schannel_int.h: clang-tidy fallback code. Used: `rg -l --sort=path CERT_FIND_HAS_PRIVATE_KEY` Closes #21621 --- lib/cf-socket.c | 7 ++--- lib/curl_setup.h | 2 +- lib/curl_sspi.h | 10 +++---- lib/urldata.h | 2 +- lib/vtls/schannel.c | 17 ++++++------ lib/vtls/schannel_int.h | 54 +++++++++++++++++++------------------- lib/vtls/schannel_verify.c | 4 +-- src/tool_doswin.c | 2 +- 8 files changed, 50 insertions(+), 48 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index fc99ff39ed54..1e244671f394 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -132,14 +132,15 @@ static void tcpkeepalive(struct Curl_cfilter *cf, VERSION_GREATER_THAN_EQUAL)) { CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd); optval = curlx_sltosi(data->set.tcp_keepidle); -/* Offered by mingw-w64 v12+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */ #ifndef TCP_KEEPALIVE #define TCP_KEEPALIVE 3 #endif -/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef TCP_KEEPCNT #define TCP_KEEPCNT 16 #endif +/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #ifndef TCP_KEEPIDLE #define TCP_KEEPIDLE TCP_KEEPALIVE #endif @@ -1369,7 +1370,7 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf, #ifdef USE_WINSOCK -/* Offered by mingw-w64 v13+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v13+, MS SDK 7.0A/VS2010+ */ #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 312df9d9c3d2..9329f5605f02 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1542,7 +1542,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #endif #if defined(USE_UNIX_SOCKETS) && defined(_WIN32) -/* Offered by mingw-w64 v10+. MS SDK 10.17763/~VS2017+. */ +/* Offered by mingw-w64 v10+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR >= 10) # include #elif !defined(UNIX_PATH_MAX) /* Replicate logic present in afunix.h */ diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 3779d5175340..2bd7eb4be88e 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -52,25 +52,25 @@ extern PSecurityFunctionTable Curl_pSecFn; #define SP_NAME_NEGOTIATE "Negotiate" #define SP_NAME_KERBEROS "Kerberos" -/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #ifndef ISC_REQ_USE_HTTP_STYLE #define ISC_REQ_USE_HTTP_STYLE 0x01000000 #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_INVALID_PARAMETER #define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_DELEGATION_POLICY #define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_POLICY_NLTM_ONLY #define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_I_SIGNATURE_NEEDED #define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif diff --git a/lib/urldata.h b/lib/urldata.h index 6ad4666280d3..0cbe177d4af9 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -410,7 +410,7 @@ struct connectdata { /*************** Request - specific items ************/ #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - CtxtHandle *sslContext; /* mingw-w64 v9+. MS SDK 7.0A+. */ + CtxtHandle *sslContext; /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #endif #ifdef USE_NTLM diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 3eadeaef2548..9466de0e14d3 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -69,7 +69,7 @@ #define SCH_DEV_SHOWBOOL(x) do {} while(0) #endif -/* Offered by mingw-w64 v8+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 7.0A/VS2010+ */ #ifndef SP_PROT_TLS1_0_CLIENT #define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT #endif @@ -80,15 +80,16 @@ #define SP_PROT_TLS1_2_CLIENT 0x00000800 #endif -/* Offered by mingw-w64 v8+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef SP_PROT_TLS1_3_CLIENT #define SP_PROT_TLS1_3_CLIENT 0x00002000 #endif +/* Offered by mingw-w64 v8+, MS SDK 8.1/~VS2013+ */ #ifndef SCH_USE_STRONG_CRYPTO #define SCH_USE_STRONG_CRYPTO 0x00400000 #endif -/* Offered by mingw-w64 v10+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v10+, MS SDK 7.0A/VS2010+ */ #ifndef SECBUFFER_ALERT #define SECBUFFER_ALERT 17 #endif @@ -105,12 +106,12 @@ * #define failf(x, y, ...) curl_mprintf(y, __VA_ARGS__) */ -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 #endif -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_FIND_HAS_PRIVATE_KEY #define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT) #endif @@ -252,12 +253,12 @@ static const struct algo algs[] = { CIPHEROPTION(CALG_SHA_384), CIPHEROPTION(CALG_SHA_512), CIPHEROPTION(CALG_ECDH), -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifdef CALG_ECMQV CIPHEROPTION(CALG_ECMQV), #endif CIPHEROPTION(CALG_ECDSA), -/* Offered by mingw-w64 v7+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v7+, MS SDK 7.0A/VS2010+ */ #ifdef CALG_ECDH_EPHEM CIPHEROPTION(CALG_ECDH_EPHEM), #endif @@ -1733,7 +1734,7 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+. MS SDK 7.0A+. */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ /* When SSPI is used in combination with Schannel * we need the Schannel context to create the Schannel * binding to pass the IIS extended protection checks. diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index 496635f2c2f0..b65ff79926d1 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -42,7 +42,7 @@ #define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)(size_t)10) #endif -/* Offered by mingw-w64 v8+, MS SDK ~10+/~VS2022+ */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.17763.0/VS2017 15.8+ */ #ifndef SCH_CREDENTIALS_VERSION #define SCH_CREDENTIALS_VERSION 0x00000005 @@ -56,42 +56,42 @@ typedef enum _eTlsAlgorithmUsage { /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _CRYPTO_SETTINGS { - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ - DWORD dwMinBitLength; - DWORD dwMaxBitLength; -} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, *PCRYPTO_SETTINGS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _TLS_PARAMETERS { - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; -} TLS_PARAMETERS, * PTLS_PARAMETERS; + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, *PTLS_PARAMETERS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _SCH_CREDENTIALS { - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT *paCred; + HCERTSTORE hRootStore; - DWORD cMappers; + DWORD cMappers; struct _HMAPPER **aphMappers; - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; -} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, *PSCH_CREDENTIALS; -#endif /* SCH_CREDENTIALS_VERSION */ +#endif /* !SCH_CREDENTIALS_VERSION */ struct Curl_schannel_cred { CredHandle cred_handle; diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index bcea2c8c81cb..d61318625318 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -71,7 +71,7 @@ struct cert_chain_engine_config_win8 { DWORD dwExclusiveFlags; }; -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG #define CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG 0x00000001 #endif @@ -358,7 +358,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(Win8_compat) { -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 #endif diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 4b2a2a34b333..d787fda5597f 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -639,7 +639,7 @@ static struct TerminalSettings { LONG valid; } TerminalSettings; -/* Offered by mingw-w64 v7+. MS SDK ~10.16299/~VS2017+. */ +/* Offered by mingw-w64 v7+, MS SDK 10.0.10586.0/VS2015 Update 1+ */ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif From 88bb7f885fe8b3fb39b8b1de6106a05732bb9af5 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 14 May 2026 23:19:54 +0200 Subject: [PATCH 0107/1062] rustls: error on CURLOPT_CRLFILE with native CA store Closes #21614 --- lib/vtls/rustls.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 24b8597045d6..e9646d2dc0f8 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1042,6 +1042,12 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, config_builder, cr_verify_none); } else if(ssl_config->native_ca_store) { + if(conn_config->CRLfile) { + failf(data, "rustls: CRL file not supported with native CA store; " + "the platform verifier has no CRL attachment API"); + rustls_client_config_builder_free(config_builder); + return CURLE_NOT_BUILT_IN; + } result = init_config_builder_platform_verifier(data, config_builder); if(result != CURLE_OK) { rustls_client_config_builder_free(config_builder); From 913529411514d864ee2f377379d872dcab2c8317 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 15 May 2026 10:14:36 +0200 Subject: [PATCH 0108/1062] urlapi: deny hostnames with more than one trailing dot Or consisting of just a single dot. Such names cannot be resolved with DNS. While they *can* still be resolved with /etc/hosts or --resolve tricks, they easily cause internal problems because their trailing dots. Let's not allow them anymore. Closes #21622 --- lib/urlapi.c | 7 +++++++ tests/http/test_17_ssl_use.py | 8 +++----- tests/libtest/lib1560.c | 20 +++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 24b8bc244a53..a3111f9db6e8 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -475,6 +475,13 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; + else if((hlen >= 2) && + (hostname[hlen - 1] == '.') && (hostname[hlen - 2] == '.')) + /* more than one trailing dot is not allowed */ + return CURLUE_BAD_HOSTNAME; + else if((hlen == 1) && (hostname[0] == '.')) + /* just a single dot is not allowed */ + return CURLUE_BAD_HOSTNAME; } return CURLUE_OK; } diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index b2339dab5165..4a4dd0bf7e04 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -127,7 +127,7 @@ def test_17_03_trailing_dot(self, env: Env, proto, httpd, nghttpx): # the SNI the server received is without trailing dot assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}' - # use hostname with double trailing dot, verify handshake + # use hostname with double trailing dot @pytest.mark.parametrize("proto", Env.http_protos()) def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx): curl = CurlClient(env=env) @@ -142,10 +142,8 @@ def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx): if proto != 'h3': # we proxy h3 assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}' assert False, f'should not have succeeded: {r.json}' - # 7 - Rustls rejects a servername with .. during setup - # 35 - LibreSSL rejects setting an SNI name with trailing dot - # 60 - peer name matching failed against certificate - assert r.exit_code in [7, 35, 60], f'{r}' + # 3 - not allowed in the URL + assert r.exit_code in [3], f'{r}' # use ip address for connect @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 6c3fff9bc999..e833c304e371 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -196,20 +196,19 @@ static const struct testcase get_parts_list[] = { "http://host:00080/", "http | [11] | [12] | [13] | host | 80 | / | [16] | [17]", 0, 0, CURLUE_OK }, - { /* Single dot host - technically valid in some contexts but often - rejected */ + { /* Single dot host - not ok */ "http://./", - "http | [11] | [12] | [13] | . | [15] | / | [16] | [17]", - 0, 0, CURLUE_OK }, + "", + 0, 0, CURLUE_BAD_HOSTNAME }, { /* Host starting with a dash (RFC 1123 technically allows it, but many parsers don't) */ "http://-atest/", "http | [11] | [12] | [13] | -atest | [15] | / | [16] | [17]", 0, 0, CURLUE_OK }, - { /* Multiple trailing dots, not okay in DNS but works in /etc/hosts */ + { /* Multiple trailing dots is not okey */ "http://example.com../", - "http | [11] | [12] | [13] | example.com.. | [15] | / | [16] | [17]", - 0, 0, CURLUE_OK }, + "", + 0, 0, CURLUE_BAD_HOSTNAME }, { /* Empty IPv6 Zone ID */ "http://[fe80::1%]/", "", 0, 0, CURLUE_BAD_IPV6 }, @@ -626,6 +625,13 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + {"http://hej./", "http://hej./", 0, 0, CURLUE_OK}, + {"http://hej../", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://hej.../", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://hej..../index.html", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://.", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://...", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"018.0.0.0", "http://018.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, {"08", "http://08/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, {"0", "http://0.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, From eb9b253d660db179b1aadd77c425de5c71f18526 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 13:32:46 +0200 Subject: [PATCH 0109/1062] libssh: add support for SHA256 host public keys Reported-by: Joshua Rogers Fixes #21605 Closes #21607 --- .github/workflows/windows.yml | 4 +- docs/cmdline-opts/hostpubsha256.md | 3 - .../CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md | 4 - lib/setopt.c | 12 +-- lib/vssh/libssh.c | 86 ++++++++++++++++--- lib/vssh/libssh2.c | 2 +- src/tool_getparam.c | 5 +- src/tool_libinfo.c | 4 - src/tool_libinfo.h | 1 - tests/data/test3021 | 4 - tests/data/test3022 | 4 - 11 files changed, 83 insertions(+), 46 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 696fd09bab9a..67db3798fef2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -522,11 +522,9 @@ jobs: fi fi if [ -n "${MATRIX_OPENSSH}" ]; then # OpenSSH-Windows - TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004' # SCP + TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004 ~3022' # SCP if [[ "${MATRIX_INSTALL} " = *'libssh '* ]]; then TFLAGS+=' ~614' # 'SFTP pre-quote chmod' SFTP, pre-quote, directory - else - TFLAGS+=' ~3022' # 'SCP correct sha256 host key' SCP, server sha256 key check fi fi if [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows' ]; then diff --git a/docs/cmdline-opts/hostpubsha256.md b/docs/cmdline-opts/hostpubsha256.md index e695a10cb588..a92dbe5d7cb9 100644 --- a/docs/cmdline-opts/hostpubsha256.md +++ b/docs/cmdline-opts/hostpubsha256.md @@ -18,6 +18,3 @@ Example: Pass a string containing a Base64-encoded SHA256 hash of the remote host's public key. curl refuses the connection with the host unless the hashes match. - -This feature requires libcurl to be built with libssh2 and does not work with -other SSH backends. diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md index 43a6d9e708e5..fce7e58f04bf 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md @@ -73,10 +73,6 @@ int main(void) } ~~~ -# NOTES - -Requires the libssh2 backend. - # %AVAILABILITY% # RETURN VALUE diff --git a/lib/setopt.c b/lib/setopt.c index 61d87be06fcf..0fc5ec7e87fa 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2342,6 +2342,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * for validation purposes. */ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: + /* + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. + */ + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); case CURLOPT_SSH_KNOWNHOSTS: /* * Store the filename to read known hosts from. @@ -2349,12 +2355,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); #endif #ifdef USE_LIBSSH2 - case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: - /* - * Option to allow for the SHA256 of the host public key to be checked - * for validation purposes. - */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); case CURLOPT_SSH_HOSTKEYDATA: /* * Custom client data to pass to the SSH keyfunc callback diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 49f9d3f93d2a..d37dcd712fa7 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -57,6 +57,7 @@ #include "multiif.h" #include "select.h" #include "vssh/vssh.h" +#include "curlx/base64.h" /* for curlx_base64_encode() */ #ifdef HAVE_UNISTD_H #include @@ -109,12 +110,14 @@ static CURLcode sftp_error_to_CURLE(int err) } /* Multiple options: - * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 + * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256] is set with a SHA256 + * hash. + * 2. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 * hash (90s style auth, not sure we should have it here) - * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first + * 3. data->set.ssh_keyfunc callback is set. Then we do trust on first * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE * is returned by it. - * 3. none of the above. We only accept if it is present on known hosts. + * 4. none of the above. We only accept if it is present on known hosts. * * Returns SSH_OK or SSH_ERROR. */ @@ -122,8 +125,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) { int rc; ssh_key pubkey; - size_t hlen; - unsigned char *hash = NULL; + unsigned char *hash_sha256 = NULL; + size_t hlen_sha256; + unsigned char *hash_md5 = NULL; + size_t hlen_md5; char *found_base64 = NULL; char *known_base64 = NULL; int vstate; @@ -139,20 +144,75 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) if(rc != SSH_OK) return rc; + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + const char *pubkey_sha256 = + data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + char *fingerprint_b64 = NULL; + size_t fingerprint_b64_len; + size_t pub_pos = 0; + size_t b64_pos = 0; + + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, + &hash_sha256, &hlen_sha256); + if(rc != SSH_OK || hlen_sha256 != 32) { + failf(data, "Denied establishing ssh session: " + "SHA256 fingerprint not available"); + goto cleanup; + } + + if(curlx_base64_encode((const uint8_t *)hash_sha256, 32, &fingerprint_b64, + &fingerprint_b64_len) != CURLE_OK) { + rc = SSH_ERROR; + goto cleanup; + } + + infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + + /* Find the position of any = padding characters in the public key */ + while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { + pub_pos++; + } + + /* Find the position of any = padding characters in the base64 coded + * hostkey fingerprint */ + while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { + b64_pos++; + } + + /* Before we authenticate we check the hostkey's SHA256 fingerprint + * against a known fingerprint, if available. + */ + if((pub_pos != b64_pos) || + strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { + failf(data, + "Denied establishing ssh session: mismatch SHA256 fingerprint. " + "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); + curlx_free(fingerprint_b64); + rc = SSH_ERROR; + goto cleanup; + } + + curlx_free(fingerprint_b64); + + rc = SSH_OK; + goto cleanup; + } + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - int i; - char md5buffer[33]; const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + char md5buffer[33]; + int i; - rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); - if(rc != SSH_OK || hlen != 16) { + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, + &hash_md5, &hlen_md5); + if(rc != SSH_OK || hlen_md5 != 16) { failf(data, "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } for(i = 0; i < 16; i++) - curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash_md5[i]); infof(data, "SSH MD5 fingerprint: %s", md5buffer); @@ -297,8 +357,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) /* !checksrc! disable BANNEDFUNC 1 */ free(known_base64); /* allocated by libssh, deallocate with system free */ } - if(hash) - ssh_clean_pubkey_hash(&hash); + if(hash_sha256) + ssh_clean_pubkey_hash(&hash_sha256); + if(hash_md5) + ssh_clean_pubkey_hash(&hash_md5); ssh_key_free(pubkey); if(knownhostsentry) { ssh_knownhosts_entry_free(knownhostsentry); diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 118bc594f641..0226ebfd2754 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -57,7 +57,7 @@ #include "curlx/fopen.h" #include "vssh/vssh.h" #include "curlx/strparse.h" -#include "curlx/base64.h" /* for base64 encoding/decoding */ +#include "curlx/base64.h" /* for curlx_base64_encode() */ static const char *sftp_libssh2_strerror(unsigned long err) { diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 176d3ebc3849..6c69acd95bf3 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -2768,10 +2768,7 @@ static ParameterError opt_string(struct OperationConfig *config, } break; case C_HOSTPUBSHA256: /* --hostpubsha256 */ - if(!feature_libssh2) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); + err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); break; case C_TLSUSER: /* --tlsuser */ if(!feature_tls_srp) diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 5a5382c00701..9aee23428090 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -71,7 +71,6 @@ bool feature_http2 = FALSE; bool feature_http3 = FALSE; bool feature_httpsproxy = FALSE; bool feature_libz = FALSE; -bool feature_libssh2 = FALSE; bool feature_ntlm = FALSE; bool feature_ntlm_wb = FALSE; bool feature_spnego = FALSE; @@ -183,9 +182,6 @@ CURLcode get_libcurl_info(void) ++feature_count; } - feature_libssh2 = curlinfo->age >= CURLVERSION_FOURTH && - curlinfo->libssh_version && - !strncmp("libssh2", curlinfo->libssh_version, 7); return CURLE_OK; } diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index ddc41a133867..e8c3517a9b2a 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -54,7 +54,6 @@ extern bool feature_http2; extern bool feature_http3; extern bool feature_httpsproxy; extern bool feature_libz; -extern bool feature_libssh2; extern bool feature_ntlm; extern bool feature_ntlm_wb; extern bool feature_spnego; diff --git a/tests/data/test3021 b/tests/data/test3021 index b7fe479cb2bc..1aa973a68843 100644 --- a/tests/data/test3021 +++ b/tests/data/test3021 @@ -17,10 +17,6 @@ test # Client-side -# so far only the libssh2 backend supports SHA256 - -libssh2 - sftp diff --git a/tests/data/test3022 b/tests/data/test3022 index 057242b0f513..6d692a817ef3 100644 --- a/tests/data/test3022 +++ b/tests/data/test3022 @@ -17,10 +17,6 @@ test # Client-side -# so far only the libssh2 backend supports SHA256 - -libssh2 - scp From 3da249e1f0716c06644ed3522a37a8bf81808012 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 14:35:21 +0200 Subject: [PATCH 0110/1062] gsasl: fix potential double free Also: - require libgsasl 1.6.0+ (2010-12-14) for a `gsasl_finish()` that handles a NULL argument. Ref: https://gitlab.com/gsasl/gsasl/-/commit/b550032df8488a9ceaa3cfd4c634947d8f219717 Reported-by: Joshua Rogers (Aisle Research) Closes #21609 --- docs/INTERNALS.md | 1 + lib/vauth/gsasl.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index c145690a2caf..77f2e4373576 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -30,6 +30,7 @@ We aim to support these or later versions. - c-ares 1.16.0 (2020-03-13) - GnuTLS 3.6.5 (2018-12-01) - libidn2 2.0.0 (2017-03-29) +- libgsasl 1.6.0 (2010-12-14) - LibreSSL 2.9.1 (2019-04-22) - libssh 0.9.0 (2019-06-28) - libssh2 1.9.0 (2019-06-20) diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 3ea77eecd1b4..10a83fdb0998 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -32,6 +32,10 @@ #include +#if GSASL_VERSION_NUMBER < 0x010600 +#error "requires libgsasl 1.6.0+" +#endif + bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, const char *mech, struct gsasldata *gsasl) @@ -47,6 +51,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client); if(res != GSASL_OK) { gsasl_done(gsasl->ctx); + gsasl->ctx = NULL; return FALSE; } From 4780e509aade2b17ef7a7cbf2212288ce88e48d7 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 00:37:37 +0200 Subject: [PATCH 0111/1062] tidy-up: prefer "initialize" with a 'z' To match the majority of usage in source. Closes #21618 --- include/curl/curl.h | 2 +- lib/conncache.c | 4 ++-- lib/conncache.h | 2 +- lib/cshutdn.c | 4 ++-- lib/cshutdn.h | 2 +- lib/curl_sha512_256.c | 8 ++++---- lib/curl_share.c | 2 +- lib/curlx/strerr.c | 2 +- lib/imap.c | 2 +- lib/pingpong.c | 6 +++--- lib/pingpong.h | 2 +- lib/pop3.c | 4 ++-- lib/select.h | 2 +- lib/smtp.c | 8 ++++---- lib/socketpair.h | 2 +- lib/socks_sspi.c | 2 +- lib/strerror.c | 2 +- lib/vauth/digest.c | 4 ++-- lib/vauth/ntlm.c | 2 +- lib/vauth/ntlm_sspi.c | 2 +- lib/vquic/vquic.c | 2 +- lib/vssh/libssh.c | 6 +++--- lib/vssh/ssh.h | 2 +- lib/vtls/openssl.c | 2 +- projects/vms/curlmsg.msg | 2 +- src/tool_cfgable.c | 2 +- src/tool_operate.c | 2 +- tests/data/test1538 | 2 +- 28 files changed, 42 insertions(+), 42 deletions(-) diff --git a/include/curl/curl.h b/include/curl/curl.h index 6961a6c4c168..76ba52525294 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -593,7 +593,7 @@ typedef enum { CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind that failed */ - CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialize ENGINE */ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not accepted and we failed to login */ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ diff --git a/lib/conncache.c b/lib/conncache.c index 33fb68b124c6..fe180be1f53d 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -122,7 +122,7 @@ void Curl_cpool_init(struct cpool *cpool, cpool->idata = idata; cpool->share = share; - cpool->initialised = TRUE; + cpool->initialized = TRUE; } /* Return the "first" connection in the pool or NULL. */ @@ -230,7 +230,7 @@ static void cpool_discard_conn(struct cpool *cpool, void Curl_cpool_destroy(struct cpool *cpool) { - if(cpool && cpool->initialised && cpool->idata) { + if(cpool && cpool->initialized && cpool->idata) { struct connectdata *conn; struct Curl_sigpipe_ctx pipe_ctx; diff --git a/lib/conncache.h b/lib/conncache.h index 7cee4d4729a5..71940d724c02 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -56,7 +56,7 @@ struct cpool { struct Curl_easy *idata; /* internal handle for maintenance */ struct Curl_share *share; /* != NULL if pool belongs to share */ BIT(locked); - BIT(initialised); + BIT(initialized); }; /* Init the pool, pass multi only if pool is owned by it. diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 27b4a9f0dd31..f284c0ea7c89 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -322,14 +322,14 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, DEBUGASSERT(multi); cshutdn->multi = multi; Curl_llist_init(&cshutdn->list, NULL); - cshutdn->initialised = TRUE; + cshutdn->initialized = TRUE; return 0; /* good */ } void Curl_cshutdn_destroy(struct cshutdn *cshutdn, struct Curl_easy *data) { - if(cshutdn->initialised && data) { + if(cshutdn->initialized && data) { int timeout_ms = 0; /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD diff --git a/lib/cshutdn.h b/lib/cshutdn.h index b2e83f3d1aad..8479524993cf 100644 --- a/lib/cshutdn.h +++ b/lib/cshutdn.h @@ -54,7 +54,7 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, struct cshutdn { struct Curl_llist list; /* connections being shut down */ struct Curl_multi *multi; /* the multi owning this */ - BIT(initialised); + BIT(initialized); }; /* Init as part of the given multi handle. */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 0e602c6ae806..f8f2053b5d6c 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -103,7 +103,7 @@ typedef EVP_MD_CTX *Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return CURLE_OK if succeed, @@ -232,7 +232,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) typedef struct sha512_256_ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -435,7 +435,7 @@ struct Curl_sha512_256ctx { typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -461,7 +461,7 @@ static CURLcode Curl_sha512_256_init(void *context) ctx->H[6] = UINT64_C(0x2B0199FC2C85B8AA); ctx->H[7] = UINT64_C(0x0EB72DDC81C52CA2); - /* Initialise number of bytes and high part of number of bits. */ + /* Initialize number of bytes and high part of number of bits. */ ctx->count = UINT64_C(0); ctx->count_bits_hi = UINT64_C(0); diff --git a/lib/curl_share.c b/lib/curl_share.c index 386a2b547d33..94469bc3ef6e 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -260,7 +260,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_CONNECT: /* It is safe to set this option several times on a share. */ - if(!share->cpool.initialised) { + if(!share->cpool.initialized) { Curl_cpool_init(&share->cpool, share->admin, share, 103); } break; diff --git a/lib/curlx/strerr.c b/lib/curlx/strerr.c index b53173c57839..5fc3bc003bea 100644 --- a/lib/curlx/strerr.c +++ b/lib/curlx/strerr.c @@ -190,7 +190,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len) p = "Winsock library is not ready"; break; case WSANOTINITIALISED: - p = "Winsock library not initialised"; + p = "Winsock library not initialized"; break; case WSAVERNOTSUPPORTED: p = "Winsock version not supported"; diff --git a/lib/imap.c b/lib/imap.c index 0a4cb5b7b72f..9eb79e5aefad 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1795,7 +1795,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn, static CURLcode imap_parse_url_path(struct Curl_easy *data, struct IMAP *imap) { - /* The imap struct is already initialised in imap_connect() */ + /* The imap struct is already initialized in imap_connect() */ CURLcode result = CURLE_OK; const char *begin = &data->state.up.path[1]; /* skip leading slash */ const char *ptr = begin; diff --git a/lib/pingpong.c b/lib/pingpong.c index 44e424418d88..6952d659a908 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -120,13 +120,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow) { - DEBUGASSERT(!pp->initialised); + DEBUGASSERT(!pp->initialized); pp->nread_resp = 0; pp->response = *pnow; /* start response time-out */ pp->pending_resp = TRUE; curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD); curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD); - pp->initialised = TRUE; + pp->initialized = TRUE; } /*********************************************************************** @@ -389,7 +389,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, CURLcode Curl_pp_disconnect(struct pingpong *pp) { - if(pp->initialised) { + if(pp->initialized) { curlx_dyn_free(&pp->sendbuf); curlx_dyn_free(&pp->recvbuf); memset(pp, 0, sizeof(*pp)); diff --git a/lib/pingpong.h b/lib/pingpong.h index 864f2c68350e..02f496123036 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -64,7 +64,7 @@ struct pingpong { CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn); bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn, const char *ptr, size_t len, int *code); - BIT(initialised); + BIT(initialized); BIT(pending_resp); /* set TRUE when a server response is pending or in progress, and is cleared once the last response is read */ diff --git a/lib/pop3.c b/lib/pop3.c index 7dbeefb6e7a2..3036ce717c81 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -231,7 +231,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) */ static CURLcode pop3_parse_url_path(struct Curl_easy *data) { - /* The POP3 struct is already initialised in pop3_connect() */ + /* The POP3 struct is already initialized in pop3_connect() */ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); const char *path = &data->state.up.path[1]; /* skip leading path */ @@ -1439,7 +1439,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) pop3c->preftype = POP3_TYPE_ANY; Curl_sasl_init(&pop3c->sasl, data, &saslpop3); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(pp, Curl_pgrs_now(data)); /* Parse the URL options */ diff --git a/lib/select.h b/lib/select.h index 87b695463fcb..dbbace4527e7 100644 --- a/lib/select.h +++ b/lib/select.h @@ -131,7 +131,7 @@ struct easy_pollset { #define CURL_EASY_POLLSET_MAGIC 0x7a657370 #endif -/* allocate and initialise */ +/* allocate and initialize */ struct easy_pollset *Curl_pollset_create(void); /* Initialize before first use */ diff --git a/lib/smtp.c b/lib/smtp.c index b5c425cd7c18..dee7a329ca1b 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -182,7 +182,7 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn, static CURLcode smtp_parse_url_path(struct Curl_easy *data, struct smtp_conn *smtpc) { - /* The SMTP struct is already initialised in smtp_connect() */ + /* The SMTP struct is already initialized in smtp_connect() */ const char *path = &data->state.up.path[1]; /* skip leading path */ char localhost[HOSTNAME_MAX + 1]; @@ -608,7 +608,7 @@ static void smtp_state(struct Curl_easy *data, * * smtp_perform_ehlo() * - * Sends the EHLO command to not only initialise communication with the ESMTP + * Sends the EHLO command to not only initialize communication with the ESMTP * server but to also obtain a list of server side supported capabilities. */ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, @@ -635,7 +635,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, * * smtp_perform_helo() * - * Sends the HELO command to initialise communication with the SMTP server. + * Sends the HELO command to initialize communication with the SMTP server. */ static CURLcode smtp_perform_helo(struct Curl_easy *data, struct smtp_conn *smtpc) @@ -1677,7 +1677,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) /* Initialize the SASL storage */ Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data)); /* Parse the URL options */ diff --git a/lib/socketpair.h b/lib/socketpair.h index 0427e72fc53d..fd08c879dac6 100644 --- a/lib/socketpair.h +++ b/lib/socketpair.h @@ -27,7 +27,7 @@ #ifndef CURL_DISABLE_SOCKETPAIR -/* return < 0 for failure to initialise */ +/* return < 0 for failure to initialize */ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking); void Curl_wakeup_destroy(curl_socket_t socks[2]); diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index a4cc9796b008..7c043093e1d9 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -164,7 +164,7 @@ static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { - failf(data, "Failed to initialise security context."); + failf(data, "Failed to initialize security context."); return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } diff --git a/lib/strerror.c b/lib/strerror.c index 1e97c2829db5..e1089f3233d9 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -172,7 +172,7 @@ const char *curl_easy_strerror(CURLcode error) return "Can not set SSL crypto engine as default"; case CURLE_SSL_ENGINE_INITFAILED: - return "Failed to initialise SSL crypto engine"; + return "Failed to initialize SSL crypto engine"; case CURLE_SEND_ERROR: return "Failed sending data to the peer"; diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 9843fd8ef71f..6d935fc99ebf 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -231,7 +231,7 @@ static bool auth_digest_get_key_value(const char *chlg, const char *key, static void auth_digest_get_qop_values(const char *options, int *value) { struct Curl_str out; - /* Initialise the output */ + /* Initialize the output */ *value = 0; while(!curlx_str_until(&options, &out, 32, ',')) { @@ -520,7 +520,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(digest->nonce) before = TRUE; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_digest_cleanup(digest); for(;;) { diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 121c6cae561f..f4af3755f14e 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -458,7 +458,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (void)service; (void)host; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); ntlmbuf = curl_maprintf(NTLMSSP_SIGNATURE "%c" diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index e3ade65c96db..bd33dceb55f3 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -90,7 +90,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, const char *service = Curl_creds_has_sasl_service(creds) ? Curl_creds_sasl_service(creds) : default_service; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); /* Query the security package for NTLM */ diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 0040ad671fb8..cf4bc5a65fe8 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -500,7 +500,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) { - /* fully initialise this on each call to `recvmsg()`. There seem to + /* fully initialize this on each call to `recvmsg()`. There seem to * operating systems out there that mess with `msg_iov.iov_len`. */ memset(&msg, 0, sizeof(msg)); msg_iov.iov_base = buf; diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index d37dcd712fa7..149b4cce0ce9 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -1871,7 +1871,7 @@ static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, static void sshc_cleanup(struct ssh_conn *sshc) { - if(sshc->initialised) { + if(sshc->initialized) { if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; @@ -1921,7 +1921,7 @@ static void sshc_cleanup(struct ssh_conn *sshc) curlx_dyn_free(&sshc->readdir_buf); curlx_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); - sshc->initialised = FALSE; + sshc->initialized = FALSE; } } @@ -2566,7 +2566,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2); - sshc->initialised = TRUE; + sshc->initialized = TRUE; if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index 44e72ab802bf..24309f5207cf 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -195,7 +195,7 @@ struct ssh_conn { const char *readdir_filename; /* points within readdir_attrs */ const char *readdir_longentry; char *readdir_tmp; - BIT(initialised); + BIT(initialized); #elif defined(USE_LIBSSH2) LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 0178acfe5930..2eeb2f349d29 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1671,7 +1671,7 @@ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name) char buf[256]; ENGINE_free(e); - failf(data, "Failed to initialise SSL Engine '%s': %s", + failf(data, "Failed to initialize SSL Engine '%s': %s", name, ossl_strerror(ERR_get_error(), buf, sizeof(buf))); result = CURLE_SSL_ENGINE_INITFAILED; e = NULL; diff --git a/projects/vms/curlmsg.msg b/projects/vms/curlmsg.msg index 8d428e88b6b2..b02fe5649c85 100644 --- a/projects/vms/curlmsg.msg +++ b/projects/vms/curlmsg.msg @@ -111,7 +111,7 @@ LDAP_INVALID_URL FILESIZE_EXCEEDED USE_SSL_FAILED SEND_FAIL_REWIND -SSL_ENGINE_INITFAILED +SSL_ENGINE_INITFAILED LOGIN_DENIED TFTP_NOTFOUND TFTP_PERM diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 515df19073c9..e0b60b7dbab8 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -316,7 +316,7 @@ CURLcode globalconf_init(void) _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; #endif - /* Initialise the global config */ + /* Initialize the global config */ global->showerror = FALSE; /* show errors when silent */ global->styled_output = TRUE; /* enable detection */ global->parallel_max = PARALLEL_DEFAULT; diff --git a/src/tool_operate.c b/src/tool_operate.c index c5ac095cb910..a8d928f496fd 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2459,7 +2459,7 @@ CURLcode operate(int argc, argv_item_t argv[]) } else { if(global->libcurl) { - /* Initialise the libcurl source output */ + /* Initialize the libcurl source output */ result = easysrc_init(); } diff --git a/tests/data/test1538 b/tests/data/test1538 index e989fdd17f67..51580576ee2c 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -94,7 +94,7 @@ e62: Unknown error e63: Maximum file size exceeded e64: Requested SSL level failed e65: Send failed since rewinding of the data stream failed -e66: Failed to initialise SSL crypto engine +e66: Failed to initialize SSL crypto engine e67: Login denied e68: TFTP: File Not Found e69: TFTP: Access Violation From de28c9cfadc2daaa5db31d42a6186846c530f518 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 11:54:14 +0200 Subject: [PATCH 0112/1062] rustls: drop two wrong leftover casts to `ssize_t` While both source and target types are already `size_t`. Spotted by GitHub Code Quality Follow-up to b7c676d13f0988bde9bb0e4c3cfc688072cdb2e0 #17593 Closes #21625 --- lib/vtls/rustls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index e9646d2dc0f8..57591949527c 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -337,7 +337,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else blen = 0; - *pnwritten += (ssize_t)backend->plain_out_buffered; + *pnwritten += backend->plain_out_buffered; backend->plain_out_buffered = 0; } @@ -370,7 +370,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } else - *pnwritten += (ssize_t)plainwritten; + *pnwritten += plainwritten; out: CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", From 71430e87fd3bcf8960b26325c9dd2b398876c121 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 14 May 2026 00:06:03 +0200 Subject: [PATCH 0113/1062] strparse: make curlx_str_until() accept zero for 'max' When asked to parse for a string with max zero bytes, it will always return error and no longer trigger an assert. This saves the caller from having to check for this condition. Closes #21600 --- lib/curlx/strparse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index 3082a6740adc..736f62050438 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -45,7 +45,7 @@ void curlx_str_trim(struct Curl_str *out, size_t len) } /* Get a word until the first DELIM or end of string. At least one byte long. - return non-zero on error */ + return non-zero on error. If 'max' is zero, it will always return error. */ int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, char delim) { @@ -54,7 +54,6 @@ int curlx_str_until(const char **linep, struct Curl_str *out, DEBUGASSERT(linep); DEBUGASSERT(*linep); DEBUGASSERT(out); - DEBUGASSERT(max); DEBUGASSERT(delim); s = *linep; From a15cfeb10057f2462ab2276c7d28aeb8baff9b8e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 14 May 2026 23:46:45 +0200 Subject: [PATCH 0114/1062] cookie: compare path case sensitively Verify with test 1645 Reported-by: Joshua Rogers Closes #21616 --- lib/cookie.c | 4 +-- tests/data/Makefile.am | 2 +- tests/data/test1645 | 73 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1645 diff --git a/lib/cookie.c b/lib/cookie.c index 13732d927440..7ecef3a666bd 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -854,7 +854,7 @@ static bool replace_existing(struct Curl_easy *data, else cllen = strlen(clist->path); - if(curl_strnequal(clist->path, co->path, cllen)) { + if(!strncmp(clist->path, co->path, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); return FALSE; @@ -878,7 +878,7 @@ static bool replace_existing(struct Curl_easy *data, /* the domains were identical */ if(clist->path && co->path && - !curl_strequal(clist->path, co->path)) + strcmp(clist->path, co->path)) replace_old = FALSE; else if(!clist->path != !co->path) replace_old = FALSE; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 7216d0a7c968..ec9620b9cd95 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -217,7 +217,7 @@ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ test1628 \ \ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ -test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ +test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1645 b/tests/data/test1645 new file mode 100644 index 000000000000..bdb0e24792d4 --- /dev/null +++ b/tests/data/test1645 @@ -0,0 +1,73 @@ + + + + +HTTP +HTTP GET +cookies +cookiejar + + +# Server-side + + + +HTTP/1.1 200 OK +Content-Length: 4 +Content-Type: text/html +Funny-head: yesyes +Set-Cookie: name=value; domain=test.curl; path=/we/want + +boo + + +HTTP/1.1 200 OK +Content-Length: 4 +Content-Type: text/html +Funny-head: yesyes +Set-Cookie: name=value; domain=test.curl; path=/WE/WANT + +boo + + + +# Client-side + + +http + + +cookies for paths using different case + + +http://test.curl:%HTTPPORT/we/want/ http://test.curl:%HTTPPORT/WE/WANT/%TESTNUMBER0002 -c %LOGDIR/jar%TESTNUMBER.txt --resolve test.curl:%HTTPPORT:%HOSTIP + + +cookies + + + +# Verify data after the test has been "shot" + + +GET /we/want/ HTTP/1.1 +Host: test.curl:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /WE/WANT/%TESTNUMBER0002 HTTP/1.1 +Host: test.curl:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +.test.curl TRUE /WE/WANT FALSE 0 name value +.test.curl TRUE /we/want FALSE 0 name value + + + From aafbe089a88c42bf81ff8cf868f20263661ad3e4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 15 May 2026 14:56:24 +0200 Subject: [PATCH 0115/1062] CURLOPT_SHARE: warn about early remove Add a warning to removing a SHARE from an EASY handle before it is finished. Closes #21633 --- docs/libcurl/opts/CURLOPT_SHARE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_SHARE.md b/docs/libcurl/opts/CURLOPT_SHARE.md index 2c6a496593fd..907e4f280519 100644 --- a/docs/libcurl/opts/CURLOPT_SHARE.md +++ b/docs/libcurl/opts/CURLOPT_SHARE.md @@ -43,6 +43,9 @@ if no share was used. Set this option to NULL again to stop using that share object. +Warning: adding a *share* and then setting it to NULL while the transfer +is ongoing is discouraged and may lead to undefined behavior. + # DEFAULT NULL From 831a1514843bfa4d4d006627fb84c06ced8ea700 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 15 May 2026 17:04:26 +0200 Subject: [PATCH 0116/1062] urlapi: consume trailing dots after IPv4 numerical addresses If the hostname is specified as an IPv4 numerical address and it is followed by a single dot, acccept that as a valid IPv4 and remove the dot when normalizing. This prevents otherwise legitimate IPv4 hostnames to have trailing dots. Seems to match what browsers do. Extended test 1560 to verify. Closes #21635 --- lib/urlapi.c | 28 ++++++++++++++++++++-------- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 21 +++++++++++++++++++-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index a3111f9db6e8..a2d3c7e35ade 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -496,6 +496,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * Output the "normalized" version of that input string in plain quad decimal * integers. * + * A single dot following the numerical address is accepted and "swallowed" as + * if it was never there. + * * Returns the host type. * * @unittest 1675 @@ -527,17 +530,26 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) else rc = curlx_str_number(&c, &l, UINT_MAX); - if(rc) - return HOST_NAME; - - parts[n] = (unsigned int)l; + if(rc) { + if(!n || (rc != STRE_NO_NUM) || *c) + return HOST_NAME; + n--; + } + else + parts[n] = (unsigned int)l; switch(*c) { case '.': - if(n == 3) - return HOST_NAME; - n++; - c++; + if(n == 3) { + if(c[1]) + /* something follows this dot */ + return HOST_NAME; + done = TRUE; + } + else { + n++; + c++; + } break; case '\0': diff --git a/tests/data/test1560 b/tests/data/test1560 index e0d792800b2e..e27229739f8e 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -37,7 +37,7 @@ lib%TESTNUMBER success -Allocations: 3100 +Allocations: 3200 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index e833c304e371..533a44e98376 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -625,6 +625,23 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + {"https://127.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.1.:443", "https://127.0.0.1:443/", 0, 0, CURLUE_OK}, + {"https://127.1.?moo", "https://127.0.0.1/?moo", 0, 0, CURLUE_OK}, + {"https://127.1.#moo", "https://127.0.0.1/#moo", 0, 0, CURLUE_OK}, + {"https://127.1.a", "https://127.1.a/", 0, 0, CURLUE_OK}, + {"https://127.1..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..:443", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..?moo", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..#moo", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1.1.", "https://127.1.0.1/", 0, 0, CURLUE_OK}, + {"https://127.1.1./foo", "https://127.1.0.1/foo", 0, 0, CURLUE_OK}, + {"https://127.1.1.1.", "https://127.1.1.1/", 0, 0, CURLUE_OK}, + {"https://127.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.0xff.", "https://127.0.0.255/", 0, 0, CURLUE_OK}, + {"https://127.0.0.1..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.0.0.256..", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"http://hej./", "http://hej./", 0, 0, CURLUE_OK}, {"http://hej../", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"http://hej.../", "", 0, 0, CURLUE_BAD_HOSTNAME}, @@ -743,9 +760,9 @@ static const struct urltestcase get_url_list[] = { {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK}, {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, - {"https://0111.02.0x3.", "https://0111.02.0x3./", 0, 0, CURLUE_OK}, + {"https://0111.02.0x3.", "https://73.2.0.3/", 0, 0, CURLUE_OK}, {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK}, - {"https://0111.02.030.", "https://0111.02.030./", 0, 0, CURLUE_OK}, + {"https://0111.02.030.", "https://73.2.0.24/", 0, 0, CURLUE_OK}, {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK}, /* IPv4 numerical overflows or syntax errors will not normalize */ From 066478f6346a2d987a9ecc3bd3bf45764d69c1c4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 13 May 2026 18:20:33 +0200 Subject: [PATCH 0117/1062] src: add `curlx_memzero()` to clear buffers securely To safely zero memory, introduce `curlx_memzero()`, and map it to `memset_s()` (C11) or `memset_explicit()` (C23) if auto-detected, or `explicit_bzero()` or `explicit_memset()` for platforms opted-in, or fall back to a local workaround if all unavailable. On Windows, always use `SecureZeroMemory()`, or `SecureZeroMemory2()` with Visual Studio and Windows SDK 10.0.26100.0+. Details above are experimental and may change if they cause issues. Also add macros/functions that zero memory before freeing a buffer: - `curlx_safefreezero()`: for buffers with size. - `curlx_safefreezeroz()`: for null-terminated buffers. - `curlx_freezero()`: for buffers with size. - `curlx_freezeroz()`: for null-terminated buffers. `curlx_memzero()` must not be passed a NULL pointer because in some implementations it is undefined behavior. Also: - curl_sha512_256: Replace hard-wired `explicit_memset()` call with new `curlx_memzero()`. Refs: https://en.cppreference.com/c/string/byte/memset https://man7.org/linux/man-pages/man3/explicit_bzero.3.html https://man.freebsd.org/cgi/man.cgi?query=explicit_bzero https://man.netbsd.org/NetBSD-7.2/explicit_memset.3 https://learn.microsoft.com/previous-versions/windows/desktop/legacy/aa366877(v=vs.85) https://learn.microsoft.com/windows/win32/memory/winbase-securezeromemory2 https://learn.microsoft.com/cpp/overview/compiler-versions https://learn.microsoft.com/windows/apps/windows-sdk/downloads https://jtsoya539.github.io/windows-sdk-versions/ Credits-to: Daniel Gustafsson Credits-to: Will Cosgrove and co-authors in libssh2 Ref: #13589 (original attempt) Ref: #21588 Closes #21598 --- .github/workflows/macos.yml | 2 +- CMake/unix-cache.cmake | 10 +++++ CMakeLists.txt | 5 +++ configure.ac | 5 +++ lib/cf-socket.c | 5 --- lib/curl_config-cmake.h.in | 6 +++ lib/curl_setup.h | 52 ++++++++++++++++++++++ lib/curl_sha512_256.c | 3 +- lib/curlx/strdup.c | 29 +++++++++++++ m4/curl-functions.m4 | 87 ++++++++++++++++++++++++++++++++++++- 10 files changed, 195 insertions(+), 9 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3cda27766ada..e0250f562421 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,7 +36,7 @@ permissions: {} # or runtime: # # - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) -# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), OCSP (runtime) +# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) # - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime) diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 8ecd20618645..e69ea5f6088e 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -65,6 +65,16 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_EVENTFD 1) endif() +if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34) + set(HAVE_MEMSET_EXPLICIT 1) +endif() +if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") # v11.2+ + set(HAVE_MEMSET_S 1) +elseif(NOT APPLE) + set(HAVE_MEMSET_S 0) +endif() set(HAVE_FCNTL 1) set(HAVE_FCNTL_H 1) set(HAVE_FCNTL_O_NONBLOCK 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0573d2f14337..89d7f8a9321f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1672,6 +1672,11 @@ if(NOT WIN32) check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) + + check_symbol_exists("memset_s" "string.h" HAVE_MEMSET_S) + if(NOT HAVE_MEMSET_S) + check_function_exists("memset_explicit" HAVE_MEMSET_EXPLICIT) + endif() endif() if(AMIGA) diff --git a/configure.ac b/configure.ac index d06024bc1064..3e8569de3eae 100644 --- a/configure.ac +++ b/configure.ac @@ -4152,6 +4152,11 @@ if test "$curl_cv_native_windows" != "yes"; then CURL_CHECK_FUNC_STRCASECMP CURL_CHECK_FUNC_STRCMPI CURL_CHECK_FUNC_STRICMP + + CURL_CHECK_FUNC_MEMSET_S + if test "$curl_cv_func_memset_s" = "no"; then + AC_CHECK_FUNCS([memset_explicit]) + fi fi if test -z "$ssl_backends"; then diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 1e244671f394..0edd0efe746b 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -49,11 +49,6 @@ #include #endif -#ifdef __DragonFly__ -/* Required for __DragonFly_version */ -#include -#endif - #include "urldata.h" #include "curl_trc.h" #include "if2ip.h" diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 41b0ddf07375..31e94d0691e7 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -234,6 +234,12 @@ /* Define to 1 if you have the `opendir' function. */ #cmakedefine HAVE_OPENDIR 1 +/* Define to 1 if you have the memset_explicit (C23) function. */ +#cmakedefine HAVE_MEMSET_EXPLICIT 1 + +/* Define to 1 if you have the memset_s (C11) function. */ +#cmakedefine HAVE_MEMSET_S 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 9329f5605f02..d4b805f9e20d 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1329,6 +1329,20 @@ extern curl_calloc_callback Curl_ccalloc; (ptr) = NULL; \ } while(0) +/* Same as curlx_safefree() but zeroes memory before freeing */ +#define curlx_safefreezero(ptr, size) \ + do { \ + curlx_freezero(ptr, size); \ + (ptr) = NULL; \ + } while(0) + +/* Same as curlx_safefreezero() but determines length with strlen() */ +#define curlx_safefreezeroz(ptr) \ + do { \ + curlx_freezeroz(ptr); \ + (ptr) = NULL; \ + } while(0) + #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ #ifdef DEBUGBUILD @@ -1608,4 +1622,42 @@ typedef struct sockaddr_un { #define NOVERBOSE(x) x #endif +/* For FreeBSD it is included from curl/curl.h */ +#if defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include /* for __DragonFly_version, OpenBSD, + __NetBSD_Version__ */ +#endif + +#ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */ +#ifdef _WIN32 +#if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ + (NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */ +#pragma comment(lib, "volatileaccessu.lib") +#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size) +#else +#define curlx_memzero(buf, size) SecureZeroMemory(buf, size) +#endif +#elif defined(HAVE_MEMSET_S) +#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size) +#elif defined(HAVE_MEMSET_EXPLICIT) +#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size) +#elif defined(__CYGWIN__) || defined(__NEWLIB__) || \ + (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \ + (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ + (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ + (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) +#define curlx_memzero(buf, size) explicit_bzero(buf, size) +#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ +#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size) +#endif +#endif /* !_CURL_LOCAL_MEMZERO */ + +#ifndef curlx_memzero +#define USE_CURLX_MEMZERO +void curlx_memzero(void *buf, size_t size); +#endif +void curlx_freezero(void *buf, size_t size); +void curlx_freezeroz(void *buf); + #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index f8f2053b5d6c..7b851788ad8d 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -54,7 +54,6 @@ * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as * the workaround reduces performance slightly. */ -# include # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ __NetBSD_Version__ < 1000000000) || \ @@ -173,7 +172,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); - explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); + curlx_memzero(tmp_digest, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; diff --git a/lib/curlx/strdup.c b/lib/curlx/strdup.c index 3c967dbe0a5d..8e788ea34c01 100644 --- a/lib/curlx/strdup.c +++ b/lib/curlx/strdup.c @@ -94,3 +94,32 @@ void *curlx_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } + +#ifdef USE_CURLX_MEMZERO +static void *(* const volatile p_curlx_memset)(void *buf, int val, + size_t size) = memset; + +/* Local fallback in case there is no system function to securely zero a memory + buffer. */ +void curlx_memzero(void *buf, size_t size) +{ + if(buf) + p_curlx_memset(buf, 0, size); +} +#endif + +/* Free 'buf' after zeroing its content. */ +void curlx_freezero(void *buf, size_t size) +{ + if(buf) + curlx_memzero(buf, size); + curlx_free(buf); +} + +/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ +void curlx_freezeroz(void *buf) +{ + if(buf) + curlx_memzero(buf, strlen(buf)); + curlx_free(buf); +} diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 9d80f2f5384a..999f96add0e9 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -4111,7 +4111,6 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ test "$tst_allow_strerror_r" = "unknown"; then AC_MSG_WARN([cannot determine strerror_r() style: edit lib/curl_config.h manually.]) fi - ]) @@ -4199,6 +4198,92 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ fi ]) + +dnl CURL_CHECK_FUNC_MEMSET_S +dnl ------------------------------------------------- +dnl Verify if memset_s is available, prototyped, and +dnl can be compiled. If all of these are true, and +dnl usage has not been previously disallowed with +dnl shell variable curl_disallow_memset_s, then +dnl HAVE_MEMSET_S will be defined. + +AC_DEFUN([CURL_CHECK_FUNC_MEMSET_S], [ + AC_REQUIRE([CURL_INCLUDES_STRING]) + + tst_links_memset_s="unknown" + tst_proto_memset_s="unknown" + tst_compi_memset_s="unknown" + tst_allow_memset_s="unknown" + + AC_MSG_CHECKING([if memset_s can be linked]) + AC_LINK_IFELSE([ + AC_LANG_FUNC_LINK_TRY([memset_s]) + ],[ + AC_MSG_RESULT([yes]) + tst_links_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_links_memset_s="no" + ]) + + if test "$tst_links_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is prototyped]) + AC_EGREP_CPP([memset_s],[ + $curl_includes_string + ],[ + AC_MSG_RESULT([yes]) + tst_proto_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_proto_memset_s="no" + ]) + fi + + if test "$tst_proto_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is compilable]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + $curl_includes_string + ]],[[ + char buf[2]; + if(memset_s(buf, sizeof(buf), 0, sizeof(buf)) != 0) + return 1; + ]]) + ],[ + AC_MSG_RESULT([yes]) + tst_compi_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_compi_memset_s="no" + ]) + fi + + if test "$tst_compi_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s usage allowed]) + if test "x$curl_disallow_memset_s" != "xyes"; then + AC_MSG_RESULT([yes]) + tst_allow_memset_s="yes" + else + AC_MSG_RESULT([no]) + tst_allow_memset_s="no" + fi + fi + + AC_MSG_CHECKING([if memset_s might be used]) + if test "$tst_links_memset_s" = "yes" && + test "$tst_proto_memset_s" = "yes" && + test "$tst_compi_memset_s" = "yes" && + test "$tst_allow_memset_s" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(HAVE_MEMSET_S, 1, + [Define to 1 if you have the memset_s function.]) + curl_cv_func_memset_s="yes" + else + AC_MSG_RESULT([no]) + curl_cv_func_memset_s="no" + fi +]) + dnl CURL_RUN_IFELSE dnl ------------------------------------------------- dnl Wrapper macro to use instead of AC_RUN_IFELSE. It From 60cd4815fd36d44805a8cfe87c2dcbc8277c103c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 15 May 2026 13:54:41 +0200 Subject: [PATCH 0118/1062] CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only curl can then reuse the connection for subsequent transfers without calling this function again. Fixes #21606 Reported-by: Joshua Rogers Closes #21628 --- docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md index 09aa83c875f6..1125ec392e25 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md @@ -38,12 +38,15 @@ shown above. It overrides CURLOPT_SSH_KNOWNHOSTS(3). This callback gets called when the verification of the SSH host key is needed. -**key** is **keylen** bytes long and is the key to check. **keytype** -says what type it is, from the **CURLKHTYPE_*** series in the -**curl_khtype** enum. +**key** is **keylen** bytes long and is the key to check. **keytype** says +what type it is, from the **CURLKHTYPE_*** series in the **curl_khtype** enum. **clientp** is a custom pointer set with CURLOPT_SSH_HOSTKEYDATA(3). +This option is used to verify new SSH connections only. Once the connection +has been vetted by this callback it is deemed vetted and may be reused again +without invoking this callback again. + The callback must return one of the following return codes to tell libcurl how to act: From 12d6d8e26f5984739993bb97f80efdfed1e427e6 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 15 May 2026 13:03:02 +0200 Subject: [PATCH 0119/1062] cf-h2-proxy: drop interim responses Any 1xx response before the CONNECT final one can be dropped as no one uses those in the HTTP/2 proxy filter. This eliminates a potential memory exhaustion by the famous malicious server on the internet. Closes #21626 --- lib/cf-h2-proxy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index a0c5b143215f..1dfd0a0a4603 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -561,7 +561,7 @@ static int proxy_h2_on_header(nghttp2_session *session, struct http_resp *resp; /* status: always comes first, we might get more than one response, - * link the previous ones for keepers */ + * discard previous, interim responses */ result = Curl_http_decode_status(&http_status, (const char *)value, valuelen); if(result) @@ -569,7 +569,8 @@ static int proxy_h2_on_header(nghttp2_session *session, result = Curl_http_resp_make(&resp, http_status, NULL); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - resp->prev = ctx->tunnel.resp; + if(ctx->tunnel.resp) + Curl_http_resp_free(ctx->tunnel.resp); ctx->tunnel.resp = resp; CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", stream_id, ctx->tunnel.resp->status); From 978ea5afec88d81cfdfd153055ab38ba0bcb4c7a Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann Date: Thu, 14 May 2026 17:27:13 +0200 Subject: [PATCH 0120/1062] KNOWN_BUGS.md: remove fixed x509asn.1 bug KNOWN_BUGS.md contains an entry about a CURLE_OUT_OF_MEMORY error on a CURLOPT_CERTINFO call when using Schannel. This bug was fixed by 137a668e8cb42dda1673bf2c79cbb24c8fe0b405. remove the entry from KNOWN_BUGS.md. Ref: https://github.com/curl/curl/issues/8741#issuecomment-4445486705 Closes #21611 --- docs/KNOWN_BUGS.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index a3b0d37889a8..70d3196b83bd 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -45,10 +45,6 @@ fail, resulting in error SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED. [curl issue 5488](https://github.com/curl/curl/issues/5488) -## `CURLOPT_CERTINFO` results in `CURLE_OUT_OF_MEMORY` with Schannel - -[curl issue 8741](https://github.com/curl/curl/issues/8741) - ## mbedTLS and CURLE_AGAIN handling [curl issue 15801](https://github.com/curl/curl/issues/15801) From d6571f7a701bbdde4314a70612fa342d5d243a07 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 14 May 2026 23:23:08 +0200 Subject: [PATCH 0121/1062] setopt: more careful cleanup of the HSTS cache Reported-by: Joshua Rogers Closes #21615 --- lib/setopt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/setopt.c b/lib/setopt.c index 0fc5ec7e87fa..2bc49868b81b 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1280,8 +1280,16 @@ static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, return CURLE_OUT_OF_MEMORY; } } - else + else if(!data->share || !data->share->hsts) { + /* throw away the HSTS cache unless shared */ Curl_hsts_cleanup(&data->hsts); + /* flush all the entries */ + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + } + else + /* detach from shared HSTS cache without freeing it */ + data->hsts = NULL; break; #endif #ifndef CURL_DISABLE_ALTSVC From 61d59c9e39b451b30feb431b780a5cc325757921 Mon Sep 17 00:00:00 2001 From: Sergio Correia Date: Wed, 13 May 2026 19:44:05 +0100 Subject: [PATCH 0122/1062] x509asn1: fix DH public key parameter extraction The dh(g) parameter was read from param->beg instead of from the cursor p returned by parsing dh(p). This caused dh(g) to always report the same value as dh(p) when inspecting DH certificates via CURLOPT_CERTINFO on non-OpenSSL backends. The DSA branch correctly advances the cursor; the DH branch lost this during what appears to be a copy-paste. Add unit1676 to verify that dh(p) and dh(g) report distinct values using a hand-crafted minimal DER certificate. Assisted by: Claude Opus 4.6 Signed-off-by: Sergio Correia Closes #21595 --- lib/vtls/x509asn1.c | 2 +- tests/data/Makefile.am | 2 +- tests/data/test1676 | 21 +++++++ tests/unit/Makefile.inc | 2 +- tests/unit/unit1676.c | 120 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1676 create mode 100644 tests/unit/unit1676.c diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index 2ab74cadf6d5..4c5dac3e8b83 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -1062,7 +1062,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, if(p) { if(do_pubkey_field(data, certnum, "dh(p)", &elem)) return 1; - if(getASN1Element(&elem, param->beg, param->end)) { + if(getASN1Element(&elem, p, param->end)) { if(do_pubkey_field(data, certnum, "dh(g)", &elem)) return 1; if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index ec9620b9cd95..bd3f0d01b0c4 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -223,7 +223,7 @@ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ test1666 test1667 test1668 test1669 \ \ -test1670 test1671 test1672 test1673 test1674 test1675 \ +test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ \ test1680 test1681 test1682 test1683 test1684 test1685 \ \ diff --git a/tests/data/test1676 b/tests/data/test1676 new file mode 100644 index 000000000000..7f4907fea330 --- /dev/null +++ b/tests/data/test1676 @@ -0,0 +1,21 @@ + + + + +unittest +x509 +DH + + + +# Client-side + + +unittest + + +x509 DH public key parameter extraction + + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index f0ce3d4eefaa..b474f3d7fcd4 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -42,7 +42,7 @@ TESTS_C = \ unit1650.c unit1651.c unit1652.c unit1653.c unit1654.c unit1655.c unit1656.c \ unit1657.c unit1658.c unit1660.c unit1661.c unit1663.c unit1664.c \ unit1666.c unit1667.c unit1668.c unit1669.c \ - unit1674.c unit1675.c \ + unit1674.c unit1675.c unit1676.c \ unit1979.c unit1980.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ diff --git a/tests/unit/unit1676.c b/tests/unit/unit1676.c new file mode 100644 index 000000000000..3cc80b9cb6c8 --- /dev/null +++ b/tests/unit/unit1676.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" +#include "vtls/x509asn1.h" +#include "vtls/vtls.h" + +static CURLcode test_unit1676(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + +#if defined(USE_GNUTLS) || defined(USE_MBEDTLS) || defined(USE_RUSTLS) || \ + defined(USE_SCHANNEL) + + /* + * Minimal DER-encoded X.509 certificate with a DH public key. + * Hand-crafted to exercise the do_pubkey() dhpublicnumber branch. + * + * The DH parameters contain two distinct INTEGER values: + * p = 0x11 (renders as "17" via int2str decimal format) + * g = 0x22 (renders as "34") + * The public key value is: + * pub_key = 0x33 (renders as "51") + * + * OID 1.2.840.10046.2.1 = dhpublicnumber + */ + static const unsigned char cert[] = { + 0x30, 0x81, 0x85, 0x30, 0x72, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, + 0x01, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x01, 0x0B, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0C, 0x04, 0x74, 0x65, 0x73, 0x74, 0x30, 0x1E, 0x17, 0x0D, 0x32, + 0x35, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, + 0x17, 0x0D, 0x32, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5A, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0C, 0x04, 0x74, 0x65, 0x73, 0x74, 0x30, 0x19, 0x30, 0x11, + 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3E, 0x02, 0x01, 0x30, 0x06, 0x02, + 0x01, 0x11, 0x02, 0x01, 0x22, 0x03, 0x04, 0x00, 0x02, 0x01, 0x33, 0x30, + 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, + 0x03, 0x02, 0x00, 0xFF + }; + + CURLcode result; + const char *beg = (const char *)&cert[0]; + const char *end = (const char *)&cert[sizeof(cert)]; + struct Curl_easy *data; + struct curl_slist *slist; + const char *dhp_value = NULL; + const char *dhg_value = NULL; + const char *dhpk_value = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + data = curl_easy_init(); + if(!data) { + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + data->set.ssl.certinfo = 1; + result = Curl_ssl_init_certinfo(data, 1); + if(result) { + curl_easy_cleanup(data); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + result = Curl_extract_certinfo(data, 0, beg, end); + fail_unless(result == CURLE_OK, "Curl_extract_certinfo returned error"); + if(result == CURLE_OK) { + /* Walk certinfo entries to find dh(p), dh(g), and dh(pub_key) */ + for(slist = data->info.certs.certinfo[0]; slist; slist = slist->next) { + if(strncmp(slist->data, "dh(p):", 6) == 0) + dhp_value = slist->data + 6; + else if(strncmp(slist->data, "dh(g):", 6) == 0) + dhg_value = slist->data + 6; + else if(strncmp(slist->data, "dh(pub_key):", 12) == 0) + dhpk_value = slist->data + 12; + } + + abort_unless(dhp_value != NULL, "dh(p) not found in certinfo"); + abort_unless(dhg_value != NULL, "dh(g) not found in certinfo"); + abort_unless(dhpk_value != NULL, "dh(pub_key) not found in certinfo"); + fail_if(strcmp(dhp_value, dhg_value) == 0, + "dh(p) and dh(g) have the same value (bug: g re-reads p)"); + fail_unless(strcmp(dhp_value, "17") == 0, "dh(p) expected 17 (0x11)"); + fail_unless(strcmp(dhg_value, "34") == 0, "dh(g) expected 34 (0x22)"); + fail_unless(strcmp(dhpk_value, "51") == 0, + "dh(pub_key) expected 51 (0x33)"); + } + + curl_easy_cleanup(data); + curl_global_cleanup(); +#else + puts("not tested since Curl_extract_certinfo() is not built in"); +#endif + UNITTEST_END_SIMPLE +} From 91dcf4e610e3094d1ad55eb3bf9b99c0b6fef27b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 12 May 2026 17:58:03 +0200 Subject: [PATCH 0123/1062] url: url_match_destination fix Match origin/via_peer also for non-SSL schemes. Closes #21573 --- lib/url.c | 54 ++++++++++++++++++------------------- tests/http/test_10_proxy.py | 10 +++++++ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/lib/url.c b/lib/url.c index 287aa584fa10..d15cdc102725 100644 --- a/lib/url.c +++ b/lib/url.c @@ -956,36 +956,36 @@ static bool url_match_auth(struct connectdata *conn, static bool url_match_destination(struct connectdata *conn, struct url_conn_match *m) { - /* Additional match requirements if talking TLS OR - * not talking to an HTTP proxy OR using a tunnel through a proxy */ - if((m->needle->scheme->flags & PROTOPT_SSL) + /* Different connect-to peers never match */ + if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) + return FALSE; + #ifndef CURL_DISABLE_PROXY - || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy -#endif - ) { - if(m->needle->scheme != conn->scheme) { - /* `needle` and `conn` do not have the same scheme... */ - if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { - /* and `conn`s protocol family is not the protocol `needle` wants. - * IMAPS would work for IMAP, but no vice versa. */ - return FALSE; - } - /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */ - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T - " has compatible protocol family, but no SSL, no match", - conn->connection_id)); - return FALSE; - } - } + if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) { + /* Talking to a non-tunneling HTTP proxy matches on proxy peers. */ + return Curl_peer_equal(m->needle->http_proxy.peer, + conn->http_proxy.peer); + } +#endif - /* `needle` must have the same hostname and port in origin and - * via_peer (if present, NULL peers are equal) */ - if(!Curl_peer_same_destination(m->needle->origin, conn->origin) || - !Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) + if(m->needle->origin->scheme != conn->origin->scheme) { + /* `needle` and `conn` not having the same scheme. + * This is allowed for the same family *if* conn is using TLS. + * - IMAP+STARTTLS works for IMAPS. + * - IMAPS works for IMAP. */ + if(get_protocol_family(conn->origin->scheme) != + m->needle->scheme->protocol) { + return FALSE; + } + if(!url_match_ssl_use(conn, m)) { + DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T + " has compatible protocol family, but no SSL, no match", + conn->connection_id)); return FALSE; + } } - return TRUE; + /* Scheme mismatch is acceptable, just compare hostname/port */ + return Curl_peer_same_destination(m->needle->origin, conn->origin); } static bool url_match_ssl_config(struct connectdata *conn, @@ -1144,8 +1144,6 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_needs(conn, m)) return FALSE; - if(!url_match_ssl_use(conn, m)) - return FALSE; if(!url_match_proxy_use(conn, m)) return FALSE; if(!url_match_ssl_config(conn, m)) diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index b1840b484d7a..169df8015e61 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -413,3 +413,13 @@ def test_10_16_proxy_ip_addr(self, env: Env, httpd): extra_args=xargs) r.check_exit_code(0), f'{r}' r.check_response(count=1, http_status=200, protocol='HTTP/1.1') + + # download via http: proxy (no tunnel), check connection reuse + def test_10_17_proxy_http(self, env: Env, httpd): + curl = CurlClient(env=env) + url1 = f'http://localhost:{env.http_port}/data.json' + url2 = f'http://127.0.0.1:{env.http_port}/data.json' + r = curl.http_download(urls=[url1, url2], alpn_proto='http/1.1', with_stats=True, + extra_args=curl.get_proxy_args(proxys=False)) + r.check_response(count=2, http_status=200) + assert r.total_connects == 1, r.dump_logs() From a15483c4caa746a265749071c0532fdf7f53e252 Mon Sep 17 00:00:00 2001 From: jeffhuang Date: Tue, 12 May 2026 16:13:15 +0000 Subject: [PATCH 0124/1062] url: compare full origin when setting credentials Closes #21575 --- lib/url.c | 4 +-- lib/vauth/vauth.c | 8 ++++- lib/vauth/vauth.h | 3 ++ tests/data/Makefile.am | 2 +- tests/data/test3106 | 77 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 tests/data/test3106 diff --git a/lib/url.c b/lib/url.c index d15cdc102725..471399123a97 100644 --- a/lib/url.c +++ b/lib/url.c @@ -106,6 +106,7 @@ #include "telnet.h" #include "tftp.h" #include "http.h" +#include "vauth/vauth.h" #include "file.h" #include "curl_ldap.h" #include "vssh/ssh.h" @@ -1437,8 +1438,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, data->set.str[STRING_BEARER] || data->set.str[STRING_SASL_AUTHZID] || data->set.str[STRING_SERVICE_NAME]) && - (data->set.allow_auth_to_other_hosts || - Curl_peer_same_destination(data->state.initial_origin, conn->origin))) { + Curl_auth_allowed_to_origin(data, conn->origin)) { result = Curl_creds_create(data->set.str[STRING_USERNAME], data->set.str[STRING_PASSWORD], data->set.str[STRING_BEARER], diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index 76de85cb2844..1bd3575af9b1 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -138,9 +138,15 @@ bool Curl_auth_user_contains_domain(struct Curl_creds *creds) * "sensitive data" can be sent to the connection's origin. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) +{ + return Curl_auth_allowed_to_origin(data, data->conn->origin); +} + +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin) { return data->set.allow_auth_to_other_hosts || - Curl_peer_equal(data->state.initial_origin, data->conn->origin); + Curl_peer_equal(data->state.initial_origin, origin); } #ifdef USE_NTLM diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 3bbecb8896b6..c21b3495715d 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -32,6 +32,7 @@ struct Curl_easy; struct Curl_creds; struct connectdata; +struct Curl_peer; #ifndef CURL_DISABLE_DIGEST_AUTH struct digestdata; @@ -59,6 +60,8 @@ struct gsasldata; * "sensitive data" can (still) be sent to this host. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data); +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin); /* This is used to build an SPN string */ #ifndef USE_WINDOWS_SSPI diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index bd3f0d01b0c4..166de82cf7cc 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -282,7 +282,7 @@ test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \ test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 \ test3032 test3033 test3034 test3035 test3036 \ \ -test3100 test3101 test3102 test3103 test3104 test3105 \ +test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ diff --git a/tests/data/test3106 b/tests/data/test3106 new file mode 100644 index 000000000000..971107e0fa87 --- /dev/null +++ b/tests/data/test3106 @@ -0,0 +1,77 @@ + + + + +HTTP +HTTPS +HTTP proxy +HTTP Basic auth +followlocation + + + +# Server-side + + +HTTP/1.1 200 OK + + + + +HTTP/1.1 302 Found +Location: http://example.com:%HTTPSPORT/%TESTNUMBER0002 +Content-Length: 0 + + + + +HTTP/1.1 200 OK +Content-Length: 2 + +OK + + + +# Client-side + + +SSL +proxy + + +https +http-proxy + + +HTTPS to HTTP redirect on same host and port without auth + + +--insecure --location --user user:secret --proxy %HOSTIP:%PROXYPORT https://example.com:%HTTPSPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +CONNECT example.com:%HTTPSPORT HTTP/1.1 +Host: example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Proxy-Connection: Keep-Alive + +GET http://example.com:%HTTPSPORT/%TESTNUMBER0002 HTTP/1.1 +Host: example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + +GET /%TESTNUMBER HTTP/1.1 +Host: example.com:%HTTPSPORT +Authorization: Basic %b64[user:secret]b64% +User-Agent: curl/%VERSION +Accept: */* + + + + From 47f411c6d840dcee63a2ac9cbc0bfbea522ac5cd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 12 May 2026 02:26:05 +0200 Subject: [PATCH 0125/1062] GHA: enable `-Wunused-macros` in clang-tidy jobs Also fix fallouts found. Windows clang-tidy CI job is a little pickier than I'd prefer due to the `_CURL_TESTS_CONCAT=ON` option used there, and all macros considered local, thus checked by the compiler. Upside: it revealed macro usage dynamics in tests. If too annoying, `first.h` may be opted-out from the concat logic. Some macros may also be deleted instead of `#if 0`-ing. Follow-up to e0e56e9ae434552bd6ac5570ed91483188d75788 #21550 Follow-up to 5fa5cb382560316a55f0954f1e8cebdbd6568cfb #20593 Closes #21554 --- .github/workflows/linux.yml | 2 ++ .github/workflows/macos.yml | 4 +++- .github/workflows/windows.yml | 4 +++- lib/parsedate.c | 4 ++++ tests/libtest/first.h | 39 ++++++++++++++++++++++++----------- tests/libtest/lib1912.c | 9 ++++---- tests/libtest/lib2301.c | 4 +++- tests/libtest/lib2302.c | 4 +++- tests/libtest/lib2304.c | 4 +++- tests/libtest/lib2700.c | 4 +++- tests/libtest/lib518.c | 4 ++-- tests/libtest/lib537.c | 4 ++-- tests/server/dnsd.c | 4 ++++ tests/unit/unit1307.c | 2 ++ tests/unit/unit1666.c | 2 -- tests/unit/unit1667.c | 5 ----- 16 files changed, 66 insertions(+), 33 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 5fbe2eb11305..e6beafe1bb44 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -321,6 +321,7 @@ jobs: install_steps: skiprun mbedtls-latest-intel rustls wolfssl-opensslextra-intel install_steps_brew: openssl@4 gsasl CC: clang-20 + CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib -Wl,-rpath,/home/runner/mbedtls/lib @@ -345,6 +346,7 @@ jobs: install_steps: skiprun install_steps_brew: libngtcp2 libnghttp3 c-ares CC: clang-20 + CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/openssl/lib -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/libngtcp2/lib diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e0250f562421..f3d71cdf473d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -300,6 +300,7 @@ jobs: compiler: clang install: llvm gnutls nettle libressl krb5 mbedtls gsasl rustls-ffi libssh fish install_steps: skiprun + CFLAGS: -Wunused-macros chkprefill: _chkprefill generate: >- -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DCURL_DEFAULT_SSL_BACKEND=openssl @@ -316,6 +317,7 @@ jobs: compiler: clang install: llvm libnghttp3 libngtcp2 openldap krb5 install_steps: skipall + CFLAGS: -Wunused-macros generate: >- -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include @@ -445,6 +447,7 @@ jobs: - name: 'configure' env: + CFLAGS: '${{ matrix.build.CFLAGS }}' MATRIX_CHKPREFILL: '${{ matrix.build.chkprefill }}' MATRIX_CONFIGURE: '${{ matrix.build.configure }}' MATRIX_GENERATE: '${{ matrix.build.generate }}' @@ -479,7 +482,6 @@ jobs: false fi else - export CFLAGS if [[ "${MATRIX_COMPILER}" = 'llvm'* ]]; then options+=" --target=$(uname -m)-apple-darwin" fi diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 67db3798fef2..9074526bdb90 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -782,7 +782,7 @@ jobs: include: - { build: 'autotools', compiler: 'gcc' } - { build: 'cmake' , compiler: 'gcc' } - - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-20 clang-tidy-20' } + - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-20 clang-tidy-20', CFLAGS: '-Wunused-macros' } steps: - name: 'install packages' timeout-minutes: 2 @@ -801,6 +801,8 @@ jobs: run: autoreconf -fi - name: 'configure' + env: + CFLAGS: '${{ matrix.CFLAGS }}' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then if [ "${MATRIX_COMPILER}" = 'clang-tidy' ]; then diff --git a/lib/parsedate.c b/lib/parsedate.c index ce358bd465ab..b19423169af9 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -98,8 +98,12 @@ const char * const Curl_month[] = { #ifndef CURL_DISABLE_PARSEDATE +#if SIZEOF_TIME_T < 5 #define PARSEDATE_LATER 1 +#endif +#if SIZEOF_TIME_T < 5 || defined(HAVE_TIME_T_UNSIGNED) #define PARSEDATE_SOONER 2 +#endif static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 9ed8a9c4582b..21da11394c37 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -58,19 +58,23 @@ extern int unitfail; /* for unittests */ #include #endif +#ifndef UNITTESTS #define test_setopt(A, B, C) \ do { \ result = curl_easy_setopt(A, B, C); \ if(result != CURLE_OK) \ goto test_cleanup; \ } while(0) +#endif /* !UNITTESTS */ +#if 0 #define test_multi_setopt(A, B, C) \ do { \ result = curl_multi_setopt(A, B, C); \ if(result != CURLE_OK) \ goto test_cleanup; \ } while(0) +#endif extern const char *libtest_arg2; /* set by first.c to the argv[2] or NULL */ extern const char *libtest_arg3; /* set by first.c to the argv[3] or NULL */ @@ -104,8 +108,10 @@ void ws_close(CURL *curl); /* just close the connection */ * * For portability reasons TEST_ERR_* values should be less than 127. */ - +#if !defined(UNITTESTS) || defined(BUILDING_LIBCURL) #define TEST_ERR_MAJOR_BAD CURLE_OBSOLETE20 +#endif +#ifndef UNITTESTS #define TEST_ERR_RUNS_FOREVER CURLE_OBSOLETE24 #define TEST_ERR_EASY_INIT CURLE_OBSOLETE29 #define TEST_ERR_MULTI CURLE_OBSOLETE32 @@ -236,8 +242,10 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) +#if 0 #define res_multi_setopt(A, B, C) \ exe_multi_setopt(A, B, C, __FILE__, __LINE__) +#endif #define chk_multi_setopt(A, B, C, Y, Z) \ do { \ @@ -290,8 +298,10 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) +#if 0 #define res_multi_remove_handle(A, B) \ exe_multi_remove_handle(A, B, __FILE__, __LINE__) +#endif #define chk_multi_remove_handle(A, B, Y, Z) \ do { \ @@ -426,8 +436,10 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) +#if 0 #define res_multi_poll(A, B, C, D, E) \ exe_multi_poll(A, B, C, D, E, __FILE__, __LINE__) +#endif #define chk_multi_poll(A, B, C, D, E, Y, Z) \ do { \ @@ -456,6 +468,7 @@ void ws_close(CURL *curl); /* just close the connection */ #define res_multi_wakeup(A) \ exe_multi_wakeup(A, __FILE__, __LINE__) +#if 0 #define chk_multi_wakeup(A, Y, Z) \ do { \ exe_multi_wakeup(A, Y, Z); \ @@ -465,6 +478,7 @@ void ws_close(CURL *curl); /* just close the connection */ #define multi_wakeup(A) \ chk_multi_wakeup(A, __FILE__, __LINE__) +#endif /* ---------------------------------------------------------------- */ @@ -518,8 +532,10 @@ void ws_close(CURL *curl); /* just close the connection */ #define res_test_timedout() \ exe_test_timedout(TEST_HANG_TIMEOUT, __FILE__, __LINE__) +#if 0 #define res_test_timedout_custom(T) \ exe_test_timedout(T, __FILE__, __LINE__) +#endif #define chk_test_timedout(T, Y, Z) \ do { \ @@ -534,6 +550,15 @@ void ws_close(CURL *curl); /* just close the connection */ #define abort_on_test_timeout_custom(T) \ chk_test_timedout(T, __FILE__, __LINE__) +#define NUM_HANDLES 4 /* global default */ + +#define res_global_init(A) \ + exe_global_init(A, __FILE__, __LINE__) + +#endif /* !UNITTESTS */ + +#if !defined(UNITTESTS) || defined(BUILDING_LIBCURL) + /* ---------------------------------------------------------------- */ #define exe_global_init(A, Y, Z) \ @@ -548,9 +573,6 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) -#define res_global_init(A) \ - exe_global_init(A, __FILE__, __LINE__) - #define chk_global_init(A, Y, Z) \ do { \ exe_global_init(A, Y, Z); \ @@ -564,13 +586,6 @@ void ws_close(CURL *curl); /* just close the connection */ #define global_init(A) \ chk_global_init(A, __FILE__, __LINE__) -#define NO_SUPPORT_BUILT_IN \ - { \ - (void)URL; \ - curl_mfprintf(stderr, "Missing support\n"); \ - return CURLE_UNSUPPORTED_PROTOCOL; \ - } - -#define NUM_HANDLES 4 /* global default */ +#endif /* !UNITTESTS || BUILDING_LIBCURL */ #endif /* HEADER_LIBTEST_FIRST_H */ diff --git a/tests/libtest/lib1912.c b/tests/libtest/lib1912.c index 98623bd339f5..65de8b3ffb95 100644 --- a/tests/libtest/lib1912.c +++ b/tests/libtest/lib1912.c @@ -23,16 +23,17 @@ ***************************************************************************/ #include "first.h" -#define print_err(name, exp) \ - curl_mfprintf(stderr, "Type mismatch for CURLOPT_%s (expected %s)\n", \ - name, exp) - static CURLcode test_lib1912(const char *URL) { /* Only test if GCC/clang type checking is available */ int error = 0; #ifdef CURLINC_TYPECHECK_GCC_H const struct curl_easyoption *o; + +#define print_err(name, exp) \ + curl_mfprintf(stderr, "Type mismatch for CURLOPT_%s (expected %s)\n", \ + name, exp) + for(o = curl_easy_option_next(NULL); o; o = curl_easy_option_next(o)) { /* Test for mismatch OR missing typecheck macros */ if(curlcheck_long_option(o->id) != diff --git a/tests/libtest/lib2301.c b/tests/libtest/lib2301.c index 8d036682a45a..dece3553a7d5 100644 --- a/tests/libtest/lib2301.c +++ b/tests/libtest/lib2301.c @@ -97,6 +97,8 @@ static CURLcode test_lib2301(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2302.c b/tests/libtest/lib2302.c index ef8990fc177f..7eb4931c5c27 100644 --- a/tests/libtest/lib2302.c +++ b/tests/libtest/lib2302.c @@ -125,6 +125,8 @@ static CURLcode test_lib2302(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2304.c b/tests/libtest/lib2304.c index fd2a29da6c46..013a4de929f3 100644 --- a/tests/libtest/lib2304.c +++ b/tests/libtest/lib2304.c @@ -85,6 +85,8 @@ static CURLcode test_lib2304(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index e8199e0364b6..e093fa72661e 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -249,6 +249,8 @@ static CURLcode test_lib2700(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index 5c50ca5cbf0a..e2c98db22de6 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -23,6 +23,8 @@ ***************************************************************************/ #include "first.h" +#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) + #include "testutil.h" #define T518_SAFETY_MARGIN 16 @@ -36,8 +38,6 @@ #define DEV_NULL "/dev/null" #endif -#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) - static int *t518_testfd = NULL; static struct rlimit t518_num_open; static char t518_msgbuff[256]; diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 82b92a02adc7..9257eaf02ea7 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -23,6 +23,8 @@ ***************************************************************************/ #include "first.h" +#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) + #include "testutil.h" #define T537_SAFETY_MARGIN 11 @@ -33,8 +35,6 @@ #define DEV_NULL "/dev/null" #endif -#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) - static int *t537_testfd = NULL; static struct rlimit t537_num_open; static char t537_msgbuff[256]; diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 9da4eefce822..aab3c8c251fb 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -159,13 +159,17 @@ static int blob_add_qname(struct blob *b, const struct Curl_str *str) #define QTYPE_AAAA 28 #define QTYPE_HTTPS 0x41 +#if 0 #define HTTPS_RR_CODE_MANDATORY 0x00 +#endif #define HTTPS_RR_CODE_ALPN 0x01 #define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 +#if 0 #define HTTPS_RR_CODE_PORT 0x03 #define HTTPS_RR_CODE_IPV4 0x04 #define HTTPS_RR_CODE_ECH 0x05 #define HTTPS_RR_CODE_IPV6 0x06 +#endif static const char *type2string(uint16_t qtype) { diff --git a/tests/unit/unit1307.c b/tests/unit/unit1307.c index 53377afdb96b..833319b611ec 100644 --- a/tests/unit/unit1307.c +++ b/tests/unit/unit1307.c @@ -43,8 +43,10 @@ #define MAC_DIFFER 0x40 #define MAC_SHIFT 16 +#if 0 #define MAC_MATCH ((CURL_FNMATCH_MATCH << MAC_SHIFT) | MAC_DIFFER) #define MAC_NOMATCH ((CURL_FNMATCH_NOMATCH << MAC_SHIFT) | MAC_DIFFER) +#endif #define MAC_FAIL ((CURL_FNMATCH_FAIL << MAC_SHIFT) | MAC_DIFFER) static const char *ret2name(int i) diff --git a/tests/unit/unit1666.c b/tests/unit/unit1666.c index 3b8fb24158a7..360279501d7b 100644 --- a/tests/unit/unit1666.c +++ b/tests/unit/unit1666.c @@ -190,8 +190,6 @@ static CURLcode test_unit1666(const char *arg) UNITTEST_END_SIMPLE } -#undef OID - #else static CURLcode test_unit1666(const char *arg) diff --git a/tests/unit/unit1667.c b/tests/unit/unit1667.c index 128db099b90a..660633252713 100644 --- a/tests/unit/unit1667.c +++ b/tests/unit/unit1667.c @@ -37,9 +37,6 @@ struct test_1667 { CURLcode result_exp; }; -/* the size of the object needs to deduct the null terminator */ -#define OID(x) x, sizeof(x) - 1 - static bool test1667(const struct test_1667 *spec, size_t i, struct dynbuf *dbuf) { @@ -326,8 +323,6 @@ static CURLcode test_unit1667(const char *arg) UNITTEST_END_SIMPLE } -#undef OID - #else static CURLcode test_unit1667(const char *arg) From 614b94eeccc9e798a5592baaa784df763bd85528 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 20:37:42 +0200 Subject: [PATCH 0126/1062] tidy-up: miscellaneous - avoid "will" in builds scripts, scripts folder, curl_easy_ssls_export.md, and few other files. - badwords: add "initialise", "nul terminated", "thread safety" and variations. - prefer "null-terminat", where missing (two places). - fix "null-terminat*" missing dash. - hostip: merge two `#if` blocks. - tool_doswin: fix comment Spotted by GitHub Code Quality Follow-up to 9a2663322c330ff11275abafd612e9c99407a94a #17572 - fix stray spaces and newlines. Closes #21638 --- .github/scripts/cmp-config.pl | 3 +- .github/workflows/label.yml | 7 ++- CMake/CurlTests.c | 4 +- CMakeLists.txt | 6 +-- RELEASE-NOTES | 2 +- acinclude.m4 | 26 ++++----- configure.ac | 12 ++--- docs/FAQ.md | 2 +- docs/THANKS-filter | 2 +- docs/libcurl/curl_easy_ssls_export.md | 3 +- docs/libcurl/libcurl-thread.md | 4 +- docs/libcurl/libcurl.md | 2 +- lib/altsvc.c | 4 +- lib/config-os400.h | 1 - lib/creds.c | 3 +- lib/creds.h | 11 ++-- lib/curlx/strcopy.c | 4 +- lib/doh.h | 1 - lib/hostip.c | 5 -- lib/hsts.c | 6 +-- lib/mprintf.c | 2 - lib/multi.c | 4 +- lib/peer.c | 2 +- lib/socks.c | 1 - lib/urlapi.c | 1 - m4/curl-compilers.m4 | 10 ++-- m4/curl-confopts.m4 | 46 ++++++++-------- m4/curl-functions.m4 | 76 +++++++++++++-------------- m4/curl-openssl.m4 | 8 +-- m4/curl-override.m4 | 2 +- m4/curl-reentrant.m4 | 16 +++--- m4/curl-rustls.m4 | 6 +-- m4/xc-lt-iface.m4 | 16 +++--- m4/zz40-xc-ovr.m4 | 2 +- m4/zz50-xc-ovr.m4 | 4 +- projects/Windows/generate.bat | 4 +- scripts/badwords | 2 +- scripts/badwords.txt | 9 ++++ scripts/checksrc.pl | 2 +- scripts/cmakelint.sh | 6 +-- scripts/mk-ca-bundle.pl | 4 +- scripts/perlcheck.sh | 4 +- scripts/release-notes.pl | 4 +- src/tool_cfgable.c | 2 +- src/tool_doswin.c | 2 +- src/tool_formparse.c | 3 +- src/var.c | 2 +- tests/CMakeLists.txt | 4 +- tests/data/DISABLED | 2 +- tests/devtest.pl | 2 +- tests/ech_combos.py | 2 +- tests/ech_tests.sh | 12 ++--- tests/ftpserver.pl | 12 ++--- tests/getpart.pm | 4 +- tests/globalconfig.pm | 2 +- tests/libtest/cli_upload_pausing.c | 4 +- tests/secureserver.pl | 2 +- tests/servers.pm | 4 +- tests/smbserver.py | 4 +- tests/sshserver.pl | 2 +- tests/test1119.pl | 2 +- tests/testutil.pm | 2 +- tests/unit/unit1666.c | 2 +- tests/unit/unit1667.c | 2 +- 64 files changed, 201 insertions(+), 211 deletions(-) diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index 66cb65756325..9c353432bbfb 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -34,8 +34,7 @@ exit; } -# this lists complete lines that will be removed from the output if -# matching +# this lists complete lines that are removed from the output if matching my %remove = ( '#define CURL_EXTERN_SYMBOL' => 1, '#define CURL_OS "Linux"' => 1, diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index e35f09304582..013ef874fed2 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -2,12 +2,11 @@ # # SPDX-License-Identifier: curl -# This workflow will triage pull requests and apply a label based on the +# This workflow triages pull requests and applies a label based on the # paths that are modified in the pull request. # -# To use this workflow, you will need to set up a .github/labeler.yml -# file with configuration. For more information, see: -# https://github.com/actions/labeler +# To use this workflow, you need to set up a .github/labeler.yml file with +# configuration. For more information, see: https://github.com/actions/labeler name: 'Labeler' diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 2cf306b58853..be3b6f73eee9 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -283,7 +283,7 @@ static void check(char c) int main(void) { char buffer[1024]; - /* This will not compile if strerror_r does not return a char* */ + /* This does not compile if strerror_r does not return a char* */ /* !checksrc! disable ERRNOVAR 1 */ check(strerror_r(EACCES, buffer, sizeof(buffer))[0]); return 0; @@ -303,7 +303,7 @@ static void check(float f) int main(void) { char buffer[1024]; - /* This will not compile if strerror_r does not return an int */ + /* This does not compile if strerror_r does not return an int */ /* !checksrc! disable ERRNOVAR 1 */ check(strerror_r(EACCES, buffer, sizeof(buffer))); return 0; diff --git a/CMakeLists.txt b/CMakeLists.txt index 89d7f8a9321f..c49e128b6273 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -592,7 +592,7 @@ option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle) option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON) if((ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) AND NOT Perl_FOUND) - message(WARNING "Perl not found. Will not build manuals.") + message(WARNING "Perl not found. Cannot build manuals.") endif() # If we are on AIX, do the _ALL_SOURCE magic @@ -2163,8 +2163,8 @@ if(NOT CURL_DISABLE_INSTALL) set(_explicit_libs "") get_target_property(_imported "${_lib}" IMPORTED) if(NOT _imported) - # Reading the LOCATION property on non-imported target will error out. - # Assume the user will not need this information in the .pc file. + # Reading the LOCATION property on non-imported target does error out. + # Assume the user does not need this information in the .pc file. continue() endif() set(_libdirs "") diff --git a/RELEASE-NOTES b/RELEASE-NOTES index de57813a6853..67cb27d1aec5 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -27,7 +27,7 @@ This release includes the following bugfixes: o ldap: fix minor leak on write callback error [24] o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] - o mbedtls: null terminate the private key blob [36] + o mbedtls: null-terminate the private key blob [36] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] o schannel_verify: avoid out of blob access [11] o setopt: changing the proxy port is also a proxy change [23] diff --git a/acinclude.m4 b/acinclude.m4 index d98d4b1bef5e..96d55ba384cd 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -25,11 +25,11 @@ dnl CURL_CHECK_DEF (SYMBOL, [INCLUDES], [SILENT]) dnl ------------------------------------------------- dnl Use the C preprocessor to find out if the given object-style symbol -dnl is defined and get its expansion. This macro will not use default -dnl includes even if no INCLUDES argument is given. This macro will run +dnl is defined and get its expansion. This macro does not use default +dnl includes even if no INCLUDES argument is given. This macro runs dnl silently when invoked with three arguments. If the expansion would -dnl result in a set of double-quoted strings the returned expansion will -dnl actually be a single double-quoted string concatenating all them. +dnl result in a set of double-quoted strings the returned expansion is +dnl actually a single double-quoted string concatenating all them. AC_DEFUN([CURL_CHECK_DEF], [ AC_REQUIRE([CURL_CPP_P]) @@ -79,9 +79,9 @@ AC_DEFUN([CURL_CHECK_DEF], [ dnl CURL_CHECK_DEF_CC (SYMBOL, [INCLUDES], [SILENT]) dnl ------------------------------------------------- dnl Use the C compiler to find out only if the given symbol is defined -dnl or not, this can not find out its expansion. This macro will not use +dnl or not, this can not find out its expansion. This macro does not use dnl default includes even if no INCLUDES argument is given. This macro -dnl will run silently when invoked with three arguments. +dnl runs silently when invoked with three arguments. AC_DEFUN([CURL_CHECK_DEF_CC], [ AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1]) @@ -860,7 +860,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ case X-"$curl_cv_gclk_LIBS" in X-unknown) AC_MSG_RESULT([cannot find clock_gettime]) - AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) + AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" ;; X-) @@ -869,7 +869,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ ;; *) if test "$dontwant_rt" = "yes"; then - AC_MSG_WARN([needs -lrt but asked not to use it, HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) + AC_MSG_WARN([needs -lrt but asked not to use it, HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" else if test -z "$curl_cv_save_LIBS"; then @@ -908,7 +908,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ AC_MSG_RESULT([yes]) ],[ AC_MSG_RESULT([no]) - AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) + AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" LIBS="$curl_cv_save_LIBS" ]) @@ -1028,7 +1028,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SELECT], [ dnl CURL_VERIFY_RUNTIMELIBS dnl ------------------------------------------------- dnl Verify that the shared libs found so far can be used when running -dnl programs, since otherwise the situation will create odd configure errors +dnl programs, since otherwise the situation creates odd configure errors dnl that are misleading people. dnl dnl Make sure this test is run BEFORE the first test in the script that @@ -1063,7 +1063,7 @@ dnl CURL_CHECK_CA_BUNDLE dnl ------------------------------------------------- dnl Check if a default ca-bundle should be used dnl -dnl regarding the paths this will scan: +dnl regarding the paths this scans: dnl /etc/ssl/certs/ca-certificates.crt Debian systems dnl /etc/pki/tls/certs/ca-bundle.crt Redhat and Mandriva dnl /usr/share/ssl/certs/ca-bundle.crt old(er) Redhat @@ -1258,7 +1258,7 @@ AS_HELP_STRING([--without-ca-embed], [Do not embed a default CA bundle in the cu AC_MSG_RESULT([$want_ca_embed]) else AC_MSG_RESULT([no]) - AC_MSG_WARN([perl was not found. Will not do CA embed.]) + AC_MSG_WARN([perl was not found. Cannot do CA embed.]) fi else AC_MSG_RESULT([no]) @@ -1498,7 +1498,7 @@ dnl CURL_CPP_P dnl dnl Check if $cpp -P should be used for extract define values due to gcc 5 dnl splitting up strings and defines between line outputs. gcc by default -dnl (without -P) will show TEST EINVAL TEST as +dnl (without -P) shows TEST EINVAL TEST as dnl dnl # 13 "conftest.c" dnl TEST diff --git a/configure.ac b/configure.ac index 3e8569de3eae..6a9071d37c52 100644 --- a/configure.ac +++ b/configure.ac @@ -678,8 +678,8 @@ esac AM_CONDITIONAL(BUILD_UNITTESTS, test "$supports_unittests" = "yes") -dnl In order to detect support of sendmmsg() and accept4(), we need to escape the POSIX -dnl jail by defining _GNU_SOURCE or will not expose it. +dnl In order to detect support of sendmmsg() and accept4(), we need to escape +dnl the POSIX jail by defining _GNU_SOURCE or does not expose it. case $host_os in *linux*|cygwin*|msys*|gnu*) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" @@ -733,7 +733,7 @@ AS_HELP_STRING([--disable-unity],[Disable unity (default)]), AC_MSG_RESULT([no]) ) if test -z "$PERL" && test "$want_unity" = "yes"; then - AC_MSG_WARN([perl was not found. Will not enable unity.]) + AC_MSG_WARN([perl was not found. Cannot enable unity.]) want_unity='no' fi AM_CONDITIONAL([USE_UNITY], [test "$want_unity" = "yes"]) @@ -1150,7 +1150,7 @@ AS_HELP_STRING([--disable-docs],[Disable documentation]), BUILD_DOCS=1 ) if test -z "$PERL" && test "$BUILD_DOCS" != "0"; then - AC_MSG_WARN([perl was not found. Will not build documentation.]) + AC_MSG_WARN([perl was not found. Cannot build documentation.]) BUILD_DOCS=0 fi @@ -3873,7 +3873,7 @@ case "$OPT_ZSH_FPATH" in ;; esac if test -z "$PERL" && test -n "$ZSH_FUNCTIONS_DIR"; then - AC_MSG_WARN([perl was not found. Will not install zsh completions.]) + AC_MSG_WARN([perl was not found. Cannot install zsh completions.]) ZSH_FUNCTIONS_DIR='' fi AM_CONDITIONAL(USE_ZSH_COMPLETION, test -n "$ZSH_FUNCTIONS_DIR") @@ -3908,7 +3908,7 @@ case "$OPT_FISH_FPATH" in ;; esac if test -z "$PERL" && test -n "$FISH_FUNCTIONS_DIR"; then - AC_MSG_WARN([perl was not found. Will not install fish completions.]) + AC_MSG_WARN([perl was not found. Cannot install fish completions.]) FISH_FUNCTIONS_DIR='' fi AM_CONDITIONAL(USE_FISH_COMPLETION, test -n "$FISH_FUNCTIONS_DIR") diff --git a/docs/FAQ.md b/docs/FAQ.md index 7e794976538a..0eafd34a375d 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -986,7 +986,7 @@ programs. libcurl uses thread-safe functions instead of non-safe ones if your system has such. Note that you must never share the same handle in multiple threads. -There may be some exceptions to thread safety depending on how libcurl was +There may be some exceptions to thread-safety depending on how libcurl was built. Please review [the guidelines for thread safety](https://curl.se/libcurl/c/threadsafe.html) to learn more. diff --git a/docs/THANKS-filter b/docs/THANKS-filter index cd99f569b14d..f9e7e1c8d2d4 100644 --- a/docs/THANKS-filter +++ b/docs/THANKS-filter @@ -25,7 +25,7 @@ # This is a list of names we have recorded that already are thanked # appropriately in THANKS. This list contains variations of their names and # their "canonical" name. This file is used for scripting purposes to avoid -# duplicate entries and will not be included in release tarballs. +# duplicate entries and is not included in release tarballs. # When removing dupes that are not identical names from THANKS, add a line # here! # diff --git a/docs/libcurl/curl_easy_ssls_export.md b/docs/libcurl/curl_easy_ssls_export.md index fdefa408e524..1eb634fb5887 100644 --- a/docs/libcurl/curl_easy_ssls_export.md +++ b/docs/libcurl/curl_easy_ssls_export.md @@ -149,8 +149,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_SHARE, share); - /* run a transfer, all TLS sessions received will be added - * to the share. */ + /* run a transfer, all TLS sessions received are added to the share. */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_perform(curl); diff --git a/docs/libcurl/libcurl-thread.md b/docs/libcurl/libcurl-thread.md index 8ef893f07588..9b26224ce6e6 100644 --- a/docs/libcurl/libcurl-thread.md +++ b/docs/libcurl/libcurl-thread.md @@ -13,12 +13,12 @@ Added-in: n/a # NAME -libcurl-thread - libcurl thread safety +libcurl-thread - libcurl thread-safety # Multi-threading with libcurl libcurl is thread-safe but has no internal thread synchronization. You may have -to provide your own locking should you meet any of the thread safety exceptions +to provide your own locking should you meet any of the thread-safety exceptions below. # Handles diff --git a/docs/libcurl/libcurl.md b/docs/libcurl/libcurl.md index 96f10656eed9..da61d4c5ea51 100644 --- a/docs/libcurl/libcurl.md +++ b/docs/libcurl/libcurl.md @@ -174,7 +174,7 @@ to select the active SSL backend. The global constant functions are thread-safe since libcurl 7.84.0 if curl_version_info(3) has the CURL_VERSION_THREADSAFE feature bit set -(most platforms). Read libcurl-thread(3) for thread safety guidelines. +(most platforms). Read libcurl-thread(3) for thread-safety guidelines. If the global constant functions are *not thread-safe*, then you must not call them when any other thread in the program is running. It diff --git a/lib/altsvc.c b/lib/altsvc.c index 81b5379ad42c..0cbee17fed2f 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -113,11 +113,11 @@ static struct altsvc *altsvc_createid(const char *srchost, return NULL; as->src.host = (char *)as + sizeof(struct altsvc); memcpy(as->src.host, srchost, hlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->dst.host = (char *)as + sizeof(struct altsvc) + hlen + 1; memcpy(as->dst.host, dsthost, dlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; diff --git a/lib/config-os400.h b/lib/config-os400.h index 3d8d59c97e4b..1bb11c48ac35 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -38,7 +38,6 @@ /* Use the system keyring as the default CA bundle. */ #define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" - /* Definition to make a library symbol externally visible. */ #define CURL_EXTERN_SYMBOL diff --git a/lib/creds.c b/lib/creds.c index 1362f92c7905..8303891967ff 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -63,7 +63,7 @@ CURLcode Curl_creds_create(const char *user, goto out; } - /* NUL terminator for user already part of struct */ + /* null-terminator for user already part of struct */ creds = curlx_calloc(1, sizeof(*creds) + ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1); if(!creds) { @@ -187,5 +187,4 @@ void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, else CURL_TRC_M(data, "%s: -", msg); } - #endif diff --git a/lib/creds.h b/lib/creds.h index 7f50d3bd8cc7..f979629f4938 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -69,17 +69,16 @@ bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); bool Curl_creds_same_user(struct Curl_creds *creds, const char *user); bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd); - /* Provides properties for creds or, if creds is NULL, the empty string */ #define Curl_creds_has_user(c) ((c) && (c)->user[0]) #define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) #define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) #define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) -#define Curl_creds_user(c) ((c)? (c)->user : "") -#define Curl_creds_passwd(c) ((c)? (c)->passwd : "") -#define Curl_creds_oauth_bearer(c) ((c)? (c)->oauth_bearer : "") -#define Curl_creds_sasl_authzid(c) ((c)? (c)->sasl_authzid : "") -#define Curl_creds_sasl_service(c) ((c)? (c)->sasl_service : "") +#define Curl_creds_user(c) ((c) ? (c)->user : "") +#define Curl_creds_passwd(c) ((c) ? (c)->passwd : "") +#define Curl_creds_oauth_bearer(c) ((c) ? (c)->oauth_bearer : "") +#define Curl_creds_sasl_authzid(c) ((c) ? (c)->sasl_authzid : "") +#define Curl_creds_sasl_service(c) ((c) ? (c)->sasl_service : "") #ifdef CURLVERBOSE void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, diff --git a/lib/curlx/strcopy.c b/lib/curlx/strcopy.c index 3e58ea24f1bd..3b80930a1950 100644 --- a/lib/curlx/strcopy.c +++ b/lib/curlx/strcopy.c @@ -30,10 +30,10 @@ * * Provide the target buffer @dest and size of the target buffer @dsize, If * the source string @src with its *string length* @slen fits in the target - * buffer it is copied there - including storing a null terminator. + * buffer it is copied there - including storing a null-terminator. * * If the target buffer is too small, the copy is not performed but if the - * target buffer has a non-zero size it gets a null terminator stored. + * target buffer has a non-zero size it gets a null-terminator stored. */ void curlx_strcopy(char *dest, /* destination buffer */ size_t dsize, /* size of target buffer */ diff --git a/lib/doh.h b/lib/doh.h index 428b230a5817..cd2aad92544c 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -167,7 +167,6 @@ void Curl_doh_cleanup(struct Curl_easy *data, struct Curl_resolv_async *async); #define Curl_doh_wanted(d) (!!(d)->set.doh) - #else /* CURL_DISABLE_DOH */ #define Curl_doh(a, b) NULL #define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST diff --git a/lib/hostip.c b/lib/hostip.c index e18c86298463..d3dd7f310ba5 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -507,7 +507,6 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, return Curl_async_get_ai(data, async, ai_family, index); } - #ifdef USE_HTTPSRR const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) @@ -525,7 +524,6 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) return TRUE; return Curl_async_knows_https(data, async); } - #endif /* USE_HTTPSRR */ #endif /* USE_CURL_ASYNC */ @@ -791,9 +789,6 @@ CURL_NORETURN static void alarmfunc(int sig) (void)sig; siglongjmp(curl_jmpenv, 1); } -#endif /* USE_ALARM_TIMEOUT */ - -#ifdef USE_ALARM_TIMEOUT static CURLcode resolv_alarm_timeout(struct Curl_easy *data, uint8_t dns_queries, diff --git a/lib/hsts.c b/lib/hsts.c index 856b525a6244..94738874b5d6 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -121,7 +121,7 @@ static CURLcode hsts_create(struct hsts *h, struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen); if(!sts) return CURLE_OUT_OF_MEMORY; - /* the null terminator is already there */ + /* the null-terminator is already there */ memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; @@ -445,7 +445,7 @@ static CURLcode hsts_add_host_expire(struct hsts *h, e = hsts_check(h, host, hostlen, subdomain); if(!e) result = hsts_create(h, host, hostlen, subdomain, expires); - /* 'host' is not necessarily null terminated */ + /* 'host' is not necessarily null-terminated */ else if((hostlen == strlen(e->host) && curl_strnequal(host, e->host, hostlen))) { /* the same hostname, use the largest expire time and keep the strictest @@ -509,7 +509,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) const char *date = e.expire; if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] || e.name[MAX_HSTS_HOSTLEN]) - /* bail out if no name was stored or if a null terminator is gone */ + /* bail out if no name was stored or if a null-terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; if(!date[0]) date = UNLIMITED; diff --git a/lib/mprintf.c b/lib/mprintf.c index 6eaea66b7055..1a7958f91399 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -423,7 +423,6 @@ static bool parse_conversion(const char f, unsigned int *flagp, return FALSE; } - static int parsefmt(const char *format, struct outsegment *out, struct va_input *in, @@ -968,7 +967,6 @@ static bool out_pointer(void *userp, * * All output is sent to the 'stream()' callback, one byte at a time. */ - static int formatf(void *userp, /* untouched by format(), sent to the stream() function in the second argument */ /* function pointer called for each output character */ diff --git a/lib/multi.c b/lib/multi.c index 53b23641eb93..b2618b4b32ea 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -78,8 +78,8 @@ /* On a debug build, we want to fail hard on multi handles that * are not NULL, but no longer have the MAGIC touch. This gives * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \ (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_MULTI_HANDLE(x) \ diff --git a/lib/peer.c b/lib/peer.c index 49669e4e0e9f..43e5aef0f040 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -117,7 +117,7 @@ static CURLcode peer_create(struct peer_parse *pp, } zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; - /* NUL terminator already part of struct */ + /* null-terminator already part of struct */ peer = curlx_calloc(1, sizeof(*peer) + pp->host_user.len + host_alen + zone_alen); if(!peer) { diff --git a/lib/socks.c b/lib/socks.c index 667e728d9abd..0acc21d7badf 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -94,7 +94,6 @@ static const char * const cf_socks_statename[] = { #define SOCKS_CHUNK_SIZE 1024 #define SOCKS_CHUNKS 1 - struct socks_ctx { enum socks_state_t state; struct bufq iobuf; diff --git a/lib/urlapi.c b/lib/urlapi.c index a2d3c7e35ade..2e7aa7824a75 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1972,7 +1972,6 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OUT_OF_MEMORY; } } - else if(what == CURLUPART_HOST) { size_t n = curlx_dyn_len(&enc); if(!n && (flags & CURLU_NO_AUTHORITY)) { diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index 4bb7196eae13..27500c879b69 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -64,7 +64,7 @@ AC_DEFUN([CURL_CHECK_COMPILER], [ *** compiler you are using, relative to the flags required to enable or *** disable generation of debug info, optimization options or warnings. *** -*** Whatever settings are present in CFLAGS will be used for this run. +*** Whatever settings are present in CFLAGS are used for this run. *** *** If you wish to help the curl project to better support your compiler *** you can report this and the required info on the libcurl development @@ -173,7 +173,7 @@ dnl CURL_CHECK_COMPILER_GNU_C dnl ------------------------------------------------- dnl Verify if compiler being used is GNU C dnl -dnl $compiler_num will be set to MAJOR * 100 + MINOR for gcc less than version +dnl $compiler_num is set to MAJOR * 100 + MINOR for gcc less than version dnl 7 and just $MAJOR * 100 for gcc version 7 and later. dnl dnl Examples: @@ -526,8 +526,8 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ CLANG|APPLECLANG) - dnl Disable warnings for unused arguments, otherwise clang will - dnl warn about compile-time arguments used during link-time, like + dnl Disable warnings for unused arguments, otherwise clang warns + dnl about compile-time arguments used during link-time, like dnl -O and -g and -pedantic. tmp_CFLAGS="$tmp_CFLAGS -Qunused-arguments" tmp_CFLAGS="$tmp_CFLAGS -Werror-implicit-function-declaration" @@ -713,7 +713,7 @@ AC_DEFUN([CURL_SET_COMPILER_OPTIMIZE_OPTS], [ dnl If optimization request setting has not been explicitly specified, dnl it has been derived from the debug setting and initially assumed. - dnl This initially assumed optimizer setting will finally be ignored + dnl This initially assumed optimizer setting are finally ignored dnl if CFLAGS or CPPFLAGS already hold optimizer flags. This implies dnl that an initially assumed optimizer setting might not be honored. diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index fe20155564a0..629b923cfa2b 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -150,25 +150,25 @@ AS_HELP_STRING([--disable-optimize],[Disable compiler optimizations]), OPT_COMPILER_OPTIMIZE=$enableval) case "$OPT_COMPILER_OPTIMIZE" in no) - dnl --disable-optimize option used. We will handle this as - dnl a request to disable compiler optimizations if possible. - dnl If the compiler is known CFLAGS and CPPFLAGS will be - dnl overridden, otherwise this can not be honored. + dnl --disable-optimize option used. We handle this as a request + dnl to disable compiler optimizations if possible. If the compiler + dnl is known CFLAGS and CPPFLAGS are overridden, otherwise this + dnl can not be honored. want_optimize="no" AC_MSG_RESULT([no]) ;; default) - dnl configure's optimize option not specified. Initially we will - dnl handle this as a request contrary to configure's setting - dnl for --enable-debug. IOW, initially, for debug-enabled builds - dnl this will be handled as a request to disable optimizations if - dnl possible, and for debug-disabled builds this will be handled - dnl initially as a request to enable optimizations if possible. - dnl Finally, if the compiler is known and CFLAGS and CPPFLAGS do - dnl not have any optimizer flag the request will be honored, in - dnl any other case the request can not be honored. + dnl configure's optimize option not specified. Initially we handle + dnl this as a request contrary to configure's setting for + dnl --enable-debug. IOW, initially, for debug-enabled builds this + dnl is handled as a request to disable optimizations if possible, + dnl and for debug-disabled builds this is handled initially as + dnl a request to enable optimizations if possible. Finally, if the + dnl compiler is known and CFLAGS and CPPFLAGS do not have any + dnl optimizer flag the request is honored, in any other case the + dnl request can not be honored. dnl IOW, existing optimizer flags defined in CFLAGS or CPPFLAGS - dnl will always take precedence over any initial assumption. + dnl always take precedence over any initial assumption. if test "$want_debug" = "yes"; then want_optimize="assume_no" AC_MSG_RESULT([(assumed) no]) @@ -178,10 +178,10 @@ AS_HELP_STRING([--disable-optimize],[Disable compiler optimizations]), fi ;; *) - dnl --enable-optimize option used. We will handle this as - dnl a request to enable compiler optimizations if possible. - dnl If the compiler is known CFLAGS and CPPFLAGS will be - dnl overridden, otherwise this can not be honored. + dnl --enable-optimize option used. We handle this as a request + dnl to enable compiler optimizations if possible. If the compiler + dnl is known CFLAGS and CPPFLAGS are overridden, otherwise this + dnl can not be honored. want_optimize="yes" AC_MSG_RESULT([yes]) ;; @@ -207,7 +207,7 @@ AS_HELP_STRING([--disable-symbol-hiding],[Disable hiding of library internal sym no) dnl --disable-symbol-hiding option used. dnl This is an indication to not attempt hiding of library internal - dnl symbols. Default symbol visibility will be used, which normally + dnl symbols. Default symbol visibility is used, which normally dnl exposes all library internal symbols. want_symbol_hiding="no" AC_MSG_RESULT([no]) @@ -361,13 +361,13 @@ dnl CURL_CONFIGURE_SYMBOL_HIDING dnl ------------------------------------------------- dnl Depending on --enable-symbol-hiding or --disable-symbol-hiding dnl configure option, and compiler capability to actually honor such -dnl option, this will modify compiler flags as appropriate and also -dnl provide needed definitions for configuration and Makefile.am files. +dnl option, this modifies compiler flags as appropriate and also +dnl provides needed definitions for configuration and Makefile.am files. dnl This macro should not be used until all compilation tests have dnl been done to prevent interferences on other tests. AC_DEFUN([CURL_CONFIGURE_SYMBOL_HIDING], [ - AC_MSG_CHECKING([whether hiding of library internal symbols will actually happen]) + AC_MSG_CHECKING([whether hiding of library internal symbols does actually happen]) CFLAG_CURL_SYMBOL_HIDING="" doing_symbol_hiding="no" if test "$want_symbol_hiding" = "yes" && @@ -456,7 +456,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ ]) if test "$want_ares" = "yes"; then - dnl finally c-ares will be used + dnl finally c-ares is used AC_DEFINE(USE_ARES, 1, [Define to enable c-ares support]) USE_ARES=1 LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libcares" diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 999f96add0e9..8c5a03e83bbb 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -467,7 +467,7 @@ dnl Verify if alarm is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_alarm, then -dnl HAVE_ALARM will be defined. +dnl HAVE_ALARM is defined. AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ AC_REQUIRE([CURL_INCLUDES_UNISTD]) @@ -552,7 +552,7 @@ dnl Verify if basename is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_basename, then -dnl HAVE_BASENAME will be defined. +dnl HAVE_BASENAME is defined. AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -643,7 +643,7 @@ dnl Verify if closesocket is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_closesocket, then -dnl HAVE_CLOSESOCKET will be defined. +dnl HAVE_CLOSESOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -733,7 +733,7 @@ dnl Verify if CloseSocket is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_closesocket_camel, -dnl then HAVE_CLOSESOCKET_CAMEL will be defined. +dnl then HAVE_CLOSESOCKET_CAMEL is defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) @@ -810,7 +810,7 @@ dnl Verify if fcntl is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_fcntl, then -dnl HAVE_FCNTL will be defined. +dnl HAVE_FCNTL is defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ AC_REQUIRE([CURL_INCLUDES_FCNTL]) @@ -895,7 +895,7 @@ dnl ------------------------------------------------- dnl Verify if fcntl with status flag O_NONBLOCK is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_FCNTL_O_NONBLOCK -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ @@ -959,7 +959,7 @@ dnl Verify if freeaddrinfo is available, prototyped, dnl and can be compiled. If all of these are true, dnl and usage has not been previously disallowed with dnl shell variable curl_disallow_freeaddrinfo, then -dnl HAVE_FREEADDRINFO will be defined. +dnl HAVE_FREEADDRINFO is defined. AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ AC_REQUIRE([CURL_INCLUDES_WS2TCPIP]) @@ -1055,7 +1055,7 @@ dnl Verify if fsetxattr is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_fsetxattr, then -dnl HAVE_FSETXATTR will be defined. +dnl HAVE_FSETXATTR is defined. AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_REQUIRE([CURL_INCLUDES_SYS_XATTR]) @@ -1177,8 +1177,8 @@ dnl Verify if getaddrinfo is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_getaddrinfo, then -dnl HAVE_GETADDRINFO will be defined. Additionally when -dnl HAVE_GETADDRINFO gets defined this will also attempt +dnl HAVE_GETADDRINFO is defined. Additionally when +dnl HAVE_GETADDRINFO gets defined this also attempts dnl to find out if getaddrinfo happens to be thread-safe, dnl defining HAVE_GETADDRINFO_THREADSAFE when true. @@ -1408,7 +1408,7 @@ dnl Verify if gethostbyname_r is available, prototyped, dnl and can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_gethostbyname_r, then -dnl HAVE_GETHOSTBYNAME_R will be defined. +dnl HAVE_GETHOSTBYNAME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ AC_REQUIRE([CURL_INCLUDES_NETDB]) @@ -1553,7 +1553,7 @@ dnl Verify if gethostname is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_gethostname, then -dnl HAVE_GETHOSTNAME will be defined. +dnl HAVE_GETHOSTNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1689,7 +1689,7 @@ dnl Verify if getpeername is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_getpeername, then -dnl HAVE_GETPEERNAME will be defined. +dnl HAVE_GETPEERNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1787,7 +1787,7 @@ dnl Verify if getsockname is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_getsockname, then -dnl HAVE_GETSOCKNAME will be defined. +dnl HAVE_GETSOCKNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1886,7 +1886,7 @@ dnl Verify if getifaddrs is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_getifaddrs, then -dnl HAVE_GETIFADDRS will be defined. +dnl HAVE_GETIFADDRS is defined. AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2004,7 +2004,7 @@ dnl Verify if gmtime_r is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_gmtime_r, then -dnl HAVE_GMTIME_R will be defined. +dnl HAVE_GMTIME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2124,7 +2124,7 @@ dnl Verify if localtime_r is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_localtime_r, then -dnl HAVE_LOCALTIME_R will be defined. +dnl HAVE_LOCALTIME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2244,7 +2244,7 @@ dnl Verify if inet_ntop is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_inet_ntop, then -dnl HAVE_INET_NTOP will be defined. +dnl HAVE_INET_NTOP is defined. AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2405,7 +2405,7 @@ dnl Verify if inet_pton is available, prototyped, can dnl be compiled and seems to work. If all of these are dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_inet_pton, then -dnl HAVE_INET_PTON will be defined. +dnl HAVE_INET_PTON is defined. AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2643,7 +2643,7 @@ dnl ------------------------------------------------- dnl Verify if ioctl with the FIONBIO command is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_IOCTL_FIONBIO -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ @@ -2699,7 +2699,7 @@ dnl ------------------------------------------------- dnl Verify if ioctl with the SIOCGIFADDR command is available, dnl struct ifreq is defined, they can be compiled, and seem to dnl work. If all of these are true, then HAVE_IOCTL_SIOCGIFADDR -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ @@ -2757,7 +2757,7 @@ dnl Verify if ioctlsocket is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_ioctlsocket, then -dnl HAVE_IOCTLSOCKET will be defined. +dnl HAVE_IOCTLSOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -2847,7 +2847,7 @@ dnl ------------------------------------------------- dnl Verify if ioctlsocket with the FIONBIO command is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_IOCTLSOCKET_FIONBIO -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ @@ -2904,7 +2904,7 @@ dnl Verify if IoctlSocket is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_ioctlsocket_camel, -dnl then HAVE_IOCTLSOCKET_CAMEL will be defined. +dnl then HAVE_IOCTLSOCKET_CAMEL is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) @@ -2978,7 +2978,7 @@ dnl CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO dnl ------------------------------------------------- dnl Verify if IoctlSocket with FIONBIO command is available, dnl can be compiled, and seems to work. If all of these are -dnl true, then HAVE_IOCTLSOCKET_CAMEL_FIONBIO will be defined. +dnl true, then HAVE_IOCTLSOCKET_CAMEL_FIONBIO is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) @@ -3036,7 +3036,7 @@ dnl Verify if memrchr is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_memrchr, then -dnl HAVE_MEMRCHR will be defined. +dnl HAVE_MEMRCHR is defined. AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3141,7 +3141,7 @@ dnl Verify if sigaction is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_sigaction, then -dnl HAVE_SIGACTION will be defined. +dnl HAVE_SIGACTION is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3226,7 +3226,7 @@ dnl Verify if siginterrupt is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_siginterrupt, then -dnl HAVE_SIGINTERRUPT will be defined. +dnl HAVE_SIGINTERRUPT is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3311,7 +3311,7 @@ dnl Verify if signal is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_signal, then -dnl HAVE_SIGNAL will be defined. +dnl HAVE_SIGNAL is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3396,7 +3396,7 @@ dnl Verify if sigsetjmp is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_sigsetjmp, then -dnl HAVE_SIGSETJMP will be defined. +dnl HAVE_SIGSETJMP is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ AC_REQUIRE([CURL_INCLUDES_SETJMP]) @@ -3503,7 +3503,7 @@ dnl Verify if socket is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_socket, then -dnl HAVE_SOCKET will be defined. +dnl HAVE_SOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -3600,7 +3600,7 @@ dnl Verify if socketpair is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_socketpair, then -dnl HAVE_SOCKETPAIR will be defined. +dnl HAVE_SOCKETPAIR is defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) @@ -3686,7 +3686,7 @@ dnl Verify if strcasecmp is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_strcasecmp, then -dnl HAVE_STRCASECMP will be defined. +dnl HAVE_STRCASECMP is defined. AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3770,7 +3770,7 @@ dnl Verify if strcmpi is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_strcmpi, then -dnl HAVE_STRCMPI will be defined. +dnl HAVE_STRCMPI is defined. AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3854,7 +3854,7 @@ dnl ------------------------------------------------- dnl Verify if strerror_r is available, prototyped, can be compiled and dnl seems to work. If all of these are true, and usage has not been dnl previously disallowed with shell variable curl_disallow_strerror_r, -dnl then HAVE_STRERROR_R will be defined, as well as one of +dnl then HAVE_STRERROR_R is defined, as well as one of dnl HAVE_GLIBC_STRERROR_R or HAVE_POSIX_STRERROR_R. dnl dnl glibc-style strerror_r: @@ -4120,7 +4120,7 @@ dnl Verify if stricmp is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_stricmp, then -dnl HAVE_STRICMP will be defined. +dnl HAVE_STRICMP is defined. AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -4205,7 +4205,7 @@ dnl Verify if memset_s is available, prototyped, and dnl can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_memset_s, then -dnl HAVE_MEMSET_S will be defined. +dnl HAVE_MEMSET_S is defined. AC_DEFUN([CURL_CHECK_FUNC_MEMSET_S], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -4356,6 +4356,7 @@ dnl CURL_ATOMIC dnl ------------------------------------------------------------- dnl Check if _Atomic works. But only check if stdatomic.h exists. dnl + AC_DEFUN([CURL_ATOMIC],[ AC_CHECK_HEADERS(stdatomic.h, [ AC_MSG_CHECKING([if _Atomic is available]) @@ -4437,5 +4438,4 @@ AC_DEFUN([CURL_SIZEOF], [ eval "$tname=$r" AC_DEFINE_UNQUOTED(TYPE, [$r], [Size of $1 in number of bytes]) - ]) diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index d0f2f261ed17..5948440201db 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -80,7 +80,7 @@ if test "x$OPT_OPENSSL" != "xno"; then dnl the user told us to look OPENSSL_PCDIR="$OPT_OPENSSL/lib/pkgconfig" if test -f "$OPENSSL_PCDIR/openssl.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$OPENSSL_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$OPENSSL_PCDIR"]) PKGTEST="yes" fi @@ -88,7 +88,7 @@ if test "x$OPT_OPENSSL" != "xno"; then dnl try lib64 instead OPENSSL_PCDIR="$OPT_OPENSSL/lib64/pkgconfig" if test -f "$OPENSSL_PCDIR/openssl.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$OPENSSL_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$OPENSSL_PCDIR"]) PKGTEST="yes" fi fi @@ -385,14 +385,14 @@ if test "$OPENSSL_ENABLED" = "1"; then ]) dnl --- - dnl Whether the OpenSSL configuration will be loaded automatically + dnl Whether the OpenSSL configuration is loaded automatically dnl --- AC_ARG_ENABLE(openssl-auto-load-config, AS_HELP_STRING([--enable-openssl-auto-load-config],[Enable automatic loading of OpenSSL configuration]) AS_HELP_STRING([--disable-openssl-auto-load-config],[Disable automatic loading of OpenSSL configuration]), [ if test "x$enableval" = "xno"; then AC_MSG_NOTICE([automatic loading of OpenSSL configuration disabled]) - AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration will not be loaded automatically]) + AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration is not loaded automatically]) fi ]) diff --git a/m4/curl-override.m4 b/m4/curl-override.m4 index 79b24567ac98..59e6548dbd35 100644 --- a/m4/curl-override.m4 +++ b/m4/curl-override.m4 @@ -28,7 +28,7 @@ dnl serial 7 dnl CURL_OVERRIDE_AUTOCONF dnl ------------------------------------------------- dnl Placing a call to this macro in configure.ac after -dnl the one to AC_INIT will make macros in this file +dnl the one to AC_INIT makes macros in this file dnl visible to the rest of the compilation overriding dnl those from Autoconf. diff --git a/m4/curl-reentrant.m4 b/m4/curl-reentrant.m4 index 6ac731bed9f1..eb585ad64d27 100644 --- a/m4/curl-reentrant.m4 +++ b/m4/curl-reentrant.m4 @@ -320,10 +320,10 @@ AC_DEFUN([CURL_CHECK_NEED_THREAD_SAFE_SYSTEM], [ dnl CURL_CONFIGURE_FROM_NOW_ON_WITH_REENTRANT dnl ------------------------------------------------- dnl This macro ensures that configuration tests done -dnl after this will execute with preprocessor symbol -dnl _REENTRANT defined. This macro also ensures that -dnl the generated config file defines NEED_REENTRANT -dnl and that in turn curl_setup.h will define _REENTRANT. +dnl after this execute with preprocessor symbol _REENTRANT +dnl defined. This macro also ensures that the generated +dnl config file defines NEED_REENTRANT and that in turn +dnl curl_setup.h defines _REENTRANT. dnl Internal macro for CURL_CONFIGURE_REENTRANT. AC_DEFUN([CURL_CONFIGURE_FROM_NOW_ON_WITH_REENTRANT], [ @@ -340,10 +340,10 @@ _EOF dnl CURL_CONFIGURE_FROM_NOW_ON_WITH_THREAD_SAFE dnl ------------------------------------------------- dnl This macro ensures that configuration tests done -dnl after this will execute with preprocessor symbol -dnl _THREAD_SAFE defined. This macro also ensures that -dnl the generated config file defines NEED_THREAD_SAFE -dnl and that in turn curl_setup.h will define _THREAD_SAFE. +dnl after this execute with preprocessor symbol_THREAD_SAFE +dnl defined. This macro also ensures that the generated +dnl config file defines NEED_THREAD_SAFE and that in turn +dnl curl_setup.h defines _THREAD_SAFE. dnl Internal macro for CURL_CONFIGURE_THREAD_SAFE. AC_DEFUN([CURL_CONFIGURE_FROM_NOW_ON_WITH_THREAD_SAFE], [ diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index 2a035680ffcf..9ca3d678e14a 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -57,7 +57,7 @@ if test "x$OPT_RUSTLS" != "xno"; then RUSTLS_PCDIR="$PREFIX_RUSTLS/lib/pkgconfig" if test -f "$RUSTLS_PCDIR/rustls.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$RUSTLS_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$RUSTLS_PCDIR"]) PKGTEST="yes" fi @@ -65,7 +65,7 @@ if test "x$OPT_RUSTLS" != "xno"; then dnl try lib64 instead RUSTLS_PCDIR="$PREFIX_RUSTLS/lib64/pkgconfig" if test -f "$RUSTLS_PCDIR/rustls.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$RUSTLS_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$RUSTLS_PCDIR"]) PKGTEST="yes" fi fi @@ -95,7 +95,7 @@ if test "x$OPT_RUSTLS" != "xno"; then SSL_CPPFLAGS="-I$PREFIX_RUSTLS/include" fi - dnl we will verify AC_CHECK_LIB later on + dnl we verify AC_CHECK_LIB later on AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) USE_RUSTLS="yes" fi diff --git a/m4/xc-lt-iface.m4 b/m4/xc-lt-iface.m4 index 0d8b0ef31fc4..9f79fdb95210 100644 --- a/m4/xc-lt-iface.m4 +++ b/m4/xc-lt-iface.m4 @@ -111,7 +111,7 @@ esac dnl dnl Default behavior on some systems where building a shared library out -dnl of non-PIC compiled objects will fail with following linker error +dnl of non-PIC compiled objects fails with following linker error dnl "relocation R_X86_64_32 can not be used when making a shared object" dnl is to build PIC objects even for static libraries. This behavior may dnl be overridden using 'configure --disable-shared --without-pic'. @@ -183,10 +183,10 @@ dnl xc_lt_build_static m4_define([_XC_CHECK_LT_BUILD_LIBRARIES], [ + # -# Verify if finally libtool shared libraries will be built +# Verify if finally libtool shared libraries are built # - case "x$enable_shared" in @%:@ (( xyes | xno) xc_lt_build_shared=$enable_shared @@ -197,9 +197,8 @@ case "x$enable_shared" in @%:@ (( esac # -# Verify if finally libtool static libraries will be built +# Verify if finally libtool static libraries are built # - case "x$enable_static" in @%:@ (( xyes | xno) xc_lt_build_static=$enable_static @@ -362,10 +361,10 @@ dnl xc_lt_build_static_only m4_define([_XC_CHECK_LT_BUILD_SINGLE_VERSION], [ + # -# Verify if libtool shared libraries will be built while static not built +# Verify if libtool shared libraries are built while static not built # - AC_MSG_CHECKING([whether to build shared libraries only]) if test "$xc_lt_build_shared" = "yes" && test "$xc_lt_build_static" = "no"; then @@ -376,9 +375,8 @@ fi AC_MSG_RESULT([$xc_lt_build_shared_only]) # -# Verify if libtool static libraries will be built while shared not built +# Verify if libtool static libraries are built while shared not built # - AC_MSG_CHECKING([whether to build static libraries only]) if test "$xc_lt_build_static" = "yes" && test "$xc_lt_build_shared" = "no"; then diff --git a/m4/zz40-xc-ovr.m4 b/m4/zz40-xc-ovr.m4 index 5d2b2d0b727d..2713dcc8cb52 100644 --- a/m4/zz40-xc-ovr.m4 +++ b/m4/zz40-xc-ovr.m4 @@ -606,7 +606,7 @@ dnl XC_CONFIGURE_PREAMBLE macro and happens early in dnl generated configure script. The second one shows and dnl logs the result of the check into config.log at a later dnl configure stage. Placement of this second stage in -dnl generated configure script will be done where first +dnl generated configure script is done where first dnl direct or indirect usage of this macro happens. AC_DEFUN([XC_CHECK_PATH_SEPARATOR], diff --git a/m4/zz50-xc-ovr.m4 b/m4/zz50-xc-ovr.m4 index 155eb7f9b494..563c5ab25a19 100644 --- a/m4/zz50-xc-ovr.m4 +++ b/m4/zz50-xc-ovr.m4 @@ -52,8 +52,8 @@ m4_define([AC_LIBTOOL_LANG_GCJ_CONFIG],[:]) dnl XC_OVR_ZZ50 dnl ------------------------------------------------- -dnl Placing a call to this macro in configure.ac will -dnl make macros in this file visible to other macros +dnl Placing a call to this macro in configure.ac +dnl makes macros in this file visible to other macros dnl used for same configure script, overriding those dnl provided elsewhere. diff --git a/projects/Windows/generate.bat b/projects/Windows/generate.bat index 8441f7f3f3bf..b5da6d9a798c 100644 --- a/projects/Windows/generate.bat +++ b/projects/Windows/generate.bat @@ -337,8 +337,8 @@ rem :seterr rem Set the caller's errorlevel. rem %1[opt]: Errorlevel as integer. - rem If %1 is empty the errorlevel will be set to 0. - rem If %1 is not empty and not an integer the errorlevel will be set to 1. + rem If %1 is empty the errorlevel is set to 0. + rem If %1 is not empty and not an integer the errorlevel is set to 1. setlocal set EXITCODE=%~1 if not defined EXITCODE set EXITCODE=0 diff --git a/scripts/badwords b/scripts/badwords index 5ed7a3ece14e..09676bacd906 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -5,7 +5,7 @@ # # bad[:=]correct # -# If separator is '=', the string will be compared case sensitively. +# If separator is '=', the string is compared case sensitively. # If separator is ':', the check is done case insensitively. # # To add white listed uses of bad words that are removed before checking for diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 2de6f5bbe8f6..a02a948bd8df 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -17,10 +17,18 @@ tool-chain:toolchain wild-card:wildcard wild card:wildcard thread safe:thread-safe +thread safety:thread-safety thread unsafe:thread-unsafe multi thread:multi-thread +nul terminate:null-terminate +null terminate:null-terminate +zero terminate:null-terminate +nul terminated:null-terminated null terminated:null-terminated zero terminated:null-terminated +nul terminator:null-terminator +null terminator:null-terminator +zero terminator:null-terminator it's:it is aren't:are not can't:cannot @@ -96,6 +104,7 @@ will:rewrite to present tense 16-bits:16 bits 32-bits:32 bits 64-bits:64 bits +initialise:initialize very:rephrase using an alternative word just:rephrase using an alternative word simply:rephrase using an alternative word diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index d796bc5da0ce..de1d3c5e626f 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -1199,7 +1199,7 @@ sub scanfile { # A rather more interesting, and correct, check would be to not test # only locally committed files but inspect all files wrt the year of # their last commit. Removing the `git rev-list origin/master..HEAD` - # condition below will enforce copyright year checks against the year + # condition below enforces copyright year checks against the year # the file was last committed (and thus edited to some degree). my $commityear = undef; @copyright = sort {$$b{year} cmp $$a{year}} @copyright; diff --git a/scripts/cmakelint.sh b/scripts/cmakelint.sh index 30a735f87edc..3fe258346df2 100755 --- a/scripts/cmakelint.sh +++ b/scripts/cmakelint.sh @@ -27,7 +27,7 @@ # https://cmake-format.readthedocs.io/en/latest/lint-usage.html # https://github.com/cheshirekow/cmake_format/blob/master/cmakelang/configuration.py -# Run cmakelint on the curl source code. It will check all files given on the +# Run cmakelint on the curl source code. It checks all files given on the # command-line, or else all relevant files in git, or if not in a git # repository, all files starting in the tree rooted in the current directory. # @@ -36,8 +36,8 @@ # # The xargs invocation is portable, but does not preserve spaces in filenames. # If such a file is ever added, then this can be portably fixed by switching to -# "xargs -I{}" and appending {} to the end of the xargs arguments (which will -# call cmakelint once per file) or by using the GNU extension "xargs -d'\n'". +# "xargs -I{}" and appending {} to the end of the xargs arguments (which calls +# cmakelint once per file) or by using the GNU extension "xargs -d'\n'". set -eu diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index 4eb759863435..f36c16892df6 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -153,7 +153,7 @@ () print " 3. certdata.txt file format may change, lag time to update this script\n"; print " 4. Generally unwise to blindly trust CAs without manual review & verification\n"; print " 5. Mozilla apps use additional security checks are not represented in certdata\n"; - print " 6. Use of this script will make a security engineer grind his teeth and\n"; + print " 6. Use of this script makes a security engineer grind his teeth and\n"; print " swear at you. ;)\n"; exit; } else { # Short Form Warning @@ -228,7 +228,7 @@ ($$@) if(scalar(@invalid) > 0) { # Tell the user which parameters were invalid and print the standard help - # message which will exit + # message which also exits print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join(", ", map { "\"$_\"" } @invalid), "\n"; HELP_MESSAGE(); } diff --git a/scripts/perlcheck.sh b/scripts/perlcheck.sh index c243f50271c5..28c5be2c1678 100755 --- a/scripts/perlcheck.sh +++ b/scripts/perlcheck.sh @@ -25,8 +25,8 @@ # The xargs invocation is portable, but does not preserve spaces in filenames. # If such a file is ever added, then this can be portably fixed by switching to -# "xargs -I{}" and appending {} to the end of the xargs arguments (which will -# call cmakelint once per file) or by using the GNU extension "xargs -d'\n'". +# "xargs -I{}" and appending {} to the end of the xargs arguments (which calls +# cmakelint once per file) or by using the GNU extension "xargs -d'\n'". set -eu diff --git a/scripts/release-notes.pl b/scripts/release-notes.pl index a4b4e2550fa0..7288bbe5146b 100755 --- a/scripts/release-notes.pl +++ b/scripts/release-notes.pl @@ -32,8 +32,8 @@ # $ ./scripts/release-notes.pl # # 2. Edit RELEASE-NOTES and remove all entries that do not belong. Unused -# references below will be cleaned up in the next step. Make sure to move -# "changes" up to the changes section. All entries will by default be listed +# references below are cleaned up in the next step. Make sure to move +# "changes" up to the changes section. All entries are by default listed # under bug-fixes as this script cannot know where to put them. # # 3. Run the cleanup script and let it sort the entries and remove unused diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index e0b60b7dbab8..3feb3e782471 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -219,7 +219,7 @@ void config_free(struct OperationConfig *config) * round to verify them. * * The main point is to make sure that what is returned is different than what - * the regular memory functions return so that mixup will trigger problems. + * the regular memory functions return so that mixup does trigger problems. * * This test setup currently only works when building with a *shared* libcurl * and not static, as in the latter case the tool and the library share some of diff --git a/src/tool_doswin.c b/src/tool_doswin.c index d787fda5597f..6795fdf05abf 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -804,7 +804,7 @@ curl_socket_t win32_stdin_read_thread(void) break; } - /* Bind to any available loopback port */ + /* Retrieve the assigned loopback port/address */ if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { errorf("getsockname error: %d", SOCKERRNO); break; diff --git a/src/tool_formparse.c b/src/tool_formparse.c index f74cb77433e0..d93e8a68d0af 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -652,8 +652,7 @@ static void param_encoder(char **ptr, char **endct, char **pencoder, * @param str Pointer to the current position in the input string. * Updated to point at the delimiter or terminator that * ended the parsed part. - * @param pdata Pointer to a char * that will receive the primary data - * word. + * @param pdata Pointer to a char * that receives the primary data word. * @param ptype [out] Optional. Receives the extracted 'type=' value. * @param pfilename [out] Optional. Receives the extracted 'filename=' value. * @param pencoder [out] Optional. Receives the extracted 'encoder=' value. diff --git a/src/var.c b/src/var.c index 79ff888dbb99..93408be28f9e 100644 --- a/src/var.c +++ b/src/var.c @@ -355,7 +355,7 @@ static ParameterError addvariable(const char *name, p = curlx_calloc(1, sizeof(struct tool_var) + nlen); if(p) { memcpy(p->name, name, nlen); - /* the null termination byte is already present from above */ + /* the null-termination byte is already present from above */ p->content = contalloc ? content : curlx_memdup0(content, clen); if(p->content) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9835bcec725f..e75ca4e14ec9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -82,8 +82,8 @@ function(curl_add_runtests _targetname _test_flags) if(_setenvs) set(_setenvs "${CMAKE_COMMAND}" -E env ${_setenvs}) endif() - # Use a special '$TFLAGS' placeholder as last argument which will be - # replaced by the contents of the environment variable in runtests.pl. + # Use a special '$TFLAGS' placeholder as last argument which is replaced + # by the contents of the environment variable in runtests.pl. # This is a workaround for CMake's limitation where commands executed by # 'make' or 'ninja' cannot portably reference environment variables. string(REPLACE " " ";" _test_flags_list "${_test_flags}") diff --git a/tests/data/DISABLED b/tests/data/DISABLED index 8607866fd039..b6fe06d7955e 100644 --- a/tests/data/DISABLED +++ b/tests/data/DISABLED @@ -28,7 +28,7 @@ # Lines starting with '#' letters are treated as comments. # # Uses SRP to "a server not supporting it" but modern stunnel versions -# will silently accept it and remain happy +# silently accept it and remain happy 323 # 594 diff --git a/tests/devtest.pl b/tests/devtest.pl index ed9958907445..47a960a66c0c 100755 --- a/tests/devtest.pl +++ b/tests/devtest.pl @@ -94,7 +94,7 @@ sub parseprotocols { # Generate a "proto-ipv6" version of each protocol to match the # IPv6 name and a "proto-unix" to match the variant which # uses Unix domain sockets. This works even if support is not - # compiled in because the test will fail. + # compiled in because the test fails. push @protocols, map(("$_-ipv6", "$_-unix"), @protocols); # 'http-proxy' is used in test cases to do CONNECT through diff --git a/tests/ech_combos.py b/tests/ech_combos.py index 8c100c2bb734..586619be33fc 100755 --- a/tests/ech_combos.py +++ b/tests/ech_combos.py @@ -26,7 +26,7 @@ # # Python3 program to print all combination of size r in an array of size n. # This is used to generate test lines in tests/ech_test.sh. -# This will be discarded in the process of moving from experimental, +# This is discarded in the process of moving from experimental, # but is worth preserving for the moment in case of changes to the # ECH command line args diff --git a/tests/ech_tests.sh b/tests/ech_tests.sh index 3944793b702b..4de58e4bc5f0 100755 --- a/tests/ech_tests.sh +++ b/tests/ech_tests.sh @@ -253,9 +253,9 @@ fi wolf_cnt=$($CURL "${CURL_PARAMS[@]}" -V 2> /dev/null | grep -c wolfSSL) if ((wolf_cnt == 1)); then using_wolf="yes" - # for some reason curl+wolfSSL dislikes certs that are ok - # for browsers, so we will test using "insecure" mode (-k) - # but that is ok here as we are only interested in ECH testing + # for some reason curl + wolfSSL dislikes certs that are ok + # for browsers, so we test using "insecure" mode (-k) + # but that is OK here as we are only interested in ECH testing CURL_PARAMS+=(-k) fi # check if we have dig and it knows https or not @@ -474,7 +474,7 @@ done # Check various command line options, if we are good so far if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then - # use this test URL as it will tell us if things worked + # use this test URL as it tells us if things worked turl="https://defo.ie/ech-check.php" echo "cli_test with $turl" echo "cli_test with $turl" >> "$logfile" @@ -498,7 +498,7 @@ if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then # ecl:ecl can be correct, incorrect or missing # ech:pn can be correct, incorrect or missing # in all cases the "last" argument provided should "win" - # but only one of hard, true, grease or false will apply + # but only one of hard, true, grease or false applies turl="https://defo.ie/ech-check.php" echconfiglist=$(get_ech_configlist defo.ie) goodecl=$echconfiglist @@ -790,7 +790,7 @@ if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then turl="https://tcd.ie" echo "cli_test with $turl" echo "cli_test with $turl" >> "$logfile" - # the params below do not matter much here as we will fail anyway + # the params below do not matter much here as we fail anyway echconfiglist=$(get_ech_configlist defo.ie) goodecl=$echconfiglist badecl="$goodecl" diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 32080b21f6f6..a15c69e5fb83 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -196,9 +196,9 @@ BEGIN my $POP3_TIMESTAMP = "<1972.987654321\@curl>"; #********************************************************************** -# exit_signal_handler will be triggered to indicate that the program +# exit_signal_handler is triggered to indicate that the program # should finish its execution in a controlled way as soon as possible. -# For now, program will also terminate from within this handler. +# For now, program also terminates from within this handler. # sub exit_signal_handler { my $signame = shift; @@ -227,9 +227,9 @@ sub ftpmsg { } #********************************************************************** -# eXsysread is a wrapper around perl's sysread() function. This will -# repeat the call to sysread() until it has actually read the complete -# number of requested bytes or an unrecoverable condition occurs. +# eXsysread is a wrapper around perl's sysread() function. This repeats +# the call to sysread() until it has actually read the complete number +# of requested bytes or an unrecoverable condition occurs. # On success returns a positive value, the number of bytes requested. # On failure or timeout returns zero. # @@ -531,7 +531,7 @@ sub senddata { #********************************************************************** # protocolsetup initializes the 'displaytext' and 'commandfunc' hashes # for the given protocol. References to protocol command callbacks are -# stored in 'commandfunc' hash, and text which will be returned to the +# stored in 'commandfunc' hash, and text which is returned to the # client before the command callback runs is stored in 'displaytext'. # sub protocolsetup { diff --git a/tests/getpart.pm b/tests/getpart.pm index 5fa4b1e62b34..ad52782de0a8 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -54,7 +54,7 @@ my $trace=0; # Normalize the part function arguments for proper caching. This includes the # filename in the arguments since that is an implied parameter that affects the -# return value. Any error messages will only be displayed the first time, but +# return value. Any error messages are only displayed the first time, but # those are disabled by default anyway, so should never been seen outside # development. sub normalize_part { @@ -195,7 +195,7 @@ sub partexists { } # The code currently never calls this more than once per part per file, so -# caching a result that will never be used again just slows things down. +# caching a result that is never used again just slows things down. # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result sub loadtest { diff --git a/tests/globalconfig.pm b/tests/globalconfig.pm index d99d4306cf2b..ac636f2748b1 100644 --- a/tests/globalconfig.pm +++ b/tests/globalconfig.pm @@ -110,7 +110,7 @@ our $pwd = getcwd(); # current working directory our $srcdir = $ENV{'srcdir'} || '.'; # root of the test source code our $perlcmd=shell_quote($^X); our $perl="$perlcmd -I. " . shell_quote("-I$srcdir"); # invoke perl like this -our $LOGDIR="log"; # root of the log directory; this will be different for +our $LOGDIR="log"; # root of the log directory; this is different for # each runner in multiprocess mode our $LIBDIR=dirsepadd("./libtest/" . ($ENV{'CURL_DIRSUFFIX'} || '')); our $UNITDIR=dirsepadd("./unit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); diff --git a/tests/libtest/cli_upload_pausing.c b/tests/libtest/cli_upload_pausing.c index c52117bf7887..228304368a32 100644 --- a/tests/libtest/cli_upload_pausing.c +++ b/tests/libtest/cli_upload_pausing.c @@ -169,12 +169,12 @@ static CURLcode test_cli_upload_pausing(const char *URL) /* We want to use our own read function. */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); - /* It will help us to continue the read function. */ + /* It helps us to continue the read function. */ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, curl); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - /* It will help us to ensure that keepalive does not help. */ + /* It helps us to ensure that keepalive does not help. */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 1L); diff --git a/tests/secureserver.pl b/tests/secureserver.pl index 9cd963261ffc..ae7a2c464996 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -366,7 +366,7 @@ sub exit_signal_handler { # Put an "exec" in front of the command so that the child process # keeps this child's process ID by being tied to the spawned shell. exec("exec $cmd") || die "Cannot exec() $cmd: $!"; - # exec() will create a new process, but ties the existence of the + # exec() creates a new process, but ties the existence of the # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect diff --git a/tests/servers.pm b/tests/servers.pm index dba3c3e1d1e1..07f08d994a60 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -1348,7 +1348,7 @@ sub runhttpsserver { if($httpspid <= 0 || !pidexists($httpspid)) { # it is NOT alive - # do not call stopserver since that will also kill the dependent + # do not call stopserver since that also kills the dependent # server that has already been started properly $doesntrun{$pidfile} = 1; $httpspid = $pid2 = 0; @@ -1552,7 +1552,7 @@ sub runsecureserver { if($protospid <= 0 || !pidexists($protospid)) { # it is NOT alive - # do not call stopserver since that will also kill the dependent + # do not call stopserver since that also kills the dependent # server that has already been started properly $doesntrun{$pidfile} = 1; $protospid = $pid2 = 0; diff --git a/tests/smbserver.py b/tests/smbserver.py index c2942079882e..000dda76e76a 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -125,7 +125,7 @@ def smbserver(options): smb_config.set("SERVER", "share type", "0") smb_config.set("SERVER", "path", SERVER_MAGIC) - # Have a share for tests. These files will be autogenerated from the + # Have a share for tests. These files are auto-generated from the # test input. smb_config.add_section("TESTS") smb_config.set("TESTS", "comment", "tests") @@ -146,7 +146,7 @@ def smbserver(options): # Start a thread that cleanly shuts down the server on a signal with ShutdownHandler(smb_server): - # This will block until smb_server.shutdown() is called + # This blocks until smb_server.shutdown() is called smb_server.serve_forever() return 0 diff --git a/tests/sshserver.pl b/tests/sshserver.pl index c4d0285782ab..58e64d2f4ed1 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -1186,7 +1186,7 @@ sub sshd_supports_opt { # Put an "exec" in front of the command so that the child process # keeps this child's process ID by being tied to the spawned shell. exec("exec $cmd") || die "Cannot exec() $cmd: $!"; - # exec() will create a new process, but ties the existence of the + # exec() creates a new process, but ties the existence of the # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect diff --git a/tests/test1119.pl b/tests/test1119.pl index 9004696f6f55..0c9ae70ed5f4 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -62,7 +62,7 @@ my %doc; my %rem; -# scanenum runs the preprocessor on curl.h so it will process all enums +# scanenum runs the preprocessor on curl.h so it processes all enums # included by it, which *should* be all headers sub scanenum { my ($file) = @_; diff --git a/tests/testutil.pm b/tests/testutil.pm index 46b645e78d94..f0a267949212 100644 --- a/tests/testutil.pm +++ b/tests/testutil.pm @@ -71,7 +71,7 @@ my @logmessages; # array holding logged messages # # logmsg must only be called by one of the runner_* entry points and functions # called by them, or else logs risk being lost, since those are the only -# functions that know about and will return buffered logs. +# functions that know about and return buffered logs. sub logmsg { if(!scalar(@_)) { return; diff --git a/tests/unit/unit1666.c b/tests/unit/unit1666.c index 360279501d7b..a86dc146a1e7 100644 --- a/tests/unit/unit1666.c +++ b/tests/unit/unit1666.c @@ -35,7 +35,7 @@ struct test_1666 { CURLcode result_exp; }; -/* the size of the object needs to deduct the null terminator */ +/* the size of the object needs to deduct the null-terminator */ #define OID(x) x, sizeof(x) - 1 static bool test1666(const struct test_1666 *spec, size_t i, diff --git a/tests/unit/unit1667.c b/tests/unit/unit1667.c index 660633252713..bcbf632a7eb2 100644 --- a/tests/unit/unit1667.c +++ b/tests/unit/unit1667.c @@ -66,7 +66,7 @@ static bool test1667(const struct test_1667 *spec, size_t i, } else if(!result) { /* use strlen on the pointer instead of curlx_dyn_len() because for some - of these type, the code explicitly adds a null terminator which is then + of these type, the code explicitly adds a null-terminator which is then counted as buffer size. */ size_t actual_len = strlen(curlx_dyn_ptr(dbuf)); if(strlen(spec->out) != actual_len) { From 51beed175dbfc37da3113f6acce60c630c070ce8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 16 May 2026 00:19:09 +0200 Subject: [PATCH 0127/1062] cookie: trim trailing dots when checking PSL Verified with test 1629 Closes #21636 --- lib/cookie.c | 13 +++++++++-- tests/data/Makefile.am | 2 +- tests/data/test1629 | 53 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1629 diff --git a/lib/cookie.c b/lib/cookie.c index 7ecef3a666bd..0b45798fca2a 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -779,12 +779,21 @@ static bool is_public_suffix(struct Curl_easy *data, char lcookie[256]; size_t dlen = strlen(domain); size_t clen = strlen(co->domain); + + /* trim trailing dots */ + if(dlen && (domain[dlen - 1] == '.')) + dlen--; + if(clen && (co->domain[clen - 1] == '.')) + clen--; + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { const psl_ctx_t *psl = Curl_psl_use(data); if(psl) { /* the PSL check requires lowercase domain name and pattern */ - Curl_strntolower(lcase, domain, dlen + 1); - Curl_strntolower(lcookie, co->domain, clen + 1); + Curl_strntolower(lcase, domain, dlen); + lcase[dlen] = 0; + Curl_strntolower(lcookie, co->domain, clen); + lcookie[clen] = 0; acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); Curl_psl_release(data); } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 166de82cf7cc..f9d0e769f675 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -214,7 +214,7 @@ test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ -test1628 \ +test1628 test1629 \ \ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 \ diff --git a/tests/data/test1629 b/tests/data/test1629 new file mode 100644 index 000000000000..6ee479ba3108 --- /dev/null +++ b/tests/data/test1629 @@ -0,0 +1,53 @@ + + + + +HTTP +HTTP GET + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 6 +Set-Cookie: something=1; Domain=co.uk.; Path=/ + +-foo- + + + +# Client-side + + +PSL +cookies + + +http + + +cookies with trailing dot after PSL domain + + +http://foo.co.uk.:%HTTPPORT/ http://bar.co.uk.:%HTTPPORT/ -b "" --resolve foo.co.uk.:%HTTPPORT:%HOSTIP --resolve bar.co.uk.:%HTTPPORT:%HOSTIP + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: foo.co.uk.:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET / HTTP/1.1 +Host: bar.co.uk.:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + From f9b9d3b1411f06010df6140b3f576256bf762c78 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 15 May 2026 14:37:58 +0200 Subject: [PATCH 0128/1062] urlapi: handle redirect without set scheme with default-scheme Verify in test 1921 Reported-by: mulan_dh on hackerone Closes #21632 --- lib/urlapi.c | 5 +++- tests/data/Makefile.am | 2 +- tests/data/test1921 | 30 ++++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib1921.c | 52 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1921 create mode 100644 tests/libtest/lib1921.c diff --git a/lib/urlapi.c b/lib/urlapi.c index 2e7aa7824a75..71f2756ca034 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1231,9 +1231,12 @@ static CURLUcode redirect_url(const char *base, const char *relurl, const char *cutoff = NULL; size_t prelen; CURLUcode uc; + /* this can get here with a NULL u->scheme only if asked to use the default + scheme, so allow fallback to that */ + const char *scheme = u->scheme ? u->scheme : DEFAULT_SCHEME; /* protsep points to the start of the hostname, after [scheme]:// */ - const char *protsep = base + strlen(u->scheme) + 3; + const char *protsep = base + strlen(scheme) + 3; DEBUGASSERT(base && relurl && u); /* all set here */ if(!base) return CURLUE_MALFORMED_INPUT; /* should never happen */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f9d0e769f675..9c63d7674e7b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -235,7 +235,7 @@ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \ test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \ -test1916 test1917 test1918 test1919 test1920 \ +test1916 test1917 test1918 test1919 test1920 test1921 \ \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ diff --git a/tests/data/test1921 b/tests/data/test1921 new file mode 100644 index 000000000000..15a3fe1ae7b9 --- /dev/null +++ b/tests/data/test1921 @@ -0,0 +1,30 @@ + + + + +urlapi + + + +# Client-side + + + +Set a URL without scheme, then redirect with default scheme + + +lib%TESTNUMBER + + + +- + + + + + +URL: https://example.com/newpath + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index b412cbc9b281..734e7f30e95d 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -102,7 +102,7 @@ TESTS_C = \ lib1662.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ - lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c \ + lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ lib1933.c lib1934.c lib1935.c lib1936.c lib1937.c lib1938.c lib1939.c \ lib1940.c lib1945.c \ lib1947.c lib1948.c \ diff --git a/tests/libtest/lib1921.c b/tests/libtest/lib1921.c new file mode 100644 index 000000000000..8799c2d96092 --- /dev/null +++ b/tests/libtest/lib1921.c @@ -0,0 +1,52 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static CURLcode test_lib1921(const char *URL) +{ + CURLU *u = curl_url(); + CURLUcode rc; + if(!u) + return CURLE_FAILED_INIT; + (void)URL; /* unused */ + /* u->scheme remains NULL */ + rc = curl_url_set(u, CURLUPART_HOST, "example.com", 0); + if(!rc) + rc = curl_url_set(u, CURLUPART_PATH, "/original", 0); + + if(!rc) + /* Relative URL + CURLU_DEFAULT_SCHEME reaches redirect_url() */ + rc = curl_url_set(u, CURLUPART_URL, "/newpath", CURLU_DEFAULT_SCHEME); + + if(!rc) { + char *url; + rc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(!rc) { + curl_mprintf("URL: %s\n", url); + curl_free(url); + } + } + curl_url_cleanup(u); + return rc ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; +} From d9514e3b9237d8525c5580d9a146753116de5b41 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sat, 16 May 2026 04:12:06 -0500 Subject: [PATCH 0129/1062] docs: end "...can be used several times..." sentences with period Closes #21644 --- scripts/managen | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/managen b/scripts/managen index d70df2fbfd0c..e117e335ce81 100755 --- a/scripts/managen +++ b/scripts/managen @@ -787,7 +787,7 @@ sub single { } elsif($multi eq "append") { push @extra, - sprintf("${pre}%s can be used several times in a command line\n", + sprintf("${pre}%s can be used several times in a command line.\n", manpageify($long, $manpage)); } elsif($multi eq "boolean") { From 3f8f725d970452356256b0f9c6520ee666553fb8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 13:26:05 +0200 Subject: [PATCH 0130/1062] schannel: enforce Extended Key Usage for custom CA roots Reported-by: Joshua Rogers (Aisle Research) Closes #21629 --- lib/vtls/schannel_verify.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index d61318625318..486fd6e00581 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -776,9 +776,13 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(result == CURLE_OK) { CERT_CHAIN_PARA ChainPara; + LPSTR serverAuthOID = CURL_UNCONST(szOID_PKIX_KP_SERVER_AUTH); memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &serverAuthOID; if(!CertGetCertificateChain(cert_chain_engine, pCertContextServer, From c46a7913e537381313a9a31e609530280379cb0b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 15 May 2026 14:33:07 +0200 Subject: [PATCH 0131/1062] setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA In AppleSecTrust or NativeCA-enabled builds, make sure override it when setting a custom `CURLOPT_PROXY_CAINFO_BLOB`. Reported-by: Joshua Rogers (Aisle Research) Follow-up to 1730407b74f41cfd33f189348be54d0504b7c291 #18279 Follow-up to eefd03c572996e5de4dec4fe295ad6f103e0eefc #18703 Closes #21631 --- lib/setopt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/setopt.c b/lib/setopt.c index 2bc49868b81b..5a3e02c76fa2 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2842,8 +2842,10 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + s->proxy_ssl.custom_cablob = TRUE; return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + } #endif return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT_BLOB: From 5688c2a8eec956ae906f627a0c50fef35821d1fe Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 01:51:17 +0200 Subject: [PATCH 0132/1062] SSLCERTS: document 8.19.0 default Native CA builds (Windows) Ref: https://curl.se/docs/sslcerts.html Follow-up to 1730407b74f41cfd33f189348be54d0504b7c291 #18279 Reported-by: chrizilla on github Fixes #21634 Closes #21639 --- docs/SSLCERTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/SSLCERTS.md b/docs/SSLCERTS.md index 3506fbd787d1..d5110e18f0f4 100644 --- a/docs/SSLCERTS.md +++ b/docs/SSLCERTS.md @@ -71,6 +71,10 @@ cert file named `curl-ca-bundle.crt` in these directories and in this order: curl 8.11.0 added a build-time option to disable this search behavior, and another option to restrict search to the application's directory. +curl 8.19.0 added a build-time option to enable Native CA by default on +Windows. This build-time option by default also disables searching for +a `curl-ca-bundle.crt` on disk. + ### Use the native store In several environments, in particular on Microsoft and Apple operating From 40c516f941ed87116f5023885da0e4d070580907 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 03:38:19 +0200 Subject: [PATCH 0133/1062] runner.pm: set `CURL_TESTNUM` for `precheck` commands Closes #21640 --- tests/runner.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/runner.pm b/tests/runner.pm index 8ebb855ca635..115d078a1ff3 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -709,6 +709,9 @@ sub singletest_precheck { $cmd = join(" ", @p); } + # provide an environment variable + $ENV{'CURL_TESTNUM'} = $testnum; + my @o = `$cmd 2> $LOGDIR/precheck-$testnum`; if($o[0]) { $why = $o[0]; From 535c575e31eadf1710fa44be94094db9f9d34655 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 03:56:33 +0200 Subject: [PATCH 0134/1062] lib678: fix to not be perma-skipped Prior to this patch the test was always skipped due to failing precheck with `CURLE_BAD_FUNCTION_ARGUMENT`, because of the zero-length blob passed to setopt. Fix by passing a non-zero long dummy blob as done in `mk-lib1521.pl`. Fixing: ``` test 0678 SKIPPED: CURLOPT_CAINFO_BLOB is not supported ``` Follow-up to 956e1ae84f2fec9f027b4ce80999744326b30992 #20705 Closes #21641 --- tests/libtest/lib678.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libtest/lib678.c b/tests/libtest/lib678.c index 73aa4ee7e972..7486505f7684 100644 --- a/tests/libtest/lib678.c +++ b/tests/libtest/lib678.c @@ -97,7 +97,7 @@ static CURLcode test_lib678(const char *URL) curl_global_init(CURL_GLOBAL_DEFAULT); if(!strcmp("check", URL)) { CURLcode w = CURLE_OK; - struct curl_blob blob = { 0 }; + struct curl_blob blob = { CURL_UNCONST("silly"), 5, 0 }; CURL *curl = curl_easy_init(); if(curl) { w = curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); From ad549c4641a493ffcf84ba00af8e1aedbfd8735f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 15 May 2026 14:11:13 +0200 Subject: [PATCH 0135/1062] unix-sockets: ignore proxy settings Fix a recent regression: when a unix-socket is configured, all proxy settings must be ignored. The `via_peer` had been checked correctly, but the connections proxy bits were not cleared. Add test_11_04 to verify. Reported-by: Fabian Keil (libcurl mailing list) Closes #21630 --- lib/url.c | 11 +++++++---- lib/urldata.h | 4 ++-- tests/http/test_11_unix.py | 13 +++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/url.c b/lib/url.c index 471399123a97..c63cf072e662 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2627,10 +2627,13 @@ static CURLcode url_create_needle(struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY - /* After the Unix socket init but before the proxy vars are used, parse and - * initialize the proxy settings. - * Any UDS `via_peer` disables proxies. */ - if(network_scheme && !(needle->via_peer && needle->via_peer->unix_socket)) { + /* Going via a unix socket ignores any proxy settings */ + if(needle->via_peer && needle->via_peer->unix_socket) { + needle->bits.socksproxy = FALSE; + needle->bits.httpproxy = FALSE; + needle->bits.proxy = FALSE; + } + else if(network_scheme) { result = url_set_conn_proxies(data, needle); if(result) goto out; diff --git a/lib/urldata.h b/lib/urldata.h index 0cbe177d4af9..8b85c674a9a0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -452,8 +452,8 @@ struct connectdata { #ifndef CURL_DISABLE_PROXY #define CURL_CONN_HOST_DISPNAME(c) \ - ((c)->bits.socksproxy ? (c)->socks_proxy.peer->user_hostname : \ - (c)->bits.httpproxy ? (c)->http_proxy.peer->user_hostname : \ + ((c)->socks_proxy.peer ? (c)->socks_proxy.peer->user_hostname : \ + (c)->http_proxy.peer ? (c)->http_proxy.peer->user_hostname : \ (c)->via_peer ? (c)->via_peer->user_hostname : \ (c)->origin->user_hostname) #else diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index fe99512a0473..774a7737c91b 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -136,3 +136,16 @@ def test_11_03_unix_connect_quic(self, env: Env, httpd, uds_faker): r.check_response(exitcode=96, http_status=None) assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}' assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}' + + # download http: via Unix socket, ignore proxy args + def test_11_04_unix_connect_http(self, env: Env, httpd, uds_faker): + curl = CurlClient(env=env) + url = f'http://{env.domain1}:{env.http_port}/data.json' + xargs = curl.get_proxy_args(proto='http/1.1', use_ip=True, proxys=False) + xargs.extend([ + '--unix-socket', uds_faker.path, + ]) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) + r.check_response(count=1, http_status=200) + assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}' + assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}' From 061136f24b5271a8360b9c128d5a2329a253ebde Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 16 May 2026 23:09:52 +0200 Subject: [PATCH 0136/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 117 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 67cb27d1aec5..e10cc1a0c84d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,43 +4,94 @@ curl and libcurl 8.21.0 Command line options: 273 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1465 - Contributors: 3668 + Authors: 1473 + Contributors: 3680 This release includes the following changes: + o curl: named globs in output file name for upload glob references [77] o lib: drop support for CURLAUTH_DIGEST_IE [4] + o libssh: add support for SHA256 host public keys [57] + o tool_urlglob: add named globs [92] This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] + o cf-h2-proxy: drop interim responses [47] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] + o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] o cmake: fix zstd CMake config name [5] + o cookie: compare path case sensitively [52] o cookie: simplify strstore(), remove outdated comment [12] + o cookie: trim trailing dots when checking PSL [39] + o creds: add sasl service name [75] + o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] + o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] o CURLOPT_ECH.md: simplify the description language [18] o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] + o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] + o CURLOPT_SHARE: warn about early remove [51] + o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] + o delta: harden external command invocations [98] + o docs: end "...can be used several times..." sentences with period [34] + o docs: fix --follow doc typo [97] + o docs: fix a couple of typos [62] + o docs: fix grammar and wording in FAQ [66] o ECH: cleanups [20] + o event: fix wakeup consumption [93] o ftp: avoid accessing EPSV response one byte past the NULL [9] o ftp: remove 2 Curl_resolv_blocking() calls [30] o ftp: remove bits.ftp_use_control_ssl [28] + o gnutls: allow building with nettle 4.0 [96] + o gnutls: fix more nettle 4+ compatibility issues [94] + o gsasl: fix potential double free [56] o gtls: fix some typos [15] + o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] + o idn: replace header guards with forward declaration [100] o ldap: fix minor leak on write callback error [24] + o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] + o lib678: fix to not be perma-skipped [10] + o lib: make `__STDC_VERSION__` literals `L` (where missing) o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] o mbedtls: null-terminate the private key blob [36] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] + o rtsp: bump buf after rtsp_filter_rtp() [88] + o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] + o rustls: error on CURLOPT_CRLFILE with native CA store [59] + o schannel: enforce Extended Key Usage for custom CA roots [29] o schannel_verify: avoid out of blob access [11] o setopt: changing the proxy port is also a proxy change [23] + o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] o setopt: gate a few proxy TLS options by checking backend support [35] + o setopt: more careful cleanup of the HSTS cache [45] o show-headers.md: mention bold headers and --no-styled-output [17] + o snpego_sspi: preserve distinction btw policy-only and uncond delegation [74] + o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] + o src: fix comment typos [83] + o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o tests: fix unit1636 with --disable-progress-meter [37] + o tftp: stricter option name checks [90] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] o tool_formparse: tool2curlparts is no longer recursive [33] o tool_urlglob: avoid overflow at end of range [22] + o tool_urlglob: better 'Duplicate glob name' position [82] + o tool_urlglob: make globbing error reported for correct position [91] + o unix-sockets: ignore proxy settings [6] + o url: compare full origin when setting credentials [42] o url: fix connection reuse for starttls protocols [27] + o url: keep the question mark for empty queries [73] o url: remove ssh_config_matches [31] + o url: url_match_destination fix [43] + o urlapi: change more lowercase percent-encoded to uppercase [71] + o urlapi: consume trailing dots after IPv4 numerical addresses [50] + o urlapi: deny hostnames with more than one trailing dot [58] + o urlapi: handle redirect without set scheme with default-scheme [38] o user-agent.md: mention double quotes too [3] + o windows: update MS SDK versions in comments [60] + o x509asn1: fix DH public key parameter extraction [44] o x509asn1: fix operator order in do_pubkey [21] This release includes the following known bugs: @@ -63,10 +114,14 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Andrew Nesbitt, Dan Fandrich, Daniel Stenberg, dependabot[bot], Elise Vance, - Jeremy Nicoll, Kai Pastor, parasol-aser, Raymond Steen, renovate[bot], - Sollace on github, Stefan Eissing, Viktor Szakats - (13 contributors) + 0xN3R3K3, Alan De Smet, amitbidlan, Andrei Rybak, Andrew Nesbitt, + Bastian Jesuiter, Bill Mill, chrizilla on github, Dan Fandrich, + Daniel Stenberg, dependabot[bot], Earnestly on github, Elise Vance, + Emanuel Krollmann, Fabian Keil, jeffhuang, Jeremy Nicoll, Joshua Rogers, + Kai Pastor, mulan_dh on hackerone, parasol-aser, Raymond Steen, + renovate[bot], Sergio Correia, Sollace on github, Song X. Gao, + Stefan Eissing, Tim Martin, Viktor Szakats, Xi Ruoyao, x-xiang on github + (31 contributors) References to bug reports and discussions on issues: @@ -75,11 +130,15 @@ References to bug reports and discussions on issues: [3] = https://curl.se/mail/archive-2026-04/0029.html [4] = https://curl.se/bug/?i=21486 [5] = https://curl.se/bug/?i=21538 + [6] = https://curl.se/bug/?i=21630 [7] = https://hackerone.com/reports/3702718 [8] = https://curl.se/bug/?i=21470 [9] = https://curl.se/bug/?i=21545 + [10] = https://curl.se/bug/?i=21641 [11] = https://curl.se/bug/?i=21543 [12] = https://curl.se/bug/?i=21541 + [13] = https://curl.se/bug/?i=21640 + [14] = https://curl.se/bug/?i=21634 [15] = https://curl.se/bug/?i=21498 [16] = https://curl.se/bug/?i=21496 [17] = https://curl.se/bug/?i=21495 @@ -91,12 +150,58 @@ References to bug reports and discussions on issues: [23] = https://curl.se/bug/?i=21485 [24] = https://curl.se/bug/?i=21530 [25] = https://curl.se/bug/?i=21480 + [26] = https://curl.se/bug/?i=21631 [27] = https://curl.se/bug/?i=21522 [28] = https://curl.se/bug/?i=21521 + [29] = https://curl.se/bug/?i=21629 [30] = https://curl.se/bug/?i=21512 [31] = https://curl.se/bug/?i=21519 [32] = https://curl.se/bug/?i=21517 [33] = https://curl.se/bug/?i=21518 + [34] = https://curl.se/bug/?i=21644 [35] = https://curl.se/bug/?i=21514 [36] = https://curl.se/bug/?i=21515 [37] = https://curl.se/bug/?i=21500 + [38] = https://curl.se/bug/?i=21632 + [39] = https://curl.se/bug/?i=21636 + [42] = https://curl.se/bug/?i=21575 + [43] = https://curl.se/bug/?i=21573 + [44] = https://curl.se/bug/?i=21595 + [45] = https://curl.se/bug/?i=21615 + [47] = https://curl.se/bug/?i=21626 + [48] = https://curl.se/bug/?i=21606 + [50] = https://curl.se/bug/?i=21635 + [51] = https://curl.se/bug/?i=21633 + [52] = https://curl.se/bug/?i=21616 + [56] = https://curl.se/bug/?i=21609 + [57] = https://curl.se/bug/?i=21605 + [58] = https://curl.se/bug/?i=21622 + [59] = https://curl.se/bug/?i=21614 + [60] = https://curl.se/bug/?i=21621 + [62] = https://curl.se/bug/?i=21617 + [66] = https://curl.se/bug/?i=21593 + [67] = https://curl.se/bug/?i=21597 + [71] = https://curl.se/bug/?i=21592 + [73] = https://curl.se/bug/?i=21544 + [74] = https://curl.se/bug/?i=21583 + [75] = https://curl.se/bug/?i=21585 + [77] = https://curl.se/bug/?i=21407 + [78] = https://curl.se/bug/?i=21582 + [79] = https://curl.se/bug/?i=21576 + [82] = https://curl.se/bug/?i=21567 + [83] = https://curl.se/bug/?i=21570 + [84] = https://curl.se/bug/?i=21569 + [87] = https://curl.se/bug/?i=21562 + [88] = https://curl.se/bug/?i=21563 + [89] = https://curl.se/bug/?i=21528 + [90] = https://curl.se/bug/?i=21560 + [91] = https://curl.se/bug/?i=21561 + [92] = https://curl.se/bug/?i=21409 + [93] = https://curl.se/bug/?i=21547 + [94] = https://curl.se/bug/?i=21557 + [96] = https://curl.se/bug/?i=21169 + [97] = https://curl.se/bug/?i=21553 + [98] = https://curl.se/bug/?i=21104 + [99] = https://curl.se/bug/?i=21523 + [100] = https://curl.se/bug/?i=21551 + [101] = https://curl.se/bug/?i=21550 From a0f08d6975d00c3ea04e1bd2fcf60dad446266ec Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 15 May 2026 13:37:36 +0200 Subject: [PATCH 0137/1062] cf-h2-prox: fix peer leak The unlinking of the new Curl_peer was happening too later after the struct had been set to zero. Move the unlink to happen before that. Fixes #21602 Reported-by: Joshua Rogers Closes #21627 --- lib/cf-h2-proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 1dfd0a0a4603..297afb3c8cb1 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -190,6 +190,7 @@ static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) } Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); + Curl_peer_unlink(&ctx->dest); tunnel_stream_clear(&ctx->tunnel); memset(ctx, 0, sizeof(*ctx)); ctx->call_data = save; @@ -199,7 +200,6 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) { if(ctx) { cf_h2_proxy_ctx_clear(ctx); - Curl_peer_unlink(&ctx->dest); curlx_free(ctx); } } From 44ede0cc5a7b2165ac3c4704bd5763c9e242dff3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 17 May 2026 00:02:08 +0200 Subject: [PATCH 0138/1062] url: remove superfluous check This pointer is already verified to be non-NULL some 15 lines above. Pointed out by CodeSonar Closes #21650 --- lib/url.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/url.c b/lib/url.c index c63cf072e662..ec4fb8b1b0f7 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2200,10 +2200,9 @@ static CURLcode override_login(struct Curl_easy *data, if(result) goto out; } - else if(data->state.creds) { + else /* only search when something is still missing */ Curl_creds_link(&ncreds_in, data->state.creds); - } break; default: /* ignore credentials from other sources */ From 7bde6cb9fcdfbb58531d5ac39e7b236ce15c52bf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 23:38:11 +0200 Subject: [PATCH 0139/1062] build: omit zlib pkg-config reference for Android In both autotools and cmake builds, because Android does not offer a `zlib.pc`. Also: - GHA/non-native: dump config files, to verify. Reported-by: sfan5 on github Fixes #21647 Closes #21648 --- .github/workflows/non-native.yml | 6 ++++++ CMakeLists.txt | 2 +- acinclude.m4 | 16 +++++++--------- configure.ac | 7 ++++++- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 1f2753fe8f1d..14dbf0137430 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -297,6 +297,12 @@ jobs: if: ${{ !cancelled() }} run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true + - name: 'dump config files' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'curl_config.h' run: | echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' diff --git a/CMakeLists.txt b/CMakeLists.txt index c49e128b6273..1c4c137addcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2177,7 +2177,7 @@ if(NOT CURL_DISABLE_INSTALL) endif() if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc set(_modules "openssl") - elseif(_lib STREQUAL ZLIB::ZLIB) + elseif(_lib STREQUAL ZLIB::ZLIB AND NOT ANDROID) # Android does not provide zlib.pc set(_modules "zlib") else() get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES) diff --git a/acinclude.m4 b/acinclude.m4 index 96d55ba384cd..73afac6e0836 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1441,15 +1441,13 @@ AC_DEFUN([CURL_PREPARE_BUILDINFO], [ *-*-*bsd*) curl_pflags="${curl_pflags} BSD";; esac - case $host in - *-*-android*) - curl_pflags="${curl_pflags} ANDROID" - ANDROID_PLATFORM_LEVEL=`echo "$host_os" | $SED -ne 's/.*android\(@<:@0-9@:>@*\).*/\1/p'` - if test -n "${ANDROID_PLATFORM_LEVEL}"; then - curl_pflags="${curl_pflags}-${ANDROID_PLATFORM_LEVEL}" - fi - ;; - esac + if test "$curl_cv_android" = "yes"; then + curl_pflags="${curl_pflags} ANDROID" + ANDROID_PLATFORM_LEVEL=`echo "$host_os" | $SED -ne 's/.*android\(@<:@0-9@:>@*\).*/\1/p'` + if test -n "${ANDROID_PLATFORM_LEVEL}"; then + curl_pflags="${curl_pflags}-${ANDROID_PLATFORM_LEVEL}" + fi + fi if test "$curl_cv_native_windows" = "yes"; then curl_pflags="${curl_pflags} WIN32" fi diff --git a/configure.ac b/configure.ac index 6a9071d37c52..82211da018dd 100644 --- a/configure.ac +++ b/configure.ac @@ -694,8 +694,10 @@ dnl ********************************************************************** CURL_CHECK_WIN32_CRYPTO +curl_cv_android='no' curl_cv_apple='no' case $host in + *-*-android*) curl_cv_android='yes';; *-apple-*) curl_cv_apple='yes';; esac @@ -1512,7 +1514,10 @@ else dnl replace 'HAVE_LIBZ' in the automake makefile.ams AMFIXLIB="1" AC_MSG_NOTICE([found both libz and libz.h header]) - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE zlib" + dnl Android does not provide zlib.pc + if test "$curl_cv_android" = "no"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE zlib" + fi curl_zlib_msg="enabled" fi fi From d74c0ada4e317566f19b4fac44d91a9eaa93d2bf Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 17 May 2026 00:27:30 +0200 Subject: [PATCH 0140/1062] urlapi: prevent a terminal `.0x` component to normalize IPv4 Extend test 1560 to verify Follow-up to 831a1514843bfa4d4d006627 Spotted by Codex Security Closes #21652 --- lib/urlapi.c | 2 ++ tests/libtest/lib1560.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/urlapi.c b/lib/urlapi.c index 71f2756ca034..dfb106dd2f0a 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -523,6 +523,8 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) if(c[1] == 'x') { c += 2; /* skip the prefix */ rc = curlx_str_hex(&c, &l, UINT_MAX); + if(rc) + return HOST_NAME; } else rc = curlx_str_octal(&c, &l, UINT_MAX); diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 533a44e98376..3eeed6b6943d 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -625,6 +625,10 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + {"https://127.1.0x", "https://127.1.0x/", 0, 0, CURLUE_OK}, + {"https://127.0x", "https://127.0x/", 0, 0, CURLUE_OK}, + {"https://127.0x.1", "https://127.0x.1/", 0, 0, CURLUE_OK}, + {"https://127.1.1.0x", "https://127.1.1.0x/", 0, 0, CURLUE_OK}, {"https://127.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://127.1.:443", "https://127.0.0.1:443/", 0, 0, CURLUE_OK}, {"https://127.1.?moo", "https://127.0.0.1/?moo", 0, 0, CURLUE_OK}, From e8c1023b00b4b1491d0436f8bc95fe589ad659ed Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 16 May 2026 23:59:05 +0200 Subject: [PATCH 0141/1062] connect: remove deref of freed pointer in trace call Spotted by CodeSonar Closes #21649 --- lib/connect.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index e0f93d3c46e0..e74bda5dfb8f 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -389,10 +389,13 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, cf->conn->socks_proxy.proxytype, cf->conn->socks_proxy.creds); + if(result) { + /* 'dest' might be freed now so it can't be dereferenced */ + CURL_TRC_CF(data, cf, "added SOCKS filter failed -> %d", result); + return result; + } CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", dest->hostname, dest->port, result); - if(result) - return result; ctx->state = CF_SETUP_CNNCT_SOCKS; if(!cf->next || !cf->next->connected) goto connect_sub_chain; From 64adc43a6ea07e4d807bbf9b5043fce56ccbccbb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 17 May 2026 13:33:07 +0200 Subject: [PATCH 0142/1062] scripts: catch Credits-to contributors Also: - THANKS: add Credits-to attribution missed earlier. Closes #21653 --- docs/THANKS | 1 + scripts/contributors.sh | 2 +- scripts/contrithanks.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/THANKS b/docs/THANKS index e02c36170096..16a3b907b25c 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -602,6 +602,7 @@ Christopher Reid Christopher R. Palmer Christopher Sauer Christopher Stone +Christopher Wellons Christoph Jabs Christoph Krey Christoph M. Becker diff --git a/scripts/contributors.sh b/scripts/contributors.sh index 37f1e5835bb8..d0ebd60f4681 100755 --- a/scripts/contributors.sh +++ b/scripts/contributors.sh @@ -62,7 +62,7 @@ CURLWWW="${CURLWWW:-../curl-www}" git -C "$CURLWWW" log --pretty=full --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ diff --git a/scripts/contrithanks.sh b/scripts/contrithanks.sh index 47438701cdd4..b2e89f158862 100755 --- a/scripts/contrithanks.sh +++ b/scripts/contrithanks.sh @@ -62,7 +62,7 @@ tail -n +7 ./docs/THANKS | sed 's/ github/ github/i' > $rand git -C "$CURLWWW" log --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ From 3c597ced16e1f3aa7bfe08609add0feaf5c8d90d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 17 May 2026 14:04:49 +0200 Subject: [PATCH 0143/1062] cmake: fix three issues generating lib options in config files - drop duplicate libs lists next to each other in `libcurl.pc`. Logic copied from libssh2. Fixing (seen in a local build): ```diff -Libs.private: -lssh2 -lz -lz -lldap -llber -lssl -lcrypto -lcrypto -lz -lbrotlidec -lbrotlicommon -lzstd -lnghttp2 -licucore -liconv -lpsl -lbacktrace +Libs.private: -lssh2 -lz -lldap -llber -lssl -lcrypto -lz -lbrotlidec -lbrotlicommon -lzstd -lnghttp2 -licucore -liconv -lpsl -lbacktrace ``` Refs: https://github.com/libssh2/libssh2/commit/e1da7b2cb89063fc253bf94570c1ccfb3f1c2e81 https://github.com/libssh2/libssh2/pull/1621 https://github.com/libssh2/libssh2/commit/6464301820a9ca4a56c5f02717430bbd4150c7b2 https://github.com/libssh2/libssh2/pull/1131 - handle `$` references. Fixing (seen in a local build using libssh2 v1.11.2-DEV): ```diff -Libs.private: -lssh2 -l$ -lz -lldap -llber [...] +Libs.private: -lssh2 -lcrypto -lz -lldap -llber [...] ``` - fix `-l-pthread` sneaking into `libcurl.pc`. Fixing (seen with Android): ```diff -Libs.private: -lz -l-pthread +Libs.private: -pthread -lz ``` Refs: https://github.com/microsoft/vcpkg/blob/2b65c20fc66eda893aa15a15a453c3cf09500b19/ports/curl/dependencies.patch#L631-L634 https://github.com/microsoft/vcpkg/commit/70b941a5d2443e79eeab62507acb41bd22201277#diff-7f2c3b2f93cd3478671a603cbd5ef818c7c403a11dc25e1d3539e9b03495a5d3 Upstream-patch-by: Kai Pastor Closes #21654 --- CMake/Macros.cmake | 4 ++++ CMakeLists.txt | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 5e26c384696f..f0968736b067 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -267,6 +267,10 @@ macro(curl_collect_target_link_options _target) get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) if(_val) foreach(_lib IN LISTS _val) + # E.g. via libssh2: "$" + if(_lib MATCHES "LINK_ONLY:") + string(REGEX MATCH "([A-Za-z0-9_-]+::[A-Za-z0-9_-]+)" _lib "${_lib}") # Extract imported target name + endif() if(TARGET "${_lib}") curl_collect_target_link_options(${_lib}) else() diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c4c137addcd..331c22dc4f61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2211,6 +2211,9 @@ if(NOT CURL_DISABLE_INSTALL) list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") endif() + elseif(_lib MATCHES "^-") # '-option' + list(APPEND _ldflags "${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") else() list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}") list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") @@ -2241,6 +2244,17 @@ if(NOT CURL_DISABLE_INSTALL) string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}") endif() if(LIBCURL_PC_LIBS_PRIVATE) + # Remove duplicates listed next to each other + set(_libs "") + set(_prev "") + foreach(_lib IN LISTS LIBCURL_PC_LIBS_PRIVATE) + if(NOT _prev STREQUAL _lib) + list(APPEND _libs "${_lib}") + set(_prev "${_lib}") + endif() + endforeach() + set(LIBCURL_PC_LIBS_PRIVATE "${_libs}") + string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}") endif() if(_ldflags) From 240408a725429b021d0e7785d12f06a20fc019e8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 17 May 2026 23:55:48 +0200 Subject: [PATCH 0144/1062] scripts/contri*: fix the Credits-to regex On my suggestion, the regex turned up wrong when looking for Credits-to in git logs. This adjustment allows the leading spaces. Follow-up to 64adc43a6ea07e4d807bbf9b5 Closes #21655 --- scripts/contributors.sh | 2 +- scripts/contrithanks.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/contributors.sh b/scripts/contributors.sh index d0ebd60f4681..44dce7e2ab1f 100755 --- a/scripts/contributors.sh +++ b/scripts/contributors.sh @@ -62,7 +62,7 @@ CURLWWW="${CURLWWW:-../curl-www}" git -C "$CURLWWW" log --pretty=full --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^Credits-to):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ diff --git a/scripts/contrithanks.sh b/scripts/contrithanks.sh index b2e89f158862..2b85d14d083f 100755 --- a/scripts/contrithanks.sh +++ b/scripts/contrithanks.sh @@ -62,7 +62,7 @@ tail -n +7 ./docs/THANKS | sed 's/ github/ github/i' > $rand git -C "$CURLWWW" log --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^Credits-to):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ From a55750af0bbd7b9a2bd88f3792be5dbfc96b4e33 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 18 May 2026 11:50:37 +0200 Subject: [PATCH 0145/1062] mk-unity.pl: `#include`, and not concatenate input headers When using `-D_CURL_TESTS_CONCAT=ON` with CMake, do not concatenate `first.h` (or any future header) into the output C file, but `#include` it instead. This is to play nice with compilers and analyzers which may apply different checker rules on logic found in headers, vs. the input source file. As seen for example with `-Wunused-macro` enabled in CI. After this patch concatenated sources behave closer to regular C sources. Also: - first.h: drop some `-Wunused-macro` silencers that became redundant with this patch. Follow-up to 47f411c6d840dcee63a2ac9cbc0bfbea522ac5cd #21554 Follow-up to 39542f09935aba0b7130c20b6aae0be5cd6ff709 #20667 Closes #21656 --- scripts/mk-unity.pl | 2 +- tests/libtest/first.h | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/mk-unity.pl b/scripts/mk-unity.pl index 014cfe8eceb9..41354c15d120 100755 --- a/scripts/mk-unity.pl +++ b/scripts/mk-unity.pl @@ -65,7 +65,7 @@ sub include($@) { my $filename = shift; - if($concat) { + if($concat && $filename =~ /([a-z0-9_]+)\.c$/) { if(! -f $filename) { foreach my $path (@incpath) { my $fullfn = $path . "/" . $filename; diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 21da11394c37..d0b22df79b61 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -108,10 +108,7 @@ void ws_close(CURL *curl); /* just close the connection */ * * For portability reasons TEST_ERR_* values should be less than 127. */ -#if !defined(UNITTESTS) || defined(BUILDING_LIBCURL) #define TEST_ERR_MAJOR_BAD CURLE_OBSOLETE20 -#endif -#ifndef UNITTESTS #define TEST_ERR_RUNS_FOREVER CURLE_OBSOLETE24 #define TEST_ERR_EASY_INIT CURLE_OBSOLETE29 #define TEST_ERR_MULTI CURLE_OBSOLETE32 @@ -152,6 +149,7 @@ void ws_close(CURL *curl); /* just close the connection */ * TEST_ERR_* values defined above. It is advisable to return this value * as test result. */ +#ifndef UNITTESTS /* ---------------------------------------------------------------- */ From 8a86fa13f39142407d84531559dcd2230d08c034 Mon Sep 17 00:00:00 2001 From: Mark Esler Date: Sat, 16 May 2026 15:07:15 -0700 Subject: [PATCH 0146/1062] vtls_scache: include signature_algorithms in the SSL peer cache key Curl_ssl_peer_key_make() omitted ssl->signature_algorithms, although match_ssl_primary_config() compares the field. Two handles differing only in CURLOPT_SSL_SIGNATURE_ALGORITHMS therefore shared a peer key and could resume each other's sessions across a shared CURLSH SSL session cache. Add :SIGALGS-%s next to the other ssl_primary_config fields. Closes #21651 --- lib/vtls/vtls_scache.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 9efb8208ea4d..900a2b90a037 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -223,6 +223,12 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, if(r) goto out; } + if(ssl->signature_algorithms) { + r = curlx_dyn_addf(&buf, ":SIGALGS-%s", + ssl->signature_algorithms); + if(r) + goto out; + } if(ssl->verifypeer) { r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); if(r) From 5c1e0179875427b68dad3827b70675c7fe82380f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 18 May 2026 14:15:28 +0200 Subject: [PATCH 0147/1062] curl_easy_setopt.md: change options when no transfer runs Underscore this. Changing them mid-transfer may cause problems. Fixes #21604 Reported-by: Joshua Rogers Closes #21657 --- docs/libcurl/curl_easy_setopt.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 13d966f3c319..aafa00f06482 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -50,6 +50,9 @@ any way reset between transfers, so if you want subsequent transfers with different options, you must change them between the transfers. You can optionally reset all options back to internal default with curl_easy_reset(3). +Changing options with curl_easy_setopt(3) while a transfer is still in +progress may cause undefined and undesired behavior. + The order in which the options are set does not matter. # STRINGS From 4ae1d7cc2643e4773a136395f12bc02fc6867854 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 15 May 2026 11:45:49 +0200 Subject: [PATCH 0148/1062] netrc: scanner refactor Refactor the netrc scanner. Add test case for checking that the last matched machine with unmatched login does not return the password as success (unit1304). Closes #21624 --- lib/netrc.c | 823 +++++++++++++++++++++++------------------- lib/netrc.h | 14 +- lib/url.c | 155 ++++---- tests/unit/unit1304.c | 117 +++--- 4 files changed, 581 insertions(+), 528 deletions(-) diff --git a/lib/netrc.c b/lib/netrc.c index eb67f2505ec9..48aaa7681617 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -36,89 +36,156 @@ #endif #include "netrc.h" +#include "urldata.h" #include "creds.h" +#include "curl_trc.h" #include "strcase.h" #include "curl_get_line.h" #include "curlx/fopen.h" #include "curlx/strparse.h" -/* Get user and password from .netrc when given a machine name */ -enum host_lookup_state { - NOTHING, - HOSTFOUND, /* the 'machine' keyword was found */ - HOSTVALID, /* this is "our" machine! */ - MACDEF -}; - -enum found_state { - NONE, - LOGIN, - PASSWORD -}; - -#define FOUND_LOGIN 1 -#define FOUND_PASSWORD 2 +/* .netrc is not really a standard. The GNU definition can be found here: + * https://www.gnu.org/software/inetutils/manual/\ + * html_node/The-_002enetrc-file.html + * This gives grammar like: + * + * LITERAL := \S+ | QUOTED + * QUOTED := "(\\[rnt\]|[^"])*" + * ANYTHING := . + * EMPTY_LINE := \r*\n\r*\n + * MACHINE := machine # case-insensitive + * LOGIN := login # case-insensitive + * PASSWD := password # case-insensitive + * ACCOUNT := account # case-insensitive + * MACDEF := macdef # case-insensitive + * DEFAULT := default # case-insensitive + * + * MACRO := MACDEF ANYTHING* EMPTY_LINE + * JUNK := LITERAL + * LKEY := ( LOGIN | PASSWD | ACCOUNT ) LITERAL + * MENTRY := MACHINE LITERAL LKEY* + * DENTRY := DEFAULT LKEY* + * NETRC := (MENTRY | DENTRY | MACRO | JUNK )* EOF + * + * Tokens are separated by whitespace or newlines. which have otherwise + * no special meaning, apart from the empty line ending a MACRO. + * + * Parsing is not strict, unmatched LITERALs are ignored + */ #define MAX_NETRC_LINE 16384 #define MAX_NETRC_FILE (128 * 1024) #define MAX_NETRC_TOKEN 4096 +#define NETRC_DEBUG 0 + /* convert a dynbuf call CURLcode error to a NETRCcode error */ -#define curl2netrc(result) \ - (((result) == CURLE_OUT_OF_MEMORY) ? \ - NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR) +#define curl2netrc(r) \ + ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \ + NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)) + +typedef enum { + NETRC_TOK_EOF, + NETRC_TOK_LITERAL, + NETRC_TOK_MACHINE, + NETRC_TOK_DEFAULT, + NETRC_TOK_ACCOUNT, + NETRC_TOK_LOGIN, + NETRC_TOK_PASSWD, + NETRC_TOK_MACDEF, + NETRC_TOK_JUNK +} curl_netrc_token; + +struct netrc_lexer { + struct Curl_easy *data; + const char *content; + const char *pos; + struct dynbuf literal; + curl_netrc_token token; + bool pushed; +}; -static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) +#if NETRC_DEBUG +static const char *netrc_tokenstr(curl_netrc_token token) { - NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ - FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + switch(token) { + case NETRC_TOK_EOF: + return "[EOF]"; + case NETRC_TOK_LITERAL: + return "[LITERAL]"; + case NETRC_TOK_MACHINE: + return "[MACHINE]"; + case NETRC_TOK_DEFAULT: + return "[DEFAULT]"; + case NETRC_TOK_ACCOUNT: + return "[ACCOUNT]"; + case NETRC_TOK_LOGIN: + return "[LOGIN]"; + case NETRC_TOK_PASSWD: + return "[PASSWORD]"; + case NETRC_TOK_MACDEF: + return "[MACDEF]"; + case NETRC_TOK_JUNK: + return "[JUNK]"; + default: + return "[???]"; + } +} - if(file) { - curlx_struct_stat stat; - if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { - CURLcode result = CURLE_OK; - bool eof; - struct dynbuf linebuf; - curlx_dyn_init(&linebuf, MAX_NETRC_LINE); - ret = NETRC_OK; - do { - const char *line; - /* Curl_get_line always returns lines ending with a newline */ - result = Curl_get_line(&linebuf, file, &eof); - if(!result) { - line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); - } - if(result) { - curlx_dyn_free(filebuf); - ret = curl2netrc(result); - break; - } - } while(!eof); - curlx_dyn_free(&linebuf); +#endif + +static void netrc_lexer_init(struct netrc_lexer *lexer, + struct Curl_easy *data, + const char *content) +{ + curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN); + lexer->data = data; + lexer->content = lexer->pos = content; +} + +static void netrc_lexer_cleanup(struct netrc_lexer *lexer) +{ + lexer->content = lexer->pos = NULL; + lexer->data = NULL; + curlx_dyn_free(&lexer->literal); +} + +static void netrc_skip_blanks(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + curlx_str_passblanks(&s); + while(*s == '\r') + ++s; + if(*s == '\n') { + ++s; } - curlx_fclose(file); + else + break; } - return ret; + lexer->pos = s; } -/* bundled parser state to keep function signatures compact */ -struct netrc_state { - struct Curl_creds *existing; - char *login; - char *password; - enum host_lookup_state state; - enum found_state keyword; - NETRCcode retcode; - unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ - bool our_login; - bool done; -}; +static void netrc_skip_to_empty_line(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + if(*s == '\r') + ++s; + else if(*s == '\n') { + ++s; + while(*s == '\r') + ++s; + if(*s == '\n') + goto out; + } + else + ++s; + } +out: + lexer->pos = s; +} /* * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t @@ -126,374 +193,370 @@ struct netrc_state { * * Returns NETRC_OK or error. */ -static NETRCcode netrc_quoted_token(const char **tok_endp, - struct dynbuf *token) +static NETRCcode netrc_lexer_quoted(struct netrc_lexer *lexer) { - bool escape = FALSE; NETRCcode rc = NETRC_SYNTAX_ERROR; - const char *tok_end = *tok_endp; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - CURLcode result; - char s = *tok_end; + const char *s = lexer->pos; + bool escape = FALSE; + CURLcode result; + + DEBUGASSERT(*s == '\"'); + ++s; /* pass the leading quote */ + while(*s) { + char c = *s; if(escape) { escape = FALSE; - switch(s) { + switch(c) { case 'n': - s = '\n'; + c = '\n'; break; case 'r': - s = '\r'; + c = '\r'; break; case 't': - s = '\t'; + c = '\t'; break; } } - else if(s == '\\') { + else if(c == '\\') { escape = TRUE; - tok_end++; + ++s; continue; } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ + else if(c == '\"') { + ++s; /* pass the ending quote */ rc = NETRC_OK; - break; + goto out; } - result = curlx_dyn_addn(token, &s, 1); + result = curlx_dyn_addn(&lexer->literal, &c, 1); if(result) { - *tok_endp = tok_end; - return curl2netrc(result); + rc = curl2netrc(result); + goto out; } - tok_end++; + ++s; } - *tok_endp = tok_end; +out: + lexer->pos = s; return rc; } -/* - * Gets the next token from the netrc buffer at *tokp. Writes the token into - * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input - * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE - * when the line is exhausted. - * - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_get_token(const char **tokp, - const char **tok_endp, - struct dynbuf *token, - enum host_lookup_state *statep, - bool *lineend) +static void netrc_lexer_push(struct netrc_lexer *lexer) { - const char *tok = *tokp; - const char *tok_end; - - *lineend = FALSE; - curlx_dyn_reset(token); - curlx_str_passblanks(&tok); - - /* tok is first non-space letter */ - if(*statep == MACDEF) { - if((*tok == '\n') || (*tok == '\r')) - *statep = NOTHING; /* end of macro definition */ - *lineend = TRUE; - *tokp = tok; - return NETRC_OK; - } + lexer->pushed = TRUE; +} - if(!*tok || (*tok == '\n')) { - /* end of line */ - *lineend = TRUE; - *tokp = tok; - return NETRC_OK; +static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer, + bool want_literal) +{ + const char *s = lexer->pos, *start; + NETRCcode rc = NETRC_OK; + size_t slen; + CURLcode result; + + if(lexer->pushed) { + lexer->pushed = FALSE; + goto out; } - tok_end = tok; - if(*tok == '\"') { - /* quoted string */ - NETRCcode ret = netrc_quoted_token(&tok_end, token); - if(ret) - return ret; - } - else { + curlx_dyn_reset(&lexer->literal); + netrc_skip_blanks(lexer); + s = lexer->pos; + + switch(*s) { + case 0: + lexer->token = NETRC_TOK_EOF; + break; + case '\"': + rc = netrc_lexer_quoted(lexer); + lexer->token = NETRC_TOK_LITERAL; + s = lexer->pos; + break; + default: /* unquoted token */ - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; + start = s; + while(*s && !ISBLANK(*s) && !ISNEWLINE(*s)) + ++s; + slen = s - start; + if(!slen) { + rc = NETRC_SYNTAX_ERROR; + } + if(want_literal) { + lexer->token = NETRC_TOK_LITERAL; + result = curlx_dyn_addn(&lexer->literal, start, slen); + rc = curl2netrc(result); + } + else if((slen == 7) && curl_strnequal(start, "machine", slen)) { + lexer->token = NETRC_TOK_MACHINE; + } + else if((slen == 7) && curl_strnequal(start, "default", slen)) { + lexer->token = NETRC_TOK_DEFAULT; } - if(!len) - return NETRC_SYNTAX_ERROR; - result = curlx_dyn_addn(token, tok, len); - if(result) - return curl2netrc(result); + else if((slen == 7) && curl_strnequal(start, "account", slen)) { + lexer->token = NETRC_TOK_ACCOUNT; + } + else if((slen == 5) && curl_strnequal(start, "login", slen)) { + lexer->token = NETRC_TOK_LOGIN; + } + else if((slen == 8) && curl_strnequal(start, "password", slen)) { + lexer->token = NETRC_TOK_PASSWD; + } + else if((slen == 6) && curl_strnequal(start, "macdef", slen)) { + lexer->token = NETRC_TOK_MACDEF; + } + else { + lexer->token = NETRC_TOK_JUNK; + } + break; } - *tok_endp = tok_end; +out: +#if NETRC_DEBUG + CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d", + netrc_tokenstr(lexer->token), + curlx_dyn_ptr(&lexer->literal), rc); +#endif + lexer->pos = s; + return rc; +} - if(curlx_dyn_len(token)) - *tokp = curlx_dyn_ptr(token); - else - /* set it to blank to avoid NULL */ - *tokp = ""; +struct netrc_scanner { + struct netrc_lexer lexer; + const char *hostname; /* non-NULL, machine to scan for */ + const char *user; /* maybe NULL, login to scan for */ + char *login; + char *passwd; + struct Curl_creds *creds; + bool matches_host; + bool found; +}; - return NETRC_OK; +static void netrc_scan_reset(struct netrc_scanner *sc) +{ + curlx_safefree(sc->login); + curlx_safefree(sc->passwd); + sc->matches_host = FALSE; } -/* - * Reset parser for a new machine entry. Frees password and optionally login - * if it was not user-specified. - */ -static void netrc_new_machine(struct netrc_state *ns) +static void netrc_scan_init(struct netrc_scanner *sc, + struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user) { - ns->keyword = NONE; - ns->found = 0; - ns->our_login = FALSE; - curlx_safefree(ns->password); - curlx_safefree(ns->login); + memset(sc, 0, sizeof(*sc)); + netrc_lexer_init(&sc->lexer, data, content); + sc->hostname = hostname; + sc->user = (user && user[0]) ? user : NULL; + netrc_scan_reset(sc); } -/* - * Process a parsed token through the HOSTVALID state machine branch. This - * handles login/password values and keyword transitions for the matched host. - * - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) +static void netrc_scan_cleanup(struct netrc_scanner *sc) { - if(ns->keyword == LOGIN) { - if(Curl_creds_has_user(ns->existing)) - ns->our_login = !Curl_timestrcmp(ns->existing->user, tok); - else { - ns->our_login = TRUE; - curlx_free(ns->login); - ns->login = curlx_strdup(tok); - if(!ns->login) - return NETRC_OUT_OF_MEMORY; - } - ns->found |= FOUND_LOGIN; - ns->keyword = NONE; - } - else if(ns->keyword == PASSWORD) { - curlx_free(ns->password); - ns->password = curlx_strdup(tok); - if(!ns->password) - return NETRC_OUT_OF_MEMORY; - ns->found |= FOUND_PASSWORD; - ns->keyword = NONE; - } - else if(curl_strequal("login", tok)) - ns->keyword = LOGIN; - else if(curl_strequal("password", tok)) - ns->keyword = PASSWORD; - else if(curl_strequal("machine", tok)) { - /* a new machine here */ - bool specific_login = Curl_creds_has_user(ns->existing); - - if((ns->found & FOUND_PASSWORD) && - /* a password was provided for this host */ - (!specific_login || ns->our_login || - /* and found a login that is suitable - (either matched specific one or simply present) */ - (specific_login && !(ns->found & FOUND_LOGIN)))) { - /* or we look for a specific login, but no login was not specified */ - - ns->done = TRUE; - return NETRC_OK; - } - - ns->state = HOSTFOUND; - netrc_new_machine(ns); - } - else if(curl_strequal("default", tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; - netrc_new_machine(ns); - } - if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) - ns->done = TRUE; - return NETRC_OK; + netrc_scan_reset(sc); + sc->hostname = NULL; + sc->user = NULL; + Curl_creds_unlink(&sc->creds); + netrc_lexer_cleanup(&sc->lexer); } -/* - * Process one parsed token through the netrc state - * machine. Updates the parser state in *ns. - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_handle_token(struct netrc_state *ns, - const char *tok, - const char *host) +static NETRCcode netrc_scan_literal(struct netrc_scanner *sc, + char **pdest) { - switch(ns->state) { - case NOTHING: - if(curl_strequal("macdef", tok)) - ns->state = MACDEF; - else if(curl_strequal("machine", tok)) { - ns->state = HOSTFOUND; - netrc_new_machine(ns); - } - else if(curl_strequal("default", tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; - } - break; - case MACDEF: - if(!*tok) - ns->state = NOTHING; - break; - case HOSTFOUND: - if(curl_strequal(host, tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; + NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE); + if(!rc) { + if(sc->lexer.token == NETRC_TOK_LITERAL) { + if(pdest && sc->matches_host) { + curlx_free(*pdest); + *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal)); + if(!*pdest) + rc = NETRC_OUT_OF_MEMORY; + } } else - ns->state = NOTHING; - break; - case HOSTVALID: - return netrc_hostvalid(ns, tok); + netrc_lexer_push(&sc->lexer); } - return NETRC_OK; + return rc; } -/* - * Finalize the parse result: fill in defaults and free - * resources on error. - */ -static NETRCcode netrc_finalize(struct netrc_state *ns, - struct store_netrc *store, - struct Curl_creds **pcreds) +static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc) { - NETRCcode retcode = ns->retcode; - if(!retcode) { - if(!ns->password && ns->our_login) { - /* success without a password, set a blank one */ - ns->password = curlx_strdup(""); - if(!ns->password) { - retcode = NETRC_OUT_OF_MEMORY; - goto out; + NETRCcode rc = NETRC_OK; +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, + "[NETRC] entry matches_host=%d, login='%s', passwd='%s'", + sc->matches_host, sc->login, sc->passwd); +#endif + if(sc->matches_host) { + if(sc->login) { + if(sc->user) { + if(Curl_timestrcmp(sc->user, sc->login)) + goto out; + /* We look for a specific user, + * entry is only interesting with password */ + sc->found = !!sc->passwd; + } + else { + sc->found = TRUE; } } - else if(!ns->login && !ns->password) { - /* a default with no credentials */ - retcode = NETRC_NO_MATCH; - goto out; + else if(sc->passwd) { + /* found a passwd that applies to any user */ + sc->found = TRUE; } - } - - if(!retcode) { - /* success - netrc_finalize() can return a password even when specific_login is set - but our_login is false (e.g., host matched but the requested login - never matched). See test 685. */ - const char *login = Curl_creds_has_user(ns->existing) ? - ns->existing->user : ns->login; - /* success without a password, set a blank one */ - const char *passwd = ns->password ? ns->password : ""; - - if(Curl_creds_create(login, passwd, NULL, NULL, NULL, CREDS_NETRC, - pcreds)) { - retcode = NETRC_OUT_OF_MEMORY; - goto out; + else { + /* entry has nothing interesting */ + } + if(sc->found) { +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found"); +#endif + if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd, + NULL, NULL, NULL, CREDS_NETRC, &sc->creds)) + rc = NETRC_OUT_OF_MEMORY; } } - out: - curlx_free(ns->login); - curlx_free(ns->password); - if(retcode) { - curlx_dyn_free(&store->filebuf); - store->loaded = FALSE; - } - return retcode; + netrc_scan_reset(sc); + return rc; } -/* - * Returns zero on success. - */ -static NETRCcode parsenetrc(struct store_netrc *store, - const char *host, - struct Curl_creds *existing, - const char *netrcfile, +static NETRCcode netrc_scan(struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user, struct Curl_creds **pcreds) { - const char *netrcbuffer; - struct dynbuf token; - struct dynbuf *filebuf = &store->filebuf; - struct netrc_state ns; - - DEBUGASSERT(!existing || !Curl_creds_has_passwd(existing)); - memset(&ns, 0, sizeof(ns)); - ns.retcode = NETRC_NO_MATCH; - ns.existing = existing; - - curlx_dyn_init(&token, MAX_NETRC_TOKEN); - - if(!store->loaded) { - NETRCcode ret = file2memory(netrcfile, filebuf); - if(ret) - return ret; - store->loaded = TRUE; - } - - netrcbuffer = curlx_dyn_ptr(filebuf); + struct netrc_scanner sc; + NETRCcode rc = NETRC_OK; - while(!ns.done) { - const char *tok = netrcbuffer; - while(tok && !ns.done) { - const char *tok_end; - bool lineend; - NETRCcode ret; - - ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); - if(ret) { - ns.retcode = ret; - goto out; - } - if(lineend) + Curl_creds_unlink(pcreds); + netrc_scan_init(&sc, data, content, hostname, user); + + while(!rc && !sc.found) { + rc = netrc_lexer_next(&sc.lexer, FALSE); + if(!rc) { + /* Does this token end any previous entry? */ + switch(sc.lexer.token) { + case NETRC_TOK_EOF: + case NETRC_TOK_MACHINE: + case NETRC_TOK_DEFAULT: + case NETRC_TOK_MACDEF: + rc = netrc_scan_end_entry(&sc); + if(rc || sc.found) + goto out; break; + default: + break; + } - ret = netrc_handle_token(&ns, tok, host); - if(ret) { - ns.retcode = ret; + switch(sc.lexer.token) { + case NETRC_TOK_EOF: goto out; - } - /* tok_end cannot point to a null byte here since lines are always - newline terminated */ - DEBUGASSERT(*tok_end); - tok = ++tok_end; - } - if(!ns.done) { - const char *nl = NULL; - if(tok) - nl = strchr(tok, '\n'); - if(!nl) + case NETRC_TOK_MACHINE: + rc = netrc_lexer_next(&sc.lexer, TRUE); + if(!rc) { + if(sc.lexer.token == NETRC_TOK_LITERAL) { + sc.matches_host = curl_strequal( + sc.hostname, curlx_dyn_ptr(&sc.lexer.literal)); + } + else { + sc.matches_host = FALSE; + netrc_lexer_push(&sc.lexer); + } + } + break; + case NETRC_TOK_DEFAULT: + sc.matches_host = TRUE; + break; + case NETRC_TOK_ACCOUNT: + rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */ + break; + case NETRC_TOK_LOGIN: + rc = netrc_scan_literal(&sc, &sc.login); break; - /* point to next line */ - netrcbuffer = &nl[1]; + case NETRC_TOK_PASSWD: + rc = netrc_scan_literal(&sc, &sc.passwd); + break; + case NETRC_TOK_MACDEF: + netrc_skip_to_empty_line(&sc.lexer); + break; + case NETRC_TOK_LITERAL: + case NETRC_TOK_JUNK: + default: + /* skip this */ + break; + } } - } /* while !done */ + } out: - curlx_dyn_free(&token); - return netrc_finalize(&ns, store, pcreds); + if(!rc) { + if(sc.creds) + Curl_creds_link(pcreds, sc.creds); + else + rc = NETRC_NO_MATCH; + } + netrc_scan_cleanup(&sc); + return rc; } -const char *Curl_netrc_strerror(NETRCcode ret) +static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) { - switch(ret) { - default: - return ""; /* not a legit error */ - case NETRC_FILE_MISSING: - return "no such file"; - case NETRC_NO_MATCH: - return "no matching entry"; - case NETRC_OUT_OF_MEMORY: - return "out of memory"; - case NETRC_SYNTAX_ERROR: - return "syntax error"; + NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ + FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + + if(file) { + curlx_struct_stat stat; + if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + CURLcode result = CURLE_OK; + bool eof; + struct dynbuf linebuf; + curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + ret = NETRC_OK; + do { + const char *line; + /* Curl_get_line always returns lines ending with a newline */ + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } + if(result) { + curlx_dyn_free(filebuf); + ret = curl2netrc(result); + break; + } + } while(!eof); + curlx_dyn_free(&linebuf); + } + curlx_fclose(file); } - /* never reached */ + return ret; +} + +static NETRCcode netrc_scan_file(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds) +{ + struct dynbuf *filebuf = &store->filebuf; + + if(!store->loaded) { + NETRCcode ret = file2memory(netrcfile, filebuf); + if(ret) { + CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile); + return ret; + } + store->loaded = TRUE; + } + + return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds); } /* @@ -502,14 +565,18 @@ const char *Curl_netrc_strerror(NETRCcode ret) * *loginp and *passwordp MUST be allocated if they are not NULL when passed * in. */ -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - struct Curl_creds *existing, +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, const char *netrcfile, struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; char *filealloc = NULL; + CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'", + netrcfile, hostname, user); Curl_creds_unlink(pcreds); if(!netrcfile) { char *home = NULL; @@ -559,7 +626,8 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, goto out; } } - retcode = parsenetrc(store, host, existing, filealloc, pcreds); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -569,14 +637,17 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, curlx_free(homea); return NETRC_OUT_OF_MEMORY; } - retcode = parsenetrc(store, host, existing, filealloc, pcreds); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); } #endif curlx_free(homea); } else - retcode = parsenetrc(store, host, existing, netrcfile, pcreds); + retcode = netrc_scan_file( + data, store, hostname, user, netrcfile, pcreds); + out: if(retcode) Curl_creds_unlink(pcreds); @@ -593,4 +664,22 @@ void Curl_netrc_cleanup(struct store_netrc *store) curlx_dyn_free(&store->filebuf); store->loaded = FALSE; } -#endif + +const char *Curl_netrc_strerror(NETRCcode ret) +{ + switch(ret) { + default: + return ""; /* not a legit error */ + case NETRC_FILE_MISSING: + return "no such file"; + case NETRC_NO_MATCH: + return "no matching entry"; + case NETRC_OUT_OF_MEMORY: + return "out of memory"; + case NETRC_SYNTAX_ERROR: + return "syntax error"; + } + /* never reached */ +} + +#endif /* !CURL_DISABLE_NETRC */ diff --git a/lib/netrc.h b/lib/netrc.h index 92dd4d47c9e6..6be9b8331621 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -29,6 +29,7 @@ #include "curlx/dynbuf.h" +struct Curl_easy; struct Curl_creds; struct store_netrc { @@ -50,15 +51,14 @@ const char *Curl_netrc_strerror(NETRCcode ret); void Curl_netrc_init(struct store_netrc *store); void Curl_netrc_cleanup(struct store_netrc *store); -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - struct Curl_creds *existing, +/* Scan a netrc file for credentials matching hostname + * and optional user. */ +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, const char *netrcfile, struct Curl_creds **pcreds); -/* Assume: (*passwordp)[0]=0, host[0] != 0. - * If (*loginp)[0] = 0, search for login and password within a machine - * section in the netrc. - * If (*loginp)[0] != 0, search for password within machine and login. - */ #else /* disabled */ #define Curl_netrc_init(x) diff --git a/lib/url.c b/lib/url.c index ec4fb8b1b0f7..a569c3e4bc10 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2161,14 +2161,11 @@ static bool str_has_ctrl(const char *input) static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn) { - CURLUcode uc; char **optionsp = &conn->options; #ifndef CURL_DISABLE_NETRC - struct Curl_creds *ncreds_in = NULL; struct Curl_creds *ncreds_out = NULL; #endif CURLcode result = CURLE_OK; - bool creds_changed = FALSE; if(data->set.str[STRING_OPTIONS]) { curlx_free(*optionsp); @@ -2180,107 +2177,97 @@ static CURLcode override_login(struct Curl_easy *data, } #ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc) { - /* Determine how to react on already existing credentials */ - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - Curl_creds_unlink(&conn->creds); - } + if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ + struct Curl_creds *ncreds_in = NULL; + bool scan_netrc = TRUE; + NETRCcode ret; + CURLUcode uc; if(data->state.creds) { switch(data->state.creds->source) { case CREDS_OPTION: - /* we never override credentials set via CURLOPT_* */ - goto out; - case CREDS_URL: + /* we never override credentials set via CURLOPT_*, leave. */ + scan_netrc = FALSE; + break; + case CREDS_URL: /* only apply when netrc is not required */ if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - /* use the URL user to search netrc */ - result = Curl_creds_create( - data->state.creds->user, NULL, NULL, NULL, NULL, CREDS_URL, - &ncreds_in); - if(result) - goto out; + /* We ignore password from URL */ + ncreds_in = data->state.creds; + } + else if(!Curl_creds_has_user(data->state.creds) || + !Curl_creds_has_passwd(data->state.creds)) { + /* We use netrc to complete what is missing */ + ncreds_in = data->state.creds; } else - /* only search when something is still missing */ - Curl_creds_link(&ncreds_in, data->state.creds); + scan_netrc = FALSE; break; - default: - /* ignore credentials from other sources */ + default: /* ignore credentials from other sources */ break; } } - /* Only search in netrc when the creds are not already complete */ - if(!Curl_creds_has_passwd(ncreds_in)) { - NETRCcode ret; - - CURL_TRC_M(data, "netrc: find credentials for %s, user %s", - conn->origin->hostname, - Curl_creds_has_user(ncreds_in) ? ncreds_in->user : "*"); - ret = Curl_parsenetrc(&data->state.netrc, - conn->origin->hostname, - ncreds_in, - data->set.str[STRING_NETRC_FILE], - &ncreds_out); - DEBUGASSERT(!ret || !ncreds_out); - if(ret == NETRC_OUT_OF_MEMORY) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - else if(ret && ((ret == NETRC_NO_MATCH) || - (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { - infof(data, "Could not find host %s in the %s file; using defaults", - conn->origin->hostname, - (data->set.str[STRING_NETRC_FILE] ? - data->set.str[STRING_NETRC_FILE] : ".netrc")); - } - else if(ret) { - const char *m = Curl_netrc_strerror(ret); - failf(data, ".netrc error: %s", m); - result = CURLE_READ_ERROR; - goto out; - } - else if(ncreds_out) { - if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { - /* if the protocol cannot handle control codes in credentials, make - sure there are none */ - if(str_has_ctrl(ncreds_out->user) || - str_has_ctrl(ncreds_out->passwd)) { - failf(data, "control code detected in .netrc credentials"); - result = CURLE_READ_ERROR; - goto out; - } - } - CURL_TRC_M(data, "netrc: using credentials for %s as %s", - conn->origin->hostname, ncreds_out->user); - result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, - data->state.creds, CREDS_NETRC, - &data->state.creds); - if(result) + if(!scan_netrc) + goto out; + + ret = Curl_netrc_scan(data, &data->state.netrc, + conn->origin->hostname, + Curl_creds_user(ncreds_in), + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else if(ret && ((ret == NETRC_NO_MATCH) || + (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { + infof(data, "Could not find host %s in the %s file; using defaults", + conn->origin->hostname, + (data->set.str[STRING_NETRC_FILE] ? + data->set.str[STRING_NETRC_FILE] : ".netrc")); + } + else if(ret) { + const char *m = Curl_netrc_strerror(ret); + failf(data, ".netrc error: %s", m); + result = CURLE_READ_ERROR; + goto out; + } + else if(ncreds_out) { + if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { + /* if the protocol cannot handle control codes in credentials, make + sure there are none */ + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { + failf(data, "control code detected in .netrc credentials"); + result = CURLE_READ_ERROR; goto out; - creds_changed = TRUE; + } } - else - DEBUGASSERT(0); + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + conn->origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + data->state.creds, CREDS_NETRC, + &data->state.creds); + if(result) + goto out; + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(data->state.creds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(data->state.creds), + CURLU_URLENCODE); + if(uc) + result = Curl_uc_to_curlcode(uc); } + else + DEBUGASSERT(0); } - #endif - if(creds_changed) { - /* for updated strings, we update them in the URL */ - uc = curl_url_set(data->state.uh, CURLUPART_USER, - Curl_creds_user(data->state.creds), CURLU_URLENCODE); - if(!uc) - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - Curl_creds_passwd(data->state.creds), CURLU_URLENCODE); - if(uc) - result = Curl_uc_to_curlcode(uc); - } - out: #ifndef CURL_DISABLE_NETRC - Curl_creds_unlink(&ncreds_in); Curl_creds_unlink(&ncreds_out); #endif return result; diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index 099f39dd916c..18bc9d421593 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -27,21 +27,23 @@ #include "netrc.h" #include "creds.h" -static void t1304_stop(struct Curl_creds **pc1, struct Curl_creds **pc2) +static CURLcode t1304_setup(struct Curl_easy **easy) { - Curl_creds_unlink(pc1); - Curl_creds_unlink(pc2); + CURLcode result = CURLE_OK; + + global_init(CURL_GLOBAL_ALL); + *easy = curl_easy_init(); + if(!*easy) { + curl_global_cleanup(); + return CURLE_OUT_OF_MEMORY; + } + return result; } -static bool t1304_set_creds(const char *user, const char *passwd, - struct Curl_creds **pcreds) +static void t1304_stop(struct Curl_easy *easy) { - Curl_creds_unlink(pcreds); - if(user || passwd) - return !Curl_creds_create(user, passwd, NULL, NULL, NULL, CREDS_NONE, - pcreds); - else - return TRUE; + curl_easy_cleanup(easy); + curl_global_cleanup(); } static bool t1304_no_user(struct Curl_creds *creds) @@ -56,130 +58,105 @@ static bool t1304_no_passwd(struct Curl_creds *creds) static CURLcode test_unit1304(const char *arg) { - struct Curl_creds *cr_out = NULL, *cr_in = NULL; - - UNITTEST_BEGIN_SIMPLE - + struct Curl_creds *cr_out = NULL; + struct Curl_easy *data; int result; struct store_netrc store; + UNITTEST_BEGIN(t1304_setup(&data)) + /* * Test a non existent host in our netrc file. */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", NULL, arg, &cr_out); + result = Curl_netrc_scan( + data, &store, "test.example.com", NULL, arg, &cr_out); fail_unless(result == 1, "expected no match"); - abort_unless(cr_out == NULL, "creds did not return NULL!"); + fail_unless(cr_out == NULL, "creds did not return NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login in our netrc file. */ - fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan(data, &store, "example.com", "me", arg, &cr_out); fail_unless(result == 1, "expected no match"); - abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); + fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login and host in our netrc file. */ - fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan( + data, &store, "test.example.com", "me", arg, &cr_out); fail_unless(result == 1, "expected no match"); - abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); + fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login (substring of an existing one) in our * netrc file. */ - fail_unless(t1304_set_creds( - "admi", NULL, &cr_in), "err set creds"); /* spellchecker:disable-line */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan( + data, &store, "example.com", "a", arg, &cr_out); fail_unless(result == 1, "expected no match"); - abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); + fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login (superstring of an existing one) * in our netrc file. */ - fail_unless(t1304_set_creds("adminn", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan( + data, &store, "example.com", "administrator", arg, &cr_out); fail_unless(result == 1, "expected no match"); - abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); + fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); /* - * Test for the first existing host in our netrc file - * with login[0] = 0. + * Test for the first existing host in our netrc file with no user */ - Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan(data, &store, "example.com", NULL, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); - abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(!t1304_no_user(cr_out), "returned NULL!"); fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, "login should be 'admin'"); Curl_netrc_cleanup(&store); /* - * Test for the first existing host in our netrc file - * with login[0] != 0. + * Test for the second existing host in our netrc file with no user */ - Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + result = Curl_netrc_scan( + data, &store, "curl.example.com", NULL, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, - "password should be 'passwd'"); - abort_unless(!t1304_no_user(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, - "login should be 'admin'"); - Curl_netrc_cleanup(&store); - - /* - * Test for the second existing host in our netrc file - * with login[0] = 0. - */ - Curl_creds_unlink(&cr_in); - Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); - fail_unless(result == 0, "Host should have been found"); - abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, "password should be 'none'"); - abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(!t1304_no_user(cr_out), "returned NULL!"); fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, "login should be 'none'"); Curl_netrc_cleanup(&store); /* - * Test for the second existing host in our netrc file - * with login[0] != 0. + * Test for the last host where we do not want to see the password + * if the login does not match. */ - Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); - fail_unless(result == 0, "Host should have been found"); - abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, - "password should be 'none'"); - abort_unless(!t1304_no_user(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, - "login should be 'none'"); + result = Curl_netrc_scan( + data, &store, "curl.example.com", "hilarious", arg, &cr_out); + fail_unless(result == 1, "expect no match"); + fail_unless(!Curl_creds_has_passwd(cr_out), "password must be NULL"); Curl_netrc_cleanup(&store); - UNITTEST_END(t1304_stop(&cr_in, &cr_out)) + Curl_creds_unlink(&cr_out); + + UNITTEST_END(t1304_stop(data)) } #else From 7ca5f939c8403db7ede21e2ccb805a65d3329ef8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 16 May 2026 00:54:13 +0200 Subject: [PATCH 0149/1062] test1646: netrc parsing without user match but user in URL Follow-up to 4ae1d7cc2643e --- tests/data/Makefile.am | 1 + tests/data/test1646 | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/data/test1646 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 9c63d7674e7b..b330af3b90f9 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -218,6 +218,7 @@ test1628 test1629 \ \ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 \ +test1646 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1646 b/tests/data/test1646 new file mode 100644 index 000000000000..3df03bed9e45 --- /dev/null +++ b/tests/data/test1646 @@ -0,0 +1,45 @@ + + + + +netrc + + + + + +HTTP/1.1 200 OK +Content-Length: 6 + +12345 + + + +# Client-side + + +http + + +netrc parsing without user match but user in URL + + +--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER http://alice@example.com:%HTTPPORT/%TESTNUMBER --resolve example.com:%HTTPPORT:%HOSTIP + + +machine example.com login bob password sekret + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: example.com:%HTTPPORT +Authorization: Basic %b64[alice:]b64% +User-Agent: curl/%VERSION +Accept: */* + + + + From 9107e8ba98f5a27f4e88401ed4ec4e6db6fbc6a6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 18 May 2026 15:41:24 +0200 Subject: [PATCH 0150/1062] curl_easy_pause.md: rephrase the stream cache when pause clause - mention HTTP/3 - it is 10 MB per stream these days Closes #21658 --- docs/libcurl/curl_easy_pause.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index 31e1cffe77c0..d690f6a3a9c7 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -85,14 +85,12 @@ direction, might cause problems or error. # MULTIPLEXED -When a connection is used multiplexed, like for HTTP/2, and one of the -transfers over the connection is paused and the others continue flowing, -libcurl might end up buffering contents for the paused transfer. It has to do -this because it needs to drain the socket for the other transfers and the -already announced window size for the paused transfer allows the server to -continue sending data up to that window size amount. By default, libcurl -announces a 32 megabyte window size, which thus can make libcurl end up -buffering 32 megabyte of data for a paused stream. +On multiplexed connections (HTTP/2 or HTTP/3), pausing an individual stream +while others remain active forces libcurl to buffer up to 10 MB of data for +the paused transfer. Because libcurl must continuously drain the shared socket +to sustain active streams, and the default flow-control window allows the +server to send up to 10 MB before halting, libcurl is forced to buffer the +incoming bytes in memory. When such a paused stream is unpaused again, any buffered data is delivered first. From 64824e439d5228f6337ce1d8de615457d47c4646 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 18 May 2026 16:05:49 +0200 Subject: [PATCH 0151/1062] VULN-DISCLOSURE-POLICY.md: test code is not secure Don't tell us about it Closes #21660 --- docs/VULN-DISCLOSURE-POLICY.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index 1ce3f4e26d4d..99fb5577a3d1 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -254,6 +254,14 @@ security problems. The same applies to scripts and software which are not installed by default through the make install rule. +## Test code + +curl has an extensive test suite with lots of code written specifically to +exercise and verify curl, libcurl and specific internal functions. The test +code and its associated test servers are *not* intended for production use. +They are not secure, you should not assume otherwise and must not report about +security problems in those. + ## URL inconsistencies URL parser inconsistencies between browsers and curl are expected and are not From b190c803e34dad8b791a01c0584d5174f5cc5847 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 18 May 2026 22:59:14 +0200 Subject: [PATCH 0152/1062] test1588: use %TESTNUMBER, not hard-coded number Closes #21662 --- tests/data/test1588 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/data/test1588 b/tests/data/test1588 index 753e98cd6b6a..30ec8ca91282 100644 --- a/tests/data/test1588 +++ b/tests/data/test1588 @@ -79,25 +79,25 @@ http://test.remote.example.com/path/%TESTNUMBER %HOSTIP %HTTPPORT silly:person c # Verify data after the test has been "shot" -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com -Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/1588", response="d0b2f000c7e3fca24452b5810713404a" +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="d0b2f000c7e3fca24452b5810713404a" Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com -Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/1588", response="d0b2f000c7e3fca24452b5810713404a" +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="d0b2f000c7e3fca24452b5810713404a" Accept: */* Proxy-Connection: Keep-Alive From 38cd720f764640c56747b9d7c9d551e827567559 Mon Sep 17 00:00:00 2001 From: Shintomon Mathew <148446196+MysticShinM@users.noreply.github.com> Date: Mon, 18 May 2026 19:32:45 +0530 Subject: [PATCH 0153/1062] creds: mask OAuth bearer token in trace logs Masked OAuth bearer tokens in credential trace output by emitting *** when a bearer token is present, matching the existing password redaction behavior and preventing sensitive token disclosure in verbose/debug logs. Closes #21659 --- lib/creds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creds.c b/lib/creds.c index 8303891967ff..e59c601b9585 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -181,7 +181,7 @@ void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, Curl_creds_user(creds), Curl_creds_has_passwd(creds) ? "***" : "", Curl_creds_sasl_authzid(creds), - Curl_creds_oauth_bearer(creds), + Curl_creds_has_oauth_bearer(creds) ? "***" : "", creds->source); } else From d24652971a636a4b2374367434ed72bb561b79bc Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Mon, 18 May 2026 22:30:57 -0700 Subject: [PATCH 0154/1062] docs/libcurl: fix the version for curl_multi_socket_action It was added in 7.16.3, not 7.15.4 (that's when curl_multi_socket was added). --- docs/libcurl/curl_multi_socket_action.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/libcurl/curl_multi_socket_action.md b/docs/libcurl/curl_multi_socket_action.md index 44a06406c3e5..4823c5ef6d2b 100644 --- a/docs/libcurl/curl_multi_socket_action.md +++ b/docs/libcurl/curl_multi_socket_action.md @@ -12,7 +12,7 @@ See-also: - the hiperfifo.c example Protocol: - All -Added-in: 7.15.4 +Added-in: 7.16.3 --- # NAME From 81da4ee249ddd0a21a57b740fb1cc93fcad592ba Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 12:29:34 +0200 Subject: [PATCH 0155/1062] vtls: use Curl_safecmp for CRLfile and pinned_key comparison Both are filesystem paths (or case-sensitive hash strings for pinned_key). curl_strequal is case-insensitive and would treat /etc/ssl/Crl.pem and /etc/ssl/crl.pem as the same file, unlike the other path fields (CApath, CAfile, issuercert, clientcert) which already use Curl_safecmp. Closes #21668 --- lib/vtls/vtls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index c83f6e667858..46005578794e 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -217,8 +217,8 @@ static bool match_ssl_primary_config(struct Curl_easy *data, curl_strequal(c1->cipher_list13, c2->cipher_list13) && curl_strequal(c1->curves, c2->curves) && curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && - curl_strequal(c1->CRLfile, c2->CRLfile) && - curl_strequal(c1->pinned_key, c2->pinned_key)) + Curl_safecmp(c1->CRLfile, c2->CRLfile) && + Curl_safecmp(c1->pinned_key, c2->pinned_key)) return TRUE; return FALSE; From 6999ccb8e016acf9fdb21fc73a5a81388f12bf76 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 18:20:37 +0200 Subject: [PATCH 0156/1062] managen: apply minor fixes and improvements - replace `goto` with `while` loop. - fix newlines in a warning message. - handle open error for `curl/curlver.h` header. Ref: #21646 Closes #21670 --- scripts/managen | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/managen b/scripts/managen index e117e335ce81..554f4c984e8f 100755 --- a/scripts/managen +++ b/scripts/managen @@ -705,7 +705,7 @@ sub single { } else { chomp; - print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring:\n:'$_';" + print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring: '$_';\n"; } } @@ -1337,25 +1337,25 @@ my $dir = "."; my $include = "../../include"; my $cmd = shift @ARGV || ''; -check: - -if($cmd eq "-d") { - # specifies source directory - $dir = shift @ARGV; - $cmd = shift @ARGV; - goto check; -} -elsif($cmd eq "-I") { - # include path root - $include = shift @ARGV; - $cmd = shift @ARGV; - goto check; -} -elsif($cmd eq "-c") { - # Column width - $colwidth = 0 + shift @ARGV; - $cmd = shift @ARGV; - goto check; +while(1) { + if($cmd eq "-d") { + # specifies source directory + $dir = shift @ARGV; + $cmd = shift @ARGV; + } + elsif($cmd eq "-I") { + # include path root + $include = shift @ARGV; + $cmd = shift @ARGV; + } + elsif($cmd eq "-c") { + # Column width + $colwidth = 0 + shift @ARGV; + $cmd = shift @ARGV; + } + else { + last; + } } my @files = @ARGV; # the rest are the files @@ -1365,7 +1365,7 @@ if($ENV{'CURL_MAKETGZ_VERSION'}) { $version = $ENV{'CURL_MAKETGZ_VERSION'}; } else { - open(INC, "<$include/curl/curlver.h"); + open(INC, "<$include/curl/curlver.h") || die "no $include/curl/curlver.h"; while() { if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) { $version = $1; From a7bfbc09d4cce16f542c11a8c0f9b822ad74c54a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 19 May 2026 10:12:35 +0200 Subject: [PATCH 0157/1062] unit1304: use enum type and values, rename `result` to `res` Ref: #21646 Closes #21673 --- tests/unit/unit1304.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index 18bc9d421593..a65a3021b851 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -60,7 +60,7 @@ static CURLcode test_unit1304(const char *arg) { struct Curl_creds *cr_out = NULL; struct Curl_easy *data; - int result; + NETRCcode res; struct store_netrc store; UNITTEST_BEGIN(t1304_setup(&data)) @@ -69,9 +69,8 @@ static CURLcode test_unit1304(const char *arg) * Test a non existent host in our netrc file. */ Curl_netrc_init(&store); - result = Curl_netrc_scan( - data, &store, "test.example.com", NULL, arg, &cr_out); - fail_unless(result == 1, "expected no match"); + res = Curl_netrc_scan(data, &store, "test.example.com", NULL, arg, &cr_out); + fail_unless(res == NETRC_NO_MATCH, "expected no match"); fail_unless(cr_out == NULL, "creds did not return NULL!"); Curl_netrc_cleanup(&store); @@ -79,8 +78,8 @@ static CURLcode test_unit1304(const char *arg) * Test a non existent login in our netrc file. */ Curl_netrc_init(&store); - result = Curl_netrc_scan(data, &store, "example.com", "me", arg, &cr_out); - fail_unless(result == 1, "expected no match"); + res = Curl_netrc_scan(data, &store, "example.com", "me", arg, &cr_out); + fail_unless(res == NETRC_NO_MATCH, "expected no match"); fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); @@ -88,9 +87,8 @@ static CURLcode test_unit1304(const char *arg) * Test a non existent login and host in our netrc file. */ Curl_netrc_init(&store); - result = Curl_netrc_scan( - data, &store, "test.example.com", "me", arg, &cr_out); - fail_unless(result == 1, "expected no match"); + res = Curl_netrc_scan(data, &store, "test.example.com", "me", arg, &cr_out); + fail_unless(res == NETRC_NO_MATCH, "expected no match"); fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); @@ -99,9 +97,8 @@ static CURLcode test_unit1304(const char *arg) * netrc file. */ Curl_netrc_init(&store); - result = Curl_netrc_scan( - data, &store, "example.com", "a", arg, &cr_out); - fail_unless(result == 1, "expected no match"); + res = Curl_netrc_scan(data, &store, "example.com", "a", arg, &cr_out); + fail_unless(res == NETRC_NO_MATCH, "expected no match"); fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); @@ -110,9 +107,9 @@ static CURLcode test_unit1304(const char *arg) * in our netrc file. */ Curl_netrc_init(&store); - result = Curl_netrc_scan( + res = Curl_netrc_scan( data, &store, "example.com", "administrator", arg, &cr_out); - fail_unless(result == 1, "expected no match"); + fail_unless(res == NETRC_NO_MATCH, "expected no match"); fail_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); @@ -120,8 +117,8 @@ static CURLcode test_unit1304(const char *arg) * Test for the first existing host in our netrc file with no user */ Curl_netrc_init(&store); - result = Curl_netrc_scan(data, &store, "example.com", NULL, arg, &cr_out); - fail_unless(result == 0, "Host should have been found"); + res = Curl_netrc_scan(data, &store, "example.com", NULL, arg, &cr_out); + fail_unless(res == NETRC_OK, "Host should have been found"); fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); fail_unless(!t1304_no_user(cr_out), "returned NULL!"); @@ -133,9 +130,8 @@ static CURLcode test_unit1304(const char *arg) * Test for the second existing host in our netrc file with no user */ Curl_netrc_init(&store); - result = Curl_netrc_scan( - data, &store, "curl.example.com", NULL, arg, &cr_out); - fail_unless(result == 0, "Host should have been found"); + res = Curl_netrc_scan(data, &store, "curl.example.com", NULL, arg, &cr_out); + fail_unless(res == NETRC_OK, "Host should have been found"); fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, "password should be 'none'"); fail_unless(!t1304_no_user(cr_out), "returned NULL!"); @@ -148,9 +144,9 @@ static CURLcode test_unit1304(const char *arg) * if the login does not match. */ Curl_netrc_init(&store); - result = Curl_netrc_scan( + res = Curl_netrc_scan( data, &store, "curl.example.com", "hilarious", arg, &cr_out); - fail_unless(result == 1, "expect no match"); + fail_unless(res == NETRC_NO_MATCH, "expect no match"); fail_unless(!Curl_creds_has_passwd(cr_out), "password must be NULL"); Curl_netrc_cleanup(&store); From 1fb734bc2de356353b4d152749de9d185e34074f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 18 May 2026 18:38:25 +0200 Subject: [PATCH 0158/1062] docs: tidy-up scheme references After this patch `://` schemes are lowercase and enclosed in backticks. Also: - docs/libcurl/libcurl-multi.md: drop a stray C code fence. - docs/libcurl/libcurl-tutorial.md: replace single/double quotes with Markdown markup where applicable. Ref: #21646 Closes #21674 --- docs/FAQ.md | 8 ++++---- docs/HISTORY.md | 6 +++--- docs/HTTPSRR.md | 2 +- docs/INFRASTRUCTURE.md | 2 +- docs/MANUAL.md | 4 ++-- docs/TODO.md | 2 +- docs/URL-SYNTAX.md | 2 +- docs/cmdline-opts/_PROTOCOLS.md | 2 +- docs/cmdline-opts/_PROXYPREFIX.md | 14 +++++++------- docs/cmdline-opts/_URL.md | 2 +- docs/cmdline-opts/hsts.md | 6 +++--- docs/cmdline-opts/http1.1.md | 2 +- docs/cmdline-opts/preproxy.md | 6 +++--- docs/cmdline-opts/proxy.md | 12 ++++++------ docs/cmdline-opts/socks4.md | 2 +- docs/cmdline-opts/socks4a.md | 2 +- docs/cmdline-opts/socks5-hostname.md | 2 +- docs/cmdline-opts/socks5.md | 2 +- docs/examples/ftpsget.c | 2 +- docs/libcurl/curl_easy_pause.md | 2 +- docs/libcurl/libcurl-errors.md | 4 ++-- docs/libcurl/libcurl-multi.md | 4 +--- docs/libcurl/libcurl-security.md | 8 ++++---- docs/libcurl/libcurl-tutorial.md | 18 +++++++++--------- docs/libcurl/libcurl-ws.md | 2 +- docs/libcurl/opts/CURLOPT_HSTS.md | 2 +- .../opts/CURLOPT_MAX_RECV_SPEED_LARGE.md | 2 +- .../opts/CURLOPT_MAX_SEND_SPEED_LARGE.md | 2 +- .../opts/CURLOPT_NEW_DIRECTORY_PERMS.md | 2 +- docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md | 2 +- docs/libcurl/opts/CURLOPT_PRE_PROXY.md | 10 +++++----- docs/libcurl/opts/CURLOPT_PROXY.md | 18 +++++++++--------- docs/libcurl/opts/CURLOPT_URL.md | 4 ++-- docs/tests/FILEFORMAT.md | 4 ++-- lib/cw-out.c | 2 +- lib/sendf.c | 2 +- 36 files changed, 83 insertions(+), 85 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 0eafd34a375d..96f6d7a0541b 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -854,11 +854,11 @@ results and fetches the new URL. curl supports FTPS (sometimes known as FTP-SSL) both implicit and explicit mode. -When a URL is used that starts with `FTPS://`, curl assumes implicit SSL on +When a URL is used that starts with `ftps://`, curl assumes implicit SSL on the control connection and therefore immediately connects and tries to speak -SSL. `FTPS://` connections default to port 990. +SSL. `ftps://` connections default to port 990. -To use explicit FTPS, you use an `FTP://` URL and the `--ssl-reqd` option (or +To use explicit FTPS, you use an `ftp://` URL and the `--ssl-reqd` option (or one of its related flavors). This is the most common method, and the one mandated by RFC 4217. This kind of connection then of course uses the standard FTP port 21 by default. @@ -893,7 +893,7 @@ software or similar that accepts the connection but does not actually do anything else. This makes (lib)curl to consider the connection connected and thus the connect timeout does not trigger. -## file:// URLs containing drive letters (Windows, NetWare) +## `file://` URLs containing drive letters (Windows, NetWare) When using curl to try to download a local file, one might use a URL in this format: diff --git a/docs/HISTORY.md b/docs/HISTORY.md index c376905ebbae..6beec33f1547 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -82,8 +82,8 @@ OpenSSL took over and SSLeay was abandoned. May: first Debian package. -August: LDAP:// and FILE:// support added. The curl website gets 1300 visits -weekly. Moved site to curl.haxx.nu. +August: `ldap://` and `file://` support added. The curl website gets 1300 +visits weekly. Moved site to curl.haxx.nu. September: Released curl 6.0. 15000 lines of code. @@ -124,7 +124,7 @@ deemed "GPL incompatible".) March 22: curl supports HTTP 1.1 starting with the release of 7.7. This also introduced libcurl's ability to do persistent connections. 24000 lines of code. The libcurl major SONAME number was bumped to 2 due to this overhaul. -The first experimental ftps:// support was added. +The first experimental `ftps://` support was added. August: The curl website gets 8000 visits weekly. Curl Corporation contacted Daniel to discuss "the name issue". After Daniel's reply, they have never diff --git a/docs/HTTPSRR.md b/docs/HTTPSRR.md index bb96526b3985..fbdebc7fde16 100644 --- a/docs/HTTPSRR.md +++ b/docs/HTTPSRR.md @@ -51,7 +51,7 @@ or The list of ALPN IDs is parsed but may not be completely respected because of what the HTTP version preference is set to, which is a problem we are working -on. Also, getting an `HTTP/1.1` ALPN in the HTTPS RR field for an HTTP:// +on. Also, getting an `HTTP/1.1` ALPN in the HTTPS RR field for an `http://` transfer should imply switching to HTTPS, HSTS style. Which curl currently does not. diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 2f24845cddc4..a4f8b5843356 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -139,7 +139,7 @@ anycast access to the site. Should be snappy from virtually everywhere across the globe. The CDN servers support HTTP/1, HTTP/2 and HTTP/3. They set HSTS for a year. -The `HTTP://` version of the site redirects to `HTTPS://`. +The `http://` version of the site redirects to `https://`. Fastly manages the TLS certificates from Let's Encrypt for the servers they run on the behalf of curl. diff --git a/docs/MANUAL.md b/docs/MANUAL.md index e6f5123d2b5b..1032098dcba6 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -99,8 +99,8 @@ or specify them with the `-u` flag like It is like FTP, but you may also want to specify and use SSL-specific options for certificates etc. -Note that using `FTPS://` as prefix is the *implicit* way as described in the -standards while the recommended *explicit* way is done by using `FTP://` and +Note that using `ftps://` as prefix is the *implicit* way as described in the +standards while the recommended *explicit* way is done by using `ftp://` and the `--ssl-reqd` option. ### SFTP / SCP diff --git a/docs/TODO.md b/docs/TODO.md index 74a0c4ce8615..15d6c02ba0e2 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -687,7 +687,7 @@ until [curl pull request 6021](https://github.com/curl/curl/pull/6021) brought the functionality with the libssh2 backend. Presumably, this support can/could be added for the libssh backend as well. -## SFTP with `SCP://` +## SFTP with `scp://` OpenSSH 9 switched their `scp` tool to speak SFTP under the hood. Going forward it might be worth having curl or libcurl attempt SFTP if SCP fails to diff --git a/docs/URL-SYNTAX.md b/docs/URL-SYNTAX.md index 45b6f5ab454c..2cd74d341bec 100644 --- a/docs/URL-SYNTAX.md +++ b/docs/URL-SYNTAX.md @@ -228,7 +228,7 @@ value of the ASCII code for the slash). ## FILE -When a `FILE://` URL is accessed on Windows systems, it can be crafted in a +When a `file://` URL is accessed on Windows systems, it can be crafted in a way so that Windows attempts to connect to a (remote) machine when curl wants to read or write such a path. diff --git a/docs/cmdline-opts/_PROTOCOLS.md b/docs/cmdline-opts/_PROTOCOLS.md index 831b944d24b7..6b1918a9b227 100644 --- a/docs/cmdline-opts/_PROTOCOLS.md +++ b/docs/cmdline-opts/_PROTOCOLS.md @@ -6,7 +6,7 @@ particular build may not support them all. ## DICT Lets you lookup words using online dictionaries. ## FILE -Read or write local files. curl does not support accessing file:// URL +Read or write local files. curl does not support accessing `file://` URL remotely, but when running on Microsoft Windows using the native UNC approach works. Only absolute paths. ## FTP(S) diff --git a/docs/cmdline-opts/_PROXYPREFIX.md b/docs/cmdline-opts/_PROXYPREFIX.md index 297b56c4b64c..0c106306d878 100644 --- a/docs/cmdline-opts/_PROXYPREFIX.md +++ b/docs/cmdline-opts/_PROXYPREFIX.md @@ -1,22 +1,22 @@ # PROXY PROTOCOL PREFIXES -The proxy string may be specified with a protocol:// prefix to specify +The proxy string may be specified with a `protocol://` prefix to specify alternative proxy protocols. (Added in 7.21.7) If no protocol is specified in the proxy string or if the string does not match a supported one, the proxy is treated as an HTTP proxy. The supported proxy protocol prefixes are as follows: -## http:// +## `http://` Makes it use it as an HTTP proxy. The default if no scheme prefix is used. -## https:// +## `https://` Makes it treated as an **HTTPS** proxy. -## socks4:// +## `socks4://` Makes it the equivalent of --socks4 -## socks4a:// +## `socks4a://` Makes it the equivalent of --socks4a -## socks5:// +## `socks5://` Makes it the equivalent of --socks5 -## socks5h:// +## `socks5h://` Makes it the equivalent of --socks5-hostname diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index 288b9d0aa713..2e69eb0bffa1 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -4,7 +4,7 @@ The URL syntax is protocol-dependent. You can find a detailed description in RFC 3986. -If you provide a URL without a leading **protocol://** scheme, curl guesses +If you provide a URL without a leading `protocol://` scheme, curl guesses what protocol you want. It then defaults to HTTP but assumes others based on often-used hostname prefixes. For example, for hostnames starting with `ftp.` curl assumes you want FTP. diff --git a/docs/cmdline-opts/hsts.md b/docs/cmdline-opts/hsts.md index 0f6673cc4ef1..f99b91c28144 100644 --- a/docs/cmdline-opts/hsts.md +++ b/docs/cmdline-opts/hsts.md @@ -20,9 +20,9 @@ Enable HSTS for the transfer. If the filename points to an existing HSTS cache file, that is used. After a completed transfer, the cache is saved to the filename again if it has been modified. -If curl is told to use HTTP:// for a transfer involving a hostname that exists -in the HSTS cache, it upgrades the transfer to use HTTPS. Each HSTS cache -entry has an individual lifetime after which the upgrade is no longer +If curl is told to use `http://` for a transfer involving a hostname that +exists in the HSTS cache, it upgrades the transfer to use HTTPS. Each HSTS +cache entry has an individual lifetime after which the upgrade is no longer performed. Specify a "" filename (zero length) to avoid loading/saving and make curl diff --git a/docs/cmdline-opts/http1.1.md b/docs/cmdline-opts/http1.1.md index 14e5c74702aa..3d241e5c5944 100644 --- a/docs/cmdline-opts/http1.1.md +++ b/docs/cmdline-opts/http1.1.md @@ -18,4 +18,4 @@ Example: # `--http1.1` -Use HTTP version 1.1. This is the default with HTTP:// URLs. +Use HTTP version 1.1. This is the default with `http://` URLs. diff --git a/docs/cmdline-opts/preproxy.md b/docs/cmdline-opts/preproxy.md index 87d94a9604fc..a108d9f09485 100644 --- a/docs/cmdline-opts/preproxy.md +++ b/docs/cmdline-opts/preproxy.md @@ -20,9 +20,9 @@ Use the specified SOCKS proxy before connecting to an HTTP or HTTPS --proxy. In such a case curl first connects to the SOCKS proxy and then connects (through SOCKS) to the HTTP or HTTPS proxy. Hence pre proxy. -The pre proxy string should be specified with a protocol:// prefix to specify -alternative proxy protocols. Use socks4://, socks4a://, socks5:// or -socks5h:// to request the specific SOCKS version to be used. No protocol +The pre proxy string should be specified with a `protocol://` prefix to specify +alternative proxy protocols. Use `socks4://`, `socks4a://`, `socks5://` or +`socks5h://` to request the specific SOCKS version to be used. No protocol specified makes curl default to SOCKS4. If the port number is not specified in the proxy string, it is assumed to be diff --git a/docs/cmdline-opts/proxy.md b/docs/cmdline-opts/proxy.md index 6cd456169d34..881f62d6a1a8 100644 --- a/docs/cmdline-opts/proxy.md +++ b/docs/cmdline-opts/proxy.md @@ -19,15 +19,15 @@ Example: Use the specified proxy. -The proxy string can be specified with a protocol:// prefix. No protocol -specified or http:// it is treated as an HTTP proxy. Use socks4://, -socks4a://, socks5:// or socks5h:// to request a specific SOCKS version to be -used. (Added in 7.21.7) +The proxy string can be specified with a `protocol://` prefix. No protocol +specified or http:// it is treated as an HTTP proxy. Use `socks4://`, +`socks4a://`, `socks5://` or `socks5h://` to request a specific SOCKS version +to be used. (Added in 7.21.7) Unix domain sockets are supported for socks proxy. Set localhost for the host part. e.g. socks5h://localhost/path/to/socket.sock -HTTPS proxy support works with the https:// protocol prefix for OpenSSL and +HTTPS proxy support works with the `https://` protocol prefix for OpenSSL and GnuTLS (added in 7.52.0). It also works for mbedTLS, Rustls, Schannel and wolfSSL (added in 7.87.0). @@ -50,7 +50,7 @@ by curl. This allows you to pass in special characters such as @ by using %40 or pass in a colon with %3a. The proxy host can be specified the same way as the proxy environment -variables, including the protocol prefix (http://) and the embedded user + +variables, including the protocol prefix (`http://`) and the embedded user + password. When a proxy is used, the active FTP mode as set with --ftp-port, cannot be diff --git a/docs/cmdline-opts/socks4.md b/docs/cmdline-opts/socks4.md index 59ec172b8d86..9d1c5671c939 100644 --- a/docs/cmdline-opts/socks4.md +++ b/docs/cmdline-opts/socks4.md @@ -30,7 +30,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks4 proxy with --proxy -using a socks4:// protocol prefix. (Added in 7.21.7) +using a `socks4://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks4a.md b/docs/cmdline-opts/socks4a.md index 9e451cf7b063..695e44cb2b39 100644 --- a/docs/cmdline-opts/socks4a.md +++ b/docs/cmdline-opts/socks4a.md @@ -29,7 +29,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks4a proxy with --proxy -using a socks4a:// protocol prefix. (Added in 7.21.7) +using a `socks4a://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks5-hostname.md b/docs/cmdline-opts/socks5-hostname.md index b558248a78b9..f8ee9fe13797 100644 --- a/docs/cmdline-opts/socks5-hostname.md +++ b/docs/cmdline-opts/socks5-hostname.md @@ -28,7 +28,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks5 hostname proxy with ---proxy using a socks5h:// protocol prefix. (Added in 7.21.7) +--proxy using a `socks5h://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks5.md b/docs/cmdline-opts/socks5.md index 3aa65b33adda..b70e88f6fa2a 100644 --- a/docs/cmdline-opts/socks5.md +++ b/docs/cmdline-opts/socks5.md @@ -29,7 +29,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks5 proxy with --proxy -using a socks5:// protocol prefix. (Added in 7.21.7) +using a `socks5://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/examples/ftpsget.c b/docs/examples/ftpsget.c index abe1d40fda17..c79d2672a15e 100644 --- a/docs/examples/ftpsget.c +++ b/docs/examples/ftpsget.c @@ -69,7 +69,7 @@ int main(void) if(curl) { /* * You better replace the URL with one that works! Note that we use an - * FTP:// URL with standard explicit FTPS. You can also do FTPS:// URLs if + * ftp:// URL with standard explicit FTPS. You can also do ftps:// URLs if * you want to do the rarer kind of transfers: implicit. */ curl_easy_setopt(curl, CURLOPT_URL, diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index d690f6a3a9c7..4a37ed1c14a7 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -80,7 +80,7 @@ Convenience define that unpauses both directions. # LIMITATIONS The pausing of transfers does not work with protocols that work without -network connectivity, like FILE://. Trying to pause such a transfer, in any +network connectivity, like `file://`. Trying to pause such a transfer, in any direction, might cause problems or error. # MULTIPLEXED diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index 7ae1319dcf80..c658361a7920 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -219,7 +219,7 @@ file boundary. ## CURLE_FILE_COULDNT_READ_FILE (37) -A file given with FILE:// could not be opened. Most likely because the file +A file given with `file://` could not be opened. Most likely because the file path does not identify an existing file. Did you check file permissions? ## CURLE_LDAP_CANNOT_BIND (38) @@ -674,7 +674,7 @@ There is no zone id set in the URL. ## CURLUE_BAD_FILE_URL (19) -The file:// URL is invalid. +The `file://` URL is invalid. ## CURLUE_BAD_FRAGMENT (20) diff --git a/docs/libcurl/libcurl-multi.md b/docs/libcurl/libcurl-multi.md index b400f61a03cc..da1331fae82c 100644 --- a/docs/libcurl/libcurl-multi.md +++ b/docs/libcurl/libcurl-multi.md @@ -175,8 +175,6 @@ A few areas in the code are still using blocking code, even when used from the multi interface. While we certainly want and intend for these to get fixed in the future, you should be aware of the following current restrictions: -~~~c - Name resolves unless the c-ares or threaded-resolver backends are used -- file:// transfers +- `file://` transfers - TELNET transfers -~~~ diff --git a/docs/libcurl/libcurl-security.md b/docs/libcurl/libcurl-security.md index cca0c31e8420..81b29cfd16f0 100644 --- a/docs/libcurl/libcurl-security.md +++ b/docs/libcurl/libcurl-security.md @@ -267,16 +267,16 @@ of how the SCP protocol is designed. E.g. Applications must not allow unsanitized SCP: URLs to be passed in for downloads. -# file:// +# `file://` -By default curl and libcurl support file:// URLs. Such a URL is always an +By default curl and libcurl support `file://` URLs. Such a URL is always an access, or attempted access, to a local resource. If your application wants to avoid that, keep control of what URLs to use and/or prevent curl/libcurl from using the protocol. -By default, libcurl prohibits redirects to file:// URLs. +By default, libcurl prohibits redirects to `file://` URLs. -# Warning: file:// on Windows +# Warning: `file://` on Windows The Windows operating system tries automatically, and without any way for applications to disable it, to establish a connection to another host over the diff --git a/docs/libcurl/libcurl-tutorial.md b/docs/libcurl/libcurl-tutorial.md index 704ee0356416..b5cfcf2922db 100644 --- a/docs/libcurl/libcurl-tutorial.md +++ b/docs/libcurl/libcurl-tutorial.md @@ -373,7 +373,7 @@ them URL encoded, as %XX where XX is a two-digit hexadecimal number. libcurl also provides options to set various passwords. The username and password as shown embedded in the URL can instead get set with the CURLOPT_USERPWD(3) option. The argument passed to libcurl should be a -char * to a string in the format "user:password". In a manner like this: +`char *` to a string in the format `"user:password"`. In a manner like this: ~~~c curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret"); @@ -391,7 +391,7 @@ CURLOPT_USERPWD(3) option, like this: There is a long time Unix "standard" way of storing FTP usernames and passwords, namely in the $HOME/.netrc file (on Windows, libcurl also checks the *%USERPROFILE% environment* variable if *%HOME%* is unset, and tries -"_netrc" as name). The file should be made private so that only the user may +`_netrc` as name). The file should be made private so that only the user may read it (see also the "Security Considerations" chapter), as it might contain the password in plain text. libcurl has the ability to use this file to figure out what set of username and password to use for a particular host. As an @@ -866,23 +866,23 @@ it defaults to assuming an HTTP proxy): libcurl automatically checks and uses a set of environment variables to know what proxies to use for certain protocols. The names of the variables are -following an old tradition and are built up as "[protocol]_proxy" (note the -lower casing). Which makes the variable 'http_proxy' checked for a name of a +following an old tradition and are built up as `[protocol]_proxy` (note the +lower casing). Which makes the variable `http_proxy` checked for a name of a proxy to use when the input URL is HTTP. Following the same rule, the variable -named 'ftp_proxy' is checked for FTP URLs. Again, the proxies are always HTTP +named `ftp_proxy` is checked for FTP URLs. Again, the proxies are always HTTP proxies, the different names of the variables allow different HTTP proxies to be used. The proxy environment variable contents should be in the format -"[protocol://][user:password@]machine[:port]". Where the protocol:// part +`[protocol://][user:password@]machine[:port]`. Where the `protocol://` part specifies which type of proxy it is, and the optional port number specifies on which port the proxy operates. If not specified, the internal default port number is used and that is most likely not the one you would like it to be. -There are two special environment variables. 'all_proxy' is what sets proxy -for any URL in case the protocol specific variable was not set, and 'no_proxy' +There are two special environment variables. `all_proxy` is what sets proxy +for any URL in case the protocol specific variable was not set, and `no_proxy` defines a list of hosts that should not use a proxy even though a variable may -say so. If 'no_proxy' is a plain asterisk ("*") it matches all hosts. +say so. If `no_proxy` is a plain asterisk (`*`) it matches all hosts. To explicitly disable libcurl's checking for and using the proxy environment variables, set the proxy name to "" - an empty string - with diff --git a/docs/libcurl/libcurl-ws.md b/docs/libcurl/libcurl-ws.md index 147740157fee..cce13abb243f 100644 --- a/docs/libcurl/libcurl-ws.md +++ b/docs/libcurl/libcurl-ws.md @@ -39,7 +39,7 @@ WebSocket is a TCP-like message-based communication protocol done over HTTP, specified in RFC 6455. To initiate a WebSocket session with libcurl, setup an easy handle to use a -URL with a "WS://" or "WSS://" scheme. "WS" is for cleartext communication +URL with a `ws://` or `wss://` scheme. "WS" is for cleartext communication over HTTP and "WSS" is for doing WebSocket securely over HTTPS. A WebSocket request is done as an HTTP/1 GET request with an "Upgrade diff --git a/docs/libcurl/opts/CURLOPT_HSTS.md b/docs/libcurl/opts/CURLOPT_HSTS.md index 2dc0c457d6cd..12efff50bc79 100644 --- a/docs/libcurl/opts/CURLOPT_HSTS.md +++ b/docs/libcurl/opts/CURLOPT_HSTS.md @@ -66,7 +66,7 @@ NULL, no filename # SECURITY CONCERNS -We strongly urge users to stick to `HTTPS://` URLs, which makes this option +We strongly urge users to stick to `https://` URLs, which makes this option unnecessary. libcurl cannot fully protect against attacks where an attacker has write diff --git a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md index 9a9117669d62..ee0c7e84bfc4 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md @@ -38,7 +38,7 @@ the given threshold over a period time. If you set *maxspeed* to a value lower than CURLOPT_BUFFERSIZE(3), libcurl might download faster than the set limit initially. -This option does not affect transfer speeds done with FILE:// URLs. +This option does not affect transfer speeds done with `file://` URLs. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md index 34566ece0ded..d64d324afd78 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md @@ -39,7 +39,7 @@ If you set *maxspeed* to a value lower than CURLOPT_UPLOAD_BUFFERSIZE(3), libcurl might "shoot over" the limit on its first send and still send off a full buffer. -This option does not affect transfer speeds done with FILE:// URLs. +This option does not affect transfer speeds done with `file://` URLs. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md index cb6731912c72..264ae897cd69 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md @@ -33,7 +33,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_NEW_DIRECTORY_PERMS, Pass a long as a parameter, containing the value of the permissions that is set on newly created directories on the remote server. The default value is *0755*, but any valid value can be used. The only protocols that can use -this are *sftp://*, *scp://*, and *file://*. +this are `sftp://`, `scp://`, and `file://`. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md index 4c9579c8264e..00766f1e7e44 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md @@ -31,7 +31,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_NEW_FILE_PERMS, Pass a long as a parameter, containing the value of the permissions that are set on newly created files on the remote server. The default value is *0644*. -The only protocols that can use this are *sftp://*, *scp://*, and *file://*. +The only protocols that can use this are `sftp://`, `scp://`, and `file://`. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_PRE_PROXY.md b/docs/libcurl/opts/CURLOPT_PRE_PROXY.md index 3b447fa907a7..06d0f320f48d 100644 --- a/docs/libcurl/opts/CURLOPT_PRE_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PRE_PROXY.md @@ -39,11 +39,11 @@ A pre proxy is a SOCKS proxy that curl connects to before it connects to the HTTP(S) proxy specified in the CURLOPT_PROXY(3) option. The pre proxy can only be a SOCKS proxy. -The pre proxy string should be prefixed with [scheme]:// to specify which kind -of socks is used. Use socks4://, socks4a://, socks5:// or socks5h:// (the last -one to enable socks5 and asking the proxy to do the resolving, also known as -*CURLPROXY_SOCKS5_HOSTNAME* type) to request the specific SOCKS version to -be used. Otherwise SOCKS4 is used as default. +The pre proxy string should be prefixed with `[scheme]://` to specify which +kind of socks is used. Use `socks4://`, `socks4a://`, `socks5://` or +`socks5h://` (the last one to enable socks5 and asking the proxy to do the +resolving, also known as *CURLPROXY_SOCKS5_HOSTNAME* type) to request the +specific SOCKS version to be used. Otherwise SOCKS4 is used as default. Setting the pre proxy string to "" (an empty string) explicitly disables the use of a pre proxy. diff --git a/docs/libcurl/opts/CURLOPT_PROXY.md b/docs/libcurl/opts/CURLOPT_PROXY.md index 3f8cf6bf5ddd..7be874d73332 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY.md @@ -33,12 +33,12 @@ should be a char * to a null-terminated string holding the hostname or dotted numerical IP address. A numerical IPv6 address must be written within [brackets]. -To specify port number in this string, append :[port] to the end of the host +To specify port number in this string, append `:[port]` to the end of the host name. The proxy's port number may optionally (but discouraged) be specified with the separate option CURLOPT_PROXYPORT(3). If not specified, libcurl defaults to using port 1080 for proxies. -The proxy string may be prefixed with [scheme]:// to specify which kind of +The proxy string may be prefixed with `[scheme]://` to specify which kind of proxy is used. Using this option multiple times makes the last set string override the @@ -47,30 +47,30 @@ previous ones. Set it to NULL to disable its use again. The application does not have to keep the string around after setting this option. -## http:// +## `http://` HTTP Proxy. Default when no scheme or proxy type is specified. -## https:// +## `https://` HTTPS Proxy. (with OpenSSL, GnuTLS, mbedTLS, Rustls, Schannel or wolfSSL.) This uses HTTP/1 by default. Setting CURLOPT_PROXYTYPE(3) to **CURLPROXY_HTTPS2** allows libcurl to negotiate using HTTP/2 with proxy. -## socks4:// +## `socks4://` SOCKS4 Proxy. -## socks4a:// +## `socks4a://` SOCKS4a Proxy. Proxy resolves URL hostname. -## socks5:// +## `socks5://` SOCKS5 Proxy. -## socks5h:// +## `socks5h://` SOCKS5 Proxy. Proxy resolves URL hostname. @@ -114,7 +114,7 @@ CURLOPT_PROXYPASSWORD(3). libcurl respects the proxy environment variables named **http_proxy**, **ftp_proxy**, **sftp_proxy** etc. If set, libcurl uses the specified proxy -for that URL scheme. For an "FTP://" URL, the **ftp_proxy** is +for that URL scheme. For an `ftp://` URL, the **ftp_proxy** is considered. **all_proxy** is used if no protocol specific proxy was set. If **no_proxy** (or **NO_PROXY**) is set, it is the exact equivalent of diff --git a/docs/libcurl/opts/CURLOPT_URL.md b/docs/libcurl/opts/CURLOPT_URL.md index 2ad4739391d8..b9e5e52b4f36 100644 --- a/docs/libcurl/opts/CURLOPT_URL.md +++ b/docs/libcurl/opts/CURLOPT_URL.md @@ -44,7 +44,7 @@ libcurl does not validate the syntax or use the URL until the transfer is started. Even if you set a crazy value here, curl_easy_setopt(3) might still return *CURLE_OK*. -If the given URL is missing a scheme name (such as "http://" or "ftp://" etc) +If the given URL is missing a scheme name (such as `http://` or `ftp://` etc) then libcurl guesses based on the host. If the outermost subdomain name matches DICT, FTP, IMAP, LDAP, POP3 or SMTP then that protocol gets used, otherwise HTTP is used. Scheme guessing can be disabled by setting a default @@ -111,7 +111,7 @@ are part of the regular URL format. The combination of a local host and a custom port number can allow external users to play tricks with your local services. -Accepting external URLs may also use other protocols than http:// or other +Accepting external URLs may also use other protocols than `http://` or other common ones. Restrict what accept with CURLOPT_PROTOCOLS_STR(3). User provided URLs can also be made to point to sites that redirect further on diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index f3121fd3c9ad..26e32ecd7fa9 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -200,8 +200,8 @@ Available substitute variables include: - `%SOCKSPORT` - Port number of the SOCKS4/5 server - `%SOCKSUNIXPATH` - Path to the Unix socket of the SOCKS server - `%SRCDIR` - Full path to the source dir -- `%SCP_PWD` - Current directory friendly for the SSH server for the scp:// protocol -- `%SFTP_PWD` - Current directory friendly for the SSH server for the sftp:// protocol +- `%SCP_PWD` - Current directory friendly for the SSH server for the `scp://` protocol +- `%SFTP_PWD` - Current directory friendly for the SSH server for the `sftp://` protocol - `%SSHKEYALGO` - SSH host and client key algorithm, e.g. `ssh-rsa` or `ssh-ed25519` - `%SSHPORT` - Port number of the SCP/SFTP server - `%SSHSRVMD5` - MD5 of SSH server's public key diff --git a/lib/cw-out.c b/lib/cw-out.c index 2af074e5871c..fad89b392cc8 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -195,7 +195,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, if(nwritten == CURL_WRITEFUNC_PAUSE) { if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is - actually only FILE:// now, and it cannot pause since the transfer is + actually only file:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; diff --git a/lib/sendf.c b/lib/sendf.c index 7c977d3b9ea5..c5936514a100 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -697,7 +697,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, case CURL_READFUNC_PAUSE: if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// now, and it cannot pause since the transfer + actually only file:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); result = CURLE_READ_ERROR; From 000de81fb1ea3418d097b1006345aeb7c9ed51c6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 19 May 2026 14:49:28 +0200 Subject: [PATCH 0159/1062] tidy-up: rename more `CURLcode` variables to `result` Follow-up to 885b553545a74365f4fc2541a0829f7745e80d37 #21348 Closes #21676 --- lib/http_aws_sigv4.c | 20 ++--- lib/mime.c | 47 +++++----- lib/multi.c | 10 +-- lib/multi_ev.c | 6 +- lib/thrdpool.c | 6 +- lib/vquic/curl_ngtcp2.c | 38 ++++---- lib/vtls/vtls_scache.c | 176 +++++++++++++++++++------------------ lib/vtls/vtls_spack.c | 144 +++++++++++++++--------------- projects/OS400/ccsidcurl.c | 20 ++--- tests/unit/unit3300.c | 54 ++++++------ tests/unit/unit3301.c | 30 +++---- 11 files changed, 277 insertions(+), 274 deletions(-) diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index f77f7a088c07..ed5dcf8f8ff0 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -384,7 +384,7 @@ static CURLcode make_headers(struct Curl_easy *data, char date_full_hdr[DATE_FULL_HDR_LEN]; struct curl_slist *head = NULL; struct curl_slist *tmp_head = NULL; - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct curl_slist *l; bool again = TRUE; @@ -516,8 +516,8 @@ static CURLcode make_headers(struct Curl_easy *data, } } while(again); - ret = merge_duplicate_headers(head); - if(ret) + result = merge_duplicate_headers(head); + if(result) goto fail; for(l = head; l; l = l->next) { @@ -540,11 +540,11 @@ static CURLcode make_headers(struct Curl_easy *data, goto fail; } - ret = CURLE_OK; + result = CURLE_OK; fail: curl_slist_free_all(head); - return ret; + return result; } #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) @@ -618,12 +618,12 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, bool empty_payload = (empty_method || data->set.filesize == 0); /* The POST payload is in memory */ bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; if(empty_payload || post_payload) { /* Calculate a real hash when we know the request payload */ - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; } else { @@ -638,9 +638,9 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, curl_msnprintf(header, CONTENT_SHA256_HDR_LEN, "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); - ret = CURLE_OK; + result = CURLE_OK; fail: - return ret; + return result; } static int compare_func(const void *a, const void *b) diff --git a/lib/mime.c b/lib/mime.c index a984254731bd..c15807a2e94e 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1123,7 +1123,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, curl_mime *mime; curl_mimepart *d; const curl_mimepart *s; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; DEBUGASSERT(dst); @@ -1132,66 +1132,67 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, case MIMEKIND_NONE: break; case MIMEKIND_DATA: - res = curl_mime_data(dst, src->data, (size_t)src->datasize); + result = curl_mime_data(dst, src->data, (size_t)src->datasize); break; case MIMEKIND_FILE: - res = curl_mime_filedata(dst, src->data); + result = curl_mime_filedata(dst, src->data); /* Do not abort duplication if file is not readable. */ - if(res == CURLE_READ_ERROR) - res = CURLE_OK; + if(result == CURLE_READ_ERROR) + result = CURLE_OK; break; case MIMEKIND_CALLBACK: - res = curl_mime_data_cb(dst, src->datasize, src->readfunc, - src->seekfunc, src->freefunc, src->arg); + result = curl_mime_data_cb(dst, src->datasize, src->readfunc, + src->seekfunc, src->freefunc, src->arg); break; case MIMEKIND_MULTIPART: /* No one knows about the cloned subparts, thus always attach ownership to the part. */ mime = curl_mime_init(data); - res = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; + result = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; /* Duplicate subparts. */ - for(s = ((curl_mime *)src->arg)->firstpart; !res && s; s = s->nextpart) { + for(s = ((curl_mime *)src->arg)->firstpart; !result && s; + s = s->nextpart) { d = curl_mime_addpart(mime); - res = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; + result = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; } break; default: /* Invalid kind: should not occur. */ DEBUGF(infof(data, "invalid MIMEKIND* attempt")); - res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ + result = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ break; } /* Duplicate headers. */ - if(!res && src->userheaders) { + if(!result && src->userheaders) { struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); if(!hdrs) - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { /* No one but this procedure knows about the new header list, so always take ownership. */ - res = curl_mime_headers(dst, hdrs, TRUE); - if(res) + result = curl_mime_headers(dst, hdrs, TRUE); + if(result) curl_slist_free_all(hdrs); } } - if(!res) { + if(!result) { /* Duplicate other fields. */ dst->encoder = src->encoder; - res = curl_mime_type(dst, src->mimetype); + result = curl_mime_type(dst, src->mimetype); } - if(!res) - res = curl_mime_name(dst, src->name); - if(!res) - res = curl_mime_filename(dst, src->filename); + if(!result) + result = curl_mime_name(dst, src->name); + if(!result) + result = curl_mime_filename(dst, src->filename); /* If an error occurred, rollback. */ - if(res) + if(result) Curl_mime_cleanpart(dst); - return res; + return result; } /* diff --git a/lib/multi.c b/lib/multi.c index b2618b4b32ea..202a30b8a197 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2648,17 +2648,17 @@ static CURLMcode multistate_did(struct Curl_multi *multi, return CURLM_CALL_MULTI_PERFORM; } -static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *result) +static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *presult) { if(data->conn) { - CURLcode res; + CURLcode result; /* post-transfer command */ - res = multi_done(data, *result, FALSE); + result = multi_done(data, *presult, FALSE); /* allow a previously set error code take precedence */ - if(!(*result)) - *result = res; + if(!(*presult)) + *presult = result; } #ifndef CURL_DISABLE_FTP diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 937e7ce48d32..478d5a48d55e 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -486,9 +486,9 @@ static CURLMcode mev_assess(struct Curl_multi *multi, Curl_pollset_init(&ps); if(conn) { - CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); - if(r) { - mresult = (r == CURLE_OUT_OF_MEMORY) ? + CURLcode result = Curl_conn_adjust_pollset(data, conn, &ps); + if(result) { + mresult = (result == CURLE_OUT_OF_MEMORY) ? CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; goto out; } diff --git a/lib/thrdpool.c b/lib/thrdpool.c index 22faa396ed66..e8c7d3643551 100644 --- a/lib/thrdpool.c +++ b/lib/thrdpool.c @@ -130,9 +130,9 @@ static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) * on activating threads that have no means to shut down. */ if((tpool->idle_time_ms > 0) && (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { - CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock, - tpool->idle_time_ms); - if((r == CURLE_OPERATION_TIMEDOUT) && + CURLcode result = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((result == CURLE_OPERATION_TIMEDOUT) && (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { goto out; } diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 2f5cae511699..4d27ebc0c197 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -2016,7 +2016,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, size_t pktcnt = 0; size_t gsolen = 0; /* this disables gso until we have a clue */ size_t send_quantum; - CURLcode curlcode; + CURLcode result; struct pkt_io_ctx local_pktx; if(!pktx) { @@ -2028,13 +2028,13 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, ngtcp2_path_storage_zero(&pktx->ps); } - curlcode = vquic_flush(cf, data, &ctx->q); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } /* In UDP, there is a maximum theoretical packet payload length and @@ -2056,12 +2056,12 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, send_quantum); for(;;) { /* add the next packet to send, if any, to our buffer */ - curlcode = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, pktx, &nread); - if(curlcode == CURLE_AGAIN) + result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &nread); + if(result == CURLE_AGAIN) break; - else if(curlcode) - return curlcode; + else if(result) + return result; else { size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); if((buflen >= send_quantum) || @@ -2079,14 +2079,14 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* The added packet is a PMTUD *or* the one(s) before the * added were PMTUD and the last one is smaller. * Flush the buffer before the last add. */ - curlcode = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } pktcnt = 0; } @@ -2102,13 +2102,13 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* time to send */ CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } pktx_update_time(data, pktx, cf); ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 900a2b90a037..88593b20ae27 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -85,10 +85,10 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, if(path[0] != '/') { char *abspath = realpath(path, NULL); if(abspath) { - CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + CURLcode result = curlx_dyn_addf(buf, ":%s-%s", name, abspath); /* !checksrc! disable BANNEDFUNC 1 */ free(abspath); /* allocated by libc, free without memdebug */ - return r; + return result; } *is_local = TRUE; } @@ -102,25 +102,25 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, const char *name, struct curl_blob *blob) { - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; if(blob && blob->len) { unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; size_t i; - r = curlx_dyn_addf(buf, ":%s-", name); - if(r) + result = curlx_dyn_addf(buf, ":%s-", name); + if(result) goto out; - r = Curl_sha256it(hash, blob->data, blob->len); - if(r) + result = Curl_sha256it(hash, blob->data, blob->len); + if(result) goto out; for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { - r = curlx_dyn_addf(buf, "%02x", hash[i]); - if(r) + result = curlx_dyn_addf(buf, "%02x", hash[i]); + if(result) goto out; } } out: - return r; + return result; } #define CURL_SSLS_LOCAL_SUFFIX ":L" @@ -143,151 +143,153 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, struct dynbuf buf; size_t key_len; bool is_local = FALSE; - CURLcode r; + CURLcode result; *ppeer_key = NULL; curlx_dyn_init(&buf, 10 * 1024); - r = curlx_dyn_addf(&buf, "%s:%d", - peer->dest->hostname, peer->dest->port); - if(r) + result = curlx_dyn_addf(&buf, "%s:%d", + peer->dest->hostname, peer->dest->port); + if(result) goto out; switch(peer->transport) { case TRNSPRT_TCP: break; case TRNSPRT_UDP: - r = curlx_dyn_add(&buf, ":UDP"); + result = curlx_dyn_add(&buf, ":UDP"); break; case TRNSPRT_QUIC: - r = curlx_dyn_add(&buf, ":QUIC"); + result = curlx_dyn_add(&buf, ":QUIC"); break; case TRNSPRT_UNIX: - r = curlx_dyn_add(&buf, ":UNIX"); + result = curlx_dyn_add(&buf, ":UNIX"); break; default: - r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); + result = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); break; } - if(r) + if(result) goto out; if(!ssl->verifypeer) { - r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); - if(r) + result = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); + if(result) goto out; } if(!ssl->verifyhost) { - r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); - if(r) + result = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); + if(result) goto out; } if(ssl->verifystatus) { - r = curlx_dyn_add(&buf, ":VRFY-STATUS"); - if(r) + result = curlx_dyn_add(&buf, ":VRFY-STATUS"); + if(result) goto out; } if(!ssl->verifypeer || !ssl->verifyhost) { if(cf->conn->via_peer) { - r = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", - cf->conn->via_peer->hostname, - cf->conn->via_peer->port); - if(r) + result = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", + cf->conn->via_peer->hostname, + cf->conn->via_peer->port); + if(result) goto out; } } if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, - (ssl->version_max >> 16)); - if(r) + result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, + (ssl->version_max >> 16)); + if(result) goto out; } if(ssl->ssl_options) { - r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); - if(r) + result = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(result) goto out; } if(ssl->cipher_list) { - r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(result) goto out; } if(ssl->cipher_list13) { - r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(result) goto out; } if(ssl->curves) { - r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); - if(r) + result = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(result) goto out; } if(ssl->signature_algorithms) { - r = curlx_dyn_addf(&buf, ":SIGALGS-%s", - ssl->signature_algorithms); - if(r) + result = curlx_dyn_addf(&buf, ":SIGALGS-%s", + ssl->signature_algorithms); + if(result) goto out; } if(ssl->verifypeer) { - r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, + &is_local); + if(result) goto out; if(ssl->ca_info_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(result) goto out; } if(ssl->issuercert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", + ssl->issuercert_blob); + if(result) goto out; } } if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(result) goto out; } if(ssl->pinned_key && ssl->pinned_key[0]) { - r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); - if(r) + result = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(result) goto out; } if(ssl->clientcert && ssl->clientcert[0]) { - r = curlx_dyn_add(&buf, ":CCERT"); - if(r) + result = curlx_dyn_add(&buf, ":CCERT"); + if(result) goto out; } #ifdef USE_TLS_SRP if(ssl->username || ssl->password) { - r = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(r) + result = curlx_dyn_add(&buf, ":SRP-AUTH"); + if(result) goto out; } #endif if(!tls_id || !tls_id[0]) { - r = CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; goto out; } - r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); - if(r) + result = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(result) goto out; - r = curlx_dyn_addf(&buf, is_local ? - CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); - if(r) + result = curlx_dyn_addf(&buf, is_local ? + CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); + if(result) goto out; *ppeer_key = curlx_dyn_take(&buf, &key_len); @@ -296,7 +298,7 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, out: curlx_dyn_free(&buf); - return r; + return result; } struct Curl_ssl_scache { @@ -1176,7 +1178,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, struct Curl_llist_node *n; size_t i; curl_off_t now = time(NULL); - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; #ifdef CURLVERBOSE size_t npeers = 0, ntickets = 0; #endif @@ -1204,35 +1206,35 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, while(n) { struct Curl_ssl_session *s = Curl_node_elem(n); if(!peer->hmac_set) { - r = cf_ssl_scache_peer_set_hmac(peer); - if(r) + result = cf_ssl_scache_peer_set_hmac(peer); + if(result) goto out; } if(!curlx_dyn_len(&hbuf)) { - r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); + if(result) goto out; - r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); + if(result) goto out; } curlx_dyn_reset(&sbuf); - r = Curl_ssl_session_pack(data, s, &sbuf); - if(r) + result = Curl_ssl_session_pack(data, s, &sbuf); + if(result) goto out; - r = export_fn(data, userptr, peer->ssl_peer_key, - curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), - curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), - s->valid_until, s->ietf_tls_id, - s->alpn, s->earlydata_max); - if(r) + result = export_fn(data, userptr, peer->ssl_peer_key, + curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), + curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), + s->valid_until, s->ietf_tls_id, + s->alpn, s->earlydata_max); + if(result) goto out; VERBOSE(++ntickets); n = Curl_node_next(n); } } - r = CURLE_OK; + result = CURLE_OK; CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers", ntickets, npeers); @@ -1240,7 +1242,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, Curl_ssl_scache_unlock(data); curlx_dyn_free(&hbuf); curlx_dyn_free(&sbuf); - return r; + return result; } #endif /* USE_SSLS_EXPORT */ diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index d96f4e41bd72..d633dcba4aef 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -130,26 +130,26 @@ static CURLcode spack_dec64(uint64_t *val, const uint8_t **src, static CURLcode spack_encstr16(struct dynbuf *buf, const char *s) { size_t slen = strlen(s); - CURLcode r; + CURLcode result; if(slen > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)slen); - if(!r) { - r = curlx_dyn_addn(buf, s, slen); + result = spack_enc16(buf, (uint16_t)slen); + if(!result) { + result = curlx_dyn_addn(buf, s, slen); } - return r; + return result; } static CURLcode spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) { uint16_t slen; - CURLcode r; + CURLcode result; *val = NULL; - r = spack_dec16(&slen, src, end); - if(r) - return r; + result = spack_dec16(&slen, src, end); + if(result) + return result; if(end - *src < slen) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), slen); @@ -160,26 +160,26 @@ static CURLcode spack_decstr16(char **val, const uint8_t **src, static CURLcode spack_encdata16(struct dynbuf *buf, const uint8_t *data, size_t data_len) { - CURLcode r; + CURLcode result; if(data_len > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)data_len); - if(!r) { - r = curlx_dyn_addn(buf, data, data_len); + result = spack_enc16(buf, (uint16_t)data_len); + if(!result) { + result = curlx_dyn_addn(buf, data, data_len); } - return r; + return result; } static CURLcode spack_decdata16(uint8_t **val, size_t *val_len, const uint8_t **src, const uint8_t *end) { uint16_t data_len; - CURLcode r; + CURLcode result; *val = NULL; - r = spack_dec16(&data_len, src, end); - if(r) - return r; + result = spack_dec16(&data_len, src, end); + if(result) + return result; if(end - *src < data_len) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), data_len); @@ -192,48 +192,48 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, struct Curl_ssl_session *s, struct dynbuf *buf) { - CURLcode r; + CURLcode result; DEBUGASSERT(s->sdata); DEBUGASSERT(s->sdata_len); if(s->valid_until < 0) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc8(buf, CURL_SPACK_VERSION); - if(!r) - r = spack_enc8(buf, CURL_SPACK_TICKET); - if(!r) - r = spack_encdata16(buf, s->sdata, s->sdata_len); - if(!r) - r = spack_enc8(buf, CURL_SPACK_IETF_ID); - if(!r) - r = spack_enc16(buf, (uint16_t)s->ietf_tls_id); - if(!r) - r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); - if(!r) - r = spack_enc64(buf, (uint64_t)s->valid_until); - if(!r && s->alpn) { - r = spack_enc8(buf, CURL_SPACK_ALPN); - if(!r) - r = spack_encstr16(buf, s->alpn); + result = spack_enc8(buf, CURL_SPACK_VERSION); + if(!result) + result = spack_enc8(buf, CURL_SPACK_TICKET); + if(!result) + result = spack_encdata16(buf, s->sdata, s->sdata_len); + if(!result) + result = spack_enc8(buf, CURL_SPACK_IETF_ID); + if(!result) + result = spack_enc16(buf, (uint16_t)s->ietf_tls_id); + if(!result) + result = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); + if(!result) + result = spack_enc64(buf, (uint64_t)s->valid_until); + if(!result && s->alpn) { + result = spack_enc8(buf, CURL_SPACK_ALPN); + if(!result) + result = spack_encstr16(buf, s->alpn); } - if(!r && s->earlydata_max) { + if(!result && s->earlydata_max) { if(s->earlydata_max > UINT32_MAX) - r = CURLE_BAD_FUNCTION_ARGUMENT; - if(!r) - r = spack_enc8(buf, CURL_SPACK_EARLYDATA); - if(!r) - r = spack_enc32(buf, (uint32_t)s->earlydata_max); + result = CURLE_BAD_FUNCTION_ARGUMENT; + if(!result) + result = spack_enc8(buf, CURL_SPACK_EARLYDATA); + if(!result) + result = spack_enc32(buf, (uint32_t)s->earlydata_max); } - if(!r && s->quic_tp && s->quic_tp_len) { - r = spack_enc8(buf, CURL_SPACK_QUICTP); - if(!r) - r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); + if(!result && s->quic_tp && s->quic_tp_len) { + result = spack_enc8(buf, CURL_SPACK_QUICTP); + if(!result) + result = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); } - if(r) - CURL_TRC_SSLS(data, "error packing data: %d", r); - return r; + if(result) + CURL_TRC_SSLS(data, "error packing data: %d", result); + return result; } CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, @@ -247,83 +247,83 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, uint16_t val16; uint32_t val32; uint64_t val64; - CURLcode r; + CURLcode result; DEBUGASSERT(buf); DEBUGASSERT(buflen); *ps = NULL; - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; if(val8 != CURL_SPACK_VERSION) { - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } s = curlx_calloc(1, sizeof(*s)); if(!s) { - r = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto out; } while(buf < end) { - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; switch(val8) { case CURL_SPACK_ALPN: - r = spack_decstr16(&s->alpn, &buf, end); - if(r) + result = spack_decstr16(&s->alpn, &buf, end); + if(result) goto out; break; case CURL_SPACK_EARLYDATA: - r = spack_dec32(&val32, &buf, end); - if(r) + result = spack_dec32(&val32, &buf, end); + if(result) goto out; s->earlydata_max = val32; break; case CURL_SPACK_IETF_ID: - r = spack_dec16(&val16, &buf, end); - if(r) + result = spack_dec16(&val16, &buf, end); + if(result) goto out; s->ietf_tls_id = val16; break; case CURL_SPACK_QUICTP: { - r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + if(result) goto out; s->quic_tp = pval8; break; } case CURL_SPACK_TICKET: { - r = spack_decdata16(&pval8, &s->sdata_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + if(result) goto out; s->sdata = pval8; break; } case CURL_SPACK_VALID_UNTIL: - r = spack_dec64(&val64, &buf, end); - if(r) + result = spack_dec64(&val64, &buf, end); + if(result) goto out; s->valid_until = (curl_off_t)val64; break; default: /* unknown tag */ - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } } out: - if(r) { - CURL_TRC_SSLS(data, "error unpacking data: %d", r); + if(result) { + CURL_TRC_SSLS(data, "error unpacking data: %d", result); Curl_ssl_session_destroy(s); } else *ps = s; - return r; + return result; } #endif /* USE_SSL && USE_SSLS_EXPORT */ diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index 0982eed639f7..a25197c1f2d0 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -537,7 +537,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; - CURLcode ret; + CURLcode result; struct Curl_easy *data; /* WARNING: unlike curl_easy_getinfo(), the strings returned by this @@ -546,9 +546,9 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) data = (struct Curl_easy *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - ret = Curl_getinfo(data, info, paramp); + result = Curl_getinfo(data, info, paramp); - if(ret == CURLE_OK) { + if(result == CURLE_OK) { unsigned int ccsid; char **cpp; struct curl_slist **slp; @@ -565,7 +565,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) *cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID, NULL); if(!*cpp) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } break; @@ -578,13 +578,13 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(cipf) { cipt = (struct curl_certinfo *)malloc(sizeof(*cipt)); if(!cipt) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { cipt->certinfo = (struct curl_slist **)calloc(cipf->num_of_certs + 1, sizeof(struct curl_slist *)); if(!cipt->certinfo) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { int i; @@ -594,13 +594,13 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(!(cipt->certinfo[i] = slist_convert(ccsid, cipf->certinfo[i], ASCII_CCSID))) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; break; } } } - if(ret != CURLE_OK) { + if(result != CURLE_OK) { curl_certinfo_free_all(cipt); cipt = (struct curl_certinfo *)NULL; } @@ -620,7 +620,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(*slp) { *slp = slist_convert(ccsid, *slp, ASCII_CCSID); if(!*slp) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } break; } @@ -628,7 +628,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) } va_end(arg); - return ret; + return result; } static int Curl_is_formadd_string(CURLformoption option) diff --git a/tests/unit/unit3300.c b/tests/unit/unit3300.c index ebef6d722889..a010f43922c4 100644 --- a/tests/unit/unit3300.c +++ b/tests/unit/unit3300.c @@ -82,26 +82,26 @@ static CURLcode test_unit3300(const char *arg) UNITTEST_BEGIN_SIMPLE struct curl_thrdpool *tpool; struct unit3300_ctx ctx; - CURLcode r; + CURLcode result; /* pool without minimum, will not start anything */ unit3300_ctx_init(&ctx, 10, 0); - r = Curl_thrdpool_create(&tpool, "unit3300a", 0, 2, 0, - unit3300_take, unit3300_process, unit3300_return, - &ctx); - fail_unless(!r, "pool-a create"); + result = Curl_thrdpool_create(&tpool, "unit3300a", 0, 2, 0, + unit3300_take, unit3300_process, + unit3300_return, &ctx); + fail_unless(!result, "pool-a create"); Curl_thrdpool_destroy(tpool, TRUE); fail_unless(!ctx.returned, "pool-a unexpected items returned"); fail_unless(!ctx.taken, "pool-a unexpected items taken"); /* pool without minimum, signal start, consumes everything */ unit3300_ctx_init(&ctx, 10, 0); - r = Curl_thrdpool_create(&tpool, "unit3300b", 0, 2, 0, - unit3300_take, unit3300_process, unit3300_return, - &ctx); - fail_unless(!r, "pool-b create"); - r = Curl_thrdpool_signal(tpool, 2); - fail_unless(!r, "pool-b signal"); + result = Curl_thrdpool_create(&tpool, "unit3300b", 0, 2, 0, + unit3300_take, unit3300_process, + unit3300_return, &ctx); + fail_unless(!result, "pool-b create"); + result = Curl_thrdpool_signal(tpool, 2); + fail_unless(!result, "pool-b signal"); Curl_thrdpool_await_idle(tpool, 0); Curl_thrdpool_destroy(tpool, TRUE); fail_unless(ctx.returned == ctx.total, "pool-b items returned missing"); @@ -109,10 +109,10 @@ static CURLcode test_unit3300(const char *arg) /* pool with minimum, consumes everything without signal */ unit3300_ctx_init(&ctx, 10, 0); - r = Curl_thrdpool_create(&tpool, "unit3300c", 1, 2, 0, - unit3300_take, unit3300_process, unit3300_return, - &ctx); - fail_unless(!r, "pool-c create"); + result = Curl_thrdpool_create(&tpool, "unit3300c", 1, 2, 0, + unit3300_take, unit3300_process, + unit3300_return, &ctx); + fail_unless(!result, "pool-c create"); Curl_thrdpool_await_idle(tpool, 0); Curl_thrdpool_destroy(tpool, TRUE); fail_unless(ctx.returned == ctx.total, "pool-c items returned missing"); @@ -120,12 +120,12 @@ static CURLcode test_unit3300(const char *arg) /* pool with many max, signal abundance, consumes everything */ unit3300_ctx_init(&ctx, 100, 0); - r = Curl_thrdpool_create(&tpool, "unit3300d", 0, 50, 0, - unit3300_take, unit3300_process, unit3300_return, - &ctx); - fail_unless(!r, "pool-d create"); - r = Curl_thrdpool_signal(tpool, 100); - fail_unless(!r, "pool-d signal"); + result = Curl_thrdpool_create(&tpool, "unit3300d", 0, 50, 0, + unit3300_take, unit3300_process, + unit3300_return, &ctx); + fail_unless(!result, "pool-d create"); + result = Curl_thrdpool_signal(tpool, 100); + fail_unless(!result, "pool-d signal"); Curl_thrdpool_await_idle(tpool, 0); Curl_thrdpool_destroy(tpool, TRUE); fail_unless(ctx.returned == ctx.total, "pool-d items returned missing"); @@ -133,12 +133,12 @@ static CURLcode test_unit3300(const char *arg) /* pool with 1 max, many to take, no await, destroy without join */ unit3300_ctx_init(&ctx, 10000000, 1); - r = Curl_thrdpool_create(&tpool, "unit3300e", 0, 1, 0, - unit3300_take, unit3300_process, unit3300_return, - &ctx); - fail_unless(!r, "pool-e create"); - r = Curl_thrdpool_signal(tpool, 100); - fail_unless(!r, "pool-e signal"); + result = Curl_thrdpool_create(&tpool, "unit3300e", 0, 1, 0, + unit3300_take, unit3300_process, + unit3300_return, &ctx); + fail_unless(!result, "pool-e create"); + result = Curl_thrdpool_signal(tpool, 100); + fail_unless(!result, "pool-e signal"); Curl_thrdpool_destroy(tpool, FALSE); fail_unless(ctx.returned < ctx.total, "pool-e returned all"); fail_unless(ctx.taken < ctx.total, "pool-e took all"); diff --git a/tests/unit/unit3301.c b/tests/unit/unit3301.c index 94b435da4956..472a14befad6 100644 --- a/tests/unit/unit3301.c +++ b/tests/unit/unit3301.c @@ -84,14 +84,14 @@ static CURLcode test_unit3301(const char *arg) struct curl_thrdq *tqueue; struct unit3301_ctx ctx; int i, count, nrecvd; - CURLcode r; + CURLcode result; /* create and teardown queue */ memset(&ctx, 0, sizeof(ctx)); - r = Curl_thrdq_create(&tqueue, "unit3301-a", 0, 0, 2, 1, - unit3301_item_free, unit3301_process, unit3301_event, - &ctx); - fail_unless(!r, "queue-a create"); + result = Curl_thrdq_create(&tqueue, "unit3301-a", 0, 0, 2, 1, + unit3301_item_free, unit3301_process, + unit3301_event, &ctx); + fail_unless(!result, "queue-a create"); Curl_thrdq_destroy(tqueue, TRUE); tqueue = NULL; fail_unless(!ctx.event, "queue-a unexpected done count"); @@ -99,25 +99,25 @@ static CURLcode test_unit3301(const char *arg) /* create queue, have it process `count` items */ count = 10; memset(&ctx, 0, sizeof(ctx)); - r = Curl_thrdq_create(&tqueue, "unit3301-b", 0, 0, 2, 1, - unit3301_item_free, unit3301_process, unit3301_event, - &ctx); - fail_unless(!r, "queue-b create"); + result = Curl_thrdq_create(&tqueue, "unit3301-b", 0, 0, 2, 1, + unit3301_item_free, unit3301_process, + unit3301_event, &ctx); + fail_unless(!result, "queue-b create"); for(i = 0; i < count; ++i) { struct unit3301_item *uitem = unit3301_item_create(i); fail_unless(uitem, "queue-b item create"); - r = Curl_thrdq_send(tqueue, uitem, NULL, 0); - fail_unless(!r, "queue-b send"); + result = Curl_thrdq_send(tqueue, uitem, NULL, 0); + fail_unless(!result, "queue-b send"); } - r = thrdq_await_done(tqueue, 0); - fail_unless(!r, "queue-b await done"); + result = thrdq_await_done(tqueue, 0); + fail_unless(!result, "queue-b await done"); nrecvd = 0; for(i = 0; i < count; ++i) { void *item; - r = Curl_thrdq_recv(tqueue, &item); - fail_unless(!r, "queue-b recv"); + result = Curl_thrdq_recv(tqueue, &item); + fail_unless(!result, "queue-b recv"); if(item) { struct unit3301_item *uitem = item; curl_mfprintf(stderr, "received item %d\n", uitem->id); From d99dcfb04a70fa0e49a7745de63ed6041e5b67f5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 19 May 2026 15:04:46 +0200 Subject: [PATCH 0160/1062] BUFQ.md: re-sync with source code Also: - move bullet points out from C code fences. - fold long lines. Follow-up to d4983ffc134addd20bea18987dec7c3b771e74a4 #17396 Closes #21678 --- docs/internals/BUFQ.md | 47 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/docs/internals/BUFQ.md b/docs/internals/BUFQ.md index 9d926537d66a..a977fcc8bdc1 100644 --- a/docs/internals/BUFQ.md +++ b/docs/internals/BUFQ.md @@ -15,26 +15,31 @@ Its basic read/write functions have a similar signature and return code handling as many internal curl read and write ones. ```c -ssize_t Curl_bufq_write(struct bufq *q, const unsigned char *buf, size_t len, CURLcode *err); - -- returns the length written into `q` or -1 on error. -- writing to a full `q` returns -1 and set *err to CURLE_AGAIN - -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode *err); +CURLcode Curl_bufq_write(struct bufq *q, + const uint8_t *buf, size_t len, + size_t *pnwritten); +``` -- returns the length read from `q` or -1 on error. -- reading from an empty `q` returns -1 and set *err to CURLE_AGAIN +- sets `pnwritten` to the length written into `q` or -1 on error. +- writing to a full `q` sets `pnwritten` to -1 and returns CURLE_AGAIN ``` +CURLcode Curl_bufq_read(struct bufq *q, uint8_t *buf, size_t len, + size_t *pnread); +``` + +- sets `pnread` to the length read from `q` or -1 on error. +- reading from an empty `q` sets `pnread` to -1 and returns CURLE_AGAIN To pass data into a `bufq` without an extra copy, read callbacks can be used. ```c -typedef ssize_t Curl_bufq_reader(void *reader_ctx, unsigned char *buf, size_t len, - CURLcode *err); +typedef CURLcode Curl_bufq_reader(void *reader_ctx, + uint8_t *buf, size_t len, + size_t *pnread); -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err); +CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, size_t *pnread); ``` `Curl_bufq_slurp()` invokes the given `reader` callback, passing it its own @@ -46,11 +51,12 @@ once or only read in a maximum amount of bytes. The analog mechanism for write out buffer data is: ```c -typedef ssize_t Curl_bufq_writer(void *writer_ctx, const unsigned char *buf, size_t len, - CURLcode *err); +typedef CURLcode Curl_bufq_writer(void *writer_ctx, + const uint8_t *buf, size_t len, + size_t *pwritten); -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err); +CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, size_t *pwritten); ``` `Curl_bufq_pass()` invokes the `writer`, passing its internal memory and @@ -61,7 +67,8 @@ remove the amount that `writer` reports. It is possible to get access to the memory of data stored in a `bufq` with: ```c -bool Curl_bufq_peek(const struct bufq *q, const unsigned char **pbuf, size_t *plen); +bool Curl_bufq_peek(struct bufq *q, + const uint8_t **pbuf, size_t *plen); ``` On returning TRUE, `pbuf` points to internal memory with `plen` bytes that one @@ -156,9 +163,11 @@ A `struct bufc_pool` may be used to create chunks for a `bufq` and keep spare ones around. It is initialized and used via: ```c -void Curl_bufcp_init(struct bufc_pool *pool, size_t chunk_size, size_t spare_max); +void Curl_bufcp_init(struct bufc_pool *pool, + size_t chunk_size, size_t spare_max); -void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, size_t max_chunks, int opts); +void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, + size_t max_chunks, int opts); ``` The pool gets the size and the mount of spares to keep. The `bufq` gets the From 5c225384b8d52c67ce8259c6e4203bc57aacb567 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 18 May 2026 23:47:11 +0200 Subject: [PATCH 0161/1062] url: detect proxy changes read from environment When a proxy is set from an environment variable, detect if that proxy is not the same as previously and flush state. Verified by test1647: verify changing proxy with env variables and make sure Digest state is flushed in the second use Closes #21666 --- lib/url.c | 11 ++++ lib/urldata.h | 1 + tests/data/Makefile.am | 8 +-- tests/data/test1647 | 103 +++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib1647.c | 120 +++++++++++++++++++++++++++++++++++++ 6 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 tests/data/test1647 create mode 100644 tests/libtest/lib1647.c diff --git a/lib/url.c b/lib/url.c index a569c3e4bc10..31f5d948d850 100644 --- a/lib/url.c +++ b/lib/url.c @@ -305,6 +305,9 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_freeset(data); Curl_headers_cleanup(data); Curl_netrc_cleanup(&data->state.netrc); +#ifndef CURL_DISABLE_DIGEST_AUTH + curlx_free(data->state.envproxy); +#endif curlx_free(data); return CURLE_OK; } @@ -2007,6 +2010,14 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, result = CURLE_UNSUPPORTED_PROTOCOL; goto out; #else +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!Curl_safecmp(data->state.envproxy, proxy)) { + /* proxy changed */ + Curl_auth_digest_cleanup(&data->state.proxydigest); + curlx_free(data->state.envproxy); + data->state.envproxy = curlx_strdup(proxy); + } +#endif /* force this connection's protocol to become HTTP if compatible */ if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && diff --git a/lib/urldata.h b/lib/urldata.h index 8b85c674a9a0..85630b05b7b0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -671,6 +671,7 @@ struct UrlState { void (*prev_signal)(int sig); #endif #ifndef CURL_DISABLE_DIGEST_AUTH + char *envproxy; /* last proxy string used for proxy-related state */ struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b330af3b90f9..a3778bdad1d3 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -214,11 +214,9 @@ test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ -test1628 test1629 \ -\ -test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ -test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 \ -test1646 \ +test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 \ +test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 \ +test1644 test1645 test1646 test1647 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1647 b/tests/data/test1647 new file mode 100644 index 000000000000..a87487fa9f0c --- /dev/null +++ b/tests/data/test1647 @@ -0,0 +1,103 @@ + + + + +HTTP +HTTP GET +HTTP proxy +HTTP proxy Digest auth +multi + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + +# then this is returned when we get proxy-auth + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +HTTP/1.1 401 OK +Content-Length: 21 +Server: no + +Denied access. Leave + + + + +# Client-side + + +http +https-proxy +https + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +!SSPI +crypto +proxy +digest +Debug + + +http_proxy=%HOSTIP:%HTTPPORT +https_proxy=https://%HOSTIP:%HTTPSPROXYPORT +CURL_ENTROPY=99376 + + +HTTP proxy auth Digest, then change proxy with env var and do it again + + +http://test.remote.example.com/path/%TESTNUMBER https://another.example.com:%HTTPSPORT/ daniel:monkey123 another:bump456 + + + +# Verify data after the test has been "shot" + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="daniel", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="7a1672891aff03248887b1a6674b8096" +Accept: */* +Proxy-Connection: Keep-Alive + + + + +CONNECT another.example.com:%HTTPSPORT HTTP/1.1 +Host: another.example.com:%HTTPSPORT +Proxy-Connection: Keep-Alive + + + +# CONNECT fails + +7 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 734e7f30e95d..586db5a2c95f 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -99,6 +99,7 @@ TESTS_C = \ lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ + lib1647.c \ lib1662.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1647.c b/tests/libtest/lib1647.c new file mode 100644 index 000000000000..8060e1bfe954 --- /dev/null +++ b/tests/libtest/lib1647.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * argv1 = the first URL + * argv2 = URL2 + * argv3 = credentials 1 + * argv4 = credentials 2 + */ + +#include "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + result = init1647(curl, url, userpwd); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1647(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + curl_mprintf("--- First get '%s'\n", URL); + result = run1647(curl, URL, libtest_arg3); + if(result) + goto test_cleanup; + + curl_mprintf("--- Then get '%s'\n", libtest_arg2); + result = run1647(curl, libtest_arg2, libtest_arg4); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} From a4dca608e185e2831dcf18ca0c0149484a554206 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 19 May 2026 18:56:01 +0200 Subject: [PATCH 0162/1062] GHA/non-native: alpha-sort BSD jobs Closes #21680 --- .github/workflows/non-native.yml | 184 +++++++++++++++---------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 14dbf0137430..7907310940bb 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -37,98 +37,6 @@ env: DO_NOT_TRACK: '1' jobs: - netbsd: - name: 'NetBSD, CM clang openssl ${{ matrix.arch }}' - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - arch: ['x86_64'] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' - with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH - operating_system: 'netbsd' - version: '10.1' - architecture: ${{ matrix.arch }} - run: | - # https://pkgsrc.se/ - time sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_USE_GSSAPI=ON \ - -DCURL_ENABLE_NTLM=ON \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8' - time cmake --build bld --target test-ci - fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' - - openbsd: - name: 'OpenBSD, CM clang libressl ${{ matrix.arch }}' - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - arch: ['x86_64'] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' - with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH - operating_system: 'openbsd' - version: '7.7' - architecture: ${{ matrix.arch }} - run: | - # https://openbsd.app/ - # https://www.openbsd.org/faq/faq15.html - time sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_ENABLE_NTLM=ON \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8 !2707' # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs - time cmake --build bld --target test-ci - fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' - freebsd: name: "FreeBSD, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} openssl${{ matrix.desc }} ${{ matrix.arch }}" runs-on: ubuntu-latest @@ -236,6 +144,98 @@ jobs: echo '::endgroup::' fi + netbsd: + name: 'NetBSD, CM clang openssl ${{ matrix.arch }}' + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + arch: ['x86_64'] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: 'cmake' + uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 + env: + MATRIX_ARCH: '${{ matrix.arch }}' + with: + environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH + operating_system: 'netbsd' + version: '10.1' + architecture: ${{ matrix.arch }} + run: | + # https://pkgsrc.se/ + time sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket + time cmake -B bld -G Ninja \ + -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ + -DCMAKE_UNITY_BUILD=ON \ + -DCURL_WERROR=ON \ + -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ + -DCURL_USE_OPENSSL=ON \ + -DCURL_USE_GSSAPI=ON \ + -DCURL_ENABLE_NTLM=ON \ + || { cat bld/CMakeFiles/CMake*.yaml; false; } + echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' + time cmake --build bld + time cmake --install bld + bld/src/curl --disable --version + if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU + time cmake --build bld --target testdeps + export TFLAGS='-j8' + time cmake --build bld --target test-ci + fi + echo '::group::build examples' + time cmake --build bld --target curl-examples-build + echo '::endgroup::' + + openbsd: + name: 'OpenBSD, CM clang libressl ${{ matrix.arch }}' + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + arch: ['x86_64'] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: 'cmake' + uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 + env: + MATRIX_ARCH: '${{ matrix.arch }}' + with: + environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH + operating_system: 'openbsd' + version: '7.7' + architecture: ${{ matrix.arch }} + run: | + # https://openbsd.app/ + # https://www.openbsd.org/faq/faq15.html + time sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket + time cmake -B bld -G Ninja \ + -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ + -DCMAKE_UNITY_BUILD=ON \ + -DCURL_WERROR=ON \ + -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ + -DCURL_USE_OPENSSL=ON \ + -DCURL_ENABLE_NTLM=ON \ + || { cat bld/CMakeFiles/CMake*.yaml; false; } + echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' + time cmake --build bld + time cmake --install bld + bld/src/curl --disable --version + if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU + time cmake --build bld --target testdeps + export TFLAGS='-j8 !2707' # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs + time cmake --build bld --target test-ci + fi + echo '::group::build examples' + time cmake --build bld --target curl-examples-build + echo '::endgroup::' + android: name: "Android ${{ matrix.platform }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.name }} arm64" runs-on: ubuntu-latest From 7541ae569d82fb308a5e2d94916027da4fa3ba3e Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 11:47:50 +0200 Subject: [PATCH 0163/1062] tls: fix incomplete mTLS config in conn reuse and session cache cert_type, key, key_type, key_passwd and key_blob lived in ssl_config_data but not in ssl_primary_config, so they were invisible to match_ssl_primary_config() and to the TLS session cache peer key. Two easy handles sharing a connection pool could reuse each other's authenticated connections when they differed only on SSLKEY, SSLKEYTYPE, KEYPASSWD, SSLCERTTYPE or SSLKEYBLOB. The second handle would silently inherit the first handle's authenticated identity. Promote all five fields into ssl_primary_config so the conn-reuse predicate and session cache key cover the complete client credential set. Also replace the fixed ":CCERT" session cache marker with the actual clientcert path so sessions are not shared across different client certificates. Verified by test 3303 and 3304 Reported-By: Joshua Rogers (AISLE Research) Closes #21667 --- lib/ldap.c | 4 +- lib/urldata.h | 10 +-- lib/vssh/libssh.c | 6 +- lib/vssh/libssh2.c | 2 +- lib/vtls/gtls.c | 14 ++-- lib/vtls/mbedtls.c | 24 +++--- lib/vtls/openssl.c | 7 +- lib/vtls/rustls.c | 13 ++-- lib/vtls/schannel.c | 10 +-- lib/vtls/vtls.c | 37 ++++++--- lib/vtls/vtls_scache.c | 97 ++++++++++++++++++----- lib/vtls/vtls_scache.h | 16 ++++ lib/vtls/wolfssl.c | 14 ++-- tests/data/Makefile.am | 2 +- tests/data/test3303 | 20 +++++ tests/data/test3304 | 20 +++++ tests/unit/Makefile.inc | 2 +- tests/unit/unit3303.c | 127 ++++++++++++++++++++++++++++++ tests/unit/unit3304.c | 168 ++++++++++++++++++++++++++++++++++++++++ 19 files changed, 512 insertions(+), 81 deletions(-) create mode 100644 tests/data/test3303 create mode 100644 tests/data/test3304 create mode 100644 tests/unit/unit3303.c create mode 100644 tests/unit/unit3304.c diff --git a/lib/ldap.c b/lib/ldap.c index 0f9e7821719b..ed74e9a19afd 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -326,8 +326,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef LDAP_OPT_X_TLS if(conn->ssl_config.verifypeer) { /* OpenLDAP SDK supports BASE64 files. */ - if(data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "PEM")) { + if(data->set.ssl.primary.cert_type && + !curl_strequal(data->set.ssl.primary.cert_type, "PEM")) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; diff --git a/lib/urldata.h b/lib/urldata.h index 85630b05b7b0..883e3cec31f2 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -150,9 +150,14 @@ struct ssl_primary_config { char *signature_algorithms; /* list of signature algorithms to use */ char *pinned_key; char *CRLfile; /* CRL to check certificate revocation */ + char *cert_type; /* format for certificate (default: PEM) */ + char *key; /* private key filename */ + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ struct curl_blob *cert_blob; struct curl_blob *ca_info_blob; struct curl_blob *issuercert_blob; + struct curl_blob *key_blob; #ifdef USE_TLS_SRP char *username; /* TLS username (for, e.g., SRP) */ char *password; /* TLS password (for, e.g., SRP) */ @@ -172,11 +177,6 @@ struct ssl_config_data { long certverifyresult; /* result from the certificate verification */ curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ void *fsslctxp; /* parameter for call back */ - char *cert_type; /* format for certificate (default: PEM) */ - char *key; /* private key filename */ - struct curl_blob *key_blob; - char *key_type; /* format for private key (default: PEM) */ - char *key_passwd; /* plain text private key password */ BIT(certinfo); /* gather lots of certificate info */ BIT(earlydata); /* use TLS 1.3 early data */ BIT(enable_beast); /* allow this flaw for interoperability's sake */ diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 149b4cce0ce9..09084765bcf0 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -862,7 +862,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, /* Two choices, (1) private key was given on CMD, * (2) use the "default" keys. */ if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - if(sshc->pubkey && !data->set.ssl.key_passwd) { + if(sshc->pubkey && !data->set.ssl.primary.key_passwd) { rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -875,7 +875,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, rc = ssh_pki_import_privkey_file(data-> set.str[STRING_SSH_PRIVATE_KEY], - data->set.ssl.key_passwd, NULL, + data->set.ssl.primary.key_passwd, NULL, NULL, &sshc->privkey); if(rc != SSH_OK) { failf(data, "Could not load private key file %s", @@ -888,7 +888,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, } else { rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, - data->set.ssl.key_passwd); + data->set.ssl.primary.key_passwd); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 0226ebfd2754..31c3024449f1 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1147,7 +1147,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - sshc->passphrase = data->set.ssl.key_passwd; + sshc->passphrase = data->set.ssl.primary.key_passwd; if(!sshc->passphrase) sshc->passphrase = ""; diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index fa4d6c42cc38..a8ffc28e8c37 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -996,10 +996,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; } - if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) { + if(ssl_config->primary.cert_type && + curl_strequal(ssl_config->primary.cert_type, "P12")) { rc = gnutls_certificate_set_x509_simple_pkcs12_file( gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER, - ssl_config->key_passwd ? ssl_config->key_passwd : ""); + ssl_config->primary.key_passwd ? ssl_config->primary.key_passwd : ""); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 potentially-encrypted key or certificate " @@ -1017,14 +1018,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, rc = gnutls_certificate_set_x509_key_file2( gtls->shared_creds->creds, config->clientcert, - ssl_config->key ? ssl_config->key : config->clientcert, - gnutls_do_file_type(ssl_config->cert_type), - ssl_config->key_passwd, + ssl_config->primary.key ? ssl_config->primary.key : + config->clientcert, + gnutls_do_file_type(ssl_config->primary.cert_type), + ssl_config->primary.key_passwd, supported_key_encryption_algorithms); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 %skey file: %s", - ssl_config->key_passwd ? "potentially-encrypted " : "", + ssl_config->primary.key_passwd ? "potentially-encrypted " : "", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 9cd890a1c05d..390570bacda1 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -486,7 +486,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, const char * const ssl_capath = conn_config->CApath; #ifdef MBEDTLS_PEM_PARSE_C struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -581,7 +581,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; #ifdef MBEDTLS_PEM_PARSE_C - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -662,12 +662,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_init(&backend->pk); - if(ssl_config->key || ssl_config->key_blob) { - if(ssl_config->key) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { + if(ssl_config->primary.key) { #ifdef MBEDTLS_FS_IO #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, - ssl_config->key_passwd); + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd); if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), @@ -677,8 +677,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else - ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, - ssl_config->key_passwd, + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd, mbedtls_ctr_drbg_random, &rng.drbg); if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || @@ -689,7 +689,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s", - ssl_config->key, -ret, errorbuf); + ssl_config->primary.key, -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else @@ -698,8 +698,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, #endif } else { - const struct curl_blob *ssl_key_blob = ssl_config->key_blob; - const char *passwd = ssl_config->key_passwd; + const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob; + const char *passwd = ssl_config->primary.key_passwd; /* Unfortunately, mbedtls_pk_parse_key() requires the data to be null-terminated if the data is PEM encoded (even when provided the exact length). */ @@ -933,7 +933,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, #endif ); - if(ssl_config->key || ssl_config->key_blob) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 2eeb2f349d29..2302ddacc66c 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3677,7 +3677,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; unsigned int ssl_version_min; char error_buffer[256]; @@ -3841,8 +3841,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(ssl_cert || ssl_cert_blob || ssl_cert_type) { result = client_cert(data, octx->ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); + ssl_config->primary.key, ssl_config->primary.key_blob, + ssl_config->primary.key_type, + ssl_config->primary.key_passwd); if(result) /* failf() is already done in client_cert() */ return result; diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 57591949527c..90a37cbedaea 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -845,14 +845,14 @@ init_config_builder_client_auth(struct Curl_easy *data, const struct rustls_certified_key *certified_key = NULL; CURLcode result = CURLE_OK; - if(conn_config->clientcert && !ssl_config->key) { + if(conn_config->clientcert && !ssl_config->primary.key) { failf(data, "rustls: must provide key with certificate '%s'", conn_config->clientcert); return CURLE_SSL_CERTPROBLEM; } - else if(!conn_config->clientcert && ssl_config->key) { + else if(!conn_config->clientcert && ssl_config->primary.key) { failf(data, "rustls: must provide certificate with key '%s'", - ssl_config->key); + ssl_config->primary.key); return CURLE_SSL_CERTPROBLEM; } @@ -866,8 +866,9 @@ init_config_builder_client_auth(struct Curl_easy *data, goto cleanup; } - if(!read_file_into(ssl_config->key, &key_contents)) { - failf(data, "rustls: failed to read key file: '%s'", ssl_config->key); + if(!read_file_into(ssl_config->primary.key, &key_contents)) { + failf(data, "rustls: failed to read key file: '%s'", + ssl_config->primary.key); result = CURLE_SSL_CERTPROBLEM; goto cleanup; } @@ -1066,7 +1067,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, } } - if(conn_config->clientcert || ssl_config->key) { + if(conn_config->clientcert || ssl_config->primary.key) { result = init_config_builder_client_auth(data, conn_config, ssl_config, diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 9466de0e14d3..e3b2263e594d 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -415,8 +415,8 @@ static CURLcode get_client_cert(struct Curl_easy *data, } } - if((fInCert || blob) && data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "P12")) { + if((fInCert || blob) && data->set.ssl.primary.cert_type && + !curl_strequal(data->set.ssl.primary.cert_type, "P12")) { failf(data, "schannel: certificate format compatibility error " "for %s", blob ? "(memory blob)" : data->set.ssl.primary.clientcert); @@ -466,15 +466,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, datablob.pbData = (BYTE *)certdata; datablob.cbData = (DWORD)certsize; - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); + if(data->set.ssl.primary.key_passwd) + pwd_len = strlen(data->set.ssl.primary.key_passwd); pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); if(pszPassword) { int str_w_len = 0; if(pwd_len > 0) str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - data->set.ssl.key_passwd, + data->set.ssl.primary.key_passwd, (int)pwd_len, pszPassword, (int)(pwd_len + 1)); diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 46005578794e..73dd3f56f1db 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -205,6 +205,7 @@ static bool match_ssl_primary_config(struct Curl_easy *data, blobcmp(c1->cert_blob, c2->cert_blob) && blobcmp(c1->ca_info_blob, c2->ca_info_blob) && blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + blobcmp(c1->key_blob, c2->key_blob) && Curl_safecmp(c1->CApath, c2->CApath) && Curl_safecmp(c1->CAfile, c2->CAfile) && Curl_safecmp(c1->issuercert, c2->issuercert) && @@ -218,7 +219,11 @@ static bool match_ssl_primary_config(struct Curl_easy *data, curl_strequal(c1->curves, c2->curves) && curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && Curl_safecmp(c1->CRLfile, c2->CRLfile) && - Curl_safecmp(c1->pinned_key, c2->pinned_key)) + Curl_safecmp(c1->pinned_key, c2->pinned_key) && + curl_strequal(c1->cert_type, c2->cert_type) && + Curl_safecmp(c1->key, c2->key) && + curl_strequal(c1->key_type, c2->key_type) && + !Curl_timestrcmp(c1->key_passwd, c2->key_passwd)) return TRUE; return FALSE; @@ -253,6 +258,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, CLONE_BLOB(cert_blob); CLONE_BLOB(ca_info_blob); CLONE_BLOB(issuercert_blob); + CLONE_BLOB(key_blob); CLONE_STRING(CApath); CLONE_STRING(CAfile); CLONE_STRING(issuercert); @@ -263,6 +269,10 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, CLONE_STRING(curves); CLONE_STRING(signature_algorithms); CLONE_STRING(CRLfile); + CLONE_STRING(cert_type); + CLONE_STRING(key); + CLONE_STRING(key_type); + CLONE_STRING(key_passwd); #ifdef USE_TLS_SRP CLONE_STRING(username); CLONE_STRING(password); @@ -283,9 +293,14 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc) curlx_safefree(sslc->cert_blob); curlx_safefree(sslc->ca_info_blob); curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->key_blob); curlx_safefree(sslc->curves); curlx_safefree(sslc->signature_algorithms); curlx_safefree(sslc->CRLfile); + curlx_safefree(sslc->cert_type); + curlx_safefree(sslc->key); + curlx_safefree(sslc->key_type); + curlx_safefree(sslc->key_passwd); #ifdef USE_TLS_SRP curlx_safefree(sslc->username); curlx_safefree(sslc->password); @@ -337,12 +352,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; #endif - sslc->cert_type = data->set.str[STRING_CERT_TYPE]; - sslc->key = data->set.str[STRING_KEY]; - sslc->key_type = data->set.str[STRING_KEY_TYPE]; - sslc->key_passwd = data->set.str[STRING_KEY_PASSWD]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; + sslc->primary.key = data->set.str[STRING_KEY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; sslc->primary.clientcert = data->set.str[STRING_CERT]; - sslc->key_blob = data->set.blobs[BLOB_KEY]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; #ifndef CURL_DISABLE_PROXY sslc = &data->set.proxy_ssl; @@ -378,12 +393,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; - sslc->cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - sslc->key = data->set.str[STRING_KEY_PROXY]; - sslc->key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - sslc->key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + sslc->primary.key = data->set.str[STRING_KEY_PROXY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; - sslc->key_blob = data->set.blobs[BLOB_KEY_PROXY]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; #ifdef USE_TLS_SRP sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 88593b20ae27..2fc563e800bd 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -50,6 +50,7 @@ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ char *clientcert; + char *key_passwd; char *srp_username; char *srp_password; struct Curl_llist sessions; @@ -123,6 +124,48 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, return result; } +static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf, + struct ssl_primary_config *ssl, + bool *is_local) +{ + CURLcode result = CURLE_OK; + if(ssl->clientcert && ssl->clientcert[0]) { + result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local); + if(result) + goto out; + } + if(ssl->key && ssl->key[0]) { + result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local); + if(result) + goto out; + } + if(ssl->key_blob) { + result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob); + if(result) + goto out; + } + if(ssl->cert_type && ssl->cert_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":CT-"); + for(i = 0; !result && ssl->cert_type[i]; i++) { + char c = Curl_raw_toupper(ssl->cert_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + if(result) + goto out; + } + if(ssl->key_type && ssl->key_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":KT-"); + for(i = 0; !result && ssl->key_type[i]; i++) { + char c = Curl_raw_toupper(ssl->key_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + } +out: + return result; +} + #define CURL_SSLS_LOCAL_SUFFIX ":L" #define CURL_SSLS_GLOBAL_SUFFIX ":G" @@ -134,12 +177,12 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key) (peer_key[len - 2] == ':'); } -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, - const char *tls_id, - char **ppeer_key) +CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, + const struct ssl_peer *peer, + const struct Curl_peer *via_peer, + const char *tls_id, + char **ppeer_key) { - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); struct dynbuf buf; size_t key_len; bool is_local = FALSE; @@ -188,10 +231,10 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out; } if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->via_peer) { + if(via_peer) { result = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", - cf->conn->via_peer->hostname, - cf->conn->via_peer->port); + via_peer->hostname, + via_peer->port); if(result) goto out; } @@ -266,11 +309,9 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out; } - if(ssl->clientcert && ssl->clientcert[0]) { - result = curlx_dyn_add(&buf, ":CCERT"); - if(result) - goto out; - } + result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local); + if(result) + goto out; #ifdef USE_TLS_SRP if(ssl->username || ssl->password) { result = curlx_dyn_add(&buf, ":SRP-AUTH"); @@ -301,6 +342,16 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, return result; } +CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); + return Curl_ssl_peer_key_build(ssl, peer, cf->conn->via_peer, tls_id, + ppeer_key); +} + struct Curl_ssl_scache { unsigned int magic; struct Curl_ssl_scache_peer *peers; @@ -409,6 +460,7 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) } peer->sobj_free = NULL; curlx_safefree(peer->clientcert); + curlx_safefree(peer->key_passwd); #ifdef USE_TLS_SRP curlx_safefree(peer->srp_username); curlx_safefree(peer->srp_password); @@ -437,8 +489,8 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) * - its peer key is not yet known, because sessions were * imported using only the salt+hmac * - the peer key is global, e.g. carrying no relative paths */ - peer->exportable = (!peer->clientcert && !peer->srp_username && - !peer->srp_password && + peer->exportable = (!peer->clientcert && !peer->key_passwd && + !peer->srp_username && !peer->srp_password && (!peer->ssl_peer_key || cf_ssl_peer_key_is_global(peer->ssl_peer_key))); } @@ -447,6 +499,7 @@ static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, const char *ssl_peer_key, const char *clientcert, + const char *key_passwd, const char *srp_username, const char *srp_password, const unsigned char *salt, @@ -475,6 +528,11 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, if(!peer->clientcert) goto out; } + if(key_passwd) { + peer->key_passwd = curlx_strdup(key_passwd); + if(!peer->key_passwd) + goto out; + } if(srp_username) { peer->srp_username = curlx_strdup(srp_username); if(!peer->srp_username) @@ -616,7 +674,7 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { if(!conn_config) { - if(peer->clientcert) + if(peer->clientcert || peer->key_passwd) return FALSE; #ifdef USE_TLS_SRP if(peer->srp_username || peer->srp_password) @@ -626,6 +684,8 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, } else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) return FALSE; + if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd)) + return FALSE; #ifdef USE_TLS_SRP if(Curl_timestrcmp(peer->srp_username, conn_config->username) || Curl_timestrcmp(peer->srp_password, conn_config->password)) @@ -754,6 +814,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, if(peer) { char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; + const char *kpasswd = conn_config ? conn_config->key_passwd : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; @@ -765,7 +826,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, "cert-%p", conn_config->cert_blob->data); ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ } - result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd, username, password, NULL, NULL); if(result) goto out; @@ -1144,7 +1205,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data, if(!peer) { peer = cf_ssl_get_free_peer(scache); if(peer) { - result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL, NULL, NULL, salt, hmac); if(result) goto out; diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index a6a36f16306b..cf270ba413a0 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -66,6 +66,22 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, const char *tls_id, char **ppeer_key); +/** + * Like Curl_ssl_peer_key_make() but takes the primary config and peer + * descriptors directly, without requiring a Curl_cfilter. Exposed for + * unit testing. + * @param ssl the primary SSL config to key on + * @param peer the peer the filter wants to talk to + * @param via_peer the connecting-through peer, or NULL + * @param tls_id identifier of TLS implementation for sessions + * @param ppeer_key on successful return, the key generated + */ +CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, + const struct ssl_peer *peer, + const struct Curl_peer *via_peer, + const char *tls_id, + char **ppeer_key); + /* Return if there is a session cache shall be used. * An ssl session might not be configured or not available for * "connect-only" transfers. diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 59574c9b6a78..90fc33173dc1 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -920,10 +920,10 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #ifndef NO_FILESYSTEM if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) { const char *cert_file = ssl_config->primary.clientcert; - const char *key_file = ssl_config->key; + const char *key_file = ssl_config->primary.key; const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -954,7 +954,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, key_file = cert_file; } else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); rc = key_blob ? wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, @@ -968,8 +968,8 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #else /* NO_FILESYSTEM */ if(ssl_config->primary.cert_blob) { const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -994,7 +994,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, if(!key_blob) key_blob = cert_blob; else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, (long)key_blob->len, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a3778bdad1d3..cde5c2873698 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -287,7 +287,7 @@ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 \ \ -test3300 test3301 test3302 \ +test3300 test3301 test3302 test3303 test3304 \ \ test4000 test4001 diff --git a/tests/data/test3303 b/tests/data/test3303 new file mode 100644 index 000000000000..697049f013cc --- /dev/null +++ b/tests/data/test3303 @@ -0,0 +1,20 @@ + + + + +unittest +TLS +mTLS + + + +# Client-side + + +unittest + + +conn-reuse match distinguishes mTLS key, cert_type, key_type and key_passwd fields + + + diff --git a/tests/data/test3304 b/tests/data/test3304 new file mode 100644 index 000000000000..4380c0819f02 --- /dev/null +++ b/tests/data/test3304 @@ -0,0 +1,20 @@ + + + + +unittest +TLS +mTLS + + + +# Client-side + + +unittest + + +TLS session cache peer key discriminates on mTLS key, key_type and cert_type fields + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index b474f3d7fcd4..c8eccd27ad4d 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -47,4 +47,4 @@ TESTS_C = \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ - unit3300.c unit3301.c unit3302.c + unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c diff --git a/tests/unit/unit3303.c b/tests/unit/unit3303.c new file mode 100644 index 000000000000..41bced542d62 --- /dev/null +++ b/tests/unit/unit3303.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" +#include "urldata.h" + +#ifdef USE_SSL +#include "vtls/vtls.h" +#endif + +static CURLcode test_unit3303(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + +#ifdef USE_SSL + { + CURL *curl; + struct connectdata *conn; + struct ssl_primary_config *primary; + char *saved; + static char alt_passwd[] = "wrong"; + static char alt_key[] = "other.key"; + static char alt_ktype[] = "DER"; + static char alt_ctype[] = "P12"; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if(!curl) { + curl_global_cleanup(); + goto unit_test_abort; + } + + curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); + curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key"); + curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret"); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); + + if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) { + curl_easy_cleanup(curl); + curl_global_cleanup(); + goto unit_test_abort; + } + + conn = curlx_calloc(1, sizeof(*conn)); + if(!conn || Curl_ssl_conn_config_init((struct Curl_easy *)curl, conn)) { + if(conn) + Curl_ssl_conn_config_cleanup(conn); + curlx_free(conn); + curl_easy_cleanup(curl); + curl_global_cleanup(); + goto unit_test_abort; + } + + /* Baseline: identical config must match. */ + fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "identical mTLS config should match"); + + primary = &((struct Curl_easy *)curl)->set.ssl.primary; + + /* Different key_passwd must not match. */ + saved = primary->key_passwd; + primary->key_passwd = alt_passwd; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key_passwd must not reuse conn"); + primary->key_passwd = saved; + + /* Different key path must not match. */ + saved = primary->key; + primary->key = alt_key; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key must not reuse conn"); + primary->key = saved; + + /* Different key type must not match. */ + saved = primary->key_type; + primary->key_type = alt_ktype; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key_type must not reuse conn"); + primary->key_type = saved; + + /* Different cert type must not match. */ + saved = primary->cert_type; + primary->cert_type = alt_ctype; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different cert_type must not reuse conn"); + primary->cert_type = saved; + + /* All fields restored: must match again. */ + fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "restored mTLS config should match"); + + Curl_ssl_conn_config_cleanup(conn); + curlx_free(conn); + curl_easy_cleanup(curl); + curl_global_cleanup(); + } +#endif /* USE_SSL */ + + UNITTEST_END_SIMPLE +} diff --git a/tests/unit/unit3304.c b/tests/unit/unit3304.c new file mode 100644 index 000000000000..7c39c60801a9 --- /dev/null +++ b/tests/unit/unit3304.c @@ -0,0 +1,168 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* Unit tests for TLS session cache peer key discrimination on mTLS fields. + * Verifies that Curl_ssl_peer_key_build() produces distinct keys when two + * handles differ only on key, key_type or cert_type. key_passwd is NOT + * embedded in the peer key; it is compared separately at session lookup via + * cf_ssl_scache_match_auth(), following the same pattern as SRP + * credentials. */ + +#include "unitcheck.h" +#include "urldata.h" +#include "peer.h" + +#ifdef USE_SSL +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#endif + +static CURLcode test_unit3304(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + +#ifdef USE_SSL + { + struct Curl_peer dest; + struct ssl_peer peer; + struct ssl_primary_config ssl; + char *key1 = NULL; + char *key2 = NULL; + static char base_hostname[] = "example.com"; + static char base_cert[] = "client.pem"; + static char base_key[] = "client.key"; + static char base_passwd[] = "secret"; + static char base_ctype[] = "PEM"; + static char base_ktype[] = "PEM"; + static char alt_key[] = "other.key"; + static char alt_ktype[] = "DER"; + static char alt_ctype[] = "P12"; + static char lc_ctype[] = "pem"; + static char lc_ktype[] = "pem"; + + memset(&dest, 0, sizeof(dest)); + dest.hostname = base_hostname; + dest.port = 443; + + memset(&peer, 0, sizeof(peer)); + peer.dest = &dest; + peer.transport = TRNSPRT_TCP; + + memset(&ssl, 0, sizeof(ssl)); + ssl.verifypeer = TRUE; + ssl.verifyhost = TRUE; + ssl.clientcert = base_cert; + ssl.key = base_key; + ssl.key_passwd = base_passwd; + ssl.cert_type = base_ctype; + ssl.key_type = base_ktype; + + /* Baseline: same config produces same key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "identical config should produce identical peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + + /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison + * via cf_ssl_scache_match_auth(), same as SRP credentials. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_passwd = NULL; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "key_passwd must not affect the peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + ssl.key_passwd = base_passwd; + + /* Different key path must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key = alt_key; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different key must produce different peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + ssl.key = base_key; + + /* Different key_type must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_type = alt_ktype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different key_type must produce different peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + ssl.key_type = base_ktype; + + /* Different cert_type must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.cert_type = alt_ctype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different cert_type must produce different peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + ssl.cert_type = base_ctype; + + /* cert_type is case-insensitive: "PEM" and "pem" must produce the + * same peer key, consistent with the conn-reuse comparison. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.cert_type = lc_ctype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "cert_type case must not affect peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + ssl.cert_type = base_ctype; + + /* key_type is case-insensitive: "PEM" and "pem" must produce the + * same peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_type = lc_ktype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "key_type case must not affect peer key"); + curlx_free(key1); key1 = NULL; + curlx_free(key2); key2 = NULL; + } +#endif /* USE_SSL */ + + UNITTEST_END_SIMPLE +} From 1c3289c85e1a7a939464d5c5e84382d2e250e611 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 00:17:58 +0200 Subject: [PATCH 0164/1062] unit3303, unit3304: tidy-ups - use `curlx_safefree()`. - drop redundant blocks. Follow-up to 7541ae569d82fb308a5e2d94916027da4fa3ba3e #21667 Closes #21684 --- tests/unit/unit3303.c | 166 +++++++++++++++-------------- tests/unit/unit3304.c | 236 +++++++++++++++++++++--------------------- 2 files changed, 199 insertions(+), 203 deletions(-) diff --git a/tests/unit/unit3303.c b/tests/unit/unit3303.c index 41bced542d62..e979cbec8d03 100644 --- a/tests/unit/unit3303.c +++ b/tests/unit/unit3303.c @@ -33,94 +33,92 @@ static CURLcode test_unit3303(const char *arg) UNITTEST_BEGIN_SIMPLE #ifdef USE_SSL - { - CURL *curl; - struct connectdata *conn; - struct ssl_primary_config *primary; - char *saved; - static char alt_passwd[] = "wrong"; - static char alt_key[] = "other.key"; - static char alt_ktype[] = "DER"; - static char alt_ctype[] = "P12"; - - curl_global_init(CURL_GLOBAL_ALL); - curl = curl_easy_init(); - if(!curl) { - curl_global_cleanup(); - goto unit_test_abort; - } - - curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); - curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key"); - curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret"); - curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); - - if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) { - curl_easy_cleanup(curl); - curl_global_cleanup(); - goto unit_test_abort; - } - - conn = curlx_calloc(1, sizeof(*conn)); - if(!conn || Curl_ssl_conn_config_init((struct Curl_easy *)curl, conn)) { - if(conn) - Curl_ssl_conn_config_cleanup(conn); - curlx_free(conn); - curl_easy_cleanup(curl); - curl_global_cleanup(); - goto unit_test_abort; - } - - /* Baseline: identical config must match. */ - fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "identical mTLS config should match"); - - primary = &((struct Curl_easy *)curl)->set.ssl.primary; - - /* Different key_passwd must not match. */ - saved = primary->key_passwd; - primary->key_passwd = alt_passwd; - fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "different key_passwd must not reuse conn"); - primary->key_passwd = saved; - - /* Different key path must not match. */ - saved = primary->key; - primary->key = alt_key; - fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "different key must not reuse conn"); - primary->key = saved; - - /* Different key type must not match. */ - saved = primary->key_type; - primary->key_type = alt_ktype; - fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "different key_type must not reuse conn"); - primary->key_type = saved; - - /* Different cert type must not match. */ - saved = primary->cert_type; - primary->cert_type = alt_ctype; - fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "different cert_type must not reuse conn"); - primary->cert_type = saved; - - /* All fields restored: must match again. */ - fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, - FALSE), - "restored mTLS config should match"); - - Curl_ssl_conn_config_cleanup(conn); + CURL *curl; + struct connectdata *conn; + struct ssl_primary_config *primary; + char *saved; + static char alt_passwd[] = "wrong"; + static char alt_key[] = "other.key"; + static char alt_ktype[] = "DER"; + static char alt_ctype[] = "P12"; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if(!curl) { + curl_global_cleanup(); + goto unit_test_abort; + } + + curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); + curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key"); + curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret"); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); + + if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) { + curl_easy_cleanup(curl); + curl_global_cleanup(); + goto unit_test_abort; + } + + conn = curlx_calloc(1, sizeof(*conn)); + if(!conn || Curl_ssl_conn_config_init((struct Curl_easy *)curl, conn)) { + if(conn) + Curl_ssl_conn_config_cleanup(conn); curlx_free(conn); curl_easy_cleanup(curl); curl_global_cleanup(); + goto unit_test_abort; } + + /* Baseline: identical config must match. */ + fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "identical mTLS config should match"); + + primary = &((struct Curl_easy *)curl)->set.ssl.primary; + + /* Different key_passwd must not match. */ + saved = primary->key_passwd; + primary->key_passwd = alt_passwd; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key_passwd must not reuse conn"); + primary->key_passwd = saved; + + /* Different key path must not match. */ + saved = primary->key; + primary->key = alt_key; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key must not reuse conn"); + primary->key = saved; + + /* Different key type must not match. */ + saved = primary->key_type; + primary->key_type = alt_ktype; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different key_type must not reuse conn"); + primary->key_type = saved; + + /* Different cert type must not match. */ + saved = primary->cert_type; + primary->cert_type = alt_ctype; + fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "different cert_type must not reuse conn"); + primary->cert_type = saved; + + /* All fields restored: must match again. */ + fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn, + FALSE), + "restored mTLS config should match"); + + Curl_ssl_conn_config_cleanup(conn); + curlx_free(conn); + curl_easy_cleanup(curl); + curl_global_cleanup(); #endif /* USE_SSL */ UNITTEST_END_SIMPLE diff --git a/tests/unit/unit3304.c b/tests/unit/unit3304.c index 7c39c60801a9..5573be39cc6e 100644 --- a/tests/unit/unit3304.c +++ b/tests/unit/unit3304.c @@ -43,125 +43,123 @@ static CURLcode test_unit3304(const char *arg) UNITTEST_BEGIN_SIMPLE #ifdef USE_SSL - { - struct Curl_peer dest; - struct ssl_peer peer; - struct ssl_primary_config ssl; - char *key1 = NULL; - char *key2 = NULL; - static char base_hostname[] = "example.com"; - static char base_cert[] = "client.pem"; - static char base_key[] = "client.key"; - static char base_passwd[] = "secret"; - static char base_ctype[] = "PEM"; - static char base_ktype[] = "PEM"; - static char alt_key[] = "other.key"; - static char alt_ktype[] = "DER"; - static char alt_ctype[] = "P12"; - static char lc_ctype[] = "pem"; - static char lc_ktype[] = "pem"; - - memset(&dest, 0, sizeof(dest)); - dest.hostname = base_hostname; - dest.port = 443; - - memset(&peer, 0, sizeof(peer)); - peer.dest = &dest; - peer.transport = TRNSPRT_TCP; - - memset(&ssl, 0, sizeof(ssl)); - ssl.verifypeer = TRUE; - ssl.verifyhost = TRUE; - ssl.clientcert = base_cert; - ssl.key = base_key; - ssl.key_passwd = base_passwd; - ssl.cert_type = base_ctype; - ssl.key_type = base_ktype; - - /* Baseline: same config produces same key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && !strcmp(key1, key2), - "identical config should produce identical peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - - /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison - * via cf_ssl_scache_match_auth(), same as SRP credentials. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.key_passwd = NULL; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && !strcmp(key1, key2), - "key_passwd must not affect the peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - ssl.key_passwd = base_passwd; - - /* Different key path must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.key = alt_key; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && strcmp(key1, key2), - "different key must produce different peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - ssl.key = base_key; - - /* Different key_type must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.key_type = alt_ktype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && strcmp(key1, key2), - "different key_type must produce different peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - ssl.key_type = base_ktype; - - /* Different cert_type must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.cert_type = alt_ctype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && strcmp(key1, key2), - "different cert_type must produce different peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - ssl.cert_type = base_ctype; - - /* cert_type is case-insensitive: "PEM" and "pem" must produce the - * same peer key, consistent with the conn-reuse comparison. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.cert_type = lc_ctype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && !strcmp(key1, key2), - "cert_type case must not affect peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - ssl.cert_type = base_ctype; - - /* key_type is case-insensitive: "PEM" and "pem" must produce the - * same peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), - "peer key build failed"); - ssl.key_type = lc_ktype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), - "peer key build failed"); - fail_unless(key1 && key2 && !strcmp(key1, key2), - "key_type case must not affect peer key"); - curlx_free(key1); key1 = NULL; - curlx_free(key2); key2 = NULL; - } + struct Curl_peer dest; + struct ssl_peer peer; + struct ssl_primary_config ssl; + char *key1 = NULL; + char *key2 = NULL; + static char base_hostname[] = "example.com"; + static char base_cert[] = "client.pem"; + static char base_key[] = "client.key"; + static char base_passwd[] = "secret"; + static char base_ctype[] = "PEM"; + static char base_ktype[] = "PEM"; + static char alt_key[] = "other.key"; + static char alt_ktype[] = "DER"; + static char alt_ctype[] = "P12"; + static char lc_ctype[] = "pem"; + static char lc_ktype[] = "pem"; + + memset(&dest, 0, sizeof(dest)); + dest.hostname = base_hostname; + dest.port = 443; + + memset(&peer, 0, sizeof(peer)); + peer.dest = &dest; + peer.transport = TRNSPRT_TCP; + + memset(&ssl, 0, sizeof(ssl)); + ssl.verifypeer = TRUE; + ssl.verifyhost = TRUE; + ssl.clientcert = base_cert; + ssl.key = base_key; + ssl.key_passwd = base_passwd; + ssl.cert_type = base_ctype; + ssl.key_type = base_ktype; + + /* Baseline: same config produces same key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "identical config should produce identical peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + + /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison + * via cf_ssl_scache_match_auth(), same as SRP credentials. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_passwd = NULL; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "key_passwd must not affect the peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + ssl.key_passwd = base_passwd; + + /* Different key path must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key = alt_key; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different key must produce different peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + ssl.key = base_key; + + /* Different key_type must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_type = alt_ktype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different key_type must produce different peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + ssl.key_type = base_ktype; + + /* Different cert_type must produce a different peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.cert_type = alt_ctype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && strcmp(key1, key2), + "different cert_type must produce different peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + ssl.cert_type = base_ctype; + + /* cert_type is case-insensitive: "PEM" and "pem" must produce the + * same peer key, consistent with the conn-reuse comparison. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.cert_type = lc_ctype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "cert_type case must not affect peer key"); + curlx_safefree(key1); + curlx_safefree(key2); + ssl.cert_type = base_ctype; + + /* key_type is case-insensitive: "PEM" and "pem" must produce the + * same peer key. */ + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + "peer key build failed"); + ssl.key_type = lc_ktype; + fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + "peer key build failed"); + fail_unless(key1 && key2 && !strcmp(key1, key2), + "key_type case must not affect peer key"); + curlx_safefree(key1); + curlx_safefree(key2); #endif /* USE_SSL */ UNITTEST_END_SIMPLE From b3f76b21c9bc36d94f5fb34a446cef8cb53266d0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 18:47:52 +0200 Subject: [PATCH 0165/1062] tidy-up: miscellaneous - fix typos and wording in documentation and comments. - KNOWN_BUGS: merge duplicate H1 section. - test_10_proxy: delete stray expressions. - Perl: `while()` -> `while(1)`. - Perl: fix indent, whitespace, drop redundant quotes and parentheses. - fix casing: URL, SSL, Windows. - badwords: readd `threadsafe`, add `well-known` (and fix it). - replace `WinXP` -> `Windows XP` to match other uses. Closes #21646 --- configure.ac | 2 +- docs/CIPHERS.md | 2 +- docs/HISTORY.md | 2 +- docs/KNOWN_BUGS.md | 12 ++++------ docs/VULN-DISCLOSURE-POLICY.md | 19 ++++++++------- docs/examples/cacertinmem.c | 4 ++-- docs/examples/simplessl.c | 2 +- docs/examples/smooth-gtk-thread.c | 2 +- docs/examples/sslbackend.c | 2 +- docs/internals/TLS-SESSIONS.md | 2 +- docs/libcurl/curl_easy_setopt.md | 2 +- docs/libcurl/curl_multi_socket_action.md | 4 ++-- docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md | 2 +- include/curl/curl.h | 16 ++++++------- lib/bufq.h | 1 + lib/curlx/fopen.c | 2 +- lib/dict.c | 2 +- lib/ftp-int.h | 4 ++-- lib/url.c | 2 +- lib/urldata.h | 4 ++-- lib/vauth/ntlm_sspi.c | 2 +- lib/vauth/spnego_sspi.c | 2 +- lib/vtls/mbedtls.c | 2 +- lib/vtls/openssl.c | 12 +++++----- lib/vtls/openssl.h | 2 +- lib/vtls/vtls.h | 2 +- lib/vtls/vtls_scache.h | 2 +- lib/vtls/wolfssl.c | 2 +- m4/curl-openssl.m4 | 4 ++-- m4/curl-rustls.m4 | 2 +- projects/vms/generate_config_vms_h_curl.com | 4 ++-- scripts/badwords.txt | 2 ++ src/tool_doswin.c | 2 +- src/tool_ssls.c | 2 +- tests/appveyor.pm | 22 ++++++++--------- tests/azure.pm | 26 ++++++++++----------- tests/data/test3207 | 2 +- tests/http/test_10_proxy.py | 4 ++-- tests/libtest/cli_hx_download.c | 2 +- tests/libtest/first.h | 2 +- tests/libtest/lib3207.c | 2 +- tests/runner.pm | 6 ++--- tests/runtests.pl | 6 ++--- tests/servers.pm | 6 ++--- 44 files changed, 106 insertions(+), 102 deletions(-) diff --git a/configure.ac b/configure.ac index 82211da018dd..31a29cd60164 100644 --- a/configure.ac +++ b/configure.ac @@ -2088,7 +2088,7 @@ Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schann dnl explicitly built without TLS ;; xD*) - AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an ssl library + AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an SSL library (e.g. --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl, --with-rustls). Since these are conflicting parameters, verify which is the desired one and drop the other.]) ;; diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 9606f2d79566..e2c3e89956cb 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -238,7 +238,7 @@ other keywords that tweak its operations. Applications or a system may define new alias names for priority strings that can then be used here. Since the order of items in priority strings is significant, it makes no -sense for curl to puzzle other ssl options somehow together. `--ciphers` +sense for curl to puzzle other SSL options somehow together. `--ciphers` is the single way to change priority. ### Examples diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 6beec33f1547..3723052f9e95 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -465,7 +465,7 @@ December 21: dropped hyper ## 2025 -February 5: first 0RTT for QUIC, ssl session import/export +February 5: first 0RTT for QUIC, SSL session import/export February: experimental HTTPS RR support diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index 70d3196b83bd..d6bdf1410fa6 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -215,6 +215,10 @@ https://curl.se/mail/lib-2012-07/0073.html # Authentication +## `--aws-sigv4` does not handle multipart/form-data correctly + +[curl issue 13351](https://github.com/curl/curl/issues/13351) + ## Digest `auth-int` for PUT/POST We do not support auth-int for Digest using PUT or POST @@ -418,7 +422,7 @@ See [curl issue 13350](https://github.com/curl/curl/issues/13350) ## `CURLOPT_CONNECT_TO` does not work for HTTPS proxy It is unclear if the same option should even cover the proxy connection or if -if requires a separate option. +it requires a separate option. See [curl issue 14481](https://github.com/curl/curl/issues/14481) @@ -516,12 +520,6 @@ cannot be built. [curl issue 6904](https://github.com/curl/curl/issues/6904) -# Authentication - -## `--aws-sigv4` does not handle multipart/form-data correctly - -[curl issue 13351](https://github.com/curl/curl/issues/13351) - # HTTP/2 ## HTTP/2 prior knowledge over proxy diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index 99fb5577a3d1..4ff284e43f2c 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -218,9 +218,11 @@ problem. There are already several benign and likely reasons for transfers to stall and never end, so applications that cannot deal with never-ending transfers already need to have counter-measures established. -Well known attacks, like [Slowloris](https://en.wikipedia.org/wiki/Slowloris_(cyber_attack)), that send partial -requests are usually not considered a flaw. If the problem avoids the regular counter-measures when it causes a never- -ending transfer, it might be a security problem. +Well-known attacks, like +[Slowloris](https://en.wikipedia.org/wiki/Slowloris_(cyber_attack)), that send +partial requests are usually not considered a flaw. If the problem bypasses +the regular counter-measures and it causes a never-ending transfer, it might +be a security problem. ## Not practically possible @@ -421,7 +423,8 @@ roles: * **incident lead** - Coordinates technical efforts * **communication lead** - Single point of public contact -It is likely that our [BDFL](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life) occupies +It is likely that our +[BDFL](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life) occupies one of these roles, though this plan does not depend on it. A declaration may also contain more detailed information but as we honor @@ -430,8 +433,8 @@ contain a brief notification that a **major incident** is occurring. ## Major incident ongoing -During the incident - all press, media, legal or commercial entities should contact -communication leader (security@curl.se). +During the incident - all press, media, legal or commercial entities should +contact communication lead (security@curl.se). Existing **curl-security** team internal communication channels are used for all internal communication. @@ -440,8 +443,8 @@ Existing vulnerability disclosure process are followed for any embargoes and fixes. Where possible, public communication are provided: -* regular communication from communication leader (for example daily update) -* asynchronous communication from incident leader +* regular communication from communication lead (for example daily update) +* asynchronous communication from incident lead * Delivered to the aforementioned curl communication channels. diff --git a/docs/examples/cacertinmem.c b/docs/examples/cacertinmem.c index 8ede167c2819..06d088c61ad0 100644 --- a/docs/examples/cacertinmem.c +++ b/docs/examples/cacertinmem.c @@ -166,10 +166,10 @@ int main(void) /* use a fresh connection (optional) this option seriously impacts * performance of multiple transfers but it is necessary order to - * demonstrate this example. recall that the ssl ctx callback is only + * demonstrate this example. recall that the SSL ctx callback is only * called _before_ an SSL connection is established, therefore it does not * affect existing verified SSL connections already in the connection - * cache associated with this handle. normally you would set the ssl ctx + * cache associated with this handle. normally you would set the SSL ctx * function before making any transfers, and not use this option. */ curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L); diff --git a/docs/examples/simplessl.c b/docs/examples/simplessl.c index f1c07a4ae239..43fe34cf3fbf 100644 --- a/docs/examples/simplessl.c +++ b/docs/examples/simplessl.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * Shows HTTPS usage with client certs and optional ssl engine use. + * Shows HTTPS usage with client certs and optional SSL engine use. * */ #ifdef _MSC_VER diff --git a/docs/examples/smooth-gtk-thread.c b/docs/examples/smooth-gtk-thread.c index 06eea1ff0e64..2bbe1a39e928 100644 --- a/docs/examples/smooth-gtk-thread.c +++ b/docs/examples/smooth-gtk-thread.c @@ -125,7 +125,7 @@ static void *create_thread(void *progress_bar) pthread_t tid[NUMT]; int i; - /* Make sure I do not create more threads than urls. */ + /* Make sure I do not create more threads than URLs. */ for(i = 0; i < NUMT && i < num_urls; i++) { int error = pthread_create(&tid[i], NULL, /* default attributes please */ diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index e10eaaa21754..ec411c217577 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * Shows HTTPS usage with client certs and optional ssl engine use. + * Shows HTTPS usage with client certs and optional SSL engine use. * */ #include diff --git a/docs/internals/TLS-SESSIONS.md b/docs/internals/TLS-SESSIONS.md index b108fbfcfbe2..c3bf7038b447 100644 --- a/docs/internals/TLS-SESSIONS.md +++ b/docs/internals/TLS-SESSIONS.md @@ -119,7 +119,7 @@ concurrent connections do not reuse the same ticket. #### Privacy and Security -As mentioned above, ssl peer keys are not intended for storage in a file +As mentioned above, SSL peer keys are not intended for storage in a file system. They clearly show which hosts the user talked to. This is not only privacy relevant, but also has security implications as an attacker might find worthy targets among your peer keys. diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index aafa00f06482..37d028954e68 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -72,7 +72,7 @@ Passing in "creative octets" like newlines where they are not expected might trigger unexpected results. Before version 7.17.0, strings were not copied. Instead the user was forced -keep them available until libcurl no longer needed them. +to keep them available until libcurl no longer needed them. # OPTIONS diff --git a/docs/libcurl/curl_multi_socket_action.md b/docs/libcurl/curl_multi_socket_action.md index 4823c5ef6d2b..4d690fd97d26 100644 --- a/docs/libcurl/curl_multi_socket_action.md +++ b/docs/libcurl/curl_multi_socket_action.md @@ -92,7 +92,7 @@ to kickstart everything. To get one or more callbacks called. 7. Wait for activity on any of libcurl's sockets, use the timeout value your callback has been told. -8, When activity is detected, call curl_multi_socket_action() for the +8. When activity is detected, call curl_multi_socket_action() for the socket(s) that got action. If no activity is detected and the timeout expires, call curl_multi_socket_action(3) with *CURL_SOCKET_TIMEOUT*. @@ -103,7 +103,7 @@ call curl_multi_socket_action(3) with *CURL_SOCKET_TIMEOUT*. ~~~c int main(void) { - /* the event-library gets told when there activity on the socket 'fd', + /* the event-library gets told when there is activity on the socket 'fd', which we translate to a call to curl_multi_socket_action() */ int running = 0; int fd = 3; /* the descriptor that had action */ diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md index a2132ee5b367..73a16ae41204 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md @@ -30,7 +30,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_CTX_DATA, void *pointer); # DESCRIPTION -Data *pointer* to pass to the ssl context callback set by the option +Data *pointer* to pass to the SSL context callback set by the option CURLOPT_SSL_CTX_FUNCTION(3), this is the pointer you get as third parameter. diff --git a/include/curl/curl.h b/include/curl/curl.h index 76ba52525294..8009df4051ca 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1362,7 +1362,7 @@ typedef enum { CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), - /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + /* Set if we should verify the peer in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate @@ -1420,7 +1420,7 @@ typedef enum { */ CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), - /* Set if we should verify the Common name from the peer certificate in ssl + /* Set if we should verify the Common name from the peer certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), @@ -1524,12 +1524,12 @@ typedef enum { Note that setting multiple bits may cause extra network round-trips. */ CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), - /* Set the ssl context callback function, currently only for OpenSSL or + /* Set the SSL context callback function, currently only for OpenSSL or wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. The function must match the curl_ssl_ctx_callback prototype. */ CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), - /* Set the userdata for the ssl context callback function's third + /* Set the userdata for the SSL context callback function's third argument */ CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109), @@ -1935,11 +1935,11 @@ typedef enum { /* Set authentication options directly */ CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), - /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS NPN extension (http2 over SSL might fail without) */ CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, 7.86.0, "Has no function"), - /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS ALPN extension (http2 over SSL might fail without) */ CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), /* Time to wait for a response to an HTTP request containing an @@ -2012,11 +2012,11 @@ typedef enum { this option is used only if PROXY_SSL_VERIFYPEER is true */ CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), - /* Set if we should verify the proxy in ssl handshake, + /* Set if we should verify the proxy in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), - /* Set if we should verify the Common name from the proxy certificate in ssl + /* Set if we should verify the Common name from the proxy certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches * the provided hostname. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), diff --git a/lib/bufq.h b/lib/bufq.h index da411b586d86..c53749d84e36 100644 --- a/lib/bufq.h +++ b/lib/bufq.h @@ -204,6 +204,7 @@ void Curl_bufq_skip(struct bufq *q, size_t amount); typedef CURLcode Curl_bufq_writer(void *writer_ctx, const uint8_t *buf, size_t len, size_t *pwritten); + /** * Passes the chunks in the buffer queue to the writer and returns * the amount of buf written. A writer may return -1 and CURLE_AGAIN diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 6733010468ad..25dc653e496a 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -80,7 +80,7 @@ static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8) } #endif -/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ +/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old Windows */ #if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \ (_WIN32_WINNT < _WIN32_WINNT_WIN10) WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *); diff --git a/lib/dict.c b/lib/dict.c index 7b83c6cff193..db25d5a7216c 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -148,7 +148,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ - /* url-decode path before further evaluation */ + /* URL-decode path before further evaluation */ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); if(result) return result; diff --git a/lib/ftp-int.h b/lib/ftp-int.h index 68d26f332721..8d7e14127488 100644 --- a/lib/ftp-int.h +++ b/lib/ftp-int.h @@ -113,10 +113,10 @@ struct ftp_conn { char *account; char *alternative_to_user; char *entrypath; /* the PWD reply when we logged on */ - const char *file; /* url-decoded filename (or path), points into rawpath */ + const char *file; /* URL-decoded filename (or path), points into rawpath */ char *rawpath; /* URL decoded, allocated, version of the path */ struct pathcomp *dirs; /* allocated array for path components */ - char *prevpath; /* url-decoded conn->path from the previous transfer */ + char *prevpath; /* URL-decoded conn->path from the previous transfer */ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a and others (A/I or zero) */ char *server_os; /* The target server operating system. */ diff --git a/lib/url.c b/lib/url.c index 31f5d948d850..d6e98804b4f7 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2857,7 +2857,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) * remaining parts like the cloned SSL configuration. */ result = Curl_ssl_conn_config_init(data, needle); if(result) { - DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); + DEBUGF(curl_mfprintf(stderr, "Error: init connection SSL config\n")); goto out; } /* attach it and no longer own it */ diff --git a/lib/urldata.h b/lib/urldata.h index 883e3cec31f2..63d231dc5cf3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -175,7 +175,7 @@ struct ssl_primary_config { struct ssl_config_data { struct ssl_primary_config primary; long certverifyresult; /* result from the certificate verification */ - curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ + curl_ssl_ctx_callback fsslctx; /* function to initialize SSL ctx */ void *fsslctxp; /* parameter for call back */ BIT(certinfo); /* gather lots of certificate info */ BIT(earlydata); /* use TLS 1.3 early data */ @@ -887,7 +887,7 @@ enum dupstring { STRING_SET_REFERER, /* custom string for the HTTP referer field */ STRING_SET_URL, /* what original URL to work on */ STRING_USERAGENT, /* User-Agent string */ - STRING_SSL_ENGINE, /* name of ssl engine */ + STRING_SSL_ENGINE, /* name of SSL engine */ STRING_USERNAME, /* , if used */ STRING_PASSWORD, /* , if used */ STRING_OPTIONS, /* , if used */ diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index bd33dceb55f3..354b31882b11 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -252,7 +252,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from schannel. + /* SSL context comes from schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc * otherwise IIS does not pass the authentication (401 response). diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 8808631e49a4..d591bd53397e 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -191,7 +191,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from Schannel. + /* SSL context comes from Schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc * otherwise IIS does not pass the authentication (401 response). diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 390570bacda1..51c19267bd8e 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -973,7 +973,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, result = (*data->set.ssl.fsslctx)(data, &backend->config, data->set.ssl.fsslctxp); if(result) - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); } return result; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 2302ddacc66c..b4a0f9684f01 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -33,7 +33,7 @@ #include "curl_trc.h" #include "httpsrr.h" #include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ +#include "url.h" /* for the SSL config check function */ #include "curlx/inet_pton.h" #include "vtls/openssl.h" #include "connect.h" @@ -1188,13 +1188,13 @@ static int engineload(struct Curl_easy *data, /* Does the engine supports LOAD_CERT_CTRL ? */ if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, CURL_UNCONST(cmd_name), NULL)) { - failf(data, "ssl engine does not support loading certificates"); + failf(data, "SSL engine does not support loading certificates"); return 0; } /* Load the certificate from the engine */ if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, 0, ¶ms, NULL, 1)) { - failf(data, "ssl engine cannot load client cert with id '%s' [%s]", + failf(data, "SSL engine cannot load client cert with id '%s' [%s]", cert_file, ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); @@ -1202,7 +1202,7 @@ static int engineload(struct Curl_easy *data, } if(!params.cert) { - failf(data, "ssl engine did not initialized the certificate properly."); + failf(data, "SSL engine did not initialized the certificate properly."); return 0; } @@ -2065,7 +2065,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, break; default: DEBUGASSERT(0); - failf(data, "unexpected ssl peer type: %d", peer->type); + failf(data, "unexpected SSL peer type: %d", peer->type); return CURLE_PEER_FAILED_VERIFICATION; } @@ -3949,7 +3949,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, FALSE); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); return result; } } diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 717058c6573e..44a0218ff5c4 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -183,7 +183,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, /* * Get the server cert, verify it and show it, etc., only call failf() if - * ssl config verifypeer or -host is set. Otherwise all this is for + * SSL config verifypeer or -host is set. Otherwise all this is for * informational purposes only! */ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 484696dffeb1..f15f2956d614 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -104,7 +104,7 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, curl_sslbackend Curl_ssl_backend(void); /** - * Init ssl config for a new easy handle. + * Init SSL config for a new easy handle. */ void Curl_ssl_easy_config_init(struct Curl_easy *data); diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index cf270ba413a0..bfb0677e8448 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -83,7 +83,7 @@ CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, char **ppeer_key); /* Return if there is a session cache shall be used. - * An ssl session might not be configured or not available for + * An SSL session might not be configured or not available for * "connect-only" transfers. */ bool Curl_ssl_scache_use(struct Curl_cfilter *cf, struct Curl_easy *data); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 90fc33173dc1..26d260ae0fa7 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1440,7 +1440,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx, data->set.ssl.fsslctxp); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); goto out; } } diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 5948440201db..aa9274fd25bc 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -33,7 +33,7 @@ AC_DEFUN([CURL_WITH_OPENSSL], [ if test "x$OPT_OPENSSL" != "xno"; then ssl_msg= - dnl backup the pre-ssl variables + dnl backup the pre-detection variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" @@ -315,7 +315,7 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" = "1"; then if test -n "$LIB_OPENSSL"; then - dnl when the ssl shared libs were found in a path that the runtime + dnl when the SSL shared libs were found in a path that the runtime dnl linker does not search through, we need to add it to CURL_LIBRARY_PATH dnl to prevent further configure tests to fail due to this if test "$cross_compiling" != "yes"; then diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index 9ca3d678e14a..cf682e43d350 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -30,7 +30,7 @@ dnl ---------------------------------------------------- if test "x$OPT_RUSTLS" != "xno"; then ssl_msg= - dnl backup the pre-ssl variables + dnl backup the pre-detection variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" diff --git a/projects/vms/generate_config_vms_h_curl.com b/projects/vms/generate_config_vms_h_curl.com index e4d97fd566c9..0a651b1a99d4 100644 --- a/projects/vms/generate_config_vms_h_curl.com +++ b/projects/vms/generate_config_vms_h_curl.com @@ -67,7 +67,7 @@ $if f$locate(",nossl,", args_lower) .lt. args_len then nossl = 1 $if .not. nossl $then $! -$! ssl$* logicals means HP ssl is present +$! ssl$* logicals means HP SSL is present $!---------------------------------------- $ if f$trnlnm("ssl$root") .nes. "" $ then @@ -96,7 +96,7 @@ $ nohpssl = 1 $ hpssl = 0 $ endif $! -$! Finally check to see if hp ssl has been specifically included. +$! Finally check to see if HP SSL has been specifically included. $!---------------------------------------------------------------- $ if f$locate(",nohpssl,", args_lower) .lt. args_len $ then diff --git a/scripts/badwords.txt b/scripts/badwords.txt index a02a948bd8df..4f806eb71821 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -14,8 +14,10 @@ run-time:runtime set-up:setup tool chain:toolchain tool-chain:toolchain +well known:well-known wild-card:wildcard wild card:wildcard +threadsafe:thread-safe thread safe:thread-safe thread safety:thread-safety thread unsafe:thread-unsafe diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 6795fdf05abf..76c5ba4e3f82 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -549,7 +549,7 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, * 4. Windows Directory (e.g. C:\Windows) * 5. all directories along %PATH% * - * For WinXP and later search order actually depends on registry value: + * For Windows XP and later search order actually depends on registry value: * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode */ CURLcode FindWin32CACert(struct OperationConfig *config, diff --git a/src/tool_ssls.c b/src/tool_ssls.c index a492d8e1e3d9..ac556a791c31 100644 --- a/src/tool_ssls.c +++ b/src/tool_ssls.c @@ -85,7 +85,7 @@ CURLcode tool_ssls_load(struct OperationConfig *config, c = memchr(line, ':', strlen(line)); if(!c) { - warnf("unrecognized line %d in ssl session file %s", i, filename); + warnf("unrecognized line %d in SSL session file %s", i, filename); continue; } *c = '\0'; diff --git a/tests/appveyor.pm b/tests/appveyor.pm index f332bc2b277a..9cfc5a9b2d88 100644 --- a/tests/appveyor.pm +++ b/tests/appveyor.pm @@ -32,9 +32,9 @@ BEGIN { use base qw(Exporter); our @EXPORT = qw( - appveyor_check_environment - appveyor_create_test_result - appveyor_update_test_result + appveyor_check_environment + appveyor_create_test_result + appveyor_update_test_result ); } @@ -48,12 +48,12 @@ sub appveyor_check_environment { } sub appveyor_create_test_result { - my ($curl, $testnum, $testname)=@_; + my ($curl, $testnum, $testname) = @_; $testname =~ s/\\/\\\\/g; $testname =~ s/\"/\\\"/g; $testname =~ s/\'/'"'"'/g; - my $appveyor_baseurl="$ENV{'APPVEYOR_API_URL'}"; - my $appveyor_result=`$curl --silent --noproxy '*' \\ + my $appveyor_baseurl = $ENV{'APPVEYOR_API_URL'}; + my $appveyor_result = `$curl --silent --noproxy '*' \\ --header 'Content-Type: application/json' \\ --data ' { @@ -69,8 +69,8 @@ sub appveyor_create_test_result { } sub appveyor_update_test_result { - my ($curl, $testnum, $error, $start, $stop)=@_; - my $testname=$APPVEYOR_TEST_NAMES{$testnum}; + my ($curl, $testnum, $error, $start, $stop) = @_; + my $testname = $APPVEYOR_TEST_NAMES{$testnum}; if(!defined $testname) { return; } @@ -96,8 +96,8 @@ sub appveyor_update_test_result { $appveyor_outcome = 'Failed'; $appveyor_category = 'Error'; } - my $appveyor_baseurl="$ENV{'APPVEYOR_API_URL'}"; - my $appveyor_result=`$curl --silent --noproxy '*' --request PUT \\ + my $appveyor_baseurl = $ENV{'APPVEYOR_API_URL'}; + my $appveyor_result = `$curl --silent --noproxy '*' --request PUT \\ --header 'Content-Type: application/json' \\ --data ' { @@ -112,7 +112,7 @@ sub appveyor_update_test_result { '$appveyor_baseurl/api/tests'`; print "AppVeyor API result: $appveyor_result\n" if($appveyor_result); if($appveyor_category eq 'Error') { - $appveyor_result=`$curl --silent --noproxy '*' \\ + $appveyor_result = `$curl --silent --noproxy '*' \\ --header 'Content-Type: application/json' \\ --data ' { diff --git a/tests/azure.pm b/tests/azure.pm index 2810f48e1777..0c9bccb2d46a 100644 --- a/tests/azure.pm +++ b/tests/azure.pm @@ -53,9 +53,9 @@ sub azure_check_environment { } sub azure_create_test_run { - my ($curl)=@_; - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_run=`$curl --silent --noproxy "*" \\ + my ($curl) = @_; + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_run = `$curl --silent --noproxy "*" \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header "Content-Type: application/json" \\ --data " @@ -73,13 +73,13 @@ sub azure_create_test_run { } sub azure_create_test_result { - my ($curl, $azure_run_id, $testnum, $testname)=@_; + my ($curl, $azure_run_id, $testnum, $testname) = @_; $testname =~ s/\\/\\\\/g; $testname =~ s/\"/\\\"/g; $testname =~ s/\'/'"'"'/g; - my $title_testnum=sprintf("%04d", $testnum); - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_result=`$curl --silent --noproxy '*' \\ + my $title_testnum = sprintf("%04d", $testnum); + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_result = `$curl --silent --noproxy '*' \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header 'Content-Type: application/json' \\ --data ' @@ -102,7 +102,7 @@ sub azure_create_test_result { } sub azure_update_test_result { - my ($curl, $azure_run_id, $azure_result_id, $testnum, $error, $start, $stop)=@_; + my ($curl, $azure_run_id, $azure_result_id, $testnum, $error, $start, $stop) = @_; if(!defined $stop) { $stop = $start; } @@ -122,8 +122,8 @@ sub azure_update_test_result { else { $azure_outcome = 'Failed'; } - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_result=`$curl --silent --noproxy '*' --request PATCH \\ + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_result = `$curl --silent --noproxy '*' --request PATCH \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header "Content-Type: application/json" \\ --data ' @@ -145,9 +145,9 @@ sub azure_update_test_result { } sub azure_update_test_run { - my ($curl, $azure_run_id)=@_; - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_run=`$curl --silent --noproxy '*' --request PATCH \\ + my ($curl, $azure_run_id) = @_; + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_run = `$curl --silent --noproxy '*' --request PATCH \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header 'Content-Type: application/json' \\ --data ' diff --git a/tests/data/test3207 b/tests/data/test3207 index 521a1047f06c..ba15b1a47975 100644 --- a/tests/data/test3207 +++ b/tests/data/test3207 @@ -32,7 +32,7 @@ OpenSSL https -concurrent HTTPS GET using shared ssl session cache +concurrent HTTPS GET using shared SSL session cache lib%TESTNUMBER diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index 169df8015e61..89c66278d485 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -397,7 +397,7 @@ def test_10_15_proxy_ip_addr(self, env: Env, httpd): xargs.append('-6') r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, extra_args=xargs) - r.check_exit_code(0), f'{r}' + r.check_exit_code(0) r.check_response(count=1, http_status=200, protocol='HTTP/1.1') # download via http: ipv6 proxy (no tunnel) using IP address, IPv4 only @@ -411,7 +411,7 @@ def test_10_16_proxy_ip_addr(self, env: Env, httpd): xargs.append('-4') r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, extra_args=xargs) - r.check_exit_code(0), f'{r}' + r.check_exit_code(0) r.check_response(count=1, http_status=200, protocol='HTTP/1.1') # download via http: proxy (no tunnel), check connection reuse diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index fca5a7dec110..8e6f174de5c5 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -587,7 +587,7 @@ static CURLcode test_cli_hx_download(const char *URL) } if(t->result) result = t->result; - else /* on success we expect ssl to have been checked */ + else /* on success we expect SSL to have been checked */ assert(t->checked_ssl); } curlx_free(transfer_d); diff --git a/tests/libtest/first.h b/tests/libtest/first.h index d0b22df79b61..7c4bb2df46aa 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -103,7 +103,7 @@ void ws_close(CURL *curl); /* just close the connection */ #endif /* - * TEST_ERR_* values must within the CURLcode range to not cause compiler + * TEST_ERR_* values must be within the CURLcode range to not cause compiler * errors. * * For portability reasons TEST_ERR_* values should be less than 127. diff --git a/tests/libtest/lib3207.c b/tests/libtest/lib3207.c index ae2a3a18d1f5..9ef77edf4667 100644 --- a/tests/libtest/lib3207.c +++ b/tests/libtest/lib3207.c @@ -74,7 +74,7 @@ static unsigned int test_thread(void *ptr) int i; /* Loop the transfer and cleanup the handle properly every lap. This will - still reuse ssl session since the pool is in the shared object! */ + still reuse SSL session since the pool is in the shared object! */ for(i = 0; i < PER_THREAD_SIZE; i++) { CURL *curl = curl_easy_init(); if(curl) { diff --git a/tests/runner.pm b/tests/runner.pm index 115d078a1ff3..4b6b12b0b87b 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -29,7 +29,7 @@ # (in controlleripccall) which is later read from and the arguments # unmarshalled (in ipcrecv) before the desired function is called normally. # The function return values are then marshalled and written into another pipe -# (again in ipcrecv) when is later read from and unmarshalled (in runnerar) +# (again in ipcrecv) which is later read from and unmarshalled (in runnerar) # before being returned to the caller. package runner; @@ -251,7 +251,7 @@ sub runner_init { ####################################################################### # Loop to execute incoming IPC calls until the shutdown call sub event_loop { - while() { + while(1) { if(ipcrecv()) { last; } @@ -666,7 +666,7 @@ sub singletest_setenv { if($s =~ /([^=]*)(.*)/) { my ($var, $content) = ($1, $2); # remember current setting, to restore it once test runs - $oldenv{$var} = ($ENV{$var}) ? "$ENV{$var}" : 'notset'; + $oldenv{$var} = $ENV{$var} ? $ENV{$var} : 'notset'; if($content =~ /^=(.*)/) { # assign it diff --git a/tests/runtests.pl b/tests/runtests.pl index b0230330c80f..e6b343c81560 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -664,9 +664,9 @@ sub checksystemfeatures { $feature{"TrackMemory"} = $feat =~ /\bDebug/; # curl was built with --enable-debug $feature{"Debug"} = $feat =~ /\bDebug/; - # ssl enabled + # SSL enabled $feature{"SSL"} = $feat =~ /SSL/i; - # multiple ssl backends available. + # multiple SSL backends available. $feature{"MultiSSL"} = $feat =~ /MultiSSL/i; # large file support $feature{"Largefile"} = $feat =~ /Largefile/i; @@ -3054,7 +3054,7 @@ sub displaylogs { $retry_left = $retry; } -while() { +while(1) { # check the abort flag if($globalabort) { logmsg singletest_dumplogs(); diff --git a/tests/servers.pm b/tests/servers.pm index 07f08d994a60..db01c1f501df 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -127,7 +127,7 @@ my %PORT = (nolisten => 47); # port we use for a local non-listening service my $server_response_maxtime=13; my $httptlssrv = find_httptlssrv(); my %run; # running server -my %runcert; # cert file currently in use by an ssl running server +my %runcert; # cert file currently in use by an SSL running server my $CLIENTIP="127.0.0.1"; # address which curl uses for incoming connections my $CLIENT6IP="[::1]"; # address which curl uses for incoming connections my $posix_pwd = build_sys_abs_path($pwd); # current working directory in POSIX format @@ -435,11 +435,11 @@ sub stopserver { # my @killservers; if($server =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) { - # given a stunnel based ssl server, also kill non-ssl underlying one + # given a stunnel based SSL server, also kill non-SSL underlying one push @killservers, "${1}${2}"; } elsif($server =~ /^(ftp|http|imap|pop3|smtp)((\d*)(-ipv6|-unix|))$/) { - # given a non-ssl server, also kill stunnel based ssl piggybacking one + # given a non-SSL server, also kill stunnel based SSL piggybacking one push @killservers, "${1}s${2}"; } elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) { From 68e0b1320985202f343e8a83c33516a01c5ea537 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 18:58:47 +0200 Subject: [PATCH 0166/1062] runner.pm: apply minor correctness fix "Lines 244-245 overwrite global variables `$runnerr` and `$runnerw` that were already assigned in the child process (lines 205-206). In the parent process context, these assignments appear incorrect and could cause issues if `runner_init` is called multiple times. The parent should only store references in the controller hashes." It could never cause an actual issue, but clarifies the intent of the code. Spotted and fixed by GitHub Code Quality Cherry-picked from #21646 Closes #21672 --- tests/runner.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/runner.pm b/tests/runner.pm index 4b6b12b0b87b..a3988009462e 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -241,8 +241,10 @@ sub runner_init { } $controllerw{$thisrunnerid} = $thiscontrollerw; - $runnerr = $thisrunnerr; - $runnerw = $thisrunnerw; + if(!$multiprocess) { + $runnerr = $thisrunnerr; + $runnerw = $thisrunnerw; + } $controllerr{$thisrunnerid} = $thiscontrollerr; return $thisrunnerid; From 25a70e18c17fde77b1c91af328fd6e99aec2f017 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 01:15:47 +0000 Subject: [PATCH 0167/1062] Dockerfile: update debian:bookworm-slim Docker digest to 0104b33 Closes #21687 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c6021752b081..33c46309ba37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:67b30a61dc87758f0caf819646104f29ecbda97d920aaf5edc834128ac8493d3 +FROM debian:bookworm-slim@sha256:0104b334637a5f19aa9c983a91b54c89887c0984081f2068983107a6f6c21eeb RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From b158d1c9f7456a8f976c74c08d2dc5a555e9cc77 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 19 May 2026 19:05:41 +0200 Subject: [PATCH 0168/1062] GHA/non-native: move BSDs to a single matrix, add DragonFly and Midnight - bump cross-platform-actions to v1.1.0. Ref: https://github.com/cross-platform-actions/action/releases/tag/v1.1.0 - merge BSD jobs into a single matrix. - split BSD jobs into build steps as used for other platforms. A new feature of cross-platform-actions v1.1.0. - sync BSD build steps with other platforms. - add DragonFlyBSD and MidnightBSD to the BSD matrix. New features of cross-platform-actions v1.1.0. MidnightBSD uses GnuTLS to add variation, also the preinstalled OpenSSL is too old (v1.1.1w) for curl. Stick with autotools for DragonFlyBSD; I could not figure out how to install cmake. Refs: https://en.wikipedia.org/wiki/DragonFly_BSD https://en.wikipedia.org/wiki/MidnightBSD - bump Intel FreeBSD jobs from v14.3 to v15.0. - fix to show `gcc` in the NetBSD job name. All these saved 50 lines of YAML. The two new jobs take 2m15s each. The bump to FreeBSD 15 needs and extra minute in total. Note, the DragonFlyBSD job seems to have reliability issues. If it remains an issue, I'll comment it out or delete it in a future commit. Closes #21681 --- .github/scripts/typos.toml | 2 +- .github/workflows/non-native.yml | 301 +++++++++++++------------------ .github/workflows/windows.yml | 1 - 3 files changed, 128 insertions(+), 176 deletions(-) diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 8d8511953a81..3192a2987cf7 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -6,7 +6,7 @@ extend-ignore-identifiers-re = [ "^(ba|fo|pn|PN|UE)$", "^(CNA|cpy|ser)$", - "^(ECT0|ECT1|HELO|htpts|PASE)$", + "^(ECT0|ECT1|HELO|htpts|mport|PASE)$", "^[A-Za-z0-9_-]*(EDE|GOST)[A-Z0-9_-]*$", # ciphers "^0x[0-9a-fA-F]+FUL$", # unsigned long hex literals ending with 'F' "^[0-9a-zA-Z+]{64,}$", # possibly base64 diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 7907310940bb..c61aa0ad69f6 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -37,204 +37,157 @@ env: DO_NOT_TRACK: '1' jobs: - freebsd: - name: "FreeBSD, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} openssl${{ matrix.desc }} ${{ matrix.arch }}" + cross: + name: "${{ matrix.os }} ${{ matrix.version }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.cc }} ${{ matrix.desc }} ${{ matrix.arch }}" runs-on: ubuntu-latest timeout-minutes: 15 + defaults: + run: + shell: cpa.sh {0} # zizmor: ignore[misfeature] + env: + CC: '${{ matrix.cc }}' + MAKEFLAGS: -j 3 + MATRIX_ARCH: '${{ matrix.arch }}' + MATRIX_BUILD: '${{ matrix.build }}' + MATRIX_OPTIONS: '${{ matrix.options }}' + MATRIX_OS: '${{ matrix.os }}' strategy: matrix: include: - - { build: 'autotools', arch: 'x86_64', compiler: 'clang' } - - { build: 'cmake' , arch: 'x86_64', compiler: 'clang', options: '-DCMAKE_UNITY_BUILD=OFF', desc: ' !unity !runtests !examples' } - - { build: 'autotools', arch: 'arm64' , compiler: 'clang', desc: ' !examples' } - - { build: 'cmake' , arch: 'arm64' , compiler: 'clang' } + - { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl !runtests', + options: '--with-openssl' } + - { os: 'freebsd', version: '15.0', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', + options: '--with-openssl --with-gssapi' } + - { os: 'freebsd', version: '15.0', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity !runtests !examples', + options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } + - { os: 'freebsd', version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', + options: '--with-openssl --with-gssapi' } + - { os: 'freebsd', version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + options: '-DCURL_USE_GSSAPI=ON' } + - { os: 'midnightbsd', version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls !runtests', + options: '-DCURL_USE_GNUTLS=ON' } + - { os: 'netbsd', version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', + options: '-DCURL_USE_GSSAPI=ON' } + - { os: 'openbsd', version: '7.7' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl' } fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: '${{ matrix.build }}' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - CC: '${{ matrix.compiler }}' - MATRIX_ARCH: '${{ matrix.arch }}' - MATRIX_BUILD: '${{ matrix.build }}' - MATRIX_DESC: '${{ matrix.desc }}' - MATRIX_OPTIONS: '${{ matrix.options }}' + + - name: 'setup VM' + uses: cross-platform-actions/action@0c165ad7eb2d6a7e8552d6af5aad2bbedfc646b0 # v1.1.0 with: - environment_variables: CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH MATRIX_BUILD MATRIX_DESC MATRIX_OPTIONS - operating_system: 'freebsd' - version: '14.3' - architecture: ${{ matrix.arch }} - run: | - export CURL_CI=github + environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_OPTIONS MATRIX_OS' + operating_system: '${{ matrix.os }}' + version: '${{ matrix.version }}' + architecture: '${{ matrix.arch }}' + - name: 'install prereqs' + run: | + if [ "${MATRIX_OS}" = 'dragonflybsd' ]; then + sudo pkg install -y autoconf automake libtool perl5 pkgconf brotli openldap26-client libidn2 libnghttp2 + elif [ "${MATRIX_OS}" = 'freebsd' ]; then # https://ports.freebsd.org/ if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time sudo pkg install -y cmake-core ninja perl5 \ - pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket + tools='cmake-core ninja perl5' else - time sudo pkg install -y autoconf automake libtool \ - pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket - export MAKEFLAGS=-j3 + tools='autoconf automake libtool' fi + sudo pkg install -y ${tools} pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket + elif [ "${MATRIX_OS}" = 'midnightbsd' ]; then + # https://app.midnightbsd.org/ + # https://man.midnightbsd.org/cgi-bin/man.cgi/mport + sudo mport -q install cmake-core ninja perl5 pkgconf brotli gnutls openldap26-client libidn2 libnghttp2 | grep -E '(Downloading.+100|Installing)' || true + elif [ "${MATRIX_OS}" = 'netbsd' ]; then + # https://pkgsrc.se/ + sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket + elif [ "${MATRIX_OS}" = 'openbsd' ]; then + # https://openbsd.app/ + # https://www.openbsd.org/faq/faq15.html + sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket + fi - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_C_COMPILER="${CC}" \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_USE_GSSAPI=ON \ - ${MATRIX_OPTIONS} \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - else - time autoreconf -fi - if [ "${MATRIX_ARCH}" != 'x86_64' ]; then - options='--disable-manual --disable-docs' # Slow with autotools, skip on emulated CPU - fi - mkdir bld && cd bld - time ../configure --prefix="$HOME"/curl-install --enable-unity --enable-debug --enable-warnings --enable-werror --disable-static \ - --disable-dependency-tracking --enable-option-checking=fatal \ - --with-openssl \ - --with-brotli --enable-ldap --enable-ldaps --with-libidn2 --with-libssh2 --with-nghttp2 --with-gssapi \ - ${options} \ - ${MATRIX_OPTIONS} \ - || { tail -n 1000 config.log; false; } - cd .. + - name: 'autoreconf' + if: ${{ matrix.build == 'autotools' }} + run: autoreconf -fi + + - name: 'configure' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake -B bld -G Ninja -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ + -DCMAKE_C_COMPILER="${CC}" \ + -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ + -DCURL_ENABLE_NTLM=ON ${MATRIX_OPTIONS} + else + if [ "${MATRIX_ARCH}" != 'x86_64' ]; then + options='--disable-manual --disable-docs' # Slow with autotools, skip on emulated CPU fi + mkdir bld && cd bld + ../configure --prefix="$HOME"/curl-install --enable-unity --enable-debug --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ + --with-brotli --enable-ldap --enable-ldaps --with-libidn2 --with-libssh2 --with-nghttp2 \ + ${options} ${MATRIX_OPTIONS} + fi - echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' + - name: 'configure log' + if: ${{ !cancelled() }} + run: cat bld/config.log bld/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld - time cmake --install bld - else - time make -C bld install - fi + - name: 'curl_config.h' + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - bld/src/curl --disable --version - - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --target testdeps - else - time make -C bld -C tests - fi - if [ "${MATRIX_DESC#*!runtests*}" = "${MATRIX_DESC}" ]; then - export TFLAGS='-j8' - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --verbose --target test-ci - else - time make -C bld V=1 test-ci - fi - fi - fi + - name: 'build' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld + else + make -C bld + fi - if [ "${MATRIX_DESC#*!examples*}" = "${MATRIX_DESC}" ]; then - echo '::group::build examples' - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --target curl-examples-build - else - time make -C bld examples - fi - echo '::endgroup::' - fi + - name: 'curl -V' + run: bld/src/curl --disable --version - netbsd: - name: 'NetBSD, CM clang openssl ${{ matrix.arch }}' - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - arch: ['x86_64'] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' - with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH - operating_system: 'netbsd' - version: '10.1' - architecture: ${{ matrix.arch }} - run: | - # https://pkgsrc.se/ - time sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_USE_GSSAPI=ON \ - -DCURL_ENABLE_NTLM=ON \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8' - time cmake --build bld --target test-ci - fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' + - name: 'curl install' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --install bld + else + make -C bld install + fi - openbsd: - name: 'OpenBSD, CM clang libressl ${{ matrix.arch }}' - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - arch: ['x86_64'] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' - with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH - operating_system: 'openbsd' - version: '7.7' - architecture: ${{ matrix.arch }} - run: | - # https://openbsd.app/ - # https://www.openbsd.org/faq/faq15.html - time sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_ENABLE_NTLM=ON \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8 !2707' # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs - time cmake --build bld --target test-ci - fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' + - name: 'build tests' + if: ${{ matrix.arch == 'x86_64' }} # Slow on emulated CPU + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target testdeps + else + make -C bld -C tests + fi + + - name: 'run tests' + if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, '!runtests') }} # Slow on emulated CPU + run: | + export TFLAGS='-j8' + if [ "${MATRIX_OS}" = 'openbsd' ]; then + TFLAGS="$TFLAGS !2707" # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs + fi + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --verbose --target test-ci + else + make -C bld V=1 test-ci + fi + + - name: 'build examples' + if: ${{ !contains(matrix.desc, '!examples') }} + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target curl-examples-build + else + make -C bld examples + fi android: name: "Android ${{ matrix.platform }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.name }} arm64" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9074526bdb90..7243d542628d 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -93,7 +93,6 @@ jobs: build: 'cmake', platform: 'x86_64', tflags: '', config: '-DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON', install: 'libssl-devel libssh2-devel' } - fail-fast: false steps: - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6.1 From 77e4e5b86de025e3f87761282f0fdac286fa2750 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 20 May 2026 10:30:25 +0200 Subject: [PATCH 0169/1062] websockets: auto-tunnel through http proxy When using a ws: or wss: url with a http proxy, automatically switch to tunneling operation mode. Add test_20_10 to check. Fixes #21663 Closes #21691 --- lib/protocol.c | 7 ++++--- lib/protocol.h | 2 ++ lib/url.c | 7 +++++-- tests/http/test_20_websockets.py | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/protocol.c b/lib/protocol.c index 36ad97618103..8d57c058c6b7 100644 --- a/lib/protocol.c +++ b/lib/protocol.c @@ -153,7 +153,8 @@ const struct Curl_scheme Curl_scheme_https = { CURLPROTO_HTTPS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE | + PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS, /* defport */ }; @@ -442,7 +443,7 @@ const struct Curl_scheme Curl_scheme_ws = { CURLPROTO_WS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTP /* defport */ }; @@ -457,7 +458,7 @@ const struct Curl_scheme Curl_scheme_wss = { CURLPROTO_WSS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS /* defport */ }; diff --git a/lib/protocol.h b/lib/protocol.h index 8ae2155ee6e6..50e320d0f66c 100644 --- a/lib/protocol.h +++ b/lib/protocol.h @@ -237,6 +237,8 @@ struct Curl_protocol { without having PROTOPT_SSL. */ #define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ #define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */ +#define PROTOPT_HTTP_PROXY_TUNNEL (1 << 18) /* Using this protocol with a + * HTTP proxy requires tunneling */ /* Everything about a URI scheme. */ struct Curl_scheme { diff --git a/lib/url.c b/lib/url.c index d6e98804b4f7..57b063902168 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2018,8 +2018,11 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, data->state.envproxy = curlx_strdup(proxy); } #endif - /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { + if(conn->scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) { + conn->bits.tunnel_proxy = TRUE; + } + else if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { + /* force this connection's protocol to become HTTP if compatible */ if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && !conn->bits.tunnel_proxy) conn->scheme = &Curl_scheme_http; diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index fdc9df6eb7d2..416c342a6005 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -206,3 +206,17 @@ def test_20_09_data_empty(self, env: Env, ws_echo, model): large = 0 r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url]) r.check_exit_code(0) + + # use ws:// url with HTTP proxy, check that it tunnels automatically + def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): + curl = CurlClient(env=env) + url = f'ws://127.0.0.1:{env.ws_port}/' + xargs = curl.get_proxy_args(proxys=False) + xargs.extend([ + '--max-time', '2' + ]) + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + extra_args=xargs) + # The CONNECT through the proxy fails as it does not allow it + r.check_exit_code(7) # CURLE_COULDNT_CONNECT + assert r.stats[0]['http_connect'] == 403, f'{r}' From edfc80c7c473b9a09eba9e98e59c4d7d167bbb9e Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Wed, 20 May 2026 01:03:31 +0200 Subject: [PATCH 0170/1062] urlapi: compare zone-id in Curl_url_same_origin() Closes #21686 --- lib/urlapi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/urlapi.c b/lib/urlapi.c index dfb106dd2f0a..21f4bbfab1be 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -2024,6 +2024,9 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) if(href->host) { if(!curl_strequal(base->host, href->host)) return FALSE; + if(!curl_strequal(base->zoneid ? base->zoneid : "", + href->zoneid ? href->zoneid : "")) + return FALSE; if(!curl_strequal(base->port, href->port)) { /* This may still match if only one has an explicit port * and it is the default for the scheme. */ From 76e1da09891a1c2c436820f1c9aac826dab70721 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 03:42:49 +0200 Subject: [PATCH 0171/1062] GHA/non-native: drop DragonFlyBSD job, due to unreliable package repo updates Example: ``` Wed, 20 May 2026 09:51:48 GMT Updating Avalon repository catalogue... Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT repository Avalon has no meta file, using default settings Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT pkg: An error occurred while fetching package: No error Wed, 20 May 2026 09:51:48 GMT Unable to update repository Avalon Wed, 20 May 2026 09:51:48 GMT Error updating repositories! Wed, 20 May 2026 09:51:48 GMT Error: Process completed with exit code 3. ``` As tested over at libssh2, retrying the install command also does not help, only repeats the same failure. Also: fix whitespace in matrix. Follow-up to b158d1c9f7456a8f976c74c08d2dc5a555e9cc77 #21681 Closes #21694 --- .github/workflows/non-native.yml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c61aa0ad69f6..b8b76e710749 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -54,21 +54,19 @@ jobs: strategy: matrix: include: - - { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl !runtests', - options: '--with-openssl' } - - { os: 'freebsd', version: '15.0', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', + - { os: 'freebsd' , version: '15.0', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', options: '--with-openssl --with-gssapi' } - - { os: 'freebsd', version: '15.0', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity !runtests !examples', + - { os: 'freebsd' , version: '15.0', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity !runtests !examples', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd', version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', options: '--with-openssl --with-gssapi' } - - { os: 'freebsd', version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', options: '-DCURL_USE_GSSAPI=ON' } - - { os: 'midnightbsd', version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls !runtests', + - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls !runtests', options: '-DCURL_USE_GNUTLS=ON' } - - { os: 'netbsd', version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', + - { os: 'netbsd' , version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', options: '-DCURL_USE_GSSAPI=ON' } - - { os: 'openbsd', version: '7.7' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl' } + - { os: 'openbsd' , version: '7.7' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl' } fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -85,9 +83,7 @@ jobs: - name: 'install prereqs' run: | - if [ "${MATRIX_OS}" = 'dragonflybsd' ]; then - sudo pkg install -y autoconf automake libtool perl5 pkgconf brotli openldap26-client libidn2 libnghttp2 - elif [ "${MATRIX_OS}" = 'freebsd' ]; then + if [ "${MATRIX_OS}" = 'freebsd' ]; then # https://ports.freebsd.org/ if [ "${MATRIX_BUILD}" = 'cmake' ]; then tools='cmake-core ninja perl5' From f902c3c4860d9b57fb57aede482c57938a2a6a48 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 12:01:40 +0200 Subject: [PATCH 0172/1062] Dockerfile: fix typo in variable name Did not cause an actual issue. Spotted by GitHub Code Quality Follow-up to 41c03b4c98dbc639a32d32486ed5146be2e73ee1 #13250 Closes #21693 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 33c46309ba37..213caebc9ab8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ ARG UID=1000 GID=1000 -RUN groupadd --gid $UID dev && \ +RUN groupadd --gid $GID dev && \ useradd --uid $UID --gid dev --shell /bin/bash --create-home dev USER dev:dev From 88c7e16cceec816a2df45c899d49b1e85513f193 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 May 2026 13:39:25 +0200 Subject: [PATCH 0173/1062] setopt: clear proxy auth properly on NULL Verify NULLed proxy credentials with test1648 Closes #21696 --- lib/setopt.c | 12 ++-- tests/data/Makefile.am | 2 +- tests/data/test1648 | 63 +++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib1648.c | 135 +++++++++++++++++++++++++++++++++++++ 5 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 tests/data/test1648 create mode 100644 tests/libtest/lib1648.c diff --git a/lib/setopt.c b/lib/setopt.c index 5a3e02c76fa2..067a8450ded0 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1661,16 +1661,16 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, result = setstropt_userpwd(ptr, &u, &p); /* URL decode the components */ - if(!result && u) { + if(!result) { curlx_safefree(s->str[STRING_PROXYUSERNAME]); - result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); - } - if(!result && p) { curlx_safefree(s->str[STRING_PROXYPASSWORD]); + if(u) + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, REJECT_ZERO); - } curlx_free(u); curlx_free(p); break; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index cde5c2873698..2621c4dc7b43 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -216,7 +216,7 @@ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 \ test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 \ -test1644 test1645 test1646 test1647 \ +test1644 test1645 test1646 test1647 test1648 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1648 b/tests/data/test1648 new file mode 100644 index 000000000000..623f3c9a81ae --- /dev/null +++ b/tests/data/test1648 @@ -0,0 +1,63 @@ + + + + +HTTP +HTTP GET +HTTP proxy +HTTP proxy auth + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + + + +# Client-side + + +http + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +proxy + + +HTTP proxy with auth, change proxy, clear auth + + +%HOSTIP %HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET http://example.com/ HTTP/1.1 +Host: example.com +Proxy-Authorization: Basic %b64[victim:secret]b64% +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://example.com/ HTTP/1.1 +Host: example.com +Accept: */* +Proxy-Connection: Keep-Alive + + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 586db5a2c95f..d3e194b0c7a3 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -99,7 +99,7 @@ TESTS_C = \ lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ - lib1647.c \ + lib1647.c lib1648.c \ lib1662.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1648.c b/tests/libtest/lib1648.c new file mode 100644 index 000000000000..e97b2bdc88ec --- /dev/null +++ b/tests/libtest/lib1648.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * URL = host + * arg2 = port + */ + +#include "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1648(CURL *curl, const char *url, const char *proxy) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY, proxy); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1648(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + result = init1648(curl, url, userpwd); + if(result) + return result; + + return curl_easy_perform(curl); +} + +#define GET_THIS "http://example.com/" + +/* + * First get the URL over 'firstproxy' with auth. + * Then clear the auth and get the URL again over 'secondproxy'. + */ +static CURLcode test_lib1648(const char *hostip) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + struct curl_slist *host = NULL; + struct curl_slist *host2 = NULL; + char proxy1_resolve[128]; + char proxy2_resolve[128]; + char proxy1_connect[128]; + char proxy2_connect[128]; + + curl_msnprintf(proxy1_resolve, sizeof(proxy1_resolve), + "firstproxy:%s:%s", libtest_arg2, hostip); + curl_msnprintf(proxy2_resolve, sizeof(proxy2_resolve), + "secondproxy:%s:%s", libtest_arg2, hostip); + + /* we connect to the fake host name but the right port number */ + curl_msnprintf(proxy1_connect, sizeof(proxy1_connect), + "firstproxy:%s", libtest_arg2); + curl_msnprintf(proxy2_connect, sizeof(proxy2_connect), + "secondproxy:%s", libtest_arg2); + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + host = curl_slist_append(NULL, proxy1_resolve); + if(!host) + goto test_cleanup; + host2 = curl_slist_append(host, proxy2_resolve); + if(!host2) + goto test_cleanup; + host = host2; + + start_test_timing(); + + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, "victim:secret"); + + curl_mprintf("--- First get over %s\n", proxy1_connect); + result = run1648(curl, GET_THIS, proxy1_connect); + if(result) + goto test_cleanup; + + easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL); + + curl_mprintf("--- Then over '%s'\n", proxy2_connect); + result = run1648(curl, GET_THIS, proxy2_connect); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(host); + return result; +} From c30db7b6be6c19e1841710eaf3babe6847f6cd02 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 18:43:41 +0200 Subject: [PATCH 0174/1062] cmake: quote `COMPONENTS` string in `curl-config.in.cmake` For consistency and for handling an accidental empty more gracefully. Follow-up to 7d546e52b21c94e1d4f6669d2d4d64f79bff0d7b #21540 Closes #21699 --- CMake/curl-config.in.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake index 1c0eec36ed81..b4300ca2108c 100644 --- a/CMake/curl-config.in.cmake +++ b/CMake/curl-config.in.cmake @@ -122,7 +122,7 @@ if("@USE_NGHTTP3@") list(APPEND _curl_libs CURL::nghttp3) endif() if("@USE_NGTCP2@") - find_dependency(NGTCP2 MODULE COMPONENTS @NGTCP2_CRYPTO_BACKEND@) + find_dependency(NGTCP2 MODULE COMPONENTS "@NGTCP2_CRYPTO_BACKEND@") list(APPEND _curl_libs CURL::ngtcp2) endif() if("@USE_GNUTLS@") From ba7b65f95736c5fbef3ccfaeb945273379e003ee Mon Sep 17 00:00:00 2001 From: penpal Date: Fri, 15 May 2026 23:38:29 +0545 Subject: [PATCH 0175/1062] sspi: clear SSPI credentials on AcquireCredentialsHandle failure - Clear credentials on AcquireCredentialsHandle failure so it is not used on a subsequent call. SSPI initialization may evaluate the credentials pointer to determine whether or not a prior call to AcquireCredentialsHandle was successful, therefore we must clear it on a failed call. Closes https://github.com/curl/curl/pull/21642 --- lib/vauth/krb5_sspi.c | 5 ++++- lib/vauth/ntlm_sspi.c | 5 ++++- lib/vauth/spnego_sspi.c | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index 506ee759df91..b41d0bcbada5 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -154,8 +154,11 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, krb5->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_free(krb5->credentials); + krb5->credentials = NULL; return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ krb5->context = curlx_calloc(1, sizeof(CtxtHandle)); diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 354b31882b11..06e3ec5ddfdf 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -139,8 +139,11 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, ntlm->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_free(ntlm->credentials); + ntlm->credentials = NULL; return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ ntlm->context = curlx_calloc(1, sizeof(CtxtHandle)); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index d591bd53397e..8ba2316d880b 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -159,8 +159,11 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, nego->credentials, NULL); - if(nego->status != SEC_E_OK) + if(nego->status != SEC_E_OK) { + curlx_free(nego->credentials); + nego->credentials = NULL; return CURLE_AUTH_ERROR; + } /* Allocate our new context handle */ nego->context = curlx_calloc(1, sizeof(CtxtHandle)); From 5e4e62962c0f8536c612a5248e2ac4eb7d67ae66 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 19 May 2026 15:31:45 +0200 Subject: [PATCH 0176/1062] cfilters: fix busy loop on blocked transfers When a transfer gets paused after the connection has been established, any data sitting in the kernel socket buffers will no longer get read. Prevent the sockets form being added to the pollsets, because they will trigger POLLIN endlessly and cause a busy poll loop. Same in event based processing. Reported-by: Harry Sintonen Fixes https://github.com/curl/curl/issues/21671 Closes https://github.com/curl/curl/pull/21675 --- lib/cfilters.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/cfilters.c b/lib/cfilters.c index f287ebfc7421..6f5793c833c1 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -889,8 +889,19 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(conn); - for(i = 0; (i < 2) && !result; ++i) { - result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + /* During connect time, connection filters may add sockets to the pollset + * even when the transfer neither wants to send nor receive. And those + * sockets, when having events, are served. + * Once connected however, a transfer that neither wants to send nor receive + * will never call the connection filters. Any sockets added by the filters + * will not change state and POLLIN/POLLOUT events will trigger forever, + * making us busy loop. See #21671 */ + if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) || + (conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(conn, SECONDARYSOCKET))) { + for(i = 0; (i < 2) && !result && conn; ++i) { + result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + } } return result; } From cce4d3b0edd98b9b10f62485f01d37b81fd46a00 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 23:32:26 +0200 Subject: [PATCH 0177/1062] schannel: fix revoke_best_effort setting for proxy - Fix revoke_best_effort reading wrong ssl config. Prior to this change the revoke_best_effort setting for the proxy was wrongly ignored in favor of the same setting for the destination host. In other words, CURLSSLOPT_REVOKE_BEST_EFFORT set via CURLOPT_PROXY_SSL_OPTIONS did not apply to the proxy and CURLSSLOPT_REVOKE_BEST_EFFORT set via CURLOPT_SSL_OPTIONS wrongly applied to the proxy. Closes https://github.com/curl/curl/pull/21683 --- lib/vtls/schannel_verify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 486fd6e00581..25b13955f264 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -805,7 +805,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - if(data->set.ssl.revoke_best_effort) { + if(ssl_config->revoke_best_effort) { /* Ignore errors when root certificates are missing the revocation * list URL, or when the list could not be downloaded because the * server is currently unreachable. */ From d3b04e56003682a927182d049f6a68ceaa2e9d93 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Mon, 4 May 2026 20:48:52 -0700 Subject: [PATCH 0178/1062] tests: add an assert to avoid IPC blocking If so much data is being sent over the internal IPC pipe that the pipe buffer fills and the syscall blocks, the program will hang. Add an assert to ensure that this limit is never reached. The buffer size is going to be different on different platforms, so choose 1KB which is likely to be a reasonable lower bound on just about any system. Currently, the maximum amount ever written is <100 bytes, so this should provide plenty of headroom. Spotted by Codex Security Closes #21688 --- tests/runner.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/runner.pm b/tests/runner.pm index a3988009462e..72cca945e1f7 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -1336,6 +1336,7 @@ sub controlleripccall { my $margs = freeze \@_; # Send IPC call via pipe + length($margs) < 1000 || die "A large IPC write risks blocking on some platforms"; my $err; while(! defined ($err = syswrite($controllerw{$runnerid}, (pack "L", length($margs)) . $margs)) || $err <= 0) { if((!defined $err && ! $!{EINTR}) || (defined $err && $err == 0)) { From bcd0497c8112e05412d2c649e8d9eea2bda8020e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 20:43:59 +0200 Subject: [PATCH 0179/1062] tidy-up: use `curlx_safefree()` Closes #21700 --- lib/cf-socket.c | 3 +-- lib/curl_gssapi.c | 3 +-- lib/ftp.c | 3 +-- lib/ftplistparser.c | 6 ++---- lib/getinfo.c | 7 ++----- lib/http.c | 6 ++---- lib/multi.c | 9 +++------ lib/url.c | 12 +++++------- lib/vauth/krb5_sspi.c | 9 +++------ lib/vauth/ntlm_sspi.c | 9 +++------ lib/vauth/spnego_sspi.c | 9 +++------ lib/vtls/apple.c | 6 ++---- lib/vtls/vtls.c | 3 +-- src/tool_getparam.c | 12 ++++-------- tests/libtest/lib518.c | 9 +++------ tests/libtest/lib537.c | 6 ++---- tests/libtest/lib678.c | 3 +-- tests/unit/unit1607.c | 3 +-- tests/unit/unit1609.c | 3 +-- 19 files changed, 41 insertions(+), 80 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 0edd0efe746b..83534d7bdf2c 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -507,8 +507,7 @@ CURLcode Curl_parse_interface(const char *input, ++host_part; *host = curlx_memdup0(host_part, len - (host_part - input)); if(!*host) { - curlx_free(*iface); - *iface = NULL; + curlx_safefree(*iface); return CURLE_OUT_OF_MEMORY; } return CURLE_OK; diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 650d1908d0f3..af63d3a2c0d3 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -302,8 +302,7 @@ static OM_uint32 stub_gss_delete_sec_context( return GSS_S_FAILURE; } - curlx_free(*context); - *context = NULL; + curlx_safefree(*context); *min = 0; return GSS_S_COMPLETE; diff --git a/lib/ftp.c b/lib/ftp.c index 68c1a46ca5dc..836bc96e8998 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -3655,8 +3655,7 @@ static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc, * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - curlx_free(ftpc->prevpath); - ftpc->prevpath = NULL; /* no path remembering */ + curlx_safefree(ftpc->prevpath); /* no path remembering */ } else { /* remember working directory for connection reuse */ const char *rawPath = ftpc->rawpath; diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index b5d9338c1bc2..0c8cea4dc90b 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -199,10 +199,8 @@ void Curl_wildcard_dtor(struct WildcardData **wcp) DEBUGASSERT(wc->ftpwc == NULL); Curl_llist_destroy(&wc->filelist, NULL); - curlx_free(wc->path); - wc->path = NULL; - curlx_free(wc->pattern); - wc->pattern = NULL; + curlx_safefree(wc->path); + curlx_safefree(wc->pattern); wc->state = CURLWC_INIT; curlx_free(wc); *wcp = NULL; diff --git a/lib/getinfo.c b/lib/getinfo.c index fab63e669a46..fde4aa4ef2d3 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -67,11 +67,8 @@ void Curl_initinfo(struct Curl_easy *data) info->httpauthpicked = 0; info->numconnects = 0; - curlx_free(info->contenttype); - info->contenttype = NULL; - - curlx_free(info->wouldredirect); - info->wouldredirect = NULL; + curlx_safefree(info->contenttype); + curlx_safefree(info->wouldredirect); memset(&info->primary, 0, sizeof(info->primary)); info->retry_after = 0; diff --git a/lib/http.c b/lib/http.c index 9cb8b17b347c..5d98aab9d7c3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1976,10 +1976,8 @@ static CURLcode http_useragent(struct Curl_easy *data) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent"))) { - curlx_free(data->state.aptr.uagent); - data->state.aptr.uagent = NULL; - } + if(Curl_checkheaders(data, STRCONST("User-Agent"))) + curlx_safefree(data->state.aptr.uagent); return CURLE_OK; } diff --git a/lib/multi.c b/lib/multi.c index 202a30b8a197..216a264a5215 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3972,8 +3972,7 @@ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, if(data->multi->xfer_buf && data->set.buffer_size > data->multi->xfer_buf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_buf); - data->multi->xfer_buf = NULL; + curlx_safefree(data->multi->xfer_buf); data->multi->xfer_buf_len = 0; } @@ -4025,8 +4024,7 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_ulbuf && data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_ulbuf); - data->multi->xfer_ulbuf = NULL; + curlx_safefree(data->multi->xfer_ulbuf); data->multi->xfer_ulbuf_len = 0; } @@ -4073,8 +4071,7 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_sockbuf && blen > data->multi->xfer_sockbuf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_sockbuf); - data->multi->xfer_sockbuf = NULL; + curlx_safefree(data->multi->xfer_sockbuf); data->multi->xfer_sockbuf_len = 0; } diff --git a/lib/url.c b/lib/url.c index 57b063902168..354505ad6c95 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1954,16 +1954,14 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, curlx_safefree(no_proxy); if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(proxy); /* Do not bother with an empty proxy string - or if the protocol does not work with network */ - proxy = NULL; + curlx_safefree(proxy); /* Do not bother with an empty proxy string + or if the protocol does not work with network */ } if(pre_proxy && (!*pre_proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(pre_proxy); /* Do not bother with an empty socks proxy string - or if the protocol does not work with - network */ - pre_proxy = NULL; + curlx_safefree(pre_proxy); /* Do not bother with an empty socks proxy + string or if the protocol does not work + with network */ } /*********************************************************************** diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index b41d0bcbada5..de1dc585391c 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -155,8 +155,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, krb5->p_identity, NULL, NULL, krb5->credentials, NULL); if(status != SEC_E_OK) { - curlx_free(krb5->credentials); - krb5->credentials = NULL; + curlx_safefree(krb5->credentials); return CURLE_LOGIN_DENIED; } @@ -433,15 +432,13 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) /* Free our security context */ if(krb5->context) { Curl_pSecFn->DeleteSecurityContext(krb5->context); - curlx_free(krb5->context); - krb5->context = NULL; + curlx_safefree(krb5->context); } /* Free our credentials handle */ if(krb5->credentials) { Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); - curlx_free(krb5->credentials); - krb5->credentials = NULL; + curlx_safefree(krb5->credentials); } /* Free our identity */ diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 06e3ec5ddfdf..67cf50faf8a4 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -140,8 +140,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, ntlm->p_identity, NULL, NULL, ntlm->credentials, NULL); if(status != SEC_E_OK) { - curlx_free(ntlm->credentials); - ntlm->credentials = NULL; + curlx_safefree(ntlm->credentials); return CURLE_LOGIN_DENIED; } @@ -328,15 +327,13 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) /* Free our security context */ if(ntlm->context) { Curl_pSecFn->DeleteSecurityContext(ntlm->context); - curlx_free(ntlm->context); - ntlm->context = NULL; + curlx_safefree(ntlm->context); } /* Free our credentials handle */ if(ntlm->credentials) { Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); - curlx_free(ntlm->credentials); - ntlm->credentials = NULL; + curlx_safefree(ntlm->credentials); } /* Free our identity */ diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 8ba2316d880b..b7d82c04ddfb 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -160,8 +160,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->p_identity, NULL, NULL, nego->credentials, NULL); if(nego->status != SEC_E_OK) { - curlx_free(nego->credentials); - nego->credentials = NULL; + curlx_safefree(nego->credentials); return CURLE_AUTH_ERROR; } @@ -323,15 +322,13 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) /* Free our security context */ if(nego->context) { Curl_pSecFn->DeleteSecurityContext(nego->context); - curlx_free(nego->context); - nego->context = NULL; + curlx_safefree(nego->context); } /* Free our credentials handle */ if(nego->credentials) { Curl_pSecFn->FreeCredentialsHandle(nego->credentials); - curlx_free(nego->credentials); - nego->credentials = NULL; + curlx_safefree(nego->credentials); } /* Free our identity */ diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index 5bd800b8cb84..f9ffa2f5c2d8 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -238,10 +238,8 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, err_desc = curlx_malloc(size + 1); if(err_desc) { if(!CFStringGetCString(error_ref, err_desc, size, - kCFStringEncodingUTF8)) { - curlx_free(err_desc); - err_desc = NULL; - } + kCFStringEncodingUTF8)) + curlx_safefree(err_desc); } } infof(data, "Apple SecTrust failure %ld%s%s", code, diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 73dd3f56f1db..e7dbad09c618 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -632,8 +632,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data) ci->certinfo[i] = NULL; } - curlx_free(ci->certinfo); /* free the actual array too */ - ci->certinfo = NULL; + curlx_safefree(ci->certinfo); /* free the actual array too */ ci->num_of_certs = 0; } } diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 6c69acd95bf3..a7458a3b5fcf 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -43,10 +43,8 @@ static ParameterError getstr(char **str, const char *val, bool allowblank) { - if(*str) { - curlx_free(*str); - *str = NULL; - } + if(*str) + curlx_safefree(*str); DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; @@ -61,10 +59,8 @@ static ParameterError getstr(char **str, const char *val, bool allowblank) static ParameterError getstrn(char **str, const char *val, size_t len, bool allowblank) { - if(*str) { - curlx_free(*str); - *str = NULL; - } + if(*str) + curlx_safefree(*str); DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index e2c98db22de6..c961ca3347d8 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -60,8 +60,7 @@ static void t518_close_file_descriptors(void) t518_num_open.rlim_cur++) if(t518_testfd[t518_num_open.rlim_cur] > 0) curlx_close(t518_testfd[t518_num_open.rlim_cur]); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); } static int t518_fopen_works(void) @@ -289,8 +288,7 @@ static int t518_test_rlimit(int keep_open) curl_msnprintf(strbuff, sizeof(strbuff), "opening of %s failed", DEV_NULL); t518_store_errmsg(strbuff, errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); curlx_free(memchunk); return -8; } @@ -330,8 +328,7 @@ static int t518_test_rlimit(int keep_open) t518_testfd[t518_num_open.rlim_cur] >= 0; t518_num_open.rlim_cur++) curlx_close(t518_testfd[t518_num_open.rlim_cur]); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); curlx_free(memchunk); return -9; } diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 9257eaf02ea7..16e2f1b333e3 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -57,8 +57,7 @@ static void t537_close_file_descriptors(void) t537_num_open.rlim_cur++) if(t537_testfd[t537_num_open.rlim_cur] > 0) curlx_close(t537_testfd[t537_num_open.rlim_cur]); - curlx_free(t537_testfd); - t537_testfd = NULL; + curlx_safefree(t537_testfd); } static int t537_fopen_works(void) @@ -291,8 +290,7 @@ static int t537_test_rlimit(int keep_open) curl_msnprintf(strbuff, sizeof(strbuff), "opening of %s failed", DEV_NULL); t537_store_errmsg(strbuff, errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); - curlx_free(t537_testfd); - t537_testfd = NULL; + curlx_safefree(t537_testfd); curlx_free(memchunk); return -7; } diff --git a/tests/libtest/lib678.c b/tests/libtest/lib678.c index 7486505f7684..90295f42a8d6 100644 --- a/tests/libtest/lib678.c +++ b/tests/libtest/lib678.c @@ -47,9 +47,8 @@ static int loadfile(const char *filename, void **filedata, size_t *filesize) continue_reading = FALSE; curlx_fclose(fInCert); if(!continue_reading) { - curlx_free(data); + curlx_safefree(data); datasize = 0; - data = NULL; } } } diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index dc12b99a5684..1380f9547e97 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -132,8 +132,7 @@ static CURLcode test_unit1607(const char *arg) goto error; dns = Curl_hash_pick(&multi->dnscache.entries, entry_id, strlen(entry_id) + 1); - curlx_free(entry_id); - entry_id = NULL; + curlx_safefree(entry_id); addr = dns ? dns->addr : NULL; diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index 9d1691863962..337fd7664dd3 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -134,8 +134,7 @@ static CURLcode test_unit1609(const char *arg) dns = Curl_hash_pick(&multi->dnscache.entries, entry_id, strlen(entry_id) + 1); - curlx_free(entry_id); - entry_id = NULL; + curlx_safefree(entry_id); addr = dns ? dns->addr : NULL; From f2692b54f74b8bb6058ecd3cf4abcc96e8ab36ba Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 23:14:01 +0200 Subject: [PATCH 0180/1062] docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend Closes #21682 --- docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md index 0590143c906b..82dd1626d140 100644 --- a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md +++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md @@ -53,6 +53,11 @@ On mismatch, *CURLE_SSL_PINNEDPUBKEYNOTMATCH* is returned. The application does not have to keep the string around after setting this option. +This option has no effect on LDAP connections when libcurl uses the legacy LDAP +backend. That backend manages TLS independently of curl's TLS layer. When +libcurl is built with USE_OPENLDAP, the OpenLDAP backend routes TLS through +curl's layer and this option is honored. + # DEFAULT NULL From 419b1c0b751b257bd54787618454d90fe88e7b79 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 00:51:04 +0200 Subject: [PATCH 0181/1062] checksrc: detect `curlx_safefree()` opportunities Follow-up to bcd0497c8112e05412d2c649e8d9eea2bda8020e #21700 Follow-up to 1c3289c85e1a7a939464d5c5e84382d2e250e611 #21684 Follow-up to c0f0e400e0bc43cbe8c42c6937ed0ac743a8d81a #5968 Follow-up to 0f4a03cbb6fdb84d05cb6aafe50444edad4f4119 Closes #21703 --- docs/internals/CHECKSRC.md | 4 ++++ scripts/checksrc.pl | 21 +++++++++++++++++++++ tests/data/test1185 | 9 ++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/internals/CHECKSRC.md b/docs/internals/CHECKSRC.md index ea6f260cf13b..b94adc3c072e 100644 --- a/docs/internals/CHECKSRC.md +++ b/docs/internals/CHECKSRC.md @@ -129,6 +129,10 @@ warnings are: - `UNUSEDIGNORE`: a `checksrc` inlined warning ignore was asked for but not used, that is an ignore that should be removed or changed to get used. +- `USESAFEFREE`: there was a `curlx_free(var)` call made right before assigning + NULL to `var`. We prefer replacing that with `curlx_safefree()`, which is + doing these two operations in a single call. + ### Extended warnings Some warnings are computationally expensive to perform, so they are turned off diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index de1d3c5e626f..748941d5c97c 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -201,6 +201,7 @@ 'TRAILINGSPACE' => 'Trailing whitespace on the line', 'TYPEDEFSTRUCT' => 'typedefed struct', 'UNUSEDIGNORE' => 'a warning ignore was not used', + 'USESAFEFREE' => 'replace curlx_free() + NULL assignment with curlx_safefree()', ); sub readskiplist { @@ -532,6 +533,8 @@ sub scanfile { my $l = ""; my $prep = 0; my $prevp = 0; + my $prevfreeindent = ""; + my $prevfreevar = ""; if($verbose) { printf "Checking file: $file\n"; @@ -973,6 +976,24 @@ sub scanfile { $line, length($1), $file, $ol, "no space before label"); } + if($prevfreevar ne "") { + if(rindex($l, "$prevfreeindent$prevfreevar = NULL;", 0) == 0) { + checkwarn("USESAFEFREE", + $line, length($prevfreeindent), $file, $ol, + "replace curlx_free() + NULL assignment with curlx_safefree()"); + } + } + if($l) { + if($l =~ /^( *)curlx_free\(([^)]+)\);/) { + $prevfreeindent = $1; + $prevfreevar = $2; + } + else { + $prevfreeindent = ""; + $prevfreevar = ""; + } + } + # scan for use of banned functions my $bl = $l; again: diff --git a/tests/data/test1185 b/tests/data/test1185 index 217a0a160479..200a01f56539 100644 --- a/tests/data/test1185 +++ b/tests/data/test1185 @@ -91,6 +91,10 @@ void startfunc(int a, int b) { int d = impl->magicbad(1); /* member function always allowed */ int e = impl.magicbad(); /* member function always allowed */ + curlx_free(ptr); /* two line + comment */ + ptr = NULL; /* comment more */ + /* comment does not end @@ -227,13 +231,16 @@ void startfunc(int a, int b) { ./%LOGDIR/code1185.c:71:2: warning: // comment (CPPCOMMENTS) // CPP comment ? ^ +./%LOGDIR/code1185.c:78:2: warning: replace curlx_free() + NULL assignment with curlx_safefree() (USESAFEFREE) + ptr = NULL; /* more comment */ + ^ ./%LOGDIR/code1185.c:1:1: error: Missing copyright statement (COPYRIGHT) %SP ^ ./%LOGDIR/code1185.c:1:1: error: Missing closing comment (OPENCOMMENT) %SP ^ -checksrc: 3 errors and 42 warnings +checksrc: 3 errors and 43 warnings 5 From af511a22cb525b22d7c1c5253e4b92a642e539b9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 11:24:15 +0200 Subject: [PATCH 0182/1062] test1185: fix to previous commit Follow-up to 419b1c0b751b257bd54787618454d90fe88e7b79 #21703 --- tests/data/test1185 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test1185 b/tests/data/test1185 index 200a01f56539..f804c7ba044f 100644 --- a/tests/data/test1185 +++ b/tests/data/test1185 @@ -232,7 +232,7 @@ void startfunc(int a, int b) { // CPP comment ? ^ ./%LOGDIR/code1185.c:78:2: warning: replace curlx_free() + NULL assignment with curlx_safefree() (USESAFEFREE) - ptr = NULL; /* more comment */ + ptr = NULL; /* comment more */ ^ ./%LOGDIR/code1185.c:1:1: error: Missing copyright statement (COPYRIGHT) %SP From c583e825f7a5b7ceda7d7bb703497dbe306713de Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 11:28:58 +0200 Subject: [PATCH 0183/1062] GHA: simplify boolean `if` comparisons Closes #21709 --- .github/workflows/http3-linux.yml | 54 +++++++++++++++---------------- .github/workflows/linux.yml | 28 ++++++++-------- .github/workflows/macos.yml | 2 +- .github/workflows/non-native.yml | 2 +- .github/workflows/windows.yml | 4 +-- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index b4939e811806..20e20fd3f645 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -189,19 +189,19 @@ jobs: - id: settings if: >- - ${{ steps.cache-awslc.outputs.cache-hit != 'true' || - steps.cache-boringssl.outputs.cache-hit != 'true' || - steps.cache-nettle.outputs.cache-hit != 'true' || - steps.cache-gnutls.outputs.cache-hit != 'true' || - steps.cache-libressl.outputs.cache-hit != 'true' || - steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' || - steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' || - steps.cache-wolfssl.outputs.cache-hit != 'true' || - steps.cache-nghttp3.outputs.cache-hit != 'true' || - steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' || - steps.cache-ngtcp2-openssl-prev.outputs.cache-hit != 'true' || - steps.cache-ngtcp2.outputs.cache-hit != 'true' || - steps.cache-nghttp2.outputs.cache-hit != 'true' }} + ${{ !steps.cache-awslc.outputs.cache-hit || + !steps.cache-boringssl.outputs.cache-hit || + !steps.cache-nettle.outputs.cache-hit || + !steps.cache-gnutls.outputs.cache-hit || + !steps.cache-libressl.outputs.cache-hit || + !steps.cache-openssl-http3-no-deprecated.outputs.cache-hit || + !steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit || + !steps.cache-wolfssl.outputs.cache-hit || + !steps.cache-nghttp3.outputs.cache-hit || + !steps.cache-ngtcp2-boringssl.outputs.cache-hit || + !steps.cache-ngtcp2-openssl-prev.outputs.cache-hit || + !steps.cache-ngtcp2.outputs.cache-hit || + !steps.cache-nghttp2.outputs.cache-hit }} run: echo 'needs-build=true' >> "$GITHUB_OUTPUT" @@ -222,7 +222,7 @@ jobs: echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'build awslc' - if: ${{ steps.cache-awslc.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-awslc.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -234,7 +234,7 @@ jobs: cmake --install . - name: 'build boringssl' - if: ${{ steps.cache-boringssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-boringssl.outputs.cache-hit }} run: | mkdir boringssl-src cd boringssl-src @@ -246,7 +246,7 @@ jobs: cmake --install . - name: 'build nettle' - if: ${{ steps.cache-nettle.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nettle.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -259,7 +259,7 @@ jobs: make install - name: 'build gnutls' - if: ${{ steps.cache-gnutls.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-gnutls.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -276,7 +276,7 @@ jobs: make install - name: 'build libressl' - if: ${{ steps.cache-libressl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-libressl.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -288,7 +288,7 @@ jobs: cmake --install . - name: 'build openssl' - if: ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-openssl-http3-no-deprecated.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl @@ -298,7 +298,7 @@ jobs: make -j1 install_sw - name: 'build openssl-prev' - if: ${{ steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -310,7 +310,7 @@ jobs: make -j1 install_sw - name: 'build wolfssl' - if: ${{ steps.cache-wolfssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-wolfssl.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl @@ -322,7 +322,7 @@ jobs: make install - name: 'build nghttp3' - if: ${{ steps.cache-nghttp3.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nghttp3.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGHTTP3_VERSION}" https://github.com/ngtcp2/nghttp3 @@ -334,7 +334,7 @@ jobs: make install - name: 'build ngtcp2' - if: ${{ steps.cache-ngtcp2.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2.outputs.cache-hit }} # building twice to get crypto libs for ossl, libressl and awslc installed run: | cd ~ @@ -357,7 +357,7 @@ jobs: make install - name: 'build ngtcp2 openssl-prev' - if: ${{ steps.cache-ngtcp2-openssl-prev.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2-openssl-prev.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl-prev @@ -369,7 +369,7 @@ jobs: make install - name: 'build ngtcp2 boringssl' - if: ${{ steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2-boringssl.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-boringssl @@ -382,7 +382,7 @@ jobs: make install - name: 'build nghttp2' - if: ${{ steps.cache-nghttp2.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nghttp2.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGHTTP2_VERSION}" https://github.com/nghttp2/nghttp2 @@ -721,7 +721,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.QUICHE_VERSION }} - name: 'build quiche and boringssl' - if: ${{ contains(matrix.build.name, 'quiche') && steps.cache-quiche.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.name, 'quiche') && !steps.cache-quiche.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "${QUICHE_VERSION}" --recursive https://github.com/cloudflare/quiche diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e6beafe1bb44..1bef3460df73 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -526,7 +526,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'build libressl (c-arm)' - if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && steps.cache-libressl-c-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && !steps.cache-libressl-c-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin @@ -547,7 +547,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }}-${{ env.FIL_C_VERSION }} - name: 'build libressl (filc)' - if: ${{ contains(matrix.build.install_steps, 'libressl-filc') && steps.cache-libressl-filc.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'libressl-filc') && !steps.cache-libressl-filc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin @@ -569,7 +569,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.FIL_C_VERSION }} - name: 'build nghttp2 (filc)' - if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') && steps.cache-nghttp2-filc.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') && !steps.cache-nghttp2-filc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.xz" --output pkg.bin @@ -592,7 +592,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'build wolfssl (all-arm)' # does not support `OPENSSL_COEXIST` - if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') && steps.cache-wolfssl-all-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') && !steps.cache-wolfssl-all-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin @@ -615,7 +615,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'build wolfssl (opensslextra-intel)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && !steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin @@ -638,7 +638,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'build wolfssl (opensslextra-arm)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && !steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin @@ -661,7 +661,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_VERSION }} - name: 'build mbedtls (latest-intel)' - if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && steps.cache-mbedtls-latest-intel.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && !steps.cache-mbedtls-latest-intel.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin @@ -685,7 +685,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_VERSION }} - name: 'build mbedtls (latest-arm)' - if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') && steps.cache-mbedtls-latest-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') && !steps.cache-mbedtls-latest-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin @@ -709,7 +709,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_PREV_VERSION }} - name: 'build mbedtls (prev)' - if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') && steps.cache-mbedtls-prev.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') && !steps.cache-mbedtls-prev.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_PREV_VERSION}/mbedtls-${MBEDTLS_PREV_VERSION}.tar.bz2" --output pkg.bin @@ -733,7 +733,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.OPENLDAP_VERSION }} - name: 'build openldap (static)' - if: ${{ contains(matrix.build.install_steps, 'openldap-static') && steps.cache-openldap-static.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'openldap-static') && !steps.cache-openldap-static.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${OPENLDAP_VERSION}.tgz" --output pkg.bin @@ -754,7 +754,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - name: 'build openssl (thread sanitizer)' - if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') && steps.cache-openssl-tsan.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') && !steps.cache-openssl-tsan.outputs.cache-hit }} run: | git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl cd openssl @@ -773,7 +773,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'build awslc' - if: ${{ contains(matrix.build.install_steps, 'awslc') && steps.cache-awslc.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'awslc') && !steps.cache-awslc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin @@ -794,7 +794,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - name: 'build boringssl' - if: ${{ contains(matrix.build.install_steps, 'boringssl') && steps.cache-boringssl.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'boringssl') && !steps.cache-boringssl.outputs.cache-hit }} run: | mkdir boringssl-src cd boringssl-src @@ -816,7 +816,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.RUSTLS_VERSION }} - name: 'fetch rustls deb' - if: ${{ contains(matrix.build.install_steps, 'rustls') && steps.cache-rustls.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'rustls') && !steps.cache-rustls.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f3d71cdf473d..9b98117e6868 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -123,7 +123,7 @@ jobs: key: iOS-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'build libressl' - if: ${{ contains(matrix.build.install_steps, 'libressl') && steps.cache-libressl.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'libressl') && !steps.cache-libressl.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index b8b76e710749..c032dd845ea7 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -316,7 +316,7 @@ jobs: key: ${{ runner.os }}-djgpp-${{ env.TOOLCHAIN_VERSION }}-amd64 - name: 'install compiler (djgpp)' - if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-compiler.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 3 --retry-connrefused \ diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7243d542628d..38fc3bf8d6f6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -59,7 +59,7 @@ jobs: lookup-only: true - name: 'install test prereqs (stunnel)' - if: ${{ steps.cache-stunnel.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-stunnel.outputs.cache-hit }} timeout-minutes: 2 shell: bash run: | @@ -647,7 +647,7 @@ jobs: key: ${{ runner.os }}-mingw-w64-${{ matrix.ver }}-${{ matrix.env }} - name: 'install compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-compiler.outputs.cache-hit }} timeout-minutes: 5 env: MATRIX_URL: '${{ matrix.url }}' From b0239417b34238121165dee465afb944cbad17ec Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 14:49:31 +0200 Subject: [PATCH 0184/1062] GHA/windows: bump windows-2025 runners to windows-2025-vs2026 To silence: ``` NOTICE: windows-2025 requests are being redirected to windows-2025-vs2026 by June 15, 2026 ``` Closes #21713 --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 38fc3bf8d6f6..d62a06642085 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -311,7 +311,7 @@ jobs: # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , # config: '--without-debug --with-schannel --disable-static', # install: 'mingw-w64-ucrt-x86_64-libssh2' } - - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', + - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025-vs2026', build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2' } @@ -899,7 +899,7 @@ jobs: env: 'ucrt-x86_64' plat: 'uwp' type: 'Debug' - image: 'windows-2025' + image: 'windows-2025-vs2026' tflags: 'skiprun' config: >- -DENABLE_DEBUG=ON From 64c51ad1785fbf09c944a35f5566163b50a861b3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 14:58:11 +0200 Subject: [PATCH 0185/1062] cmake: opt in `MSVC_VERSION` 1951 to picky warnings Closes #21714 --- CMake/PickyWarnings.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index aca79ec947b7..2de72a6d849e 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -391,7 +391,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-Wno-conversion") # Avoid false positives endif() endif() - elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1950) # Skip for untested/unreleased newer versions + elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1951) # Skip for untested/unreleased newer versions list(APPEND _picky "-Wall") list(APPEND _picky "-wd4061") # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label list(APPEND _picky "-wd4191") # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)' From a076f821e1744a68e9c659ebe7092bf9bedbd0b9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 11:16:49 +0200 Subject: [PATCH 0186/1062] multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test - GHA/windows: bump dl-mingw job from gcc 15 to 16. - multi: silence warning while building libcurlu: ``` In function 'multi_ischanged', inlined from 'multi_socket.isra' at D:/a/curl/curl/lib/multi.c:3282:6: D:/a/curl/curl/lib/multi.c:1710:17: error: null pointer dereference [-Werror=null-dereference] 1710 | bool retval = (bool)multi->recheckstate; | ^~~~~~~~~~~~~~~~~~~~~~~~~ D:/a/curl/curl/lib/multi.c:1712:25: error: null pointer dereference [-Werror=null-dereference] 1712 | multi->recheckstate = FALSE; | ^ ``` Ref: https://github.com/curl/curl/actions/runs/26217071531/job/77142119137?pr=21707 - multi: silence another `-Wnull-dereference`, popping up in libcurl with gcc 13 after the previous silencing: ``` In function 'Curl_multi_xfers_running', inlined from 'multi_socket.isra' at ../../lib/multi.c:3292:28: ../../lib/multi.c:4132:15: error: null pointer dereference [-Werror=null-dereference] 4132 | return multi->xfers_alive; | ~~~~~^~~~~~~~~~~~~ ``` Ref: https://github.com/curl/curl/actions/runs/26218822231/job/77148186045 - multi: also add `DEBUGASSERT(multi)` to the two updated functions. Closes #21707 --- .github/workflows/windows.yml | 8 ++++---- lib/multi.c | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d62a06642085..75df6b75c5c3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -574,13 +574,13 @@ jobs: strategy: matrix: include: - - name: 'schannel +analyzer' # mingw-w64 12.0 + - name: 'schannel +analyzer' # mingw-w64 14.0 sys: 'mingw64' dir: 'w64devkit' env: 'x86_64' - ver: '15.1.0' - url: 'https://github.com/skeeto/w64devkit/releases/download/v2.2.0/w64devkit-x64-2.2.0.7z.exe' - SHA256: e02de30b97196329662007d64bc4509fbd7f5e14339d344075c7f1223dead4a2 + ver: '16.1.0' + url: 'https://github.com/skeeto/w64devkit/releases/download/v2.8.0/w64devkit-x64-2.8.0.7z.exe' + SHA256: 6252bf34fe2231a55ac7f03d482b36d2c7c58697990551bba508102cfb3f342e config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON' type: 'Release' - name: 'schannel' # mingw-w64 10.0 diff --git a/lib/multi.c b/lib/multi.c index 216a264a5215..1948bbda7705 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1707,9 +1707,13 @@ CURLMcode curl_multi_wakeup(CURLM *m) */ static bool multi_ischanged(struct Curl_multi *multi, bool clear) { - bool retval = (bool)multi->recheckstate; - if(clear) - multi->recheckstate = FALSE; + bool retval = FALSE; + DEBUGASSERT(multi); + if(multi) { + retval = (bool)multi->recheckstate; + if(clear) + multi->recheckstate = FALSE; + } return retval; } @@ -4126,6 +4130,9 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) { + DEBUGASSERT(multi); + if(!multi) + return 0; return multi->xfers_alive; } From bb5500a7525a555cef092dd72f30645606b7ae4e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 04:21:33 +0200 Subject: [PATCH 0187/1062] units: tidy up begin/end blocks - use `UNITTEST_BEGIN_SIMPLE`/`UNITTEST_END_SIMPLE` where missing. - drop redundant `(void)arg;` where using `UNITTEST_BEGIN*`. - unit1636: drop redundant block after `UNITTEST_BEGIN*`. - unit1609: fix typo in comment. - unit1627: merge to `if`s. Closes #21715 --- tests/unit/unit1609.c | 2 +- tests/unit/unit1625.c | 9 ++--- tests/unit/unit1626.c | 10 +++--- tests/unit/unit1627.c | 11 +++--- tests/unit/unit1636.c | 80 +++++++++++++++++++++---------------------- tests/unit/unit1675.c | 1 - tests/unit/unit3300.c | 2 -- tests/unit/unit3301.c | 2 -- 8 files changed, 56 insertions(+), 61 deletions(-) diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index 337fd7664dd3..90a75b7e5584 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -97,7 +97,7 @@ static CURLcode test_unit1609(const char *arg) struct curl_slist *list = NULL; /* important: we setup cache outside of the loop - and also clean cache after the loop. In contrast,for example, + and also clean cache after the loop. In contrast, for example, test 1607 sets up and cleans cache on each iteration. */ for(i = 0; i < CURL_ARRAYSIZE(tests); ++i) { diff --git a/tests/unit/unit1625.c b/tests/unit/unit1625.c index ca310cfa5187..be52ba2d83a8 100644 --- a/tests/unit/unit1625.c +++ b/tests/unit/unit1625.c @@ -36,6 +36,8 @@ struct check1625 { static CURLcode test_unit1625(const char *arg) { + UNITTEST_BEGIN_SIMPLE + size_t i; static const struct check1625 list[] = { /* basic case */ @@ -101,7 +103,6 @@ static CURLcode test_unit1625(const char *arg) /* hyphenated second token */ { "Encoding: extra-good, super-nice", "Encoding:", "super-nice", TRUE }, }; - (void)arg; for(i = 0; i < CURL_ARRAYSIZE(list); i++) { bool check = Curl_compareheader(list[i].in, @@ -123,12 +124,12 @@ static CURLcode test_unit1625(const char *arg) if(i != CURL_ARRAYSIZE(list)) return CURLE_FAILED_INIT; - return CURLE_OK; + UNITTEST_END_SIMPLE } #else /* CURL_DISABLE_HTTP */ static CURLcode test_unit1625(const char *arg) { - (void)arg; - return CURLE_OK; + UNITTEST_BEGIN_SIMPLE + UNITTEST_END_SIMPLE } #endif diff --git a/tests/unit/unit1626.c b/tests/unit/unit1626.c index 30189fdf8dcb..81ce3b2e0a2d 100644 --- a/tests/unit/unit1626.c +++ b/tests/unit/unit1626.c @@ -34,6 +34,8 @@ struct check1626 { static CURLcode test_unit1626(const char *arg) { + UNITTEST_BEGIN_SIMPLE + size_t i; static const struct check1626 list[] = { /* basic */ @@ -94,8 +96,6 @@ static CURLcode test_unit1626(const char *arg) { "Header : value", "value" }, }; - (void)arg; - for(i = 0; i < CURL_ARRAYSIZE(list); i++) { bool ok; char *get = Curl_copy_header_value(list[i].in); @@ -117,13 +117,13 @@ static CURLcode test_unit1626(const char *arg) if(i != CURL_ARRAYSIZE(list)) return CURLE_FAILED_INIT; - return CURLE_OK; + UNITTEST_END_SIMPLE } #else /* for HTTP-disabled builds */ static CURLcode test_unit1626(const char *arg) { - (void)arg; - return CURLE_OK; + UNITTEST_BEGIN_SIMPLE + UNITTEST_END_SIMPLE } #endif diff --git a/tests/unit/unit1627.c b/tests/unit/unit1627.c index c3c679bb0eee..f0764709b25c 100644 --- a/tests/unit/unit1627.c +++ b/tests/unit/unit1627.c @@ -28,6 +28,8 @@ static CURLcode test_unit1627(const char *arg) { + UNITTEST_BEGIN_SIMPLE + size_t i, j; /* existing schemes in different cases */ static const char *okay[] = { @@ -65,8 +67,6 @@ static CURLcode test_unit1627(const char *arg) "GhJk", "LzXc", "VbNm" }; - (void)arg; - for(i = 0; i < CURL_ARRAYSIZE(okay); i++) { char buffer[32]; const struct Curl_scheme *get = Curl_get_scheme(okay[i]); @@ -96,10 +96,9 @@ static CURLcode test_unit1627(const char *arg) curl_mprintf("%zu invokes\n", i + j); - if(i != CURL_ARRAYSIZE(okay)) - return CURLE_FAILED_INIT; - if(j != CURL_ARRAYSIZE(notokay)) + if(i != CURL_ARRAYSIZE(okay) || + j != CURL_ARRAYSIZE(notokay)) return CURLE_FAILED_INIT; - return CURLE_OK; + UNITTEST_END_SIMPLE } diff --git a/tests/unit/unit1636.c b/tests/unit/unit1636.c index adb048a87297..ec72b8ab31a6 100644 --- a/tests/unit/unit1636.c +++ b/tests/unit/unit1636.c @@ -27,56 +27,56 @@ static CURLcode test_unit1636(const char *arg) { UNITTEST_BEGIN_SIMPLE - { - char buffer[9]; - curl_off_t secs; - int i; - static const curl_off_t check[] = { - /* bytes to check */ - 131072, - 12645826, - 1073741824, - 12938588979, - 1099445657078333, - 0 /* end of list */ - }; - puts("time2str"); - for(i = 0, secs = 0; i < 63; i++) { - time2str(buffer, sizeof(buffer), secs); - curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 7) { - curl_mprintf("^^ was too long!\n"); - } - secs *= 2; - secs++; + char buffer[9]; + curl_off_t secs; + int i; + static const curl_off_t check[] = { + /* bytes to check */ + 131072, + 12645826, + 1073741824, + 12938588979, + 1099445657078333, + 0 /* end of list */ + }; + + puts("time2str"); + for(i = 0, secs = 0; i < 63; i++) { + time2str(buffer, sizeof(buffer), secs); + curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); + if(strlen(buffer) != 7) { + curl_mprintf("^^ was too long!\n"); } - puts("max6out"); - for(i = 0, secs = 0; i < 63; i++) { - max6out(secs, buffer, sizeof(buffer)); - curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 6) { - curl_mprintf("^^ was too long!\n"); - } - secs *= 2; - secs++; + secs *= 2; + secs++; + } + puts("max6out"); + for(i = 0, secs = 0; i < 63; i++) { + max6out(secs, buffer, sizeof(buffer)); + curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); + if(strlen(buffer) != 6) { + curl_mprintf("^^ was too long!\n"); } - for(i = 0; check[i]; i++) { - secs = check[i]; - max6out(secs, buffer, sizeof(buffer)); - curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); - if(strlen(buffer) != 6) { - curl_mprintf("^^ was too long!\n"); - } + secs *= 2; + secs++; + } + for(i = 0; check[i]; i++) { + secs = check[i]; + max6out(secs, buffer, sizeof(buffer)); + curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer); + if(strlen(buffer) != 6) { + curl_mprintf("^^ was too long!\n"); } } + UNITTEST_END(curl_global_cleanup()) } #else /* CURL_DISABLE_PROGRESS_METER */ static CURLcode test_unit1636(const char *arg) { - (void)arg; - return CURLE_OK; + UNITTEST_BEGIN_SIMPLE + UNITTEST_END_SIMPLE } #endif diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index d4243d29bb15..024c7ff40028 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -27,7 +27,6 @@ static CURLcode test_unit1675(const char *arg) { - (void)arg; UNITTEST_BEGIN_SIMPLE /* Test ipv4_normalize */ diff --git a/tests/unit/unit3300.c b/tests/unit/unit3300.c index a010f43922c4..f6d7ed34ce6e 100644 --- a/tests/unit/unit3300.c +++ b/tests/unit/unit3300.c @@ -154,11 +154,9 @@ static CURLcode test_unit3300(const char *arg) } #else - static CURLcode test_unit3300(const char *arg) { UNITTEST_BEGIN_SIMPLE - (void)arg; UNITTEST_END_SIMPLE } #endif /* USE_THREADS */ diff --git a/tests/unit/unit3301.c b/tests/unit/unit3301.c index 472a14befad6..67b126200dd3 100644 --- a/tests/unit/unit3301.c +++ b/tests/unit/unit3301.c @@ -134,11 +134,9 @@ static CURLcode test_unit3301(const char *arg) } #else - static CURLcode test_unit3301(const char *arg) { UNITTEST_BEGIN_SIMPLE - (void)arg; UNITTEST_END_SIMPLE } #endif /* USE_THREADS */ From 8e549fbdd36be99a62019218cd171ec225f25506 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 21 May 2026 19:09:35 +0200 Subject: [PATCH 0188/1062] GHA/checksrc: add auditor-level zizmor (warning-only) CI time cost is 1s. It may replace existing pedantic check, if this level isn't bringing false-positives or annoyance. Officially it's not meant for CI, but curl has been passing this in the last couple of months when checked locally. Closes #21718 --- .github/workflows/checksrc.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index c05a48d6a0a4..18e9f1ba65c0 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -165,6 +165,13 @@ jobs: eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" zizmor --persona pedantic .github/workflows/*.yml .github/dependabot.yml + - name: 'zizmor GHA (auditor, warning-only)' + env: + GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + run: | + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + zizmor --persona auditor .github/workflows/*.yml .github/dependabot.yml || true + - name: 'actionlint' run: | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" From 7e350dd147ff64aead4595c3658c1f60e2dc749f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 21 May 2026 23:00:55 +0200 Subject: [PATCH 0189/1062] urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set Verified by test 1967 Reported-by: Joshua Rogers Closes #21721 --- lib/urlapi.c | 5 +++-- tests/data/Makefile.am | 2 +- tests/data/test1967 | 30 +++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib1967.c | 42 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 tests/data/test1967 create mode 100644 tests/libtest/lib1967.c diff --git a/lib/urlapi.c b/lib/urlapi.c index 21f4bbfab1be..ef5b2b48e909 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1731,8 +1731,9 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, return parseurl_and_replace(url, u, flags); /* if the old URL is incomplete (we cannot get an absolute URL in - 'oldurl'), replace the existing with the new */ - uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); + 'oldurl'), replace the existing with the new. + Always include "scheme://" to make the URL "complete" */ + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags& ~CURLU_NO_GUESS_SCHEME); if(uc == CURLUE_OUT_OF_MEMORY) return uc; else if(uc) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 2621c4dc7b43..08b2f17dd0ff 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -239,7 +239,7 @@ test1916 test1917 test1918 test1919 test1920 test1921 \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ test1955 test1956 test1957 test1958 test1959 test1960 test1964 test1965 \ -test1966 \ +test1966 test1967 \ \ test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ diff --git a/tests/data/test1967 b/tests/data/test1967 new file mode 100644 index 000000000000..c012a70ba63c --- /dev/null +++ b/tests/data/test1967 @@ -0,0 +1,30 @@ + + + + +HTTP +urlapi + + + + + + +curl_url_set() a URL without guessing a scheme + + +lib%TESTNUMBER + + + +http://%HOSTIP:%NOLISTENPORT/not-there/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +URL http://a.b/x + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index d3e194b0c7a3..ad86411a7f29 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -108,7 +108,7 @@ TESTS_C = \ lib1940.c lib1945.c \ lib1947.c lib1948.c \ lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \ - lib1964.c lib1965.c lib1970.c \ + lib1964.c lib1965.c lib1967.c lib1970.c \ lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \ lib2023.c lib2032.c lib2082.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ diff --git a/tests/libtest/lib1967.c b/tests/libtest/lib1967.c new file mode 100644 index 000000000000..e271df28d94d --- /dev/null +++ b/tests/libtest/lib1967.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static CURLcode test_lib1967(const char *URL) +{ + CURLU *u = curl_url(); + (void)URL; + if(u) { + char *url; + curl_url_set(u, CURLUPART_URL, "a.b", CURLU_GUESS_SCHEME); + curl_url_set(u, CURLUPART_URL, "/x", CURLU_NO_GUESS_SCHEME); + + if(!curl_url_get(u, CURLUPART_URL, &url, 0)) { + curl_mprintf("URL %s\n", url); + curl_free(url); + } + curl_url_cleanup(u); + } + return CURLE_OK; +} From 2056498625d183248a9d435a43a2b41f58b2e74d Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Fri, 22 May 2026 00:03:51 +0200 Subject: [PATCH 0190/1062] KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug The entry is about GnuTLS not sending the client cert when it doesn't match the `DN` the server requested. OpenSSL does the opposite. The issue was already fixed by #4958 and removed from KNOWN_BUGS, but it was added back to the list by #16677, seemingly by mistake. The issue is still fixed for GnuTLS >= 3.5.0. As curl only supports GnuTLS >= 3.6.5, remove the bug entry from KNOWN_BUGS.md Fixes #21720 Closes #21722 --- docs/KNOWN_BUGS.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index d6bdf1410fa6..a2d3b62729e8 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -25,14 +25,6 @@ instead seems to trigger a crash. See [curl issue 17626](https://github.com/curl/curl/issues/17626) -## Client cert handling with Issuer `DN` differs between backends - -When the specified client certificate does not match any of the -server-specified `DN` fields, the OpenSSL and GnuTLS backends behave -differently. The GitHub discussion may contain a solution. - -See [curl issue 1411](https://github.com/curl/curl/issues/1411) - ## Client cert (MTLS) issues with Schannel See [curl issue 3145](https://github.com/curl/curl/issues/3145) From bfbff7852f050232edd3e5ca5c6bf2021c340f5a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 22 May 2026 09:11:41 +0200 Subject: [PATCH 0191/1062] http2: remove stream dependency tracking The HTTP/2 feature is deprecated, few servers implement it and our implementation is complicated by its state management. Make the two CURLOPT_* involved a nop and deprecate them. Closes #21723 --- docs/libcurl/curl_easy_setopt.md | 5 +- docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md | 8 ++ docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md | 8 ++ docs/libcurl/symbols-in-versions | 4 +- include/curl/curl.h | 6 +- lib/http2.c | 18 ++-- lib/setopt.c | 9 +- lib/url.c | 98 ------------------- lib/urldata.h | 8 -- 9 files changed, 34 insertions(+), 130 deletions(-) diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 37d028954e68..5f75596cff60 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -1165,11 +1165,12 @@ Redirect stderr to another stream. See CURLOPT_STDERR(3) ## CURLOPT_STREAM_DEPENDS -This HTTP/2 stream depends on another. See CURLOPT_STREAM_DEPENDS(3) +**Deprecated option** This HTTP/2 stream depends on another. See +CURLOPT_STREAM_DEPENDS(3) ## CURLOPT_STREAM_DEPENDS_E -This HTTP/2 stream depends on another exclusively. See +**Deprecated option** This HTTP/2 stream depends on another exclusively. See CURLOPT_STREAM_DEPENDS_E(3) ## CURLOPT_STREAM_WEIGHT diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md index 5e3e177dce8f..e0e8ef0c53c3 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md @@ -44,6 +44,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -69,6 +73,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md index fe95ae8d7101..fe8f97f950d8 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md @@ -47,6 +47,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -72,6 +76,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 6516f7823de8..4dc670da6eee 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -882,8 +882,8 @@ CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0 CURLOPT_SSLKEYTYPE 7.9.3 CURLOPT_SSLVERSION 7.1 CURLOPT_STDERR 7.1 -CURLOPT_STREAM_DEPENDS 7.46.0 -CURLOPT_STREAM_DEPENDS_E 7.46.0 +CURLOPT_STREAM_DEPENDS 7.46.0 8.21.0 +CURLOPT_STREAM_DEPENDS_E 7.46.0 8.21.0 CURLOPT_STREAM_WEIGHT 7.46.0 CURLOPT_SUPPRESS_CONNECT_HEADERS 7.54.0 CURLOPT_TCP_FASTOPEN 7.49.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 8009df4051ca..cb36eefad463 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1985,10 +1985,12 @@ typedef enum { CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), /* Set stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240, + 8.21.0, "Has no function"), /* Set E-xclusive stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241, + 8.21.0, "Has no function"), /* Do not send any tftp option requests to the server */ CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), diff --git a/lib/http2.c b/lib/http2.c index c8ecb28b5a68..9eb1e0aeaa41 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1778,16 +1778,12 @@ static int sweight_in_effect(const struct Curl_easy *data) * struct. */ -static void h2_pri_spec(struct cf_h2_ctx *ctx, - struct Curl_easy *data, +static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { struct Curl_data_priority *prio = &data->set.priority; - struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); - int32_t depstream_id = depstream ? depstream->id : 0; - nghttp2_priority_spec_init(pri_spec, depstream_id, - sweight_wanted(data), - data->set.priority.exclusive); + nghttp2_priority_spec_init(pri_spec, 0, + sweight_wanted(data), FALSE); data->state.priority = *prio; } @@ -1805,13 +1801,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, int rv = 0; if(stream && stream->id > 0 && - ((sweight_wanted(data) != sweight_in_effect(data)) || - (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent))) { + (sweight_wanted(data) != sweight_in_effect(data))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, @@ -2123,7 +2117,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, goto out; } - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); diff --git a/lib/setopt.c b/lib/setopt.c index 067a8450ded0..2e08a310ebdc 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1527,13 +1527,10 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, #ifdef USE_HTTP2 case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: { - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) - return Curl_data_priority_add_child(dep, data, - option == CURLOPT_STREAM_DEPENDS_E); + case CURLOPT_STREAM_DEPENDS_E: + /* not doing stream dependencies any longer, but accept options + * for backward compatibility */ break; - } #endif default: diff --git a/lib/url.c b/lib/url.c index 354505ad6c95..796d35e2296a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -119,12 +119,6 @@ #include "smtp.h" #include "ws.h" -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data); -#else -#define data_priority_cleanup(x) -#endif - /* Some parts of the code (e.g. chunked encoding) assume this buffer has more * than a few bytes to play with. Do not let it become too small or bad things * will happen. @@ -275,8 +269,6 @@ CURLcode Curl_close(struct Curl_easy **datap) curlx_safefree(data->info.contenttype); curlx_safefree(data->info.wouldredirect); - data_priority_cleanup(data); - /* No longer a dirty share, if it exists */ if(Curl_share_easy_unlink(data)) DEBUGASSERT(0); @@ -3009,96 +3001,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) #if defined(USE_HTTP2) || defined(USE_HTTP3) -#ifdef USE_NGHTTP2 - -static void priority_remove_child(struct Curl_easy *parent, - struct Curl_easy *child) -{ - struct Curl_data_prio_node **pnext = &parent->set.priority.children; - struct Curl_data_prio_node *pnode = parent->set.priority.children; - - DEBUGASSERT(child->set.priority.parent == parent); - while(pnode && pnode->data != child) { - pnext = &pnode->next; - pnode = pnode->next; - } - - DEBUGASSERT(pnode); - if(pnode) { - *pnext = pnode->next; - curlx_free(pnode); - } - - child->set.priority.parent = 0; - child->set.priority.exclusive = FALSE; -} - -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(child->set.priority.parent) { - priority_remove_child(child->set.priority.parent, child); - } - - if(parent) { - struct Curl_data_prio_node **tail; - struct Curl_data_prio_node *pnode; - - pnode = curlx_calloc(1, sizeof(*pnode)); - if(!pnode) - return CURLE_OUT_OF_MEMORY; - pnode->data = child; - - if(parent->set.priority.children && exclusive) { - /* exclusive: move all existing children underneath the new child */ - struct Curl_data_prio_node *node = parent->set.priority.children; - while(node) { - node->data->set.priority.parent = child; - node = node->next; - } - - tail = &child->set.priority.children; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.priority.children; - parent->set.priority.children = 0; - } - - tail = &parent->set.priority.children; - while(*tail) { - (*tail)->data->set.priority.exclusive = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = pnode; - } - - child->set.priority.parent = parent; - child->set.priority.exclusive = exclusive; - return CURLE_OK; -} - -#endif /* USE_NGHTTP2 */ - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data) -{ - while(data->set.priority.children) { - struct Curl_easy *tmp = data->set.priority.children->data; - priority_remove_child(data, tmp); - if(data->set.priority.parent) - Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); - } - - if(data->set.priority.parent) - priority_remove_child(data->set.priority.parent, data); -} -#endif - void Curl_data_priority_clear_state(struct Curl_easy *data) { memset(&data->state.priority, 0, sizeof(data->state.priority)); diff --git a/lib/urldata.h b/lib/urldata.h index 63d231dc5cf3..4ee5108b1750 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -592,15 +592,7 @@ struct Curl_data_prio_node { * on the same connection. */ struct Curl_data_priority { -#ifdef USE_NGHTTP2 - /* tree like dependencies only implemented in nghttp2 */ - struct Curl_easy *parent; - struct Curl_data_prio_node *children; -#endif int weight; -#ifdef USE_NGHTTP2 - BIT(exclusive); -#endif }; /* Timers */ From f69405b38f0144cab73aa1237d5b9dc46a6f50b2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 22 May 2026 09:48:59 +0200 Subject: [PATCH 0192/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 93 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e10cc1a0c84d..0d41fffc7dba 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,12 +4,13 @@ curl and libcurl 8.21.0 Command line options: 273 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1473 - Contributors: 3680 + Authors: 1477 + Contributors: 3688 This release includes the following changes: o curl: named globs in output file name for upload glob references [77] + o http2: remove stream dependency tracking [40] o lib: drop support for CURLAUTH_DIGEST_IE [4] o libssh: add support for SHA256 host public keys [57] o tool_urlglob: add named globs [92] @@ -17,14 +18,25 @@ This release includes the following changes: This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] + o BUFQ.md: re-sync with source code [111] + o build: omit zlib pkg-config reference for Android [130] + o cf-h2-prox: fix peer leak [132] o cf-h2-proxy: drop interim responses [47] + o cfilters: fix busy loop on blocked transfers [72] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] + o cmake: fix three issues generating lib options in config files [126] o cmake: fix zstd CMake config name [5] + o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] + o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] + o connect: remove deref of freed pointer in trace call [128] o cookie: compare path case sensitively [52] o cookie: simplify strstore(), remove outdated comment [12] o cookie: trim trailing dots when checking PSL [39] o creds: add sasl service name [75] + o creds: mask OAuth bearer token in trace logs [117] + o curl_easy_pause.md: rephrase the stream cache when pause clause [120] + o curl_easy_setopt.md: change options when no transfer runs [122] o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] o CURLOPT_ECH.md: simplify the description language [18] @@ -33,10 +45,12 @@ This release includes the following bugfixes: o CURLOPT_SHARE: warn about early remove [51] o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] o delta: harden external command invocations [98] + o docs/libcurl: fix the version for curl_multi_socket_action o docs: end "...can be used several times..." sentences with period [34] o docs: fix --follow doc typo [97] o docs: fix a couple of typos [62] o docs: fix grammar and wording in FAQ [66] + o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] o ECH: cleanups [20] o event: fix wakeup consumption [93] o ftp: avoid accessing EPSV response one byte past the NULL [9] @@ -48,21 +62,30 @@ This release includes the following bugfixes: o gtls: fix some typos [15] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] o idn: replace header guards with forward declaration [100] + o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o ldap: fix minor leak on write callback error [24] o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] o lib678: fix to not be perma-skipped [10] o lib: make `__STDC_VERSION__` literals `L` (where missing) o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] + o managen: apply minor fixes and improvements [115] o mbedtls: null-terminate the private key blob [36] + o mk-unity.pl: `#include`, and not concatenate input headers [124] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] + o netrc: scanner refactor [121] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o rtsp: bump buf after rtsp_filter_rtp() [88] + o runner.pm: apply minor correctness fix [105] o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] o rustls: error on CURLOPT_CRLFILE with native CA store [59] o schannel: enforce Extended Key Usage for custom CA roots [29] + o schannel: fix revoke_best_effort setting for proxy [70] o schannel_verify: avoid out of blob access [11] + o scripts: catch Credits-to contributors [127] o setopt: changing the proxy port is also a proxy change [23] + o setopt: clear proxy auth properly on NULL [81] o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] @@ -71,8 +94,13 @@ This release includes the following bugfixes: o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] o src: fix comment typos [83] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] + o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] + o test1588: use %TESTNUMBER, not hard-coded number [118] + o tests: add an assert to avoid IPC blocking [69] o tests: fix unit1636 with --disable-progress-meter [37] o tftp: stricter option name checks [90] + o tidy-up: miscellaneous [106] + o tls: fix incomplete mTLS config in conn reuse and session cache [108] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] o tool_formparse: tool2curlparts is no longer recursive [33] @@ -81,15 +109,23 @@ This release includes the following bugfixes: o tool_urlglob: make globbing error reported for correct position [91] o unix-sockets: ignore proxy settings [6] o url: compare full origin when setting credentials [42] + o url: detect proxy changes read from environment [110] o url: fix connection reuse for starttls protocols [27] o url: keep the question mark for empty queries [73] o url: remove ssh_config_matches [31] + o url: remove superfluous check [131] o url: url_match_destination fix [43] o urlapi: change more lowercase percent-encoded to uppercase [71] + o urlapi: compare zone-id in Curl_url_same_origin() [95] o urlapi: consume trailing dots after IPv4 numerical addresses [50] o urlapi: deny hostnames with more than one trailing dot [58] + o urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set [46] o urlapi: handle redirect without set scheme with default-scheme [38] o user-agent.md: mention double quotes too [3] + o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] + o vtls_scache: include signature_algorithms in the SSL peer cache key [123] + o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] + o websockets: auto-tunnel through http proxy [102] o windows: update MS SDK versions in comments [60] o x509asn1: fix DH public key parameter extraction [44] o x509asn1: fix operator order in do_pubkey [21] @@ -114,14 +150,16 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 0xN3R3K3, Alan De Smet, amitbidlan, Andrei Rybak, Andrew Nesbitt, - Bastian Jesuiter, Bill Mill, chrizilla on github, Dan Fandrich, - Daniel Stenberg, dependabot[bot], Earnestly on github, Elise Vance, - Emanuel Krollmann, Fabian Keil, jeffhuang, Jeremy Nicoll, Joshua Rogers, - Kai Pastor, mulan_dh on hackerone, parasol-aser, Raymond Steen, - renovate[bot], Sergio Correia, Sollace on github, Song X. Gao, - Stefan Eissing, Tim Martin, Viktor Szakats, Xi Ruoyao, x-xiang on github - (31 contributors) + 0xN3R3K3, 11soda11, Alan De Smet, amitbidlan, Andrei Rybak, Andrew Nesbitt, + Bastian Jesuiter, Bill Mill, chrizilla on github, co-authors in libssh2, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, + dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, + Fabian Keil, Harry Sintonen, jeffhuang, Jeremy Nicoll, Joshua Rogers, + Kai Pastor, Mark Esler, mulan_dh on hackerone, parasol-aser, penpal, + Raymond Steen, Ray Satiro, renovate[bot], Sergio Correia, sfan5 on github, + Shintomon Mathew, Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, + Viktor Szakats, Will Cosgrove, Xi Ruoyao, x-xiang on github + (42 contributors) References to bug reports and discussions on issues: @@ -164,30 +202,42 @@ References to bug reports and discussions on issues: [37] = https://curl.se/bug/?i=21500 [38] = https://curl.se/bug/?i=21632 [39] = https://curl.se/bug/?i=21636 + [40] = https://curl.se/bug/?i=21723 + [41] = https://curl.se/bug/?i=21720 [42] = https://curl.se/bug/?i=21575 [43] = https://curl.se/bug/?i=21573 [44] = https://curl.se/bug/?i=21595 [45] = https://curl.se/bug/?i=21615 + [46] = https://curl.se/bug/?i=21721 [47] = https://curl.se/bug/?i=21626 [48] = https://curl.se/bug/?i=21606 [50] = https://curl.se/bug/?i=21635 [51] = https://curl.se/bug/?i=21633 [52] = https://curl.se/bug/?i=21616 + [54] = https://curl.se/bug/?i=21707 + [55] = https://curl.se/bug/?i=21714 [56] = https://curl.se/bug/?i=21609 [57] = https://curl.se/bug/?i=21605 [58] = https://curl.se/bug/?i=21622 [59] = https://curl.se/bug/?i=21614 [60] = https://curl.se/bug/?i=21621 [62] = https://curl.se/bug/?i=21617 + [65] = https://curl.se/bug/?i=21682 [66] = https://curl.se/bug/?i=21593 [67] = https://curl.se/bug/?i=21597 + [69] = https://curl.se/bug/?i=21688 + [70] = https://curl.se/bug/?i=21683 [71] = https://curl.se/bug/?i=21592 + [72] = https://curl.se/bug/?i=21671 [73] = https://curl.se/bug/?i=21544 [74] = https://curl.se/bug/?i=21583 [75] = https://curl.se/bug/?i=21585 + [76] = https://curl.se/bug/?i=21642 [77] = https://curl.se/bug/?i=21407 [78] = https://curl.se/bug/?i=21582 [79] = https://curl.se/bug/?i=21576 + [80] = https://curl.se/bug/?i=21699 + [81] = https://curl.se/bug/?i=21696 [82] = https://curl.se/bug/?i=21567 [83] = https://curl.se/bug/?i=21570 [84] = https://curl.se/bug/?i=21569 @@ -199,9 +249,32 @@ References to bug reports and discussions on issues: [92] = https://curl.se/bug/?i=21409 [93] = https://curl.se/bug/?i=21547 [94] = https://curl.se/bug/?i=21557 + [95] = https://curl.se/bug/?i=21686 [96] = https://curl.se/bug/?i=21169 [97] = https://curl.se/bug/?i=21553 [98] = https://curl.se/bug/?i=21104 [99] = https://curl.se/bug/?i=21523 [100] = https://curl.se/bug/?i=21551 [101] = https://curl.se/bug/?i=21550 + [102] = https://curl.se/bug/?i=21663 + [105] = https://curl.se/bug/?i=21672 + [106] = https://curl.se/bug/?i=21646 + [108] = https://curl.se/bug/?i=21667 + [110] = https://curl.se/bug/?i=21666 + [111] = https://curl.se/bug/?i=21678 + [115] = https://curl.se/bug/?i=21670 + [116] = https://curl.se/bug/?i=21668 + [117] = https://curl.se/bug/?i=21659 + [118] = https://curl.se/bug/?i=21662 + [119] = https://curl.se/bug/?i=21660 + [120] = https://curl.se/bug/?i=21658 + [121] = https://curl.se/bug/?i=21624 + [122] = https://curl.se/bug/?i=21604 + [123] = https://curl.se/bug/?i=21651 + [124] = https://curl.se/bug/?i=21656 + [126] = https://curl.se/bug/?i=21654 + [127] = https://curl.se/bug/?i=21653 + [128] = https://curl.se/bug/?i=21649 + [130] = https://curl.se/bug/?i=21647 + [131] = https://curl.se/bug/?i=21650 + [132] = https://curl.se/bug/?i=21602 From 7b9613fa9b1a5e04301a3920eef58e8138dad05e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 21 May 2026 14:21:59 +0200 Subject: [PATCH 0193/1062] ngtcp2: fail handshake directly When certificate verification fails, error out of the handshake callback, forcing ngtcp2 to stop processing the connection any further. Closes #21712 --- lib/vquic/curl_ngtcp2.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 4d27ebc0c197..fb7fd618893d 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -504,7 +504,7 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); if(!ctx || !data) - return NGHTTP3_ERR_CALLBACK_FAILURE; + return NGTCP2_ERR_CALLBACK_FAILURE; ctx->handshake_at = *Curl_pgrs_now(data); ctx->tls_handshake_complete = TRUE; @@ -512,6 +512,9 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); + if(ctx->tls_vrfy_result) + return NGTCP2_ERR_CALLBACK_FAILURE; + #ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const ngtcp2_transport_params *rp; @@ -1491,6 +1494,8 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", stream ? stream->id : -1, blen, result, *pnread); @@ -1817,6 +1822,8 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : -1, len, result, *pnwritten); @@ -2763,6 +2770,8 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, } out: + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; if(ctx->qconn && ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && ngtcp2_conn_in_draining_period(ctx->qconn)) { From 2ba0a0e41e6296c23e735981168c64972a13ed70 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 21 May 2026 14:00:09 -0400 Subject: [PATCH 0194/1062] CIPHERS.md: fix the example that uses only TLS 1.3 - Add --tls-max 1.3 to set the maximum version to TLS 1.3. - Remove Schannel because it doesn't support TLS 1.3 ciphers since 6238888. Prior to this change the example set the minimum version to TLS 1.3 but not the maximum version to TLS 1.3. Ref: https://github.com/curl/curl/issues/21702 Closes https://github.com/curl/curl/pull/21719 --- docs/CIPHERS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index e2c3e89956cb..f0ece576e3bf 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -188,12 +188,13 @@ mbedTLS and wolfSSL. ```sh curl \ --tlsv1.3 \ + --tls-max 1.3 \ --tls13-ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 \ https://example.com/ ``` Restrict to only TLS 1.3 with `aes128-gcm` and `chacha20` ciphers. Works with -OpenSSL, LibreSSL, mbedTLS, wolfSSL and Schannel. +OpenSSL, LibreSSL, mbedTLS and wolfSSL. ```sh curl \ From fc90bdbaf9c9336912671715af0a778529b8f6c1 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Fri, 22 May 2026 09:59:17 +0200 Subject: [PATCH 0195/1062] schannel: error on TLS 1.3-only with cipher list The legacy SCHANNEL_CRED path cannot negotiate TLS 1.3. When TLS 1.3 is the only enabled protocol and a cipher list is set, fail instead of silently downgrading to TLS 1.2. Fixes https://github.com/curl/curl/issues/21702 Closes https://github.com/curl/curl/pull/21725 --- lib/vtls/schannel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index e3b2263e594d..84a078a5e22f 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -654,6 +654,11 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, if(ciphers) { if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + if(!(enabled_protocols & ~SP_PROT_TLS1_3_CLIENT)) { + failf(data, "schannel: TLS 1.3 is not supported with a cipher list; " + "remove the cipher list or allow a lower TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } infof(data, "schannel: WARNING: This version of Schannel " "negotiates a less-secure TLS version than TLS 1.3 because the " "user set an algorithm cipher list."); From 307cfd008b167534a28e7949087477039ab5d0e7 Mon Sep 17 00:00:00 2001 From: Max Dymond Date: Sun, 24 May 2026 09:02:33 +0100 Subject: [PATCH 0196/1062] KNOWN_BUGS: remove stale Threads::Threads entry The old CMake bug about exporting -lpthread instead of Threads::Threads no longer matches current master. As of 2d546d239ecd455b6459e68b85ef8d4b045c0a00 ("cmake: use Threads::Threads imported target for POSIX Threads"), the build now uses Threads::Threads and the generated CMake package config resolves the dependency explicitly, so this KNOWN_BUGS entry is stale. Closes #21734 --- docs/KNOWN_BUGS.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index a2d3b62729e8..b0702bc7a45e 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -490,10 +490,6 @@ Something in the SONAME generation seems to be wrong in the cmake build. [curl issue 11158](https://github.com/curl/curl/issues/11158) -## uses `-lpthread` instead of `Threads::Threads` - -See [curl issue 6166](https://github.com/curl/curl/issues/6166) - ## generated `.pc` file contains strange entries The `Libs.private` field of the generated `.pc` file contains `-lgcc -lgcc_s From dc8a87fc74136970b7927a76dfc8a491ca9c8e91 Mon Sep 17 00:00:00 2001 From: Max Dymond Date: Sun, 24 May 2026 10:22:21 +0100 Subject: [PATCH 0197/1062] mailmap: cmeister2@gmail is primary for Max Dymond I'd rather all my commits be attributed to cmeister2@gmail.com instead of anything else; especially not my old Microsoft email address! Closes #21735 --- .mailmap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index d8558a2ec3e3..268060eee87d 100644 --- a/.mailmap +++ b/.mailmap @@ -84,8 +84,8 @@ Tobias Nyholm Timur Artikov Michał Antoniak <47522782+MAntoniak@users.noreply.github.com> Gleb Ivanovsky -Max Dymond -Max Dymond +Max Dymond +Max Dymond Abhinav Singh Malik Idrees Hasan Khan <77000356+MalikIdreesHasanKhan@users.noreply.github.com> Yongkang Huang From 252b82f693574e884fb36dfde9371b409716a0fc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 23 May 2026 11:05:27 +0200 Subject: [PATCH 0198/1062] quiche: bump cloudflare/quiche to v0.29.0, update pytest workaround Also: - drop no longer necessary quiche build workaround. - update build for boringssl's new location (since v0.29.0, it's no longer vendored) within the quiche tree. - move boringssl install dir out of quiche tree, and shorten it. Ref: https://github.com/cloudflare/quiche/issues/2277 Ref: https://github.com/cloudflare/quiche/pull/2278 Ref: #21620 Closes #21730 --- .github/workflows/http3-linux.yml | 20 +++++++++----------- tests/http/test_05_errors.py | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 20e20fd3f645..f9f473b5df60 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -52,7 +52,7 @@ env: OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.24.7 + QUICHE_VERSION: 0.29.0 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com @@ -520,7 +520,7 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/quiche/target/release PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-openssl=/home/runner/quiche/quiche/deps/boringssl/src + --with-openssl=/home/runner/quiche-boringssl --with-quiche=/home/runner/quiche/target/release --with-ca-fallback --enable-unity @@ -528,7 +528,7 @@ jobs: - name: 'quiche' PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig:/home/runner/quiche/target/release generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/quiche/quiche/deps/boringssl/src + -DOPENSSL_ROOT_DIR=/home/runner/quiche-boringssl -DUSE_QUICHE=ON -DCURL_CA_FALLBACK=ON @@ -726,19 +726,17 @@ jobs: cd ~ git clone --quiet --depth 1 --branch "${QUICHE_VERSION}" --recursive https://github.com/cloudflare/quiche cd quiche - #### Work-around https://github.com/curl/curl/issues/7927 ####### - #### See https://github.com/alexcrichton/cmake-rs/issues/131 #### - sed -i -e 's/cmake = "0.1"/cmake = "=0.1.45"/' quiche/Cargo.toml - cargo build -v --package quiche --release --features ffi,pkg-config-meta,qlog --verbose ln -s libquiche.so target/release/libquiche.so.0 - mkdir -v quiche/deps/boringssl/src/lib - find target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche/deps/boringssl/src/lib \; + cd .. + mkdir -p quiche-boringssl/lib + find quiche/target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche-boringssl/lib \; + find quiche/target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../{}' quiche-boringssl \; # include dir - # /home/runner/quiche/quiche/deps/boringssl/src/include + # /home/runner/quiche-boringssl/include # lib dir - # /home/runner/quiche/quiche/deps/boringssl/src/lib + # /home/runner/quiche-boringssl/lib - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index a78b9c37463b..8d82c093eb60 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -43,7 +43,7 @@ class TestErrors: @pytest.mark.parametrize("proto", Env.http_protos()) def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.24.8'): + not env.curl_lib_version_at_least('quiche', '0.29.1'): pytest.skip("quiche issue #2277 not fixed") count = 1 curl = CurlClient(env=env) @@ -64,7 +64,7 @@ def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_mplx_protos()) def test_05_02_partial_20(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.24.8'): + not env.curl_lib_version_at_least('quiche', '0.29.1'): pytest.skip("quiche issue #2277 not fixed") count = 20 curl = CurlClient(env=env) From 4102400028612bf83c61755efbe4cdef49b231b7 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 24 May 2026 15:12:54 +0200 Subject: [PATCH 0199/1062] GHA/http3-linux: fixup quiche cache Closes #21740 --- .github/workflows/http3-linux.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index f9f473b5df60..5f9e8c7b1625 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -520,7 +520,7 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/quiche/target/release PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-openssl=/home/runner/quiche-boringssl + --with-openssl=/home/runner/quiche/boringssl --with-quiche=/home/runner/quiche/target/release --with-ca-fallback --enable-unity @@ -528,7 +528,7 @@ jobs: - name: 'quiche' PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig:/home/runner/quiche/target/release generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/quiche-boringssl + -DOPENSSL_ROOT_DIR=/home/runner/quiche/boringssl -DUSE_QUICHE=ON -DCURL_CA_FALLBACK=ON @@ -729,14 +729,14 @@ jobs: cargo build -v --package quiche --release --features ffi,pkg-config-meta,qlog --verbose ln -s libquiche.so target/release/libquiche.so.0 cd .. - mkdir -p quiche-boringssl/lib - find quiche/target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche-boringssl/lib \; - find quiche/target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../{}' quiche-boringssl \; + mkdir -p quiche/boringssl/lib + find quiche/target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche/boringssl/lib \; + find quiche/target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../../{}' quiche/boringssl \; # include dir - # /home/runner/quiche-boringssl/include + # /home/runner/quiche/boringssl/include # lib dir - # /home/runner/quiche-boringssl/lib + # /home/runner/quiche/boringssl/lib - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: From 0b8dbbc63c98777e4584cb9fbd71df3464008ad1 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Fri, 22 May 2026 09:48:15 +0200 Subject: [PATCH 0200/1062] libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH Host key type mismatch from libssh was incorrectly reported as missing, causing key callbacks to accept instead of reject. Reported by: Joshua Rogers (Aisle Research) Closes #21724 --- lib/vssh/libssh.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 09084765bcf0..817a463a2cce 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -277,6 +277,8 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) keymatch = CURLKHMATCH_OK; break; case SSH_KNOWN_HOSTS_OTHER: + keymatch = CURLKHMATCH_MISMATCH; + break; case SSH_KNOWN_HOSTS_NOT_FOUND: case SSH_KNOWN_HOSTS_UNKNOWN: case SSH_KNOWN_HOSTS_ERROR: From 230a98663687284eafb80c19f3940f10f4701f12 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 23 May 2026 14:40:55 +0200 Subject: [PATCH 0201/1062] ldap: switch of chasing referrals It is switched off in the OpenLDAP backend, so we should do the same here. Follow-up to cdc1da912066535680f02eb31 Closes #21732 --- lib/ldap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ldap.c b/lib/ldap.c index ed74e9a19afd..d8ec859126c5 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -315,6 +315,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #endif ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + /* Do not chase referrals. */ + ldap_set_option(server, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP From a4d8fd7a2a6b799a8e8064c11cefcd9b5c7ec1c9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 26 May 2026 09:09:24 +0200 Subject: [PATCH 0202/1062] VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part Closes #21747 --- docs/VULN-DISCLOSURE-POLICY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index 4ff284e43f2c..379a6d0da56b 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -36,6 +36,10 @@ announcement. [HackerOne](https://hackerone.com/curl). Issues filed there reach a handful of selected and trusted people. +- The curl project cannot handle vulnerability reports sent to us over email. + We lose track of the reports. We cannot easily disclose them. Please do not + send us reports over email. + - Messages that do not relate to the reporting or managing of an undisclosed security vulnerability in curl or libcurl are ignored and no further action is required. From 862e8a74a84478d82973471b4f49dc2746c1780e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 25 May 2026 16:43:00 +0200 Subject: [PATCH 0203/1062] transfer: clear referer when set to NULL Verify in test 1649 Closes #21741 --- lib/transfer.c | 2 + tests/data/Makefile.am | 2 +- tests/data/test1649 | 55 +++++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib1649.c | 90 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 tests/data/test1649 create mode 100644 tests/libtest/lib1649.c diff --git a/lib/transfer.c b/lib/transfer.c index 721ad8d9cec7..49930518eeac 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -511,6 +511,8 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(data->set.str[STRING_SET_REFERER]) Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], 0, NULL); + else + Curl_bufref_free(&data->state.referer); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 08b2f17dd0ff..78779a55188e 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -216,7 +216,7 @@ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 \ test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 \ -test1644 test1645 test1646 test1647 test1648 \ +test1644 test1645 test1646 test1647 test1648 test1649 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1649 b/tests/data/test1649 new file mode 100644 index 000000000000..d2fd7799bd87 --- /dev/null +++ b/tests/data/test1649 @@ -0,0 +1,55 @@ + + + + +HTTP +Referer + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 200 OK +Content-Length: 6 + +hello + + + + +# Client-side + + +http + + + +lib%TESTNUMBER + + +Set referer first then NULL it + + +http://%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Referer: https://secret.example.com/ + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index ad86411a7f29..d9a94a1e715b 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -99,7 +99,7 @@ TESTS_C = \ lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ - lib1647.c lib1648.c \ + lib1647.c lib1648.c lib1649.c \ lib1662.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1649.c b/tests/libtest/lib1649.c new file mode 100644 index 000000000000..2dd66c0231a2 --- /dev/null +++ b/tests/libtest/lib1649.c @@ -0,0 +1,90 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1649(CURL *curl, const char *url) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1649(CURL *curl, const char *url) +{ + CURLcode result = CURLE_OK; + + result = init1649(curl, url); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1649(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + easy_setopt(curl, CURLOPT_REFERER, "https://secret.example.com/"); + + result = run1649(curl, URL); + if(result) + goto test_cleanup; + + /* reset it */ + easy_setopt(curl, CURLOPT_REFERER, NULL); + + result = run1649(curl, URL); + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} From 5ab34cba42e4ee4282fe8bab43f311d51b9bf9bd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 26 May 2026 09:52:19 +0200 Subject: [PATCH 0204/1062] multi: handle pause in multi socket callback The mev_sh_entry object might be removed if curl_easy_pause() is called from within the socket callback. Introduced a 'magic' struct field to to 'mev_sh_entry' to make it easier to programmatically detect/assert if the pointer is bad - in debug builds. Reported-by: Joshua Rogers Closes #21748 --- lib/multi_ev.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 478d5a48d55e..7ea3b2827e30 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -40,6 +40,8 @@ static void mev_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } +#define SH_ENTRY_MAGIC 0x570091d + /* Information about a socket for which we inform the libcurl application * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE) */ @@ -51,6 +53,9 @@ struct mev_sh_entry { * libcurl application to watch out for */ unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ +#ifdef DEBUGBUILD + unsigned int magic; +#endif BIT(announced); /* this socket has been passed to the socket callback at least once */ }; @@ -75,6 +80,9 @@ static void mev_sh_entry_dtor(void *freethis) { struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis; Curl_uint32_spbset_destroy(&entry->xfers); +#ifdef DEBUGBUILD + entry->magic = 0; +#endif curlx_free(entry); } @@ -113,7 +121,9 @@ static struct mev_sh_entry *mev_sh_entry_add(struct Curl_hash *sh, mev_sh_entry_dtor(check); return NULL; /* major failure */ } - +#ifdef DEBUGBUILD + check->magic = SH_ENTRY_MAGIC; +#endif return check; /* things are good in sockhash land */ } @@ -223,6 +233,7 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, /* we should only be called when the callback exists */ DEBUGASSERT(multi->socket_cb); + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); if(!multi->socket_cb) return CURLM_OK; @@ -272,12 +283,18 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->user_data); mev_in_callback(multi, FALSE); - entry->announced = TRUE; if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; } - entry->action = (unsigned int)comboaction; + /* curl_easy_pause() is documented as callable from any callback; it + * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + entry = mev_sh_entry_get(&multi->ev.sh_entries, s); + if(entry) { + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); + entry->announced = TRUE; + entry->action = (unsigned int)comboaction; + } return CURLM_OK; } From 32227f83b4c52c23d4b64314e5d29a980e83d2a6 Mon Sep 17 00:00:00 2001 From: mik <16636149+mik-at@users.noreply.github.com> Date: Thu, 21 May 2026 10:25:49 +0200 Subject: [PATCH 0205/1062] docs: fix odd wording in CONTRIBUTE.md Found with AI assistance, verified manually Closes #21705 --- docs/CONTRIBUTE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CONTRIBUTE.md b/docs/CONTRIBUTE.md index 316809193059..bfbc9220f674 100644 --- a/docs/CONTRIBUTE.md +++ b/docs/CONTRIBUTE.md @@ -143,10 +143,10 @@ it into a pull request for you, to have the CI jobs verify it proper before it can be merged. Be prepared that some feedback on the proposed change might then come on GitHub. -Your changes be reviewed and discussed and you are expected to correct flaws -pointed out and update accordingly, or the change risks stalling and -eventually getting deleted without action. As a submitter of a change, you are -the owner of that change until it has been merged. +As your changes are reviewed and discussed, you are expected to address any +flaws pointed out and update accordingly. Otherwise your changes risk stalling +and eventually being deleted without action. As a submitter of a change, you +are the owner of that change until it has been merged. Respond on the list or on GitHub about the change and answer questions and/or fix nits/flaws. This is important. We take lack of replies as a sign that you From eb8f31e18b042fb0fbaca416e19b75343627f01d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 11:38:25 +0200 Subject: [PATCH 0206/1062] multi_ev: silence clang-tidy nonsense About a "unnecessary define" - my ass. Closes #21752 --- lib/multi_ev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 7ea3b2827e30..0da5b0e9044b 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -40,7 +40,9 @@ static void mev_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } +#ifdef DEBUGBUILD #define SH_ENTRY_MAGIC 0x570091d +#endif /* Information about a socket for which we inform the libcurl application * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE) From f27233e9843b0e8d510cb8b79622238ba853befe Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 11:25:01 +0200 Subject: [PATCH 0207/1062] GnuTLS: require 3.7.2 for earlydata Since all API features we need for TLSv1.3 earlydata support do exist only from version 3.7.2 onwards, make that the minimal version required. Fixes #21750 Reported-by: Johannes Schlatow Closes #21751 --- lib/vtls/gtls.c | 2 +- tests/http/testenv/env.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index a8ffc28e8c37..22001c339125 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -72,7 +72,7 @@ static void tls_log_func(int level, const char *str) #endif #undef CURL_GNUTLS_EARLY_DATA -#if GNUTLS_VERSION_NUMBER >= 0x03060d +#if GNUTLS_VERSION_NUMBER >= 0x030702 #define CURL_GNUTLS_EARLY_DATA #endif diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 30fc0d0a734a..c7bbfc4c5461 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -523,7 +523,7 @@ def curl_resolv_threaded() -> bool: @staticmethod def curl_can_early_data() -> bool: if Env.curl_uses_lib('gnutls'): - return Env.curl_lib_version_at_least('gnutls', '3.6.13') + return Env.curl_lib_version_at_least('gnutls', '3.7.2') return Env.curl_uses_any_libs(['wolfssl', 'quictls', 'openssl']) @staticmethod From 01d8191b25a05e8fa91553a6c0d48acb99907d26 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 26 May 2026 13:22:20 +0200 Subject: [PATCH 0208/1062] GHA: bump LibreSSL to 4.3.2 Also switch back to ftp.openbsd.org download server. More often than not the GitHub release entry is missing the download artifacts at the time of detecting a new version, breaking automatic bumps. We cache the download so it does not bang the origin server with many requests. Follow-up to 800b0bec18e9c77e35912fac8321c791d7b57863 #19082 Closes #21742 Closes #21754 --- .github/workflows/http3-linux.yml | 4 ++-- .github/workflows/linux.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 5f9e8c7b1625..c316b66dcdad 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -45,7 +45,7 @@ env: # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com GNUTLS_VERSION: 3.8.11 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.3.1 + LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com OPENSSL_VERSION: 4.0.0 # manually bumped @@ -280,7 +280,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl/build diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1bef3460df73..9e4f26194a46 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -41,7 +41,7 @@ env: # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.678 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.3.1 + LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com MBEDTLS_VERSION: 4.0.0 # manually bumped @@ -529,7 +529,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && !steps.cache-libressl-c-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl -DCURL_ENABLE_NTLM=ON @@ -550,7 +550,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'libressl-filc') && !steps.cache-libressl-filc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl \ From 90a7732d467eae7c5a59fc07c5a072970926f8c6 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 10:25:22 +0200 Subject: [PATCH 0209/1062] test1981: explicitly set the locale Otherwise we may get a different month name in the output. Closes #21749 --- tests/data/test1981 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/data/test1981 b/tests/data/test1981 index 1e2519a72a9b..9c9c1f35f15c 100644 --- a/tests/data/test1981 +++ b/tests/data/test1981 @@ -38,6 +38,7 @@ Debug CURL_TIME=1754037103 +LC_TIME=C http://%HOSTIP:%HTTPPORT/%TESTNUMBER --write-out='Time: %time{%d/%b/%Y %H:%M:%S.%f %z %Z}\n' -s -o %LOGDIR/dump From 500820682ce570f21586f567ddec4dbea4e6dad5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 26 May 2026 15:59:18 +0200 Subject: [PATCH 0210/1062] GHA: require HTTPS protocol in redirections (where missing) Also: - drop following redirects on `openldap.org`. Closes #21757 --- .github/workflows/distcheck.yml | 6 +++--- .github/workflows/http3-linux.yml | 6 +++--- .github/workflows/linux.yml | 22 +++++++++++----------- .github/workflows/macos.yml | 2 +- .github/workflows/non-native.yml | 2 +- .github/workflows/windows.yml | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index 9d09bc94b02b..eb8552effc44 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -285,7 +285,7 @@ jobs: if [[ "${MATRIX_IMAGE}" = *'windows'* ]]; then cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-win64-x64.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-win64-x64.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OLD_CMAKE_SHA256_WIN_INTEL}" && unzip -q pkg.bin && rm -f pkg.bin printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-win64-x64/bin/cmake.exe > ~/old-cmake-path.txt elif [[ "${MATRIX_IMAGE}" = *'ubuntu'* ]]; then @@ -293,14 +293,14 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 install libpsl-dev libssl-dev cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-Linux-aarch64.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-Linux-aarch64.tar.gz" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OLD_CMAKE_SHA256_LINUX_ARM}" && tar -xzf pkg.bin && rm -f pkg.bin printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-Linux-aarch64/bin/cmake > ~/old-cmake-path.txt else brew install libpsl openssl cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-macos-universal.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-macos-universal.tar.gz" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OLD_CMAKE_SHA256_MACOS_UNI}" && tar -xzf pkg.bin && rm -f pkg.bin printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-macos-universal/CMake.app/Contents/bin/cmake > ~/old-cmake-path.txt fi diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index c316b66dcdad..91a0e735aab7 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -226,7 +226,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "aws-lc-${AWSLC_VERSION}" cmake -B . -G Ninja -DBUILD_SHARED_LIBS=ON -DBUILD_TOOL=OFF -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/awslc/build @@ -250,7 +250,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://ftpmirror.gnu.org/nettle/nettle-${NETTLE_VERSION}.tar.gz" --output pkg.bin + --location --proto-redir =https "https://ftpmirror.gnu.org/nettle/nettle-${NETTLE_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "nettle-${NETTLE_VERSION}" autoreconf -fi @@ -302,7 +302,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin cd "openssl-${OPENSSL_PREV_VERSION}" ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9e4f26194a46..49ff43037a14 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -510,7 +510,7 @@ jobs: run: | cd /home/runner curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/pizlonator/fil-c/releases/download/v${FIL_C_VERSION}/filc-${FIL_C_VERSION}-linux-x86_64.tar.xz" --output pkg.bin + --location --proto-redir =https "https://github.com/pizlonator/fil-c/releases/download/v${FIL_C_VERSION}/filc-${FIL_C_VERSION}-linux-x86_64.tar.xz" --output pkg.bin sha256sum pkg.bin && tar -xJf pkg.bin && rm -f pkg.bin && mv "filc-${FIL_C_VERSION}-linux-x86_64" filc cd filc ./setup.sh @@ -572,7 +572,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') && !steps.cache-nghttp2-filc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.xz" --output pkg.bin + --location --proto-redir =https "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.xz" --output pkg.bin sha256sum pkg.bin && tar -xJf pkg.bin && rm -f pkg.bin cd "nghttp2-${NGHTTP2_VERSION}" cmake -B . -G Ninja -DENABLE_LIB_ONLY=ON -DBUILD_TESTING=OFF -DENABLE_DOC=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/nghttp2 \ @@ -595,7 +595,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') && !steps.cache-wolfssl-all-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh @@ -618,7 +618,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && !steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh @@ -641,7 +641,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && !steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh @@ -664,7 +664,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && !steps.cache-mbedtls-latest-intel.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin + --location --proto-redir =https "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin sha256sum pkg.bin && tar -xjf pkg.bin && rm -f pkg.bin cd "mbedtls-${MBEDTLS_VERSION}" ./scripts/config.py set MBEDTLS_THREADING_C @@ -688,7 +688,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') && !steps.cache-mbedtls-latest-arm.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin + --location --proto-redir =https "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin sha256sum pkg.bin && tar -xjf pkg.bin && rm -f pkg.bin cd "mbedtls-${MBEDTLS_VERSION}" ./scripts/config.py set MBEDTLS_THREADING_C @@ -712,7 +712,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') && !steps.cache-mbedtls-prev.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_PREV_VERSION}/mbedtls-${MBEDTLS_PREV_VERSION}.tar.bz2" --output pkg.bin + --location --proto-redir =https "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_PREV_VERSION}/mbedtls-${MBEDTLS_PREV_VERSION}.tar.bz2" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${MBEDTLS_PREV_SHA256}" && tar -xjf pkg.bin && rm -f pkg.bin cd "mbedtls-${MBEDTLS_PREV_VERSION}" ./scripts/config.py set MBEDTLS_THREADING_C @@ -736,7 +736,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'openldap-static') && !steps.cache-openldap-static.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${OPENLDAP_VERSION}.tgz" --output pkg.bin + "https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${OPENLDAP_VERSION}.tgz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "openldap-${OPENLDAP_VERSION}" autoreconf -fi @@ -776,7 +776,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'awslc') && !steps.cache-awslc.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "aws-lc-${AWSLC_VERSION}" cmake -B . -G Ninja -DCMAKE_INSTALL_PREFIX=/home/runner/awslc -DBUILD_TOOL=OFF -DBUILD_TESTING=OFF @@ -820,7 +820,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/rustls/rustls-ffi/releases/download/v${RUSTLS_VERSION}/librustls_${RUSTLS_VERSION}_amd64.deb.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/rustls/rustls-ffi/releases/download/v${RUSTLS_VERSION}/librustls_${RUSTLS_VERSION}_amd64.deb.zip" --output pkg.bin sha256sum pkg.bin && unzip pkg.bin -d rustls && rm -f pkg.bin - name: 'build rustls' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9b98117e6868..e982dc81e15c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -126,7 +126,7 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'libressl') && !steps.cache-libressl.outputs.cache-hit }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + --location --proto-redir =https "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" cmake -B . -G Ninja \ diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c032dd845ea7..6aca90bbdc6b 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -320,7 +320,7 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 3 --retry-connrefused \ - --location "https://github.com/andrewwutw/build-djgpp/releases/download/v${TOOLCHAIN_VERSION}/djgpp-linux64-gcc1220.tar.bz2" --output pkg.bin + --location --proto-redir =https "https://github.com/andrewwutw/build-djgpp/releases/download/v${TOOLCHAIN_VERSION}/djgpp-linux64-gcc1220.tar.bz2" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${TOOLCHAIN_SHA256}" && tar -xjf pkg.bin && rm -f pkg.bin cd djgpp curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 75df6b75c5c3..bd8c214d45b5 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -485,11 +485,11 @@ jobs: cd /c # no D: drive on windows-11-arm runners if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_ARM64}" && unzip pkg.bin && rm -f pkg.bin else curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_WIN64}" && unzip pkg.bin && rm -f pkg.bin fi fi @@ -1139,11 +1139,11 @@ jobs: cd /c # no D: drive on windows-11-arm runners if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_ARM64}" && unzip pkg.bin && rm -f pkg.bin else curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_WIN64}" && unzip pkg.bin && rm -f pkg.bin fi fi From 2cc171cbd4a9eac84f5c62c5b987347e5f8880e1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 26 May 2026 15:56:27 +0200 Subject: [PATCH 0211/1062] GHA: verify tarball downloads Detect latest tarball version via the https://curl.se/downloads.html page, download the signing key from a public keyserver then verify source download signatures. To ensure that public downloads are intact. Closes #21759 --- .github/workflows/distcheck.yml | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index eb8552effc44..2d381fbc8891 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -367,3 +367,41 @@ jobs: export TEST_CMAKE_FLAGS='-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DOPENSSL_ROOT_DIR=C:/msys64/mingw64' fi ./tests/cmake/test.sh find_package ${TESTOPTS} -DCURL_USE_OPENSSL=ON + + verify-tarball-downloads: + name: 'Verify tarball downloads' + runs-on: ubuntu-slim + timeout-minutes: 2 + steps: + - name: 'download and import GPG key' + env: + CURL_GPG_ID: 27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2 + run: | + for keyserver in \ + https://keyserver.ubuntu.com/ \ + https://pgpkeys.eu/ \ + ; do + echo "--- Downloading from ${keyserver}..." + if curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + "${keyserver}pks/lookup?op=get&options=mr&exact=on&search=0x${CURL_GPG_ID}" \ + | gpg --batch --keyserver-options timeout=15 --display-charset utf-8 --keyid-format 0xlong --import --status-fd 1 2>&1; then + break + fi + done + + - name: 'download and verify tarballs' + run: | + echo "--- Detecting latest curl tarball version..." + curl_version="$(curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused https://curl.se/download.html \ + | grep -o -E 'curl [0-9]+\.[0-9]+\.[0-9]+' | cut -c 6-)" + + for suffix in .tar.bz2 .tar.gz .tar.xz .zip; do + echo "--- Downloading ${curl_version} ${suffix}..." + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --output pkg.bin "https://curl.se/download/curl-${curl_version}${suffix}" \ + --output pkg.sig "https://curl.se/download/curl-${curl_version}${suffix}.asc" + echo "--- Verifying ${curl_version} ${suffix}..." + gpg --batch --keyserver-options timeout=15 --display-charset utf-8 --keyid-format 0xlong --verify-options show-primary-uid-only \ + --verify pkg.sig pkg.bin 2>&1 + echo '---' + done From 0cb455aa85c4ae49b82760bc8ae894a3fc607425 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 26 May 2026 21:32:19 +0200 Subject: [PATCH 0212/1062] INSTALL-CMAKE.md: drop two obsolete sections Follow-up to 89043ba90689418a115e967633e261139b48ce23 #20407 Closes #21761 --- docs/INSTALL-CMAKE.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index a84faf72f209..e07ec455dad8 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -103,16 +103,6 @@ arguments in the build. Building statically is not for the faint of heart. -### Fallback for CMake before version 3.13 - -CMake before version 3.13 does not support the `--build` option. In that -case, you have to `cd` to the build directory and use the building tool that -corresponds to the build files that CMake generated for you. This example -assumes that CMake generates `Makefile`: - - $ cd ../curl-build - $ make - # Testing (The test suite does not yet work with the cmake build) @@ -129,16 +119,6 @@ to set a custom install prefix for curl, set [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) when configuring the CMake build. -### Fallback for CMake before version 3.15 - -CMake before version 3.15 does not support the `--install` option. In that -case, you have to `cd` to the build directory and use the building tool that -corresponds to the build files that CMake generated for you. This example -assumes that CMake generates `Makefile`: - - $ cd ../curl-build - $ make install - # CMake usage This section describes how to locate and use curl/libcurl from CMake-based From efc3f2309e1c87c67700350f7df8da508cd307cd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 26 May 2026 11:40:15 +0200 Subject: [PATCH 0213/1062] GHA: fix locale tests on macOS, extend to verify test 1981 - fix macOS locale tests to clear existing variables. (Without this, the system-defined `LC_ALL` takes precedence, and the custom envs in CI are ignored.) - trigger test 1981 issue by setting `LC_TIME` to non-English, on macOS. (On Linux it'd require explicitly installing a non-English locale, I skipped this for simplicity.) ``` [...] -Time: 01/Aug/2025 08:31:43.037103 +0000 UTC[CR][LF] +Time: 01/ao%c3%bb/2025 08:31:43.037103 +0000 UTC[CR][LF] [...] FAIL 1981: '%time output with --write-out' HTTP, HTTP GET ``` Follow-up to 90a7732d467eae7c5a59fc07c5a072970926f8c6 #21749 Follow-up to 1cc8a5235f76e744433cbf28ec98ecb972158387 #17988 Follow-up to c221c0ee5935497168c52686a9d8cc87b45bbca9 #17938 Closes #21753 --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 49ff43037a14..9546aa2cb5aa 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -89,7 +89,7 @@ jobs: - name: 'libressl krb5' image: ubuntu-24.04-arm install_packages: libidn2-dev libnghttp2-dev libldap-dev libkrb5-dev - install_steps: libressl-c-arm pytest codeset-test + install_steps: libressl-c-arm pytest codeset-test1 LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib configure: --with-openssl=/home/runner/libressl --with-gssapi --enable-debug @@ -953,7 +953,7 @@ jobs: TFLAGS+=' --buildinfo' # only test-ci sets this by default, set it manually for test-torture fi [ -f ~/venv/bin/activate ] && source ~/venv/bin/activate - if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test'* ]]; then + if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test1'* ]]; then locale || true export LC_ALL=C export LC_CTYPE=C diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e982dc81e15c..dec6e5ceed84 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -347,12 +347,13 @@ jobs: - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 install: brotli mbedtls zstd - install_steps: codeset-test + install_steps: codeset-test1 generate: -DCURL_USE_MBEDTLS=ON -DCURL_DISABLE_LDAP=ON -DCURL_DEFAULT_SSL_BACKEND=mbedtls -DCURL_USE_OPENSSL=ON -DUSE_APPLE_IDN=ON -DCURL_ENABLE_NTLM=ON - name: 'GnuTLS !ldap krb5 +examples' compiler: clang install: gnutls nettle krb5 + install_steps: codeset-test2 generate: >- -DENABLE_DEBUG=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=OFF -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 @@ -560,11 +561,20 @@ jobs: TFLAGS+=' --buildinfo' # only test-ci sets this by default, set it manually for test-torture fi source ~/venv/bin/activate - if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test'* ]]; then + if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test1'* ]]; then locale || true - export LC_ALL=C + unset LANG + unset LC_ALL + unset LC_COLLATE + unset LC_MESSAGES + unset LC_MONETARY + unset LC_TIME export LC_CTYPE=C export LC_NUMERIC=fr_FR.UTF-8 + elif [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test2'* ]]; then + locale || true + unset LC_ALL + export LC_TIME=fr_FR fi rm -f ~/.curlrc if [ "${MATRIX_BUILD}" = 'cmake' ]; then From e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 Mon Sep 17 00:00:00 2001 From: Aritra Basu Date: Mon, 27 Apr 2026 19:35:38 -0400 Subject: [PATCH 0214/1062] HTTP/3: add proxy CONNECT and MASQUE CONNECT-UDP support (ngtcp2 QUIC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds two major proxy capabilities to curl (ngtcp2 QUIC): - HTTP/3 Proxy CONNECT: Tunnel HTTP/1.1 or HTTP/2 traffic through an HTTPS proxy that speaks HTTP/3 (QUIC) using the standard CONNECT method over an HTTP/3 connection. - MASQUE CONNECT-UDP: Tunnel HTTP/3 (QUIC) traffic through an HTTP proxy (speaking HTTP/1.1, HTTP/2, or HTTP/3) using the extended CONNECT method with the CONNECT-UDP protocol (RFC9297 & RFC9298). Public API additions: - `CURLPROXY_HTTPS3`: new proxy type constant for HTTP/3 proxy - `--proxy-http3`: new CLI flag to negotiate HTTP/3 with HTTPS proxy The implementation adds two new filters: - `H3-PROXY` - enables negotiating HTTP/3 (QUIC) to the proxy and running CONNECT/CONNECT-UDP through that proxy transport. - `CAPSULE` - dedicated filter inserted between QUIC transport and HTTP-PROXY to handle datagram capsule encapsulation/decapsulation. Here is how the curl filter chaining looks in different scenarios: - HTTP/3 Proxy CONNECT (tunneling TCP protocols over QUIC proxy): conn -> HTTP/1.1 or HTTP/2 -> SSL -> HTTP-PROXY -> H3-PROXY -> HAPPY-EYEBALLS -> UDP - MASQUE CONNECT-UDP (tunneling QUIC over any proxy): conn -> HTTP/3 -> CAPSULE -> HTTP-PROXY -> H3-PROXY -> HAPPY-EYEBALLS -> UDP conn -> HTTP/3 -> CAPSULE -> HTTP-PROXY -> H1-PROXY or H2-PROXY -> SSL -> HAPPY-EYEBALLS -> TCP - Both features currently require the ngtcp2 QUIC backend. - Both features are experimental (disabled by default). Enable with `--enable-proxy-http3`(autotools) or `-DUSE_PROXY_HTTP3=ON`(CMake). Tests: - tests/unit/unit3400.c: Unit tests for capsule protocol encode/decode - tests/http/test_60_h3_proxy.py: Comprehensive pytest integration suite - tests/http/testenv/h2o.py: Managing h2o instances with HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) listeners, proxy.connect and proxy.connect-udp enabled. References: RFC 9297 - HTTP Datagrams and the Capsule Protocol RFC 9298 - Proxying UDP in HTTP RFC 9000 §16 — Variable-Length Integer Encoding Signed-off-by: Aritra Basu Closes #21153 --- .github/scripts/pyspelling.words | 3 + CMakeLists.txt | 17 + configure.ac | 67 + docs/EXPERIMENTAL.md | 10 + docs/INSTALL-CMAKE.md | 1 + docs/cmdline-opts/Makefile.inc | 1 + docs/cmdline-opts/proxy-http2.md | 4 +- docs/cmdline-opts/proxy-http3.md | 31 + docs/internals/CONNECTION-FILTERS.md | 39 +- docs/libcurl/curl_version_info.md | 7 + docs/libcurl/opts/CURLOPT_PROXY.md | 6 +- docs/libcurl/opts/CURLOPT_PROXYTYPE.md | 6 + docs/libcurl/symbols-in-versions | 1 + docs/options-in-versions | 1 + docs/tests/HTTP.md | 3 + include/curl/curl.h | 8 +- lib/Makefile.inc | 6 + lib/capsule.c | 281 ++ lib/capsule.h | 77 + lib/cf-capsule.c | 253 ++ lib/cf-capsule.h | 40 + lib/cf-h1-proxy.c | 239 +- lib/cf-h1-proxy.h | 3 +- lib/cf-h2-proxy.c | 114 +- lib/cf-h2-proxy.h | 3 +- lib/cf-h3-proxy.c | 3478 ++++++++++++++++++++++++ lib/cf-h3-proxy.h | 42 + lib/cf-ip-happy.c | 25 + lib/cf-ip-happy.h | 9 + lib/connect.c | 151 +- lib/curl_config-cmake.h.in | 3 + lib/curl_trc.c | 4 + lib/http.c | 17 +- lib/http.h | 2 - lib/http2.c | 3 + lib/http_proxy.c | 391 ++- lib/http_proxy.h | 45 +- lib/peer.c | 56 +- lib/peer.h | 5 + lib/setopt.c | 6 +- lib/url.c | 27 +- lib/version.c | 3 + lib/vquic/curl_ngtcp2.c | 229 +- lib/vquic/curl_ngtcp2.h | 2 + lib/vquic/curl_quiche.c | 1 + lib/vquic/vquic-tls.c | 2 + lib/vquic/vquic.c | 69 +- lib/vquic/vquic.h | 2 + lib/vtls/openssl.c | 15 +- lib/vtls/vtls.c | 3 + lib/vtls/vtls_int.h | 9 +- src/tool_getparam.c | 16 +- src/tool_getparam.h | 1 + src/tool_listhelp.c | 3 + tests/data/Makefile.am | 2 + tests/data/test3400 | 19 + tests/http/CMakeLists.txt | 6 + tests/http/Makefile.am | 2 + tests/http/config.ini.in | 3 + tests/http/conftest.py | 117 +- tests/http/test_60_h3_proxy.py | 689 +++++ tests/http/testenv/curl.py | 9 +- tests/http/testenv/env.py | 491 ++-- tests/http/testenv/h2o.py | 428 +++ tests/unit/Makefile.inc | 2 +- tests/unit/unit3400.c | 268 ++ 66 files changed, 7402 insertions(+), 474 deletions(-) create mode 100644 docs/cmdline-opts/proxy-http3.md create mode 100644 lib/capsule.c create mode 100644 lib/capsule.h create mode 100644 lib/cf-capsule.c create mode 100644 lib/cf-capsule.h create mode 100644 lib/cf-h3-proxy.c create mode 100644 lib/cf-h3-proxy.h create mode 100644 tests/data/test3400 create mode 100644 tests/http/test_60_h3_proxy.py create mode 100644 tests/http/testenv/h2o.py create mode 100644 tests/unit/unit3400.c diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 7d9f6ffb36c5..63e5143191b3 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -167,8 +167,10 @@ CWE cyassl Cygwin daniel +datagrams datatracker dbg +decapsulation Debian DEBUGBUILD decrypt @@ -234,6 +236,7 @@ EGD EHLO EINTR else's +encapsulation encodings enctype endianness diff --git a/CMakeLists.txt b/CMakeLists.txt index 331c22dc4f61..4a34f8524ef6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1118,6 +1118,8 @@ if(USE_SSLS_EXPORT) endif() endif() +option(USE_PROXY_HTTP3 "Enable experimental HTTP/3 proxy support" OFF) + option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) find_package(NGHTTP2 MODULE) @@ -1186,6 +1188,20 @@ if(USE_QUICHE) endif() endif() +if(USE_PROXY_HTTP3) + if(CURL_DISABLE_PROXY) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires proxy support") + elseif(CURL_DISABLE_HTTP) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires HTTP support") + elseif(NOT USE_NGTCP2 OR NOT USE_NGHTTP3) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires ngtcp2 + nghttp3") + elseif(NOT USE_OPENSSL) + message(FATAL_ERROR "USE_PROXY_HTTP3 currently requires OpenSSL") + else() + message(STATUS "HTTP/3 proxy support enabled (experimental)") + endif() +endif() + if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) set(USE_TLS_SRP 1) endif() @@ -2046,6 +2062,7 @@ curl_add_if("NTLM" CURL_ENABLE_NTLM AND curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) +curl_add_if("PROXY-HTTP3" USE_PROXY_HTTP3) curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL) curl_add_if("HTTPS-proxy" NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR diff --git a/configure.ac b/configure.ac index 31a29cd60164..0601371baacc 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,30 @@ CURL_CHECK_OPTION_RT CURL_CHECK_OPTION_HTTPSRR CURL_CHECK_OPTION_ECH CURL_CHECK_OPTION_SSLS_EXPORT +AC_MSG_CHECKING([whether to enable HTTP/3 proxy support]) +OPT_PROXY_HTTP3="default" +AC_ARG_ENABLE(proxy-http3, +AS_HELP_STRING([--enable-proxy-http3],[Enable experimental HTTP/3 proxy support]) +AS_HELP_STRING([--disable-proxy-http3],[Disable experimental HTTP/3 proxy support]), + OPT_PROXY_HTTP3=$enableval) +case "$OPT_PROXY_HTTP3" in + no) + want_proxy_http3="no" + curl_proxy_http3_msg="no (--enable-proxy-http3)" + AC_MSG_RESULT([no]) + ;; + default) + want_proxy_http3="no" + curl_proxy_http3_msg="no (--enable-proxy-http3)" + AC_MSG_RESULT([no]) + ;; + *) + want_proxy_http3="yes" + curl_proxy_http3_msg="enabled (--disable-proxy-http3)" + AC_MSG_RESULT([yes]) + ;; +esac +USE_PROXY_HTTP3=0 XC_CHECK_PATH_SEPARATOR @@ -318,6 +342,22 @@ AS_HELP_STRING([--with-test-caddy=PATH],[where to find caddy for testing]), ) AC_SUBST(CADDY) +if test -x /usr/local/bin/h2o; then + H2O=/usr/local/bin/h2o +elif test -x /usr/bin/h2o; then + H2O=/usr/bin/h2o +elif test -x "`brew --prefix 2>/dev/null`/bin/h2o"; then + H2O=`brew --prefix`/bin/h2o +fi +AC_ARG_WITH(test-h2o,dnl +AS_HELP_STRING([--with-test-h2o=PATH],[where to find h2o for testing]), + H2O=$withval + if test "x$H2O" = "xno"; then + H2O="" + fi +) +AC_SUBST(H2O) + if test -x /usr/sbin/vsftpd; then VSFTPD=/usr/sbin/vsftpd elif test -x /usr/local/sbin/vsftpd; then @@ -5028,6 +5068,28 @@ if test "$want_ssls_export" != "no"; then fi fi +dnl ************************************************************* +dnl check whether experimental HTTP/3 proxy support is enabled +dnl +if test "$want_proxy_http3" = "yes"; then + AC_MSG_CHECKING([whether HTTP/3 proxy support is available]) + + if test "$CURL_DISABLE_PROXY" = "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires proxy support]) + elif test "$CURL_DISABLE_HTTP" = "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires HTTP support]) + elif test "$USE_NGTCP2_H3" != "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires ngtcp2 + nghttp3]) + elif test "x$OPENSSL_ENABLED" != "x1"; then + AC_MSG_ERROR([--enable-proxy-http3 currently requires OpenSSL]) + else + AC_DEFINE(USE_PROXY_HTTP3, 1, [if HTTP/3 proxy support is available]) + USE_PROXY_HTTP3=1 + AC_MSG_RESULT([yes]) + experimental="$experimental PROXY-HTTP3" + fi +fi + dnl ************************************************************ dnl hiding of library internal symbols dnl @@ -5141,6 +5203,10 @@ if test "$curl_psl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES PSL" fi +if test "$USE_PROXY_HTTP3" = "1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES PROXY-HTTP3" +fi + if test "$curl_gsasl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES gsasl" fi @@ -5485,6 +5551,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: HTTP1: ${curl_h1_msg} HTTP2: ${curl_h2_msg} HTTP3: ${curl_h3_msg} + Proxy-HTTP3: ${curl_proxy_http3_msg} ECH: ${curl_ech_msg} HTTPS RR: ${curl_httpsrr_msg} SSLS-EXPORT: ${curl_ssls_export_msg} diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index 43fc0fdeed88..ca8277fa14ca 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -43,6 +43,16 @@ Graduation requirements: - Using HTTP/3 with the given build should perform without risking busy-loops +### HTTP/3 proxy and CONNECT-UDP support + +Support for HTTP/3 proxy and CONNECT-UDP tunneling is experimental and +requires an explicit build-time opt-in (`--enable-proxy-http3` for +autotools, `-DUSE_PROXY_HTTP3=ON` for CMake). + +Graduation requirements: + +- implementation stability over time with no known severe regressions + ### The Rustls backend Graduation requirements: diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index e07ec455dad8..83eb9df68e8f 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -254,6 +254,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `USE_SSLS_EXPORT`: Enable experimental SSL session import/export. Default: `OFF` - `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` - `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` +- `USE_PROXY_HTTP3`: Enable experimental HTTP/3 proxy support. Default: `OFF` ## Disabling features diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index f7236af1b127..f8fd01ccd63e 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -212,6 +212,7 @@ DPAGES = \ proxy-digest.md \ proxy-header.md \ proxy-http2.md \ + proxy-http3.md \ proxy-insecure.md \ proxy-key-type.md \ proxy-key.md \ diff --git a/docs/cmdline-opts/proxy-http2.md b/docs/cmdline-opts/proxy-http2.md index ca6a091f328e..a38da9e87ed8 100644 --- a/docs/cmdline-opts/proxy-http2.md +++ b/docs/cmdline-opts/proxy-http2.md @@ -5,7 +5,7 @@ Long: proxy-http2 Tags: Versions HTTP/2 Protocols: HTTP Added: 8.1.0 -Mutexed: +Mutexed: proxy-http3 Requires: HTTP/2 Help: Use HTTP/2 with HTTPS proxy Category: http proxy @@ -22,3 +22,5 @@ Negotiate HTTP/2 with an HTTPS proxy. The proxy might still only offer HTTP/1 and then curl sticks to using that version. This has no effect for any other kinds of proxies. + +This option is mutually exclusive with `--proxy-http3`. diff --git a/docs/cmdline-opts/proxy-http3.md b/docs/cmdline-opts/proxy-http3.md new file mode 100644 index 000000000000..6533b980b6cc --- /dev/null +++ b/docs/cmdline-opts/proxy-http3.md @@ -0,0 +1,31 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: proxy-http3 +Tags: Versions HTTP/3 +Protocols: HTTP +Added: 8.21.0 +Mutexed: proxy-http2 +Requires: HTTP/3 +Help: Use HTTP/3 with HTTPS proxy +Category: http proxy +Multi: boolean +See-also: + - proxy + - proxy-http2 +Example: + - --proxy-http3 -x proxy $URL +--- + +# `--proxy-http3` + +Negotiate HTTP/3 with an HTTPS proxy. +Fails to perform the transfer if the given proxy does not support HTTP/3. + +This has no effect for any other kinds of proxies. + +This option is mutually exclusive with `--proxy-http2`. + +This feature is experimental and requires a build with HTTP/3 proxy support +enabled. For autotools builds, use `--enable-proxy-http3`. For CMake builds, +use `-DUSE_PROXY_HTTP3=ON`. diff --git a/docs/internals/CONNECTION-FILTERS.md b/docs/internals/CONNECTION-FILTERS.md index 619ca0e3407d..1a817a15672a 100644 --- a/docs/internals/CONNECTION-FILTERS.md +++ b/docs/internals/CONNECTION-FILTERS.md @@ -156,9 +156,9 @@ The currently existing filter types (curl 8.5.0) are: `accept()`ed in a `listen()` * `SSL`: filter that applies TLS en-/decryption and handshake. Manages the underlying TLS backend implementation. -* `HTTP-PROXY`, `H1-PROXY`, `H2-PROXY`: the first manages the connection to an - HTTP proxy server and uses the other depending on which ALPN protocol has - been negotiated. +* `HTTP-PROXY`, `H1-PROXY`, `H2-PROXY`, `H3-PROXY`: the first manages the + connection to an HTTP proxy server and uses the other depending on which + ALPN protocol has been negotiated. * `SOCKS-PROXY`: filter for the various SOCKS proxy protocol variations * `HAPROXY`: filter for the protocol of the same name, providing client IP information to a server. @@ -166,7 +166,7 @@ The currently existing filter types (curl 8.5.0) are: connection * `HTTP/3`: filter for handling multiplexed transfers over an HTTP/3+QUIC connection -* `HAPPY-EYEBALLS`: meta filter that implements IPv4/IPv6 "happy eyeballing". +* `HAPPY-EYEBALLS`: meta filter that implements IPv4/IPv6 "happy eyeballs". It creates up to 2 sub-filters that race each other for a connection. * `SETUP`: meta filter that manages the creation of sub-filter chains for a specific transport (e.g. TCP or QUIC). @@ -220,6 +220,37 @@ as an `SSL` flagged filter is seen first. `conn3` is also encrypted as the Similar checks can determine if a connection is multiplexed or not. +## Adding CONNECT-UDP support +HTTP/3 on top of HTTP/1.1 (MASQUE CONNECT-UDP): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H1-PROXY --> SSL --> HAPPY-EYEBALLS --> TCP +``` + +HTTP/3 on top of HTTP/2 (MASQUE CONNECT-UDP): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H2-PROXY --> SSL --> HAPPY-EYEBALLS --> TCP +``` + +The CAPSULE filter handles RFC 9297 capsule protocol encapsulation and +decapsulation of UDP datagrams. It is inserted automatically when the +HTTP-PROXY filter completes a successful CONNECT-UDP tunnel. + +## Adding H3-PROXY support +HTTP/1.1 on top of HTTP/3 (CONNECT over QUIC): +``` +conn --> HTTP/1.1 --> SSL --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + +HTTP/2 on top of HTTP/3 (CONNECT over QUIC): +``` +conn --> HTTP/2 --> SSL --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + +HTTP/3 on top of HTTP/3 (MASQUE CONNECT-UDP over QUIC): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + ## Filter Tracing Filters may make use of special trace macros like `CURL_TRC_CF(data, cf, msg, diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index fd589a834cc3..ec29fa66e778 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -298,6 +298,13 @@ supports HTTP NTLM libcurl was built with support for NTLM delegation to a winbind helper. This feature was removed from curl in 8.8.0. +## `PROXY-HTTP3` + +*features* mask bit: non-existent + +libcurl was built with EXPERIMENTAL support for HTTP/3 proxy tunneling +(Added in 8.21.0) + ## `PSL` *features* mask bit: CURL_VERSION_PSL diff --git a/docs/libcurl/opts/CURLOPT_PROXY.md b/docs/libcurl/opts/CURLOPT_PROXY.md index 7be874d73332..072dfd48093e 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY.md @@ -58,7 +58,11 @@ HTTPS Proxy. (with OpenSSL, GnuTLS, mbedTLS, Rustls, Schannel or wolfSSL.) This uses HTTP/1 by default. Setting CURLOPT_PROXYTYPE(3) to **CURLPROXY_HTTPS2** allows libcurl to negotiate using HTTP/2 with proxy. -## `socks4://` +Setting CURLOPT_PROXYTYPE(3) to **CURLPROXY_HTTPS3** allows libcurl to +negotiate using HTTP/3 with proxy. This feature is experimental and requires +a build with HTTP/3 proxy support enabled. + +## socks4:// SOCKS4 Proxy. diff --git a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md index 6000d10b00bc..1dc1a1328a01 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md @@ -41,6 +41,12 @@ HTTPS Proxy using HTTP/1. (Added in 7.52.0 for OpenSSL and GnuTLS. Since HTTPS Proxy and attempt to speak HTTP/2 over it. (Added in 8.1.0) +## CURLPROXY_HTTPS3 + +HTTPS Proxy and attempt to speak HTTP/3 over it. (Added in 8.21.0) +This feature is experimental and requires a build with HTTP/3 proxy support +enabled. + ## CURLPROXY_HTTP_1_0 HTTP 1.0 Proxy. This is similar to CURLPROXY_HTTP except it uses HTTP/1.0 for diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 4dc670da6eee..5bad9a98425d 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -993,6 +993,7 @@ CURLPROXY_HTTP 7.10 CURLPROXY_HTTP_1_0 7.19.4 CURLPROXY_HTTPS 7.52.0 CURLPROXY_HTTPS2 8.1.0 +CURLPROXY_HTTPS3 8.21.0 CURLPROXY_SOCKS4 7.10 CURLPROXY_SOCKS4A 7.18.0 CURLPROXY_SOCKS5 7.10 diff --git a/docs/options-in-versions b/docs/options-in-versions index 95d84a4bfec2..fa20b2dd29b2 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -177,6 +177,7 @@ --proxy-digest 7.12.0 --proxy-header 7.37.0 --proxy-http2 8.1.0 +--proxy-http3 8.21.0 --proxy-insecure 7.52.0 --proxy-key 7.52.0 --proxy-key-type 7.52.0 diff --git a/docs/tests/HTTP.md b/docs/tests/HTTP.md index 88fb9a0c4b4e..79f3fac20054 100644 --- a/docs/tests/HTTP.md +++ b/docs/tests/HTTP.md @@ -62,6 +62,9 @@ Via curl's `configure` script you may specify: * `--with-test-nghttpx=` if you have nghttpx to use somewhere outside your `$PATH`. + * `--with-test-h2o=` if you have h2o to use somewhere + outside your `$PATH`. + * `--with-test-httpd=` if you have an Apache httpd installed somewhere else. On Debian/Ubuntu it otherwise looks into `/usr/bin` and `/usr/sbin` to find those. diff --git a/include/curl/curl.h b/include/curl/curl.h index cb36eefad463..c790760b88bd 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -802,9 +802,11 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ #define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along the hostname rather than the IP address. added in 7.18.0 */ +#define CURLPROXY_HTTPS3 8L /* HTTPS and attempt HTTP/3 + added in 8.21.0 */ typedef enum { - CURLPROXY_LAST = 8 /* never use */ + CURLPROXY_LAST = 9 /* never use */ } curl_proxytype; /* this enum was added in 7.10 */ /* @@ -1494,8 +1496,8 @@ typedef enum { CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and - CURLPROXY_SOCKS5. */ + CURLPROXY_HTTPS, CURLPROXY_HTTPS2, CURLPROXY_HTTPS3, CURLPROXY_SOCKS4, + CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 2c7259af0dd4..0a9e6ce31143 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -150,8 +150,11 @@ LIB_CFILES = \ bufq.c \ bufref.c \ cf-dns.c \ + capsule.c \ + cf-capsule.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ + cf-h3-proxy.c \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ @@ -282,8 +285,11 @@ LIB_HFILES = \ bufq.h \ bufref.h \ cf-dns.h \ + capsule.h \ + cf-capsule.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ + cf-h3-proxy.h \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ diff --git a/lib/capsule.c b/lib/capsule.c new file mode 100644 index 000000000000..698cdcdb5669 --- /dev/null +++ b/lib/capsule.c @@ -0,0 +1,281 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include +#include "urldata.h" +#include "curlx/dynbuf.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "bufq.h" +#include "capsule.h" + + +/** + * Convert 64-bit value from network byte order to host byte order + */ +static uint64_t capsule_ntohll(uint64_t value) +{ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + return value; +#elif (defined(__GNUC__) || defined(__clang__)) && \ + defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return __builtin_bswap64(value); +#else + union { + uint64_t u64; + uint32_t u32[2]; + } src, dst; + + src.u64 = value; + dst.u32[0] = ntohl(src.u32[1]); + dst.u32[1] = ntohl(src.u32[0]); + return dst.u64; +#endif +} + +/** + * Encode a variable-length integer into a plain buffer. + * @param buf Output buffer (must have at least 8 bytes) + * @param value Value to encode (must be <= 0x3FFFFFFFFFFFFFFF) + * @return Number of bytes written + */ +static size_t capsule_encode_varint_buf(uint8_t *buf, uint64_t value) +{ + DEBUGASSERT(value <= 0x3FFFFFFFFFFFFFFF); + + if(value <= 0x3F) { + buf[0] = (uint8_t)value; + return 1; + } + else if(value <= 0x3FFF) { + uint16_t encoded = (uint16_t)value & 0x3FFF; + encoded = ntohs(encoded | 0x4000); + memcpy(buf, &encoded, 2); + return 2; + } + else if(value <= 0x3FFFFFFF) { + uint32_t encoded = (uint32_t)value & 0x3FFFFFFF; + encoded = ntohl(encoded | 0x80000000); + memcpy(buf, &encoded, 4); + return 4; + } + else { + uint64_t encoded = (uint64_t)value & 0x3FFFFFFFFFFFFFFF; + encoded = capsule_ntohll(encoded | 0xC000000000000000); + memcpy(buf, &encoded, 8); + return 8; + } +} + +static CURLcode capsule_peek_u8(struct bufq *recvbufq, + size_t offset, + uint8_t *pbyte) +{ + const unsigned char *peek = NULL; + size_t peeklen = 0; + + if(!Curl_bufq_peek_at(recvbufq, offset, &peek, &peeklen) || !peeklen) + return CURLE_AGAIN; + *pbyte = peek[0]; + return CURLE_OK; +} + +static CURLcode capsule_decode_varint_at(struct bufq *recvbufq, + size_t offset, + uint64_t *pvalue, + size_t *pconsumed) +{ + uint8_t first_byte, byte; + uint64_t value; + size_t nbytes; + size_t i; + CURLcode result; + + result = capsule_peek_u8(recvbufq, offset, &first_byte); + if(result) + return result; + + nbytes = (size_t)1 << (first_byte >> 6); /* 1, 2, 4 or 8 bytes */ + value = first_byte & 0x3F; + + for(i = 1; i < nbytes; ++i) { + result = capsule_peek_u8(recvbufq, offset + i, &byte); + if(result) + return result; + value = (value << 8) | byte; + } + + *pvalue = value; + *pconsumed = nbytes; + return CURLE_OK; +} + +size_t Curl_capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len) +{ + size_t off = 0; + DEBUGASSERT(hdrlen >= HTTP_CAPSULE_HEADER_MAX_SIZE); + if(hdrlen < HTTP_CAPSULE_HEADER_MAX_SIZE) + return 0; + hdr[off++] = 0; /* capsule type: HTTP Datagram */ + off += capsule_encode_varint_buf(hdr + off, (uint64_t)payload_len + 1); + hdr[off++] = 0; /* context ID */ + return off; +} + +CURLcode Curl_capsule_encap_udp_datagram(struct dynbuf *dyn, + const void *buf, size_t blen) +{ + CURLcode result; + uint8_t hdr[HTTP_CAPSULE_HEADER_MAX_SIZE]; + size_t hdr_len; + + curlx_dyn_init(dyn, HTTP_CAPSULE_HEADER_MAX_SIZE + blen); + hdr_len = Curl_capsule_encap_udp_hdr(hdr, sizeof(hdr), blen); + DEBUGASSERT(hdr_len); + if(!hdr_len) + return CURLE_FAILED_INIT; + + result = curlx_dyn_addn(dyn, hdr, hdr_len); + if(result) + return result; + + return curlx_dyn_addn(dyn, buf, blen); +} + +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err) +{ + const unsigned char *context_id, *capsule_type; + size_t read_size, varint_len; + uint64_t capsule_length; + size_t offset, payload_len; + size_t bytes_read = 0; + CURLcode result = CURLE_OK; + + if(!len) { + *err = CURLE_BAD_FUNCTION_ARGUMENT; + return 0; + } + + if(Curl_bufq_is_empty(recvbufq)) { + *err = CURLE_AGAIN; + return 0; + } + + if(!Curl_bufq_peek(recvbufq, &capsule_type, &read_size) || !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(capsule_type[0]) { + infof(data, "Error! Invalid capsule type: %d", capsule_type[0]); + Curl_bufq_skip(recvbufq, 1); + *err = CURLE_RECV_ERROR; + return 0; + } + + offset = 1; + result = capsule_decode_varint_at(recvbufq, offset, &capsule_length, + &varint_len); + if(result == CURLE_AGAIN) { + *err = CURLE_AGAIN; + return 0; + } + else if(result) { + *err = CURLE_RECV_ERROR; + return 0; + } + offset += varint_len; + + if(!Curl_bufq_peek_at(recvbufq, offset, &context_id, &read_size) || + !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(*context_id) { + infof(data, "Error! Invalid context ID: %02x", *context_id); + Curl_bufq_skip(recvbufq, offset + 1); + *err = CURLE_RECV_ERROR; + return 0; + } + offset += 1; + + if(!capsule_length) { + infof(data, "Error! Invalid capsule length: 0"); + Curl_bufq_skip(recvbufq, offset); + *err = CURLE_RECV_ERROR; + return 0; + } + if(capsule_length - 1 >= (uint64_t)SIZE_MAX) { + infof(data, "Error! Capsule length too large: %" CURL_FORMAT_CURL_OFF_T, + (curl_off_t)capsule_length); + *err = CURLE_RECV_ERROR; + return 0; + } + payload_len = (size_t)(capsule_length - 1); + + if(Curl_bufq_len(recvbufq) < offset + payload_len) { + *err = CURLE_AGAIN; + return 0; + } + + if(payload_len > len) { + infof(data, "UDP payload does not fit destination buffer: %zu > %zu", + payload_len, len); + Curl_bufq_skip(recvbufq, offset + payload_len); + *err = CURLE_RECV_ERROR; + return 0; + } + + Curl_bufq_skip(recvbufq, offset); + if(!payload_len) { + *err = CURLE_OK; + return 0; + } + result = Curl_bufq_read(recvbufq, buf, payload_len, &bytes_read); + if(result || (bytes_read != payload_len)) { + infof(data, "Error! Read less than expected %zu %zu", + payload_len, bytes_read); + *err = CURLE_RECV_ERROR; + return 0; + } + + if(cf && data) { + CURL_TRC_CF(data, cf, "Processed UDP capsule raw: size=%zu " + "length_left %zu", payload_len, Curl_bufq_len(recvbufq)); + } + *err = CURLE_OK; + return bytes_read; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/capsule.h b/lib/capsule.h new file mode 100644 index 000000000000..fa7dec19cb0d --- /dev/null +++ b/lib/capsule.h @@ -0,0 +1,77 @@ +#ifndef HEADER_CURL_CAPSULE_H +#define HEADER_CURL_CAPSULE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "curlx/dynbuf.h" +#include "bufq.h" + +/* HTTP Capsule constants */ +#define HTTP_CAPSULE_HEADER_MAX_SIZE 10 + +/* HTTP Capsule function prototypes */ + +/** + * Write the capsule header (type + varint length + context ID) into `hdr`. + * @param hdr Output buffer (must be >= HTTP_CAPSULE_HEADER_MAX_SIZE) + * @param hdrlen Size of `hdr` in bytes + * @param payload_len Length of the UDP payload that follows + * @return Number of header bytes written, or 0 on error + */ +size_t Curl_capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len); + +/** + * Encapsulate UDP payload into HTTP Datagram capsule format + * @param dyn Dynamic buffer to write capsule to + * @param buf Payload buffer + * @param blen Payload buffer length + * @return CURLE_OK on success, error code on failure + */ +CURLcode Curl_capsule_encap_udp_datagram(struct dynbuf *dyn, + const void *buf, size_t blen); + +/** + * Process one UDP capsule from buffer into raw datagram payload bytes. + * @param cf Connection filter + * @param data Easy handle + * @param recvbufq Buffer queue containing capsule data + * @param buf Output buffer for one datagram payload + * @param len Size of output buffer in bytes + * @param err Error code output + * @return Number of payload bytes written. Check `err` for status. + */ +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err); + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CAPSULE_H */ diff --git a/lib/cf-capsule.c b/lib/cf-capsule.c new file mode 100644 index 000000000000..dd740c0f157f --- /dev/null +++ b/lib/cf-capsule.c @@ -0,0 +1,253 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "curlx/dynbuf.h" +#include "bufq.h" +#include "capsule.h" +#include "cf-capsule.h" + +/* recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */ +#define CAPSULE_RECV_CHUNKS 4 +#define CAPSULE_CHUNK_SIZE (16 * 1024) + +struct cf_capsule_ctx { + struct bufq recvbuf; + struct cf_call_data call_data; + unsigned char *pending; /* unsent capsule bytes from partial write */ + size_t pending_len; /* total length of pending buffer */ + size_t pending_offset; /* bytes already sent from pending */ + size_t pending_payload; /* original payload len for pending capsule */ +}; + +static void capsule_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx->pending); + curlx_safefree(ctx); + } +} + +static void capsule_cf_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "close"); + cf->connected = FALSE; + if(ctx) { + Curl_bufq_reset(&ctx->recvbuf); + curlx_safefree(ctx->pending); + ctx->pending_len = 0; + ctx->pending_offset = 0; + ctx->pending_payload = 0; + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static CURLcode capsule_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + if(cf->next) { + CURLcode result = cf->next->cft->do_connect(cf->next, data, done); + if(!result && *done) + cf->connected = TRUE; + return result; + } + *done = FALSE; + return CURLE_OK; +} + +static CURLcode capsule_cf_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + struct dynbuf dyn; + size_t nwritten = 0; + size_t capsule_len; + size_t remaining; + CURLcode result; + + (void)eos; + *pnwritten = 0; + + if(ctx->pending) { + /* flush remaining bytes from a partially sent capsule */ + remaining = ctx->pending_len - ctx->pending_offset; + result = Curl_conn_cf_send(cf->next, data, + ctx->pending + ctx->pending_offset, + remaining, FALSE, &nwritten); + if(result && result != CURLE_AGAIN) { + curlx_safefree(ctx->pending); + return result; + } + ctx->pending_offset += nwritten; + if(ctx->pending_offset < ctx->pending_len) + return CURLE_AGAIN; + /* pending capsule has been fully flusehd */ + *pnwritten = ctx->pending_payload; + curlx_safefree(ctx->pending); + return CURLE_OK; + } + + /* encapsulate new payload into a capsule */ + result = Curl_capsule_encap_udp_datagram(&dyn, buf, len); + if(result) { + curlx_dyn_free(&dyn); + return result; + } + capsule_len = curlx_dyn_len(&dyn); + + result = Curl_conn_cf_send(cf->next, data, + (const uint8_t *)curlx_dyn_ptr(&dyn), + capsule_len, FALSE, &nwritten); + if(result && result != CURLE_AGAIN) { + curlx_dyn_free(&dyn); + return result; + } + + if(nwritten < capsule_len) { + /* partial or zero write - save unsent capsule bytes as pending */ + remaining = capsule_len - nwritten; + ctx->pending = curlx_malloc(remaining); + if(!ctx->pending) { + curlx_dyn_free(&dyn); + return CURLE_OUT_OF_MEMORY; + } + memcpy(ctx->pending, + curlx_dyn_ptr(&dyn) + nwritten, remaining); + ctx->pending_len = remaining; + ctx->pending_offset = 0; + ctx->pending_payload = len; + curlx_dyn_free(&dyn); + return CURLE_AGAIN; + } + + /* entire capsule sent */ + curlx_dyn_free(&dyn); + *pnwritten = len; + return CURLE_OK; +} + +static CURLcode capsule_cf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result; + size_t nread; + + *pnread = 0; + + /* fill our receive buffer from the filter below */ + while(!Curl_bufq_is_full(&ctx->recvbuf)) { + result = Curl_cf_recv_bufq(cf->next, data, &ctx->recvbuf, 0, &nread); + if(result == CURLE_AGAIN) + break; + if(result) + return result; + if(!nread) + break; + } + + /* try to extract a complete capsule datagram */ + *pnread = Curl_capsule_process_udp_raw(cf, data, &ctx->recvbuf, + (unsigned char *)buf, len, + &result); + return result; +} + +static bool capsule_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + if(ctx && !Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +struct Curl_cftype Curl_cft_capsule = { + "CAPSULE", + 0, + 0, + capsule_cf_destroy, + capsule_cf_connect, + capsule_cf_close, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + capsule_cf_data_pending, + capsule_cf_send, + capsule_cf_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + struct cf_capsule_ctx *ctx; + CURLcode result; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) + return CURLE_OUT_OF_MEMORY; + + Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx); + if(result) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + return result; + } + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/cf-capsule.h b/lib/cf-capsule.h new file mode 100644 index 000000000000..437c9681b6cc --- /dev/null +++ b/lib/cf-capsule.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_CF_CAPSULE_H +#define HEADER_CURL_CF_CAPSULE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +/* Insert a capsule protocol filter after `cf_at` in the filter chain. + * The capsule filter encapsulates/decapsulates UDP datagrams using + * the HTTP Datagram capsule format (RFC 9297). */ +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_capsule; + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CF_CAPSULE_H */ diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 0f1c392d4847..5dd02b2b0a06 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -25,6 +25,8 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include #include "urldata.h" #include "curlx/dynbuf.h" #include "sendf.h" @@ -33,6 +35,7 @@ #include "http_proxy.h" #include "select.h" #include "progress.h" +#include "multiif.h" #include "cfilters.h" #include "cf-h1-proxy.h" #include "connect.h" @@ -40,7 +43,6 @@ #include "strcase.h" #include "curlx/strparse.h" - typedef enum { H1_TUNNEL_INIT, /* init/default/no tunnel state */ H1_TUNNEL_CONNECT, /* CONNECT request is being send */ @@ -72,6 +74,12 @@ struct h1_tunnel_state { BIT(leading_unfold); }; +/* Persistent context for the H1-PROXY filter */ +struct cf_h1_proxy_ctx { + struct h1_tunnel_state *ts; + BIT(udp_tunnel); +}; + static bool tunnel_is_established(struct h1_tunnel_state *ts) { return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); @@ -82,6 +90,12 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts) return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); } +static bool h1_proxy_is_udp(struct Curl_cfilter *cf) +{ + struct cf_h1_proxy_ctx *pctx = cf->ctx; + return (pctx->udp_tunnel ? TRUE : FALSE); +} + static CURLcode tunnel_reinit(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) @@ -97,6 +111,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf, ts->close_connection = FALSE; ts->maybe_folded = FALSE; ts->leading_unfold = FALSE; + ts->nsent = 0; + ts->headerlines = 0; return CURLE_OK; } @@ -158,7 +174,9 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, case H1_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "new tunnel state 'established'"); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP proxy", + h1_proxy_is_udp(cf) ? "-UDP" : ""); + data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -195,11 +213,12 @@ static void cf_tunnel_free(struct Curl_cfilter *cf, struct Curl_easy *data) { if(cf) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; if(ts) { h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); tunnel_free(ts, data); - cf->ctx = NULL; + pctx->ts = NULL; } } } @@ -217,17 +236,17 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, int http_minor; CURLcode result; + DEBUGASSERT(data); /* This only happens if we have looped here due to authentication reasons, and we do not really use the newly cloned URL here then. Free it. */ curlx_safefree(data->req.newurl); - result = Curl_http_proxy_create_CONNECT(&req, cf, data, - ts->dest, ts->httpversion); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ts->dest, + PROXY_HTTP_V1, + h1_proxy_is_udp(cf)); if(result) goto out; - infof(data, "Establish HTTP proxy tunnel to %s", req->authority); - curlx_dyn_reset(&ts->request_data); ts->nsent = 0; ts->headerlines = 0; @@ -280,6 +299,92 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, return result; } +static CURLcode on_resp_header_udp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + const char *header) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + + if((checkprefix("WWW-Authenticate:", header) && (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", header) && (407 == k->httpcode))) { + + bool proxy = (k->httpcode == 407); + char *auth = Curl_copy_header_value(header); + if(!auth) + return CURLE_OUT_OF_MEMORY; + + CURL_TRC_CF(data, cf, "CONNECT-UDP: fwd auth header '%s'", header); + result = Curl_http_input_auth(data, proxy, auth); + + curlx_free(auth); + + if(result) + return result; + } + else if(checkprefix("Content-Length:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Content-Length in CONNECT-UDP %03d response", + k->httpcode); + } + else { + const char *p = header + strlen("Content-Length:"); + if(curlx_str_numblanks(&p, &ts->cl)) { + failf(data, "Unsupported Content-Length value"); + return CURLE_WEIRD_SERVER_REPLY; + } + } + } + else if(checkprefix("Transfer-Encoding:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Transfer-Encoding in " + "CONNECT-UDP %03d response", k->httpcode); + } + else if(Curl_compareheader(header, + STRCONST("Transfer-Encoding:"), + STRCONST("chunked"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> " + "Transfer-Encoding: chunked"); + ts->chunked_encoding = TRUE; + /* reset our chunky engine */ + Curl_httpchunk_reset(data, &ts->ch, TRUE); + } + } + else if(checkprefix("Capsule-protocol:", header)) { + if(Curl_compareheader(header, + STRCONST("Capsule-protocol:"), + STRCONST("?1"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Capsule-protocol: ?1"); + } + } + else if(Curl_compareheader(header, + STRCONST("Connection:"), STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Connection: close"); + } + else if(Curl_compareheader(header, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, + "CONNECT-UDP Response --> Proxy-Connection: close"); + } + else if(!strncmp(header, "HTTP/1.", 7) && + ((header[7] == '0') || (header[7] == '1')) && + (header[8] == ' ') && + ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && + !ISDIGIT(header[12])) { + /* store the HTTP code from the proxy */ + data->info.httpproxycode = k->httpcode = + ((header[9] - '0') * 100) + + ((header[10] - '0') * 10) + + (header[11] - '0'); + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> %d", k->httpcode); + } + return result; +} + static CURLcode on_resp_header(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, @@ -418,7 +523,13 @@ static CURLcode single_header(struct Curl_cfilter *cf, return result; } - result = on_resp_header(cf, data, ts, linep); + if(h1_proxy_is_udp(cf)) { + result = on_resp_header_udp(cf, data, ts, linep); + } + else { + result = on_resp_header(cf, data, ts, linep); + } + if(result) return result; @@ -460,6 +571,13 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, } if(!nread) { + if(ts->maybe_folded) { + /* EOF right after LF: finalize the pending header line. */ + result = single_header(cf, data, ts); + if(result) + return result; + ts->maybe_folded = FALSE; + } if(data->set.proxyauth && data->state.authproxy.avail && data->req.hd_proxy_auth) { /* proxy auth was requested and there was proxy auth available, @@ -551,12 +669,16 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, ts->maybe_folded = TRUE; } + if(result) + return result; } /* while there is buffer left and loop is requested */ if(error) result = CURLE_RECV_ERROR; *done = (ts->keepon == KEEPON_DONE); - if(!result && *done && data->info.httpproxycode / 100 != 2) { + if(!result && *done && + data->info.httpproxycode / 100 != 2 && + !(h1_proxy_is_udp(cf) && data->info.httpproxycode == 101)) { /* Deal with the possibly already received authenticate headers. 'newurl' is set to a new URL if we must loop. */ result = Curl_http_auth_act(data); @@ -637,7 +759,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, infof(data, "Connect me again please"); Curl_conn_cf_close(cf, data); result = Curl_conn_cf_connect(cf->next, data, &done); - goto out; + return result; } else { /* staying on this connection, reset state */ @@ -653,17 +775,36 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, } while(data->req.newurl); DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); - if(data->info.httpproxycode / 100 != 2) { - /* a non-2xx response and we have no next URL to try. */ - curlx_safefree(data->req.newurl); - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_COULDNT_CONNECT; + if(h1_proxy_is_udp(cf)) { + /* RFC 9298: Accept 101 Upgrade for HTTP/1.1 and + * 2xx responses for HTTP/2 and HTTP/3 proxies. */ + if(data->info.httpproxycode / 100 != 2 && + data->info.httpproxycode != 101) { + curlx_safefree(data->req.newurl); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT-UDP tunnel failed, response %d", + data->req.httpcode); + return CURLE_COULDNT_CONNECT; + } + } + else { + if(data->info.httpproxycode / 100 != 2) { + /* a non-2xx response and we have no next URL to try. */ + curlx_safefree(data->req.newurl); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); + return CURLE_COULDNT_CONNECT; + } } /* 2xx response, SUCCESS! */ + /* 101 Switching Protocol for CONNECT-UDP */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); - infof(data, "CONNECT tunnel established, response %d", - data->info.httpproxycode); + if(h1_proxy_is_udp(cf)) + infof(data, "CONNECT-UDP tunnel established, response %d", + data->info.httpproxycode); + else + infof(data, "CONNECT tunnel established, response %d", + data->info.httpproxycode); result = CURLE_OK; out: @@ -677,7 +818,8 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, bool *done) { CURLcode result; - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; if(cf->connected) { *done = TRUE; @@ -694,7 +836,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = tunnel_init(cf, data, &ts); if(result) return result; - cf->ctx = ts; + pctx->ts = ts; } /* We want "seamless" operations through HTTP proxy tunnel */ @@ -705,14 +847,13 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, curlx_safefree(data->req.hd_proxy_auth); out: - *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); + *done = (result == CURLE_OK) && tunnel_is_established(pctx->ts); if(*done) { cf->connected = TRUE; /* The real request will follow the CONNECT, reset request partially */ Curl_req_soft_reset(&data->req, data); Curl_client_reset(data); Curl_pgrsReset(data); - cf_tunnel_free(cf, data); } return result; @@ -722,7 +863,8 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; CURLcode result = CURLE_OK; if(!cf->connected) { @@ -742,37 +884,49 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, else result = Curl_pollset_set_out_only(data, ps, sock); } + else { + if(cf->next) + result = cf->next->cft->adjust_pollset(cf->next, data, ps); + } return result; } +static bool cf_h1_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "destroy"); cf_tunnel_free(cf, data); + curlx_safefree(cf->ctx); } static void cf_h1_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_h1_proxy_ctx *pctx = cf->ctx; CURL_TRC_CF(data, cf, "close"); - if(cf) { - cf->connected = FALSE; - if(cf->ctx) { - h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); - } + cf->connected = FALSE; + if(pctx && pctx->ts) + h1_tunnel_go_state(cf, pctx->ts, H1_TUNNEL_INIT, data); + if(cf->next) + cf->next->cft->do_close(cf->next, data); } static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; switch(query) { case CF_QUERY_HOST_PORT: + if(!ts || !ts->dest) + break; *pres1 = (int)ts->dest->port; *((const char **)pres2) = ts->dest->hostname; return CURLE_OK; @@ -799,7 +953,7 @@ struct Curl_cftype Curl_cft_h1_proxy = { cf_h1_proxy_close, Curl_cf_def_shutdown, cf_h1_proxy_adjust_pollset, - Curl_cf_def_data_pending, + cf_h1_proxy_data_pending, Curl_cf_def_send, Curl_cf_def_recv, Curl_cf_def_cntrl, @@ -811,9 +965,11 @@ struct Curl_cftype Curl_cft_h1_proxy = { CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - int httpversion) + int httpversion, + bool udp_tunnel) { struct Curl_cfilter *cf; + struct cf_h1_proxy_ctx *pctx; struct h1_tunnel_state *ts; CURLcode result; @@ -834,9 +990,18 @@ CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); Curl_httpchunk_init(data, &ts->ch, TRUE); - result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, ts); - if(result) + pctx = curlx_calloc(1, sizeof(*pctx)); + if(!pctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pctx->udp_tunnel = udp_tunnel; + pctx->ts = ts; + result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, pctx); + if(result) { + curlx_free(pctx); goto out; + } ts = NULL; Curl_conn_cf_insert_after(cf_at, cf); diff --git a/lib/cf-h1-proxy.h b/lib/cf-h1-proxy.h index 10adcdfb4fdb..3255bf79f240 100644 --- a/lib/cf-h1-proxy.h +++ b/lib/cf-h1-proxy.h @@ -32,7 +32,8 @@ struct Curl_peer; CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - int httpversion); + int httpversion, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h1_proxy; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 297afb3c8cb1..b2cc49896fb9 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -42,6 +42,7 @@ #include "sendf.h" #include "select.h" #include "cf-h2-proxy.h" +#include "capsule.h" #define PROXY_H2_CHUNK_SIZE (16 * 1024) @@ -96,6 +97,20 @@ static CURLcode tunnel_stream_init(struct tunnel_stream *ts, return CURLE_OK; } +static void tunnel_stream_reset(struct tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + Curl_bufq_reset(&ts->recvbuf); + Curl_bufq_reset(&ts->sendbuf); + ts->stream_id = -1; + ts->error = 0; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->reset = FALSE; + ts->state = H2_TUNNEL_INIT; +} + static void tunnel_stream_clear(struct tunnel_stream *ts) { Curl_http_resp_free(ts->resp); @@ -109,9 +124,11 @@ static void tunnel_stream_clear(struct tunnel_stream *ts) static void h2_tunnel_go_state(struct Curl_cfilter *cf, struct tunnel_stream *ts, h2_tunnel_state new_state, - struct Curl_easy *data) + struct Curl_easy *data, + bool udp_tunnel) { (void)cf; + (void)udp_tunnel; if(ts->state == new_state) return; @@ -127,7 +144,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, switch(new_state) { case H2_TUNNEL_INIT: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); - tunnel_stream_clear(ts); + tunnel_stream_reset(ts); break; case H2_TUNNEL_CONNECT: @@ -143,7 +160,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, case H2_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", ts->stream_id); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP/2 proxy", + udp_tunnel ? "-UDP" : ""); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -175,6 +193,7 @@ struct cf_h2_proxy_ctx { BIT(rcvd_goaway); BIT(sent_goaway); BIT(nw_out_blocked); + BIT(udp_tunnel); }; /* How to access `call_data` from a cf_h2 filter */ @@ -211,7 +230,8 @@ static void drain_tunnel(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; (void)cf; if(!tunnel->closed && !tunnel->reset && - !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) + (!Curl_bufq_is_empty(&ctx->tunnel.sendbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) Curl_multi_mark_dirty(data); } @@ -749,15 +769,15 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, CURLcode result; struct httpreq *req = NULL; - result = Curl_http_proxy_create_CONNECT(&req, cf, data, ctx->dest, 20); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ctx->dest, + PROXY_HTTP_V2, + (bool)ctx->udp_tunnel); if(result) goto out; result = Curl_creader_set_null(data); if(result) goto out; - infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); - result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, NULL, ts, tunnel_send_callback, cf); if(result) { @@ -777,41 +797,30 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, struct Curl_easy *data, struct tunnel_stream *ts) { - CURLcode result = CURLE_OK; - struct dynhds_entry *auth_reply = NULL; - (void)cf; - - DEBUGASSERT(ts->resp); - if(ts->resp->status / 100 == 2) { - infof(data, "CONNECT tunnel established, response %d", ts->resp->status); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); - return CURLE_OK; - } - - if(ts->resp->status == 401) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); - } - else if(ts->resp->status == 407) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); - } + struct cf_h2_proxy_ctx *ctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; - if(auth_reply) { - CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", - auth_reply->value); - result = Curl_http_input_auth(data, ts->resp->status == 407, - auth_reply->value); - if(result) - return result; - if(data->req.newurl) { - /* Indicator that we should try again */ - curlx_safefree(data->req.newurl); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); - return CURLE_OK; - } + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)ctx->udp_tunnel, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data, + (bool)ctx->udp_tunnel); + break; + case PROXY_INSPECT_FAILED: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data, + (bool)ctx->udp_tunnel); + break; } - - /* Seems to have failed */ - return CURLE_COULDNT_CONNECT; + return result; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -831,7 +840,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, result = submit_CONNECT(cf, data, ts); if(result) goto out; - h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data, + (bool)ctx->udp_tunnel); FALLTHROUGH(); case H2_TUNNEL_CONNECT: @@ -840,12 +850,14 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, if(!result) result = proxy_h2_progress_egress(cf, data); if(result && result != CURLE_AGAIN) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); break; } if(ts->has_final_response) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data, + (bool)ctx->udp_tunnel); } else { result = CURLE_OK; @@ -874,7 +886,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, out: if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); return result; } @@ -1231,7 +1244,8 @@ static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ @@ -1297,7 +1311,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ @@ -1477,7 +1492,8 @@ struct Curl_cftype Curl_cft_h2_proxy = { CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, struct Curl_easy *data, - struct Curl_peer *dest) + struct Curl_peer *dest, + bool udp_tunnel) { struct Curl_cfilter *cf_h2_proxy = NULL; struct cf_h2_proxy_ctx *ctx; @@ -1488,6 +1504,7 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, if(!ctx) goto out; Curl_peer_link(&ctx->dest, dest); + ctx->udp_tunnel = udp_tunnel; result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); if(result) @@ -1501,3 +1518,6 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, } #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY && USE_NGHTTP2 */ + +/* Do not leak this filter's call_data accessor in unity builds. */ +#undef CF_CTX_CALL_DATA diff --git a/lib/cf-h2-proxy.h b/lib/cf-h2-proxy.h index 1056a329076c..07e3c9aedf1a 100644 --- a/lib/cf-h2-proxy.h +++ b/lib/cf-h2-proxy.h @@ -29,7 +29,8 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, struct Curl_easy *data, - struct Curl_peer *dest); + struct Curl_peer *dest, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h2_proxy; diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c new file mode 100644 index 000000000000..1896ba6302dc --- /dev/null +++ b/lib/cf-h3-proxy.c @@ -0,0 +1,3478 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +#include +#include +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#include +#elif defined(OPENSSL_QUIC_API2) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#endif /* USE_OPENSSL */ + +#include + +#include "urldata.h" +#include "hash.h" +#include "sendf.h" +#include "multiif.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curlx/dynbuf.h" +#include "dynhds.h" +#include "http_proxy.h" +#include "select.h" +#include "uint-hash.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "curl_trc.h" +#include "cf-h3-proxy.h" +#include "url.h" +#include "capsule.h" +#include "rand.h" + +/* A stream window is the maximum amount we need to buffer for + * each active transfer. We use HTTP/3 flow control and only ACK + * when we take things out of the buffer. + * Chunk size is large enough to take a full DATA frame */ +#define PROXY_H3_STREAM_WINDOW_SIZE (128 * 1024) +#define PROXY_H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) +#define PROXY_H3_STREAM_CHUNK_SIZE (16 * 1024) + +/* The pool keeps spares around and half of a full stream window + * seems good. More does not seem to improve performance. + * The benefit of the pool is that stream buffer to not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#define PROXY_H3_STREAM_POOL_SPARES \ + ((PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) / 2) + +#define PROXY_H3_STREAM_RECV_CHUNKS \ + (PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) +#define PROXY_H3_STREAM_SEND_CHUNKS \ + (PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) + +#define PROXY_QUIC_MAX_STREAMS (256*1024) +#define PROXY_QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) + +typedef enum +{ + H3_TUNNEL_INIT, /* init/default/no tunnel state */ + H3_TUNNEL_CONNECT, /* CONNECT request is being sent */ + H3_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H3_TUNNEL_ESTABLISHED, + H3_TUNNEL_FAILED +} h3_tunnel_state; + +struct h3_proxy_stream_ctx; + +struct h3_tunnel_stream +{ + struct http_resp *resp; + char *authority; + struct h3_proxy_stream_ctx *stream; + int64_t stream_id; + h3_tunnel_state state; + BIT(has_final_response); + BIT(closed); +}; + +static CURLcode h3_tunnel_stream_init(struct h3_tunnel_stream *ts, + struct Curl_peer *dest) +{ + ts->state = H3_TUNNEL_INIT; + ts->stream_id = -1; + ts->has_final_response = FALSE; + + /* host:port with IPv6 support */ + ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); + if(!ts->authority) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void h3_tunnel_stream_reset(struct h3_tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + ts->stream = NULL; + ts->stream_id = -1; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_stream_clear(struct h3_tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + curlx_safefree(ts->authority); + memset(ts, 0, sizeof(*ts)); + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_go_state(struct Curl_cfilter *cf, + struct h3_tunnel_stream *ts, + h3_tunnel_state new_state, + struct Curl_easy *data, + bool udp_tunnel) +{ + (void)cf; + (void)udp_tunnel; + + if(ts->state == new_state) + return; + /* leaving this one */ + switch(ts->state) { + case H3_TUNNEL_CONNECT: + data->req.ignorebody = FALSE; + break; + default: + break; + } + /* entering this one */ + switch(new_state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'init'", + ts->stream_id); + h3_tunnel_stream_reset(ts); + break; + + case H3_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'connect'", + ts->stream_id); + ts->state = H3_TUNNEL_CONNECT; + break; + + case H3_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'response'", + ts->stream_id); + ts->state = H3_TUNNEL_RESPONSE; + break; + + case H3_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'established'", + ts->stream_id); + infof(data, "CONNECT%s phase completed for HTTP/3 proxy", + udp_tunnel ? "-UDP" : ""); + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + FALLTHROUGH(); + case H3_TUNNEL_FAILED: + if(new_state == H3_TUNNEL_FAILED) + CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'failed'", + ts->stream_id); + ts->state = new_state; + /* If a proxy-authorization header was used for the proxy, then we should + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ + curlx_safefree(data->req.hd_proxy_auth); + break; + } +} + +struct cf_ngtcp2_proxy_ctx { + struct cf_quic_ctx q; + struct ssl_peer peer; + struct curl_tls_ctx tls; +#ifdef OPENSSL_QUIC_API2 + ngtcp2_crypto_ossl_ctx *ossl_ctx; +#endif /* OPENSSL_QUIC_API2 */ + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_ccerr last_error; + ngtcp2_crypto_conn_ref conn_ref; + struct cf_call_data call_data; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; + /* hash `data->mid` to `h3_proxy_stream_ctx` */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ + int qlogfd; + BIT(initialized); + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ +}; + +struct cf_h3_proxy_ctx +{ + struct cf_ngtcp2_proxy_ctx *ngtcp2_ctx; + struct cf_call_data call_data; /* fallback before backend ctx exists */ + struct bufq inbufq; /* network receive buffer */ + struct Curl_peer *dest; /* where to tunnel to */ + struct h3_tunnel_stream tunnel; /* our tunnel CONNECT stream */ + BIT(connected); + BIT(udp_tunnel); +}; + +/** + * All about the H3 internals of a stream + */ +struct h3_proxy_stream_ctx +{ + int64_t id; /* HTTP/3 stream identifier */ + struct bufq sendbuf; /* h3 request body */ + size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t tun_data_recvd; /* number of bytes received over tunnel */ + uint64_t rx_offset; /* current receive offset */ + uint64_t rx_offset_max; /* allowed receive offset */ + uint64_t window_size_max; /* max flow control window set for stream */ + int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +#define H3_PROXY_STREAM_CTX(ctx, data) \ + ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) + +#define H3_STREAM_ID(stream) ((stream)->id) + +static void h3_proxy_stream_ctx_free(struct h3_proxy_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + curlx_free(stream); +} + +static void h3_proxy_stream_hash_free(unsigned int id, void *stream) +{ + (void)id; + DEBUGASSERT(stream); + h3_proxy_stream_ctx_free((struct h3_proxy_stream_ctx *)stream); +} + +static void cf_ngtcp2_proxy_ctx_init(struct cf_ngtcp2_proxy_ctx *ctx) +{ + DEBUGASSERT(!ctx->initialized); + ctx->q.sockfd = CURL_SOCKET_BAD; + ctx->qlogfd = -1; + ctx->version = NGTCP2_PROTO_VER_MAX; + Curl_bufcp_init(&ctx->stream_bufcp, PROXY_H3_STREAM_CHUNK_SIZE, + PROXY_H3_STREAM_POOL_SPARES); + curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h3_proxy_stream_hash_free); + ctx->initialized = TRUE; +} + +static void cf_ngtcp2_proxy_ctx_free(struct cf_ngtcp2_proxy_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + curlx_dyn_free(&ctx->scratch); + Curl_uint32_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->peer); + } + curlx_free(ctx); +} + +static void cf_ngtcp2_proxy_ctx_close(struct cf_ngtcp2_proxy_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(!ctx->initialized) + return; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + } + ctx->qlogfd = -1; + Curl_vquic_tls_cleanup(&ctx->tls); + Curl_ssl_peer_cleanup(&ctx->peer); + vquic_ctx_free(&ctx->q); + if(ctx->h3conn) { + nghttp3_conn_del(ctx->h3conn); + ctx->h3conn = NULL; + } + if(ctx->qconn) { + ngtcp2_conn_del(ctx->qconn); + ctx->qconn = NULL; + } +#ifdef OPENSSL_QUIC_API2 + if(ctx->ossl_ctx) { + ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); + ctx->ossl_ctx = NULL; + } +#endif /* OPENSSL_QUIC_API2 */ + ctx->call_data = save; +} + +static void cf_ngtcp2_proxy_setup_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + const ngtcp2_transport_params *rp; + /* Peer should have sent us its transport parameters. If it + * announces a positive `max_idle_timeout` it will close the + * connection when it does not hear from us for that time. + * + * Some servers use this as a keep-alive timer at a rather low + * value. We are doing HTTP/3 here and waiting for the response + * to a request may take a considerable amount of time. We need + * to prevent the peer's QUIC stack from closing in this case. + */ + if(!ctx->qconn) + return; + + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(!rp || !rp->max_idle_timeout) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); + } + else if(!Curl_uint32_hash_count(&ctx->streams)) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); + } + else { + ngtcp2_duration keep_ns; + keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); + CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " + "set keep-alive to %" PRIu64 " ms.", + (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS), + (uint64_t)(keep_ns / NGTCP2_MILLISECONDS)); + } +} + +struct proxy_pkt_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + ngtcp2_path_storage ps; +}; + +static void proxy_pktx_update_time(struct proxy_pkt_io_ctx *pktx, + struct Curl_cfilter *cf) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + const struct curltime *pnow = Curl_pgrs_now(pktx->data); + + vquic_ctx_update_time(&ctx->q, pnow); + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} + +static void proxy_pktx_init(struct proxy_pkt_io_ctx *pktx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + const struct curltime *pnow = Curl_pgrs_now(data); + + pktx->cf = cf; + pktx->data = data; + ngtcp2_path_storage_zero(&pktx->ps); + vquic_ctx_set_time(&ctx->q, pnow); + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} + +static ngtcp2_conn *proxy_get_conn(ngtcp2_crypto_conn_ref *conn_ref) +{ + struct Curl_cfilter *cf = conn_ref->user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + return ctx->qconn; +} + +#ifdef DEBUG_NGTCP2 +static void proxy_quic_printf(void *user_data, const char *fmt, ...) +{ + va_list ap; + (void)user_data; + va_start(ap, fmt); + curl_mvfprintf(stderr, fmt, ap); + va_end(ap); + curl_mfprintf(stderr, "\n"); +} +#endif /* DEBUG_NGTCP2 */ + +static void proxy_qlog_callback(void *user_data, uint32_t flags, + const void *data, size_t datalen) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + (void)flags; + if(ctx->qlogfd != -1) { + ssize_t rc = write(ctx->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + curlx_close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } +} + +static void quic_settings_proxy(struct cf_ngtcp2_proxy_ctx *ctx, + struct Curl_easy *data, + struct proxy_pkt_io_ctx *pktx) +{ + ngtcp2_settings *s = &ctx->settings; + ngtcp2_transport_params *t = &ctx->transport_params; + + ngtcp2_settings_default(s); + ngtcp2_transport_params_default(t); +#ifdef DEBUG_NGTCP2 + s->log_printf = proxy_quic_printf; +#else + s->log_printf = NULL; +#endif /* DEBUG_NGTCP2 */ + + s->initial_ts = pktx->ts; + s->handshake_timeout = (data->set.connecttimeout > 0) ? + data->set.connecttimeout * NGTCP2_MILLISECONDS : + PROXY_QUIC_HANDSHAKE_TIMEOUT; + s->max_window = 100 * PROXY_H3_STREAM_WINDOW_SIZE; + s->max_stream_window = 10 * PROXY_H3_STREAM_WINDOW_SIZE; + s->no_pmtud = FALSE; +#ifdef NGTCP2_SETTINGS_V3 + /* try ten times the ngtcp2 defaults here for problems with Caddy */ + s->glitch_ratelim_burst = 1000 * 10; + s->glitch_ratelim_rate = 33 * 10; +#endif /* NGTCP2_SETTINGS_V3 */ + t->initial_max_data = 10 * PROXY_H3_STREAM_WINDOW_SIZE; + t->initial_max_stream_data_bidi_local = PROXY_H3_STREAM_WINDOW_SIZE; + t->initial_max_stream_data_bidi_remote = PROXY_H3_STREAM_WINDOW_SIZE; + t->initial_max_stream_data_uni = PROXY_H3_STREAM_WINDOW_SIZE; + t->initial_max_streams_bidi = PROXY_QUIC_MAX_STREAMS; + t->initial_max_streams_uni = PROXY_QUIC_MAX_STREAMS; + t->max_idle_timeout = 0; /* no idle timeout from our side */ + if(ctx->qlogfd != -1) { + s->qlog_write = proxy_qlog_callback; + } +} + +static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +static bool cf_ngtcp2_proxy_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} + +static void cf_ngtcp2_proxy_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); + } + else { + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); + } + } + if(cf_ngtcp2_proxy_err_is_fatal(code)) + cf_ngtcp2_proxy_conn_close(cf, data); +} + +static bool cf_ngtcp2_proxy_h3_err_is_fatal(int code) +{ + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); +} + +static void cf_ngtcp2_proxy_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); + } + if(cf_ngtcp2_proxy_h3_err_is_fatal(code)) + cf_ngtcp2_proxy_conn_close(cf, data); +} + +/* How to access `call_data` from a cf_h3_proxy filter */ +#undef CF_CTX_CALL_DATA +static struct cf_call_data *cf_h3_proxy_call_data(struct Curl_cfilter *cf) +{ + struct cf_h3_proxy_ctx *ctx = cf ? cf->ctx : NULL; + static struct cf_call_data no_ctx; + + if(!ctx) + return &no_ctx; + if(ctx->ngtcp2_ctx) + return &ctx->ngtcp2_ctx->call_data; + return &ctx->call_data; +} + +#define CF_CTX_CALL_DATA(cf) (*cf_h3_proxy_call_data(cf)) + +static void cf_h3_proxy_ctx_clear(struct cf_h3_proxy_ctx *ctx) +{ + Curl_bufq_free(&ctx->inbufq); + Curl_peer_unlink(&ctx->dest); + h3_tunnel_stream_clear(&ctx->tunnel); + memset(ctx, 0, sizeof(*ctx)); +} + +static void cf_h3_proxy_ctx_free(struct cf_h3_proxy_ctx *ctx) +{ + if(ctx) { + cf_h3_proxy_ctx_clear(ctx); + curlx_free(ctx); + } +} + +static CURLcode h3_proxy_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream = NULL; + + if(!data) + return CURLE_FAILED_INIT; + + if(!ctx) + return CURLE_FAILED_INIT; + + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(stream) + return CURLE_OK; + + stream = curlx_calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = PROXY_H3_STREAM_WINDOW_SIZE; + /* on send, we control how much we put into the buffer */ + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + PROXY_H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + stream->sendbuf_len_in_flight = 0; + stream->window_size_max = PROXY_H3_STREAM_WINDOW_SIZE; + + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { + h3_proxy_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + if(Curl_uint32_hash_count(&ctx->streams) == 1) + cf_ngtcp2_proxy_setup_keep_alive(cf, data); + + return CURLE_OK; +} + +static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + struct h3_proxy_stream_ctx *stream; + size_t skiplen; + + if(!ctx) + return 0; + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(!stream) + return 0; + /* The server acknowledged `datalen` of bytes from our request body. + * This is a delta. We have kept this data in `sendbuf` for + * re-transmissions and can free it now. */ + if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) + skiplen = stream->sendbuf_len_in_flight; + else + skiplen = (size_t)datalen; + Curl_bufq_skip(&stream->sendbuf, skiplen); + stream->sendbuf_len_in_flight -= skiplen; + + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int cb_h3_proxy_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + struct h3_proxy_stream_ctx *stream; + bool tunnel_stream = FALSE; + (void)conn; + + if(!ctx) + return 0; + stream = H3_PROXY_STREAM_CTX(ctx, data); + tunnel_stream = (stream_id == proxy_ctx->tunnel.stream_id); + /* we might be called by nghttp3 after we already cleaned up */ + if(!stream) { + if(tunnel_stream) { + proxy_ctx->tunnel.stream = NULL; + proxy_ctx->tunnel.closed = TRUE; + } + return 0; + } + + stream->closed = TRUE; + stream->error3 = app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, + H3_STREAM_ID(stream), stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", H3_STREAM_ID(stream)); + } + if(tunnel_stream) { + proxy_ctx->tunnel.stream = NULL; + proxy_ctx->tunnel.closed = TRUE; + } + Curl_multi_mark_dirty(data); + return 0; +} + +static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + uint64_t cur_win, wanted_win = PROXY_H3_STREAM_WINDOW_SIZE_MAX; + + /* how much does rate limiting allow us to acknowledge? */ + if(Curl_rlimit_active(&data->progress.dl.rlimit)) { + int64_t avail; + + /* start rate limit updates only after first bytes arrived */ + if(!stream->rx_offset) + return; + + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, + Curl_pgrs_now(data)); + if(avail <= 0) { + /* nothing available, do not extend the rx offset */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 + " tokens)", stream->id, avail); + return; + } + wanted_win = CURLMIN((uint64_t)avail, PROXY_H3_STREAM_WINDOW_SIZE_MAX); + } + + if(stream->rx_offset_max < stream->rx_offset) { + DEBUGASSERT(0); + return; + } + cur_win = stream->rx_offset_max - stream->rx_offset; + if(cur_win < wanted_win) { + /* We have exhausted the credit we gave the QUIC peer for DATA. + * We extend it with the amount we can give (rate limit) */ + uint64_t ext = wanted_win - cur_win; + + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, ext); + ngtcp2_conn_extend_max_offset(ctx->qconn, ext); + stream->rx_offset_max += ext; + if(stream->rx_offset_max > stream->window_size_max) { + stream->window_size_max = stream->rx_offset_max; + CURL_TRC_CF(data, cf, "[%" PRId64 "] max window now -> %" PRIu64, + stream->id, stream->window_size_max); + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx_offset_max -> %" PRIu64 + " (ext %" PRIu64 ", win %" PRIu64 ")", + stream->id, stream->rx_offset_max, ext, wanted_win); + } +} + +static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + struct h3_proxy_stream_ctx *stream; + size_t nwritten; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream3_id; + + if(!ctx) + return NGHTTP3_ERR_CALLBACK_FAILURE; + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(!stream) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + stream->tun_data_recvd += (curl_off_t)buflen; + CURL_TRC_CF(data, cf, "[cb_h3_proxy_recv_data] " + "[%" PRIu64 "] DATA len=%zu, total=%zd", + H3_STREAM_ID(stream), buflen, stream->tun_data_recvd); + + result = Curl_bufq_write(&proxy_ctx->inbufq, buf, buflen, &nwritten); + if(result || (nwritten < buflen)) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + /* DATA has been moved into our local recv buffer. Update stream offsets + * and give QUIC read credit back so long transfers over proxy tunnels + * do not stall on stream/connection flow-control limits. */ + stream->rx_offset += buflen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; + + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); + cf_h3_proxy_upd_rx_win(cf, data, stream); + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + (void)conn; + (void)stream_user_data; + + if(!ctx) + return 0; + + /* nghttp3 has consumed bytes on the QUIC stream and we need to + * tell the QUIC connection to increase its flow control */ + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + + return 0; +} + +static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t sid, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + int64_t stream_id = sid; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct h3_proxy_stream_ctx *stream; + CURLcode result = CURLE_OK; + int http_status; + struct http_resp *resp; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + + /* stream_user_data might be NULL for control streams */ + if(!data) { + /* Silently ignore headers on streams without user data (control, etc) */ + return 0; + } + + if(!ctx) + return 0; + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(!stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] recv_header: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + } + + /* we might have cleaned up this transfer already */ + if(!stream) + return 0; + + if(proxy_ctx->tunnel.has_final_response) { + /* we do not do anything with trailers for tunnel streams */ + return 0; + } + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + http_status = stream->status_code; + result = Curl_http_resp_make(&resp, http_status, NULL); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + if(proxy_ctx->tunnel.resp) + Curl_http_resp_free(proxy_ctx->tunnel.resp); + proxy_ctx->tunnel.resp = resp; + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base); + result = Curl_dynhds_add(&proxy_ctx->tunnel.resp->headers, + (const char *)h3name.base, h3name.len, + (const char *)h3val.base, h3val.len); + if(result) { + return -1; + } + } + return 0; +} + +static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t sid, + int fin, void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + int64_t stream_id = sid; + struct h3_proxy_stream_ctx *stream; + (void)conn; + (void)stream_id; + (void)fin; + + /* stream_user_data might be NULL for control streams */ + if(!data) { + /* Silently ignore for streams without user data */ + return 0; + } + + if(!ctx) + return 0; + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(!stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + } + + if(!stream) + return 0; + + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); + + if(!proxy_ctx->tunnel.has_final_response) { + if(stream->status_code / 100 != 1) { + proxy_ctx->tunnel.has_final_response = TRUE; + } + } + + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + (void)conn; + + (void)stream_user_data; + + if(ctx) { + int rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, sid, + app_error_code); + + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t sid, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + int64_t stream_id = sid; + int rv; + (void)conn; + + if(!ctx) + return 0; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + if(stream_id == proxy_ctx->tunnel.stream_id) { + proxy_ctx->tunnel.stream = NULL; + proxy_ctx->tunnel.closed = TRUE; + } + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_ssize +cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + struct h3_proxy_stream_ctx *stream; + size_t nwritten = 0; + size_t nvecs = 0; + const unsigned char *buf_base; + (void)conn; + (void)stream_id; + (void)veccnt; + + if(!ctx) + return NGHTTP3_ERR_CALLBACK_FAILURE; + stream = H3_PROXY_STREAM_CTX(ctx, data); + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_proxy_acked_req_body()` for updates). + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * that we have already passed to nghttp3, but which have not been + * ACKed yet. + * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * to nghttp3. Do that now, if we can. */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + nvecs = 0; + while(nvecs < veccnt) { + if(!Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + &buf_base, + &vec[nvecs].len)) + break; + vec[nvecs].base = (uint8_t *)(uintptr_t)buf_base; + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ + } + + if(nwritten > 0 && + stream->upload_left != -1 && + (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) + stream->upload_left -= nwritten; + + /* When we stopped sending and everything in `sendbuf` is "in flight", + * we are at the end of the request body. */ + /* We should NOT set send_closed = TRUE for tunnel stream */ + if(stream->upload_left == 0 && + (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) { + *pflags = NGHTTP3_DATA_FLAG_EOF; + stream->send_closed = TRUE; + } + + else if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + H3_STREAM_ID(stream)); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", + H3_STREAM_ID(stream), (int)nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); + return (nghttp3_ssize)nvecs; +} + +static nghttp3_callbacks ngh3_proxy_callbacks = { + cb_h3_proxy_acked_req_body, /* acked_stream_data */ + cb_h3_proxy_stream_close, + cb_h3_proxy_recv_data, + cb_h3_proxy_deferred_consume, + NULL, /* begin_headers */ + cb_h3_proxy_recv_header, + cb_h3_proxy_end_headers, + NULL, /* begin_trailers */ + cb_h3_proxy_recv_header, + NULL, /* end_trailers */ + cb_h3_proxy_stop_sending, + NULL, /* end_stream */ + cb_h3_proxy_reset_stream, + NULL, /* shutdown */ + NULL, /* recv_settings (deprecated) */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ + NULL, /* recv_origin */ + NULL, /* end_origin */ + NULL, /* rand */ +#endif /* NGHTTP3_CALLBACKS_V2 */ +#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ + NULL, /* recv_settings2 */ +#endif /* NGHTTP3_CALLBACKS_V3 */ +}; + +#if NGTCP2_VERSION_NUM < 0x011100 +struct cf_ngtcp2_proxy_sfind_ctx { + int64_t stream_id; + struct h3_proxy_stream_ctx *stream; + uint32_t mid; +}; + +static bool cf_ngtcp2_proxy_sfind(uint32_t mid, void *value, + void *user_data) +{ + struct cf_ngtcp2_proxy_sfind_ctx *fctx = user_data; + struct h3_proxy_stream_ctx *stream = value; + + if(fctx->stream_id == H3_STREAM_ID(stream)) { + fctx->mid = mid; + fctx->stream = stream; + return FALSE; + } + return TRUE; /* continue */ +} + +static struct h3_proxy_stream_ctx * +cf_ngtcp2_proxy_get_stream(struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) +{ + struct cf_ngtcp2_proxy_sfind_ctx fctx; + fctx.stream_id = stream_id; + fctx.stream = NULL; + Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_proxy_sfind, &fctx); + return fctx.stream; +} +#else +static struct h3_proxy_stream_ctx * +cf_ngtcp2_proxy_get_stream(struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) +{ + struct Curl_easy *data = + ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); + + if(!data) { + return NULL; + } + return H3_PROXY_STREAM_CTX(ctx, data); +} +#endif /* NGTCP2_VERSION_NUM < 0x011100 */ + +static CURLcode cf_ngtcp2_h3conn_init(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + int rc; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_proxy_callbacks, + &ctx->h3settings, + Curl_nghttp3_mem(), + cf); + if(rc) { + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack encoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack decoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 qpack streams: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + + CURL_TRC_CF(data, cf, "HTTP/3 connection initialized"); + return CURLE_OK; +} + +static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data; + + (void)tconn; + DEBUGASSERT(ctx); + data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!ctx || !data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + ctx->handshake_at = *Curl_pgrs_now(data); + ctx->tls_handshake_complete = TRUE; + Curl_vquic_report_handshake(&ctx->tls, cf, data); + + ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, + data, &ctx->peer); +#ifdef CURLVERBOSE + if(Curl_trc_is_verbose(data)) { + const ngtcp2_transport_params *rp; + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T + "ms, remote transport[max_udp_payload=%" PRIu64 + ", initial_max_data=%" PRIu64 + "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); + } +#endif + + /* In case of earlydata, where we simulate being connected, update + * the handshake time when we really did connect */ + if(ctx->use_earlydata) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + if(ctx->use_earlydata) { +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_accepted = + (SSL_get_early_data_status(ctx->tls.ossl.ssl) != + SSL_EARLY_DATA_REJECTED); +#endif +#ifdef USE_GNUTLS + int flags = gnutls_session_get_flags(ctx->tls.gtls.session); + ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); +#endif /* USE_GNUTLS */ +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_accepted = + (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) != + WOLFSSL_EARLY_DATA_REJECTED); +#else + DEBUGASSERT(0); /* should not come here if ED is disabled. */ + ctx->earlydata_accepted = FALSE; +#endif /* WOLFSSL_EARLY_DATA */ +#endif /* USE_WOLFSSL */ + CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", + ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); + Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? + (curl_off_t)ctx->earlydata_skip : + -(curl_off_t)ctx->earlydata_skip); + } + + /* Initialize HTTP/3 connection after successful handshake */ + if(!ctx->h3conn) { + CURLcode result = cf_ngtcp2_h3conn_init(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "HTTP/3 initialization failed: %d", result); + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int cb_ngtcp2_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t sid, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + int64_t stream_id = (int64_t)sid; + nghttp3_ssize nconsumed; + int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; + struct Curl_easy *data = stream_user_data; + (void)offset; + (void)data; + + nconsumed = + nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + if(!data) + data = CF_DATA_CURRENT(cf); + if(data) + CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed); + if(nconsumed < 0) { + struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); + if(data && stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " + "reset=%d, closed=%d", + stream_id, stream->reset, stream->closed); + } + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + if(nconsumed) { + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, + (uint64_t)nconsumed); + ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); + } + + return 0; +} + +static int cb_ngtcp2_acked_stream_data_offset(ngtcp2_conn *tconn, + int64_t stream_id, + uint64_t offset, + uint64_t datalen, + void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int cb_ngtcp2_stream_close(ngtcp2_conn *tconn, uint32_t flags, + int64_t sid, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = stream_user_data; + int64_t stream_id = (int64_t)sid; + int rv; + + (void)tconn; + /* stream is closed... */ + if(!data) + data = CF_DATA_CURRENT(cf); + if(!data) + return NGTCP2_ERR_CALLBACK_FAILURE; + + if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { + app_error_code = NGHTTP3_H3_NO_ERROR; + } + + rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" + PRIu64 ") -> %d", stream_id, (uint64_t)app_error_code, + rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + cf_ngtcp2_proxy_h3_err_set(cf, data, rv); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int cb_ngtcp2_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)tconn; + ctx->max_bidi_streams = max_streams; + if(data) + CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 + ", used %" PRIu64, (uint64_t)ctx->max_bidi_streams, + (uint64_t)ctx->used_bidi_streams); + return 0; +} + +static void cb_ngtcp2_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, just fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + +/* for ngtcp2 data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ +static int cb_ngtcp2_get_new_connection_id2(ngtcp2_conn *tconn, + ngtcp2_cid *cid, struct ngtcp2_stateless_reset_token *token, + size_t cidlen, void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token->data, sizeof(token->data)); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} +#endif + +static int cb_ngtcp2_stream_reset(ngtcp2_conn *tconn, int64_t sid, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + int64_t stream_id = (int64_t)sid; + struct Curl_easy *data = stream_user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + (void)data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + if(stream_id == proxy_ctx->tunnel.stream_id) { + proxy_ctx->tunnel.stream = NULL; + proxy_ctx->tunnel.closed = TRUE; + } + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int cb_ngtcp2_extend_max_stream_data(ngtcp2_conn *tconn, + int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *s_data = stream_user_data; + struct h3_proxy_stream_ctx *stream = NULL; + int rv; + (void)tconn; + (void)max_data; + + rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + stream = H3_PROXY_STREAM_CTX(ctx, s_data); + if(stream && stream->quic_flow_blocked) { + CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", + (int64_t)stream_id); + stream->quic_flow_blocked = FALSE; + Curl_multi_mark_dirty(s_data); + } + return 0; +} + +static int cb_ngtcp2_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + int rv; + (void)tconn; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int cb_ngtcp2_recv_rx_key(ngtcp2_conn *tconn, + ngtcp2_encryption_level level, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + (void)tconn; + + if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) + return 0; + + DEBUGASSERT(ctx); + DEBUGASSERT(data); + if(ctx && data && !ctx->h3conn) { + if(cf_ngtcp2_h3conn_init(cf, data)) + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +#if defined(_MSC_VER) && defined(_DLL) +#pragma warning(push) +#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + +static ngtcp2_callbacks ngtcp2_proxy_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + ngtcp2_crypto_recv_crypto_data_cb, + cb_ngtcp2_proxy_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_ngtcp2_recv_stream_data, + cb_ngtcp2_acked_stream_data_offset, + NULL, /* stream_open */ + cb_ngtcp2_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_ngtcp2_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + cb_ngtcp2_rand, + cb_ngtcp2_get_new_connection_id, /* for ngtcp2 cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + ngtcp2_pkt_info pi; + ngtcp2_path path; + size_t offset, pktlen; + int rv; + + if(ecn) + CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", + buflen, gso_size, ecn); + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + (socklen_t)ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, + remote_addrlen); + pi.ecn = (uint8_t)ecn; + + for(offset = 0; offset < buflen; offset += gso_size) { + pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, + buf + offset, pktlen, pktx->ts); + if(rv) { + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); + cf_ngtcp2_proxy_err_set(pktx->cf, pktx->data, rv); + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } + } + return CURLE_OK; +} + +static CURLcode proxy_h3_progress_ingress_ngtcp2(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct proxy_pkt_io_ctx *pktx) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct proxy_pkt_io_ctx local_pktx; + CURLcode result = CURLE_OK; + + if(!ctx) + return CURLE_RECV_ERROR; + if(!data || !data->multi) + return CURLE_RECV_ERROR; + + if(!pktx) { + proxy_pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + proxy_pktx_update_time(pktx, cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; + + if(ctx->q.sockfd == CURL_SOCKET_BAD) + return CURLE_RECV_ERROR; + + return vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts_proxy, pktx); +} + +/** + * Read a network packet to send from ngtcp2 into `buf`. + * Return number of bytes written or -1 with *err set. + */ +static CURLcode proxy_read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + size_t *pnread) +{ + struct proxy_pkt_io_ctx *x = userp; + struct cf_h3_proxy_ctx *proxy_ctx = x->cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + nghttp3_vec vec[16]; + nghttp3_ssize veccnt; + ngtcp2_ssize ndatalen; + uint32_t flags; + int64_t stream_id; + int fin; + ssize_t n; + + *pnread = 0; + veccnt = 0; + stream_id = -1; + fin = 0; + + /* ngtcp2 may want to put several frames from different streams into + * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. + * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make + * another iteration. + * When ngtcp2 is happy (because it has no other frame that would fit + * or it has nothing more to send), it returns the total length + * of the assembled packet. This may be 0 if there was nothing to send. */ + for(;;) { + + if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { + veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, + CURL_ARRAYSIZE(vec)); + if(veccnt < 0) { + failf(x->data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)veccnt)); + cf_ngtcp2_proxy_h3_err_set(x->cf, x->data, (int)veccnt); + return CURLE_SEND_ERROR; + } + } + + flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, + NULL, buf, buflen, + &ndatalen, flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, x->ts); + if(n == 0) { + /* nothing to send */ + return CURLE_AGAIN; + } + else if(n < 0) { + switch(n) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: { + struct h3_proxy_stream_ctx *stream = NULL; + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_block_stream(ctx->h3conn, stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", + (int64_t)stream_id); + stream = cf_ngtcp2_proxy_get_stream(ctx, stream_id); + if(stream) /* it might be not one of our h3 streams? */ + stream->quic_flow_blocked = TRUE; + n = 0; + break; + } + case NGTCP2_ERR_STREAM_SHUT_WR: + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); + n = 0; + break; + case NGTCP2_ERR_WRITE_MORE: + /* ngtcp2 wants to send more. update the flow of the stream whose data + * is in the buffer and continue */ + DEBUGASSERT(ndatalen >= 0); + n = 0; + break; + default: + DEBUGASSERT(ndatalen == -1); + failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", + ngtcp2_strerror((int)n)); + cf_ngtcp2_proxy_err_set(x->cf, x->data, (int)n); + return CURLE_SEND_ERROR; + } + } + + if(ndatalen >= 0) { + /* we add the amount of data bytes to the flow windows */ + int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); + if(rv) { + failf(x->data, "nghttp3_conn_add_write_offset returned error: %s", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + } + + if(n > 0) { + /* packet assembled, leave */ + *pnread = (size_t)n; + return CURLE_OK; + } + } +} + +static CURLcode proxy_h3_progress_egress_ngtcp2(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct proxy_pkt_io_ctx *pktx) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + size_t nread; + size_t max_payload_size, path_max_payload_size; + size_t pktcnt = 0; + size_t gsolen = 0; /* this disables gso until we have a clue */ + size_t send_quantum; + CURLcode result; + struct proxy_pkt_io_ctx local_pktx; + + if(!pktx) { + proxy_pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + proxy_pktx_update_time(pktx, cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + + /* In UDP, there is a maximum theoretical packet payload length and + * a minimum payload length that is "guaranteed" to work. + * To detect if this minimum payload can be increased, ngtcp2 sends + * now and then a packet payload larger than the minimum. It that + * is ACKed by the peer, both parties know that it works and + * the subsequent packets can use a larger one. + * This is called PMTUD (Path Maximum Transmission Unit Discovery). + * Since a PMTUD might be rejected right on send, we do not want it + * be followed by other packets of lesser size. Because those would + * also fail then. If we detect a PMTUD while buffering, we flush. + */ + max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); + path_max_payload_size = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); + CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", + send_quantum); + for(;;) { + /* add the next packet to send, if any, to our buffer */ + result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + proxy_read_pkt_to_send, pktx, &nread); + if(result == CURLE_AGAIN) + break; + else if(result) + return result; + else { + size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); + if((buflen >= send_quantum) || + ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) + break; + DEBUGASSERT(nread > 0); + ++pktcnt; + if(pktcnt == 1) { + /* first packet in buffer. This is either of a known, "good" + * payload size or it is a PMTUD. We shall see. */ + gsolen = nread; + } + else if(nread > gsolen || + (gsolen > path_max_payload_size && nread != gsolen)) { + /* The added packet is a PMTUD *or* the one(s) before the + * added were PMTUD and the last one is smaller. + * Flush the buffer before the last add. */ + result = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + pktcnt = 0; + } + else if(nread < gsolen) { + /* Reached capacity of our buffer *or* + * last add was shorter than the previous ones, flush */ + break; + } + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* time to send */ + CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", + pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + proxy_pktx_update_time(pktx, cf); + ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); + } + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_call_data save; + struct proxy_pkt_io_ctx pktx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } + + if(!cf->next) { + Curl_bufq_reset(&ctx->q.sendbuf); + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + proxy_pktx_init(&pktx, cf, data); + + if(!ctx->shutdown_started) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_ssize nwritten; + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + PRIu64 ") -> %zd", ctx->last_error.type, + (uint64_t)ctx->last_error.error_code, (ssize_t)nwritten); + /* there are cases listed in ngtcp2 documentation where this call + * may fail. Since we are doing a connection shutdown as graceful + * as we can, such an error is ignored here. */ + if(nwritten > 0) { + /* Ignore amount written. sendbuf was empty and has always room for + * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which + * case `result` is set non zero. */ + size_t n; + result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &n); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", result); + goto out; + } + + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. We are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; + } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + bool done; + cf_ngtcp2_proxy_shutdown(cf, data, &done); +} + +static void cf_ngtcp2_proxy_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + if(ctx && ctx->qconn) { + cf_ngtcp2_proxy_conn_close(cf, data); + cf_ngtcp2_proxy_ctx_close(ctx); + CURL_TRC_CF(data, cf, "close"); + } + cf->connected = FALSE; + CF_DATA_RESTORE(cf, save); +} + +static void cf_ngtcp2_proxy_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + DEBUGASSERT(data); + DEBUGASSERT(stream); + + if(stream->id == proxy_ctx->tunnel.stream_id) { + proxy_ctx->tunnel.stream = NULL; + proxy_ctx->tunnel.closed = TRUE; + } + + if(ctx->h3conn) + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + if(ctx->qconn) + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; + + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", + stream->id, result); + } +} + +/** + * Connection maintenance like timeouts on packet ACKs etc. are done by us, not + * the OS like for TCP. POLL events on the socket therefore are not + * sufficient. + * ngtcp2 tells us when it wants to be invoked again. We handle that via + * the `Curl_expire()` mechanisms. + */ +static CURLcode check_and_set_expiry_ngtcp2(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct proxy_pkt_io_ctx *pktx) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct proxy_pkt_io_ctx local_pktx; + ngtcp2_tstamp expiry; + + if(!ctx) + return CURLE_OK; + + if(!pktx) { + proxy_pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + proxy_pktx_update_time(pktx, cf); + } + + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= pktx->ts) { + CURLcode result; + int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + cf_ngtcp2_proxy_err_set(cf, data, rv); + return CURLE_SEND_ERROR; + } + result = proxy_h3_progress_ingress_ngtcp2(cf, data, pktx); + if(result) + return result; + result = proxy_h3_progress_egress_ngtcp2(cf, data, pktx); + if(result) + return result; + /* ask again, things might have changed */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + } + + if(expiry > pktx->ts) { + ngtcp2_duration timeout = expiry - pktx->ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); + } + } + return CURLE_OK; +} + +static ssize_t proxy_recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream, + CURLcode *err) +{ + ssize_t nread = -1; + *err = CURLE_OK; + + if(stream->reset) { + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); + data->state.refused_stream = TRUE; + *err = CURLE_RECV_ERROR; + goto out; + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + nread = 0; + goto out; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; + goto out; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + *err = CURLE_HTTP3; + goto out; + } + nread = 0; + +out: + return nread; +} + +static struct h3_proxy_stream_ctx * +h3_proxy_resolve_send_stream(struct cf_h3_proxy_ctx *proxy_ctx, + struct cf_ngtcp2_proxy_ctx *ctx, + struct Curl_easy *data) +{ + struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); + + if(stream) + return stream; + + /* send can be driven by a different easy handle during shutdown */ + if(proxy_ctx->tunnel.stream && !proxy_ctx->tunnel.closed) { + return proxy_ctx->tunnel.stream; + } + return NULL; +} + +static CURLcode h3_proxy_sendbuf_add(struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream, + const uint8_t *buf, size_t len, + size_t *pnwritten) +{ + CURLcode result; + *pnwritten = 0; + (void)data; + + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + return result; +} + +static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream = NULL; + struct cf_call_data save; + struct proxy_pkt_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + proxy_pktx_init(&pktx, cf, data); + *pnwritten = 0; + + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + (void)eos; /* use for stream EOF and block handling */ + result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); + if(result) + goto out; + + stream = h3_proxy_resolve_send_stream(proxy_ctx, ctx, data); + if(!stream) { + result = CURLE_SEND_ERROR; + goto denied; + } + + if(proxy_ctx->tunnel.closed) { + result = CURLE_SEND_ERROR; + goto denied; + } + + if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; + } + else { + result = h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + + DEBUGASSERT(!result); + result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); + +out: + result = Curl_1st_fatal(result, + check_and_set_expiry_ngtcp2(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; +} + +/* incoming data frames on the h3 stream */ +static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); + struct cf_call_data save; + struct proxy_pkt_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *pnread = 0; + + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + proxy_pktx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + result = CURLE_RECV_ERROR; + goto out; + } + + if(!Curl_bufq_is_empty(&proxy_ctx->inbufq)) { + result = Curl_bufq_cread(&proxy_ctx->inbufq, + buf, len, pnread); + if(result) + goto out; + } + + result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); + if(result) + goto out; + + /* inbufq had nothing before, maybe after progressing ingress? */ + if(!*pnread && !Curl_bufq_is_empty(&proxy_ctx->inbufq)) { + result = Curl_bufq_cread(&proxy_ctx->inbufq, + buf, len, pnread); + if(result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) " + "-> %zd, %d", + stream->id, len, *pnread, result); + goto out; + } + } + + if(*pnread) { + Curl_multi_mark_dirty(data); + } + else { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", + stream->id); + cf_ngtcp2_proxy_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + ssize_t nread = proxy_recv_closed_stream(cf, data, stream, &result); + if(nread > 0) + *pnread = (size_t)nread; + goto out; + } + result = CURLE_AGAIN; + } + +out: + result = Curl_1st_fatal(result, + proxy_h3_progress_egress_ngtcp2(cf, data, &pktx)); + result = Curl_1st_fatal(result, + check_and_set_expiry_ngtcp2(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, result, *pnread); + CF_DATA_RESTORE(cf, save); + return result; +} + +static void proxy_h3_submit(int64_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct httpreq *req, + CURLcode *err) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream = NULL; + + struct dynhds h2_headers; + nghttp3_nv *nva = NULL; + size_t nheader; + + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + *err = Curl_http_req_to_h2(&h2_headers, req, data); + if(*err) + goto out; + + *err = h3_proxy_data_setup(cf, data); + if(*err) + goto out; + + if(!ctx) { + *err = CURLE_FAILED_INIT; + goto out; + } + + stream = H3_PROXY_STREAM_CTX(ctx, data); + + DEBUGASSERT(stream); + if(!stream) { + *err = CURLE_FAILED_INIT; + goto out; + } + + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + *err = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + /* Open a bidirectional stream */ + { + int64_t sid; + int rv; + + DEBUGASSERT(stream->id == -1); + rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + if(rv) { + failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); + *err = CURLE_SEND_ERROR; + goto out; + } + stream->id = (int64_t)sid; + ++ctx->used_bidi_streams; + + /* Set stream user data in ngtcp2 connection for callbacks */ + rv = ngtcp2_conn_set_stream_user_data(ctx->qconn, sid, data); + if(rv) { + failf(data, "cannot set stream user data: %s", ngtcp2_strerror(rv)); + *err = CURLE_SEND_ERROR; + goto out; + } + proxy_ctx->tunnel.stream = stream; + CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); + } + + /* CONNECT-UDP request stream remains open for capsules, no fixed EOF. */ + stream->upload_left = -1; + stream->send_closed = 0; + reader.read_data = cb_h3_read_data_for_tunnel_stream; + preader = &reader; + + rc = nghttp3_conn_submit_request(ctx->h3conn, H3_STREAM_ID(stream), + nva, nheader, preader, data); + + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " + "connection is closing", + H3_STREAM_ID(stream)); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", + H3_STREAM_ID(stream), rc, nghttp3_strerror(rc)); + break; + } + *err = CURLE_SEND_ERROR; + goto out; + } + + if(Curl_trc_is_verbose(data)) { + CURL_TRC_CF(data, cf, "[H3-PROXY] [%" PRId64 "] OPENED stream " + "for %s", H3_STREAM_ID(stream), + Curl_bufref_ptr(&data->state.url)); + } + +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + if(*err == CURLE_OK) { + *pstream_id = H3_STREAM_ID(stream); + } +} + +static bool cf_h3_proxy_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + bool alive = FALSE; + const ngtcp2_transport_params *rp; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + *input_pending = FALSE; + + if(!ctx || !ctx->qconn || ctx->shutdown_started) + goto out; + if(proxy_ctx->tunnel.closed) + goto out; + + /* We do not announce a max idle timeout, but when the peer does + * it closes the connection when it expires. */ + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(rp && rp->max_idle_timeout) { + timediff_t idletime_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); + if(idletime_ms > 0) { + uint64_t max_idle_ms = + (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS); + if((uint64_t)idletime_ms > max_idle_ms) + goto out; + } + } + + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + goto out; + + alive = TRUE; + if(*input_pending) { + CURLcode result; + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + *input_pending = FALSE; + if(!data || !data->multi) { + alive = FALSE; + goto out; + } + result = proxy_h3_progress_ingress_ngtcp2(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result ? FALSE : TRUE; + } + +out: + CF_DATA_RESTORE(cf, save); + return alive; +} + +static CURLcode cf_ngtcp2_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_call_data save; + + if(!ctx) + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + DEBUGASSERT(pres1); + CF_DATA_SAVE(save, cf, data); + /* Set after transport params arrived and continually updated + * by callback. QUIC counts the number over the lifetime of the + * connection, ever increasing. + * We count the *open* transfers plus the budget for new ones. */ + if(!ctx->qconn || ctx->shutdown_started) { + *pres1 = 0; + } + else if(ctx->max_bidi_streams) { + uint64_t avail_bidi_streams = 0; + uint64_t max_streams = cf->conn->attached_xfers; + if(ctx->max_bidi_streams > ctx->used_bidi_streams) + avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; + max_streams += avail_bidi_streams; + *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; + } + else /* transport params not arrived yet? take our default. */ + *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " + "MAX_CONCURRENT -> %d (%u in use)", + cf->conn->connection_id, *pres1, cf->conn->attached_xfers); + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->q.got_first_byte) { + timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, + &ctx->started_at); + *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->q.got_first_byte) + *when = ctx->q.first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + if(Curl_vquic_tls_get_ssl_info(&ctx->tls, + (query == CF_QUERY_SSL_CTX_INFO), info)) + return CURLE_OK; + break; + } + case CF_QUERY_ALPN_NEGOTIATED: { + const char **palpn = pres2; + DEBUGASSERT(palpn); + *palpn = cf->connected ? "h3" : NULL; + return CURLE_OK; + } + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static CURLcode cf_ngtcp2_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + + if(!ctx->qconn) + return CURLE_OK; + + Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) + want_send = TRUE; + + if(want_recv || want_send) { + struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; + + CF_DATA_SAVE(save, cf, data); + c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); + s_exhaust = want_send && stream && H3_STREAM_ID(stream) >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + return result; +} + +static CURLcode cf_h3_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + + if(!proxy_ctx) + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; + return cf_ngtcp2_proxy_query(cf, data, query, pres1, pres2); +} + +static CURLcode cf_h3_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + + if(!proxy_ctx) + return cf->next ? + cf->next->cft->adjust_pollset(cf->next, data, ps) : + CURLE_OK; + return cf_ngtcp2_proxy_adjust_pollset(cf, data, ps); +} + +static bool cf_h3_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + if(!proxy_ctx) + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; + if(!Curl_bufq_is_empty(&proxy_ctx->inbufq)) + return TRUE; + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +#ifdef USE_OPENSSL +static int proxy_quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +{ + ngtcp2_crypto_conn_ref *cref; + struct Curl_cfilter *cf; + struct cf_h3_proxy_ctx *proxy_ctx; + struct cf_ngtcp2_proxy_ctx *ctx; + struct Curl_easy *data; + + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref ? cref->user_data : NULL; + proxy_ctx = cf ? cf->ctx : NULL; + ctx = proxy_ctx ? proxy_ctx->ngtcp2_ctx : NULL; + data = cf ? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; +#ifdef HAVE_OPENSSL_EARLYDATA + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } +#endif /* HAVE_OPENSSL_EARLYDATA */ + Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, + SSL_version(ssl), "h3", quic_tp, quic_tp_len); + } + return 0; +} +#endif /* USE_OPENSSL */ + +static CURLcode cf_ngtcp2_proxy_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) +{ + struct curl_tls_ctx *ctx = user_data; + +#ifdef USE_OPENSSL +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#elif defined(OPENSSL_QUIC_API2) + /* nothing to do */ +#else + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif + if(Curl_ssl_scache_use(cf, data)) { + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, proxy_quic_ossl_new_session_cb); + } + +#else +#error "ngtcp2 TLS backend not configured" +#endif /* USE_OPENSSL */ + + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_proxy_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_max = scs->earlydata_max; +#endif +#ifdef USE_GNUTLS + ctx->earlydata_max = + gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); +#endif /* USE_GNUTLS */ +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_max = scs->earlydata_max; +#else + ctx->earlydata_max = 0; +#endif /* WOLFSSL_EARLY_DATA */ +#endif /* USE_WOLFSSL */ +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if((!ctx->earlydata_max)) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); + } + else if(!scs->quic_tp || !scs->quic_tp_len) { + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); + } + else { + int rv; + rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( + ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); + if(rv) + CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " + "parameters: %s", ngtcp2_strerror(rv)); + else { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); + result = cf_ngtcp2_h3conn_init(cf, data); + if(!result) { + ctx->use_earlydata = TRUE; + proxy_ctx->connected = TRUE; + *do_early_data = TRUE; + } + } + } +#else /* not supported in the TLS backend */ + (void)data; + (void)ctx; + (void)scs; + (void)alpns; +#endif + return result; +} + +static CURLcode cf_h3_proxy_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = NULL; + int rc; + int rv; + CURLcode result = CURLE_OK; + const struct Curl_sockaddr_ex *sockaddr = NULL; + int qfd; + static const struct alpn_spec ALPN_SPEC_H3 = {{ "h3", "h3-29" }, 2}; + struct proxy_pkt_io_ctx pktx; + + ctx = curlx_calloc(1, sizeof(struct cf_ngtcp2_proxy_ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_ngtcp2_proxy_ctx_init(ctx); + + memset(&proxy_ctx->tunnel, 0, sizeof(proxy_ctx->tunnel)); + + Curl_bufq_init2(&proxy_ctx->inbufq, PROXY_H3_STREAM_CHUNK_SIZE, + PROXY_H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + + result = h3_tunnel_stream_init(&proxy_ctx->tunnel, proxy_ctx->dest); + if(result) + goto out; + + DEBUGASSERT(ctx->initialized); + ctx->started_at = *Curl_pgrs_now(data); + + /* Initialize connection IDs BEFORE creating the connection */ + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + goto out; + + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + goto out; + + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + + result = CURLE_QUIC_CONNECT_ERROR; + if(!cf->next) { + CURL_TRC_CF(data, cf, "h3_proxy_ctx_init: no lower filter"); + goto out; + } + ctx->q.sockfd = Curl_conn_cf_get_socket(cf->next, data); + if(ctx->q.sockfd == CURL_SOCKET_BAD) + goto out; + /* Get remote address from the socket filter below */ + if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&sockaddr))) + goto out; + if(!sockaddr) + goto out; + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + goto out; + + /* Initialize vquic context BEFORE proxy_pktx_init which needs it */ + result = vquic_ctx_init(data, &ctx->q); + if(result) + goto out; + + /* Set ngtcp2_ctx in proxy_ctx BEFORE proxy_pktx_init which accesses it */ + proxy_ctx->ngtcp2_ctx = ctx; + + /* Now we can safely initialize pktx and settings */ + proxy_pktx_init(&pktx, cf, data); + quic_settings_proxy(ctx, data, &pktx); + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ngtcp2_proxy_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + + ctx->conn_ref.get_conn = proxy_get_conn; + ctx->conn_ref.user_data = cf; + + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, + cf_ngtcp2_proxy_tls_ctx_setup, &ctx->tls, + &ctx->conn_ref, + cf_ngtcp2_proxy_on_session_reuse); + if(result) + goto out; + +#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) + if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); + result = CURLE_FAILED_INIT; + goto out; + } + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); + if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); + result = CURLE_FAILED_INIT; + goto out; + } +#elif defined(USE_OPENSSL) + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#else +#error "ngtcp2 TLS backend not defined" +#endif /* USE_OPENSSL */ + + ngtcp2_ccerr_default(&ctx->last_error); + + proxy_ctx->connected = FALSE; + +out: + if(result) { + if(ctx) { + proxy_ctx->ngtcp2_ctx = NULL; /* Clear before freeing on error */ + cf_ngtcp2_proxy_ctx_free(ctx); + } + } + CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); + return result; +} + +static CURLcode h3_submit_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, + proxy_ctx->dest, + PROXY_HTTP_V3, + (bool)proxy_ctx->udp_tunnel); + if(result) + goto out; + result = Curl_creader_set_null(data); + if(result) + goto out; + + proxy_h3_submit(&ts->stream_id, cf, data, req, &result); + +out: + if(req) + Curl_http_req_free(req); + if(result) + failf(data, "Failed sending CONNECT to proxy"); + return result; +} + +static CURLcode +h3_proxy_inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; + + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)proxy_ctx->udp_tunnel, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_ESTABLISHED, data, + (bool)proxy_ctx->udp_tunnel); + break; + case PROXY_INSPECT_FAILED: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, + (bool)proxy_ctx->udp_tunnel); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_INIT, data, + (bool)proxy_ctx->udp_tunnel); + break; + } + return result; +} + +static CURLcode cf_h3_proxy_quic_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + struct proxy_pkt_io_ctx pktx; + + if(proxy_ctx->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the sub-chain (UDP via happy eyeballs) */ + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + *done = FALSE; + + if(!proxy_ctx->ngtcp2_ctx) { + result = cf_h3_proxy_ctx_init(cf, data); + if(result) + return result; + } + + /* Initialize pktx AFTER ensuring ngtcp2_ctx exists */ + proxy_pktx_init(&pktx, cf, data); + + CF_DATA_SAVE(save, cf, data); + + if(!proxy_ctx->ngtcp2_ctx->qconn) { + proxy_ctx->ngtcp2_ctx->started_at = *Curl_pgrs_now(data); + if(proxy_ctx->connected) { + *done = TRUE; + goto out; + } + result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); + if(result) + goto out; + + result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); + if(result) + goto out; + + if(ngtcp2_conn_get_handshake_completed(proxy_ctx->ngtcp2_ctx->qconn)) { + result = proxy_ctx->ngtcp2_ctx->tls_vrfy_result; + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + proxy_ctx->connected = TRUE; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + +out: + if(proxy_ctx->ngtcp2_ctx->qconn && + ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && + ngtcp2_conn_in_draining_period(proxy_ctx->ngtcp2_ctx->qconn)) { + const ngtcp2_ccerr *cerr = + ngtcp2_conn_get_ccerr(proxy_ctx->ngtcp2_ctx->qconn); + + result = CURLE_COULDNT_CONNECT; + if(cerr) { + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" + PRIu64, + cerr->type, (uint64_t)cerr->error_code); + switch(cerr->type) { + case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: + CURL_TRC_CF(data, cf, "error in version negotiation"); + break; + default: + if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { + CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", + (unsigned int)(cerr->error_code & 0xffU)); + } + else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { + CURL_TRC_CF(data, cf, "connection refused by server"); + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE with this code right away. We want + * to keep on trying in this case. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + } + } + } + +#ifdef CURLVERBOSE + if(result) { + bool is_ipv6; + struct ip_quadruple ip; + if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ip)) + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } +#endif + if(!result && proxy_ctx->ngtcp2_ctx->qconn) { + result = check_and_set_expiry_ngtcp2(cf, data, &pktx); + } + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode H3_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(ts); + DEBUGASSERT(ts->authority); + + do { + switch(ts->state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); + result = h3_submit_CONNECT(cf, data, ts); + if(result) + goto out; + h3_tunnel_go_state(cf, ts, H3_TUNNEL_CONNECT, data, + (bool)ctx->udp_tunnel); + + result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); + if(result) + goto out; + FALLTHROUGH(); + + case H3_TUNNEL_CONNECT: + /* Non-blocking: call ingress/egress once and return. + * The multi interface will call us again when ready. */ + result = proxy_h3_progress_ingress_ngtcp2(cf, data, NULL); + if(result) + goto out; + result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); + if(result && result != CURLE_AGAIN) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); + goto out; + } + + if(ts->has_final_response) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_RESPONSE, data, + (bool)ctx->udp_tunnel); + } + else { + /* Not done yet, return and let multi interface call us again */ + result = CURLE_OK; + goto out; + } + FALLTHROUGH(); + + case H3_TUNNEL_RESPONSE: + DEBUGASSERT(ts->has_final_response); + result = h3_proxy_inspect_response(cf, data, ts); + if(result) + goto out; + ctx->connected = TRUE; + break; + + case H3_TUNNEL_ESTABLISHED: + return CURLE_OK; + + case H3_TUNNEL_FAILED: + return CURLE_RECV_ERROR; + + default: + break; + } + + } while(ts->state == H3_TUNNEL_INIT); + +out: + if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, (bool)ctx->udp_tunnel); + return result; +} + +static CURLcode +cf_h3_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_call_data save = {0}; + CURLcode result = CURLE_OK; + timediff_t check; + struct h3_tunnel_stream *ts = &proxy_ctx->tunnel; + bool data_saved = FALSE; + + /* Curl_cft_http_proxy --> Curl_cft_h3_proxy --> HAPPY-EYEBALLS --> UDP */ + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + + check = Curl_timeleft_ms(data); + if(check <= 0) { + failf(data, "Proxy CONNECT aborted due to timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + result = cf_h3_proxy_quic_connect(cf, data, done); + if(*done != TRUE) + goto out; + + CF_DATA_SAVE(save, cf, data); + data_saved = TRUE; + + /* At this point the QUIC is connected, but the proxy isn't connected */ + *done = FALSE; + + result = H3_CONNECT(cf, data, ts); + +out: + *done = (result == CURLE_OK) && (ts->state == H3_TUNNEL_ESTABLISHED); + if(*done) { + cf->connected = TRUE; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + } + + if(data_saved) + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode h3_proxy_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + (void)cf; + if(!pause) { + /* unpaused. make it run again right away */ + Curl_multi_mark_dirty(data); + } + return CURLE_OK; +} + +static void h3_proxy_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream; + + if(!ctx) + return; + + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", + stream->id); + cf_ngtcp2_proxy_stream_close(cf, data, stream); + Curl_uint32_hash_remove(&ctx->streams, data->mid); + if(!Curl_uint32_hash_count(&ctx->streams)) + cf_ngtcp2_proxy_setup_keep_alive(cf, data); + } +} + +static CURLcode cf_h3_proxy_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + break; + case CF_CTRL_DATA_PAUSE: + result = h3_proxy_data_pause(cf, data, (arg1 != 0)); + break; + case CF_CTRL_DATA_DONE: + h3_proxy_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: { + struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct h3_proxy_stream_ctx *stream = NULL; + if(ctx) { + stream = H3_PROXY_STREAM_CTX(ctx, data); + if(stream && !stream->send_closed && + (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) { + stream->send_closed = TRUE; + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; + (void)nghttp3_conn_resume_stream(ctx->h3conn, H3_STREAM_ID(stream)); + } + } + break; + } + case CF_CTRL_CONN_INFO_UPDATE: + if(!cf->sockindex && cf->connected) { + cf->conn->httpversion_seen = 30; + Curl_conn_set_multiplex(cf->conn); + } + break; + default: + break; + } + + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + + if(ctx) { + /* Clean up the ngtcp2 context properly */ + if(ctx->ngtcp2_ctx) { + CURL_TRC_CF(data, cf, "cf_ngtcp2_proxy_ctx_close()"); + cf_ngtcp2_proxy_close(cf, data); + cf_ngtcp2_proxy_ctx_free(ctx->ngtcp2_ctx); + ctx->ngtcp2_ctx = NULL; + } + cf_h3_proxy_ctx_free(ctx); + cf->ctx = NULL; + } +} + +static void cf_h3_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + + if(ctx) { + if(ctx->ngtcp2_ctx) { + cf_ngtcp2_proxy_close(cf, data); + cf_ngtcp2_proxy_ctx_free(ctx->ngtcp2_ctx); + ctx->ngtcp2_ctx = NULL; + } + cf_h3_proxy_ctx_clear(ctx); + cf->connected = FALSE; + } + + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + return cf_ngtcp2_proxy_shutdown(cf, data, done); +} + +struct Curl_cftype Curl_cft_h3_proxy = { + "H3-PROXY", + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, + CURL_LOG_LVL_NONE, + cf_h3_proxy_destroy, + cf_h3_proxy_connect, + cf_h3_proxy_close, + cf_h3_proxy_shutdown, + cf_h3_proxy_adjust_pollset, + cf_h3_proxy_data_pending, + cf_h3_proxy_send, + cf_h3_proxy_recv, + cf_h3_proxy_cntrl, + cf_h3_proxy_is_alive, + Curl_cf_def_conn_keep_alive, + cf_h3_proxy_query, +}; + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + (void)data; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + Curl_peer_link(&ctx->dest, dest); + ctx->udp_tunnel = udp_tunnel; + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + + /* H3-PROXY uses the UDP socket created by happy eyeballs below it. + Curl_conn_cf_insert_after chains the existing sub-filters, i.e. + "HAPPY-EYEBALLS -> UDP" as cf->next of H3-PROXY. */ + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + return result; +} + +#endif + +/* Do not leak this filter's call_data accessor in unity builds. */ +#undef CF_CTX_CALL_DATA diff --git a/lib/cf-h3-proxy.h b/lib/cf-h3-proxy.h new file mode 100644 index 000000000000..c1d5dd151144 --- /dev/null +++ b/lib/cf-h3-proxy.h @@ -0,0 +1,42 @@ +#ifndef HEADER_CURL_H3_PROXY_H +#define HEADER_CURL_H3_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel); + +extern struct Curl_cftype Curl_cft_h3_proxy; + +#endif + +#endif /* HEADER_CURL_H3_PROXY_H */ diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 17b2821b087f..965415d45850 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -1023,3 +1023,28 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, Curl_conn_cf_insert_after(cf_at, cf); return CURLE_OK; } + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + defined(USE_PROXY_HTTP3) +CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + /* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw + UDP sockets with TRNSPRT_QUIC transport. Using TRNSPRT_QUIC causes + cf_udp_connect() to call cf_udp_setup_quic() which connects the + socket to the peer address, making send() work without an explicit + destination. We use Curl_cf_udp_create (not Curl_cf_quic_create) + because H3-PROXY manages its own ngtcp2 QUIC stack on top. */ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(cf_at); + result = cf_ip_happy_create(&cf, data, cf_at->conn, + Curl_cf_udp_create, TRNSPRT_QUIC); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 547ee4b4ac90..5805d6397c59 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -52,6 +52,15 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, uint8_t transport); +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + defined(USE_PROXY_HTTP3) +/* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw UDP + sockets with TRNSPRT_QUIC transport so the socket is connected to the + proxy peer. H3-PROXY manages its own ngtcp2 QUIC stack on top. */ +CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ + extern struct Curl_cftype Curl_cft_ip_happy; #endif /* HEADER_CURL_IP_HAPPY_H */ diff --git a/lib/connect.c b/lib/connect.c index e74bda5dfb8f..64ec2ff941f6 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -63,6 +63,7 @@ #include "curlx/inet_ntop.h" #include "curlx/strparse.h" #include "vtls/vtls.h" /* for vtls cfilters */ +#include "vquic/vquic.h" /* for QUIC cfilters */ #include "progress.h" #include "conncache.h" #include "multihandle.h" @@ -341,6 +342,66 @@ struct cf_setup_ctx { uint8_t transport; }; +#ifndef CURL_DISABLE_PROXY +static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_setup_ctx *ctx) +{ + CURLcode result = CURLE_OK; +#ifndef USE_SSL + (void)cf; + (void)data; + (void)ctx; +#else + /* Skipping the Curl_conn_is_ssl check because SSL is a part of QUIC + For CURLPROXY_HTTPS and CURLPROXY_HTTPS2: + Curl_cft_setup --> Curl_cft_ssl --> Curl_cft_http_proxy --> ... + For CURLPROXY_HTTPS3: + Curl_cft_setup --> Curl_cft_http3 --> Curl_cft_http_proxy --> ... */ + if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy) { + if(!IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { + result = Curl_cf_ssl_proxy_insert_after(cf, data); + if(result) + return result; + } + } + else { + if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) + && !Curl_conn_is_ssl(cf->conn, cf->sockindex) + && !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { + result = Curl_cf_ssl_proxy_insert_after(cf, data); + if(result) + return result; + } + } +#endif /* USE_SSL */ + +#ifndef CURL_DISABLE_HTTP + if(cf->conn->bits.tunnel_proxy) { + struct Curl_peer *dest; /* where HTTP should tunnel to */ + bool udp_tun = false; + dest = Curl_conn_get_destination(cf->conn, cf->sockindex); + /* Use CONNECT-UDP only for explicit HTTP/3-only target tunnels. + Do not derive this from proxy transport (for example HTTPS3 proxy). */ + if(data->state.http_neg.wanted == CURL_HTTP_V3x) { +#ifdef USE_PROXY_HTTP3 + udp_tun = TRUE; +#else + failf(data, "HTTP/3 proxy tunnel support not built-in"); + return CURLE_NOT_BUILT_IN; +#endif /* USE_PROXY_HTTP3 */ + } + result = Curl_cf_http_proxy_insert_after(cf, data, dest, + cf->conn->http_proxy.proxytype, + udp_tun); + if(result) + return result; + } +#endif /* !CURL_DISABLE_HTTP */ + return result; +} +#endif /* !CURL_DISABLE_PROXY */ + static CURLcode cf_setup_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -364,7 +425,35 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, } if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - result = cf_ip_happy_insert_after(cf, data, ctx->transport); +#ifndef CURL_DISABLE_PROXY +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + defined(USE_PROXY_HTTP3) + if(IS_QUIC_PROXY(cf->conn->http_proxy.proxytype) && + cf->conn->bits.tunnel_proxy) { + /* For HTTPS3 proxy tunnels, H3-PROXY manages the QUIC connection + on top of the UDP socket. Let happy eyeballs race IPv4/IPv6 using + QUIC-transport UDP sockets so the socket is connected to the + proxy peer and H3-PROXY can send directly via send(). + Filter chains: + H1/H2 target (CONNECT over QUIC): + SETUP --> HTTP/1.1 or HTTP/2 --> SSL --> HTTP-PROXY --> + H3-PROXY --> HAPPY-EYEBALLS --> UDP + H3 target (MASQUE CONNECT-UDP over QUIC): + SETUP --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> + H3-PROXY --> HAPPY-EYEBALLS --> UDP */ + result = cf_ip_happy_quic_udp_insert_after(cf, data); + } + /* When tunneling QUIC through an HTTP proxy (CONNECT-UDP), + the underlying conn to the proxy is TCP. */ + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ + if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy + && !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) + result = cf_ip_happy_insert_after(cf, data, TRNSPRT_TCP); + else +#endif /* !CURL_DISABLE_PROXY */ + result = cf_ip_happy_insert_after(cf, data, ctx->transport); + if(result) return result; ctx->state = CF_SETUP_CNNCT_EYEBALLS; @@ -402,25 +491,9 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, } if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { -#ifdef USE_SSL - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && - !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* USE_SSL */ - -#ifndef CURL_DISABLE_HTTP - if(cf->conn->bits.tunnel_proxy) { - struct Curl_peer *dest; /* where HTTP should tunnel to */ - dest = Curl_conn_get_destination(cf->conn, cf->sockindex); - result = Curl_cf_http_proxy_insert_after( - cf, data, dest, cf->conn->http_proxy.proxytype); - if(result) - return result; - } -#endif /* !CURL_DISABLE_HTTP */ + result = cf_setup_add_http_proxy(cf, data, ctx); + if(result) + return result; ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; if(!cf->next || !cf->next->connected) goto connect_sub_chain; @@ -445,21 +518,41 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, goto connect_sub_chain; } - if(ctx->state < CF_SETUP_CNNCT_SSL) { -#ifdef USE_SSL - if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || - (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ - !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ - result = Curl_cf_ssl_insert_after(cf, data); + /* Adding Curl_cf_quic_insert_after() because now we + need the next filter to be QUIC/HTTP/3 (which has SSL) */ +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + defined(USE_PROXY_HTTP3) + if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && + cf->conn->bits.tunnel_proxy && + (data->state.http_neg.wanted == CURL_HTTP_V3x)) { + if(ctx->state < CF_SETUP_CNNCT_SSL) { + result = Curl_cf_quic_insert_after(cf); if(result) return result; + ctx->state = CF_SETUP_CNNCT_SSL; } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; if(!cf->next || !cf->next->connected) goto connect_sub_chain; } + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ + { + if(ctx->state < CF_SETUP_CNNCT_SSL) { +#ifdef USE_SSL + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || + (ctx->ssl_mode != CURL_CF_SSL_DISABLE && + cf->conn->scheme->flags & PROTOPT_SSL)) /* we want SSL */ + && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + result = Curl_cf_ssl_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } + } ctx->state = CF_SETUP_DONE; cf->connected = TRUE; diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 31e94d0691e7..5c7fdd670be1 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -712,6 +712,9 @@ ${SIZEOF_TIME_T_CODE} /* if libuv is in use */ #cmakedefine USE_LIBUV 1 +/* if HTTP/3 proxy support is available */ +#cmakedefine USE_PROXY_HTTP3 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UV_H 1 diff --git a/lib/curl_trc.c b/lib/curl_trc.c index c6115cf7f636..d54c171a549d 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -35,6 +35,7 @@ #include "http_proxy.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" +#include "cf-h3-proxy.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -578,6 +579,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_h1_proxy, TRC_CT_PROXY }, #ifdef USE_NGHTTP2 { &Curl_cft_h2_proxy, TRC_CT_PROXY }, +#endif +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) + { &Curl_cft_h3_proxy, TRC_CT_PROXY }, #endif { &Curl_cft_http_proxy, TRC_CT_PROXY }, #endif /* !CURL_DISABLE_HTTP */ diff --git a/lib/http.c b/lib/http.c index 5d98aab9d7c3..c935d4f69f20 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1757,6 +1757,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } #else (void)is_connect; @@ -2721,7 +2727,11 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) alpn = Curl_conn_get_alpn_negotiated(data, conn); if(alpn && !strcmp("h3", alpn)) { - DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); +#ifndef CURL_DISABLE_PROXY + if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy || + conn->bits.tunnel_proxy) +#endif + DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); info_version = "HTTP/3"; } else if(alpn && !strcmp("h2", alpn)) { @@ -4847,7 +4857,6 @@ struct name_const { size_t namelen; }; -/* keep them sorted by length! */ static const struct name_const H2_NON_FIELD[] = { { STRCONST("Host") }, { STRCONST("Upgrade") }, @@ -4861,10 +4870,8 @@ static bool h2_permissible_field(struct dynhds_entry *e) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) { - if(e->namelen < H2_NON_FIELD[i].namelen) - return TRUE; if(e->namelen == H2_NON_FIELD[i].namelen && - curl_strequal(H2_NON_FIELD[i].name, e->name)) + curl_strnequal(H2_NON_FIELD[i].name, e->name, e->namelen)) return FALSE; } return TRUE; diff --git a/lib/http.h b/lib/http.h index 9c25471d3330..ed93d265e308 100644 --- a/lib/http.h +++ b/lib/http.h @@ -83,8 +83,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req); CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, int httpversion, struct dynbuf *req); -CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, - struct dynhds *hds); void Curl_http_to_fold(struct dynbuf *bf); diff --git a/lib/http2.c b/lib/http2.c index 9eb1e0aeaa41..9e755a0e1da5 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -3021,3 +3021,6 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) } #endif /* !CURL_DISABLE_HTTP && USE_NGHTTP2 */ + +/* Do not leak this filter's call_data accessor in unity builds. */ +#undef CF_CTX_CALL_DATA diff --git a/lib/http_proxy.c b/lib/http_proxy.c index fd87c1db1918..373865272bd7 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -33,13 +33,15 @@ #include "cfilters.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" +#include "cf-h3-proxy.h" +#include "cf-capsule.h" #include "connect.h" #include "vauth/vauth.h" #include "curlx/strparse.h" static CURLcode dynhds_add_custom(struct Curl_easy *data, bool is_connect, int httpversion, - struct dynhds *hds) + bool is_udp, struct dynhds *hds) { struct connectdata *conn = data->conn; struct curl_slist *h[2]; @@ -49,10 +51,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, enum Curl_proxy_use proxy; - if(is_connect) + if(is_connect && !is_udp) proxy = HEADER_CONNECT; + else if(is_connect && is_udp) + proxy = HEADER_CONNECT_UDP; else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ? + proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { @@ -72,6 +76,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } /* loop through one or two lists */ @@ -166,15 +176,30 @@ struct cf_proxy_ctx { struct Curl_peer *dest; /* tunnel destination */ uint8_t proxytype; BIT(sub_filter_installed); + BIT(udp_tunnel); }; +static int proxy_http_ver_major(proxy_http_ver ver) +{ + switch(ver) { + case PROXY_HTTP_V1: + return 11; + case PROXY_HTTP_V2: + return 20; + case PROXY_HTTP_V3: + return 30; + } + return 0; +} + CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, struct Curl_peer *dest, - int httpversion) + proxy_http_ver ver) { char *authority = NULL; + int httpversion = proxy_http_ver_major(ver); CURLcode result; struct httpreq *req = NULL; @@ -201,7 +226,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; /* If user is not overriding Host: header, we add for HTTP/1.x */ - if(httpversion < 20 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { result = Curl_dynhds_cadd(&req->headers, "Host", authority); if(result) @@ -223,14 +248,180 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(httpversion < 20 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; } - result = dynhds_add_custom(data, TRUE, httpversion, &req->headers); + result = dynhds_add_custom(data, TRUE, httpversion, + FALSE, &req->headers); + +out: + if(result && req) { + Curl_http_req_free(req); + req = NULL; + } + curlx_free(authority); + *preq = req; + return result; +} + +CURLcode Curl_http_proxy_create_CONNECTUDP(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) +{ + const char *proxy_scheme = "http"; + const char *proxy_host = cf->conn->http_proxy.peer->hostname; + int httpversion = proxy_http_ver_major(ver); + char *authority = NULL; + char *path = NULL; + char *encoded_host = NULL; + struct httpreq *req = NULL; + bool proxy_ipv6_ip; + CURLcode result; + + if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS2 || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS3) + proxy_scheme = "https"; + + proxy_ipv6_ip = cf->conn->http_proxy.peer->ipv6 != 0; + + authority = curl_maprintf("%s%s%s:%d", + proxy_ipv6_ip ? "[" : "", + proxy_host, + proxy_ipv6_ip ? "]" : "", + cf->conn->http_proxy.peer->port); + if(!authority) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(dest->ipv6) { + /* RFC 9298: colons in IPv6 addresses MUST be percent-encoded + * in the URI template (e.g. "2001:db8::1" -> "2001%3Adb8%3A%3A1") */ + const char *s = dest->hostname; + char *d; + size_t hlen = strlen(s); + encoded_host = curlx_malloc(hlen * 3 + 1); + if(!encoded_host) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + d = encoded_host; + while(*s) { + if(*s == ':') { + *d++ = '%'; + *d++ = '3'; + *d++ = 'A'; + } + else + *d++ = *s; + s++; + } + *d = '\0'; + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + encoded_host, (unsigned int)dest->port); + } + else { + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + dest->hostname, (unsigned int)dest->port); + } + + if(!path) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_http_req_make(&req, "GET", sizeof("GET")-1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else if(ver == PROXY_HTTP_V2 || ver == PROXY_HTTP_V3) { + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else { + result = CURLE_FAILED_INIT; + goto out; + } + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, + req->authority, NULL, TRUE); + if(result) + goto out; + + /* If user is not overriding Host: header, we add for HTTP/1.x */ + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + result = Curl_dynhds_cadd(&req->headers, "Host", authority); + if(result) + goto out; + } + + if(data->req.hd_proxy_auth) { + result = Curl_dynhds_h1_cadd_line(&req->headers, + data->req.hd_proxy_auth); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", + data->set.str[STRING_USERAGENT]); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_dynhds_cadd(&req->headers, "Connection", "Upgrade"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Upgrade", "connect-udp"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + else { + result = Curl_dynhds_cadd(&req->headers, ":Protocol", "connect-udp"); + if(result) + goto out; + + if(ver >= PROXY_HTTP_V2) { + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + } + + result = dynhds_add_custom(data, TRUE, httpversion, + TRUE, &req->headers); out: if(result && req) { @@ -238,37 +429,166 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, req = NULL; } curlx_free(authority); + curlx_free(path); + curlx_free(encoded_host); *preq = req; return result; } +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel) +{ + CURLcode result; + + if(udp_tunnel) + result = Curl_http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver); + else + result = Curl_http_proxy_create_CONNECT(preq, cf, data, dest, ver); + if(result) + return result; + + if(udp_tunnel) + infof(data, "Establishing %s proxy UDP tunnel to %s:%s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + data->state.up.hostname, data->state.up.port); + else + infof(data, "Establishing %s proxy tunnel to %s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + (*preq)->authority); + return CURLE_OK; +} + +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult) +{ + struct dynhds_entry *capsule_protocol = NULL; + struct dynhds_entry *auth_reply = NULL; + size_t i, header_count; + CURLcode result = CURLE_OK; + + DEBUGASSERT(resp); + + header_count = Curl_dynhds_count(&resp->headers); + if(udp_tunnel) + infof(data, "CONNECT-UDP Response Status %d", resp->status); + else + infof(data, "CONNECT Response Status %d", resp->status); + infof(data, "Response Headers (%zu total):", header_count); + for(i = 0; i < header_count; i++) { + struct dynhds_entry *entry = Curl_dynhds_getn(&resp->headers, i); + if(entry) + infof(data, " %s: %s", entry->name, entry->value); + } + + if(resp->status == 401) { + auth_reply = Curl_dynhds_cget(&resp->headers, "WWW-Authenticate"); + } + else if(resp->status == 407) { + auth_reply = Curl_dynhds_cget(&resp->headers, "Proxy-Authenticate"); + } + + if(auth_reply) { + CURL_TRC_CF(data, cf, "[0] CONNECT%s: fwd auth header '%s'", + udp_tunnel ? "-UDP" : "", auth_reply->value); + result = Curl_http_input_auth(data, resp->status == 407, + auth_reply->value); + if(result) + return result; + if(data->req.newurl) { + curlx_safefree(data->req.newurl); + *presult = PROXY_INSPECT_AUTH_RETRY; + return CURLE_OK; + } + } + + if(udp_tunnel) { + if(resp->status / 100 == 2) { + capsule_protocol = Curl_dynhds_cget(&resp->headers, + "capsule-protocol"); + if(capsule_protocol) { + if(strncmp(capsule_protocol->value, "?1", 2) == 0 && + !capsule_protocol->value[2]) { + infof(data, "CONNECT-UDP tunnel established, response %d", + resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + failf(data, "Failed to establish CONNECT-UDP tunnel, response %d, " + "unsupported capsule-protocol value '%s'", + resp->status, capsule_protocol->value); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + else { + /* NOTE proxies may not set capsule protocol in the headers */ + infof(data, "CONNECT-UDP tunnel established, response %d " + "but no capsule-protocol header found", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + } + else { + failf(data, "Failed to establish CONNECT-UDP tunnel, " + "response %d", resp->status); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + } + + if(resp->status / 100 == 2) { + infof(data, "CONNECT tunnel established, response %d", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; +} + static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; + const char *tunnel_type; /* Determine tunnel type once and reuse */ + + tunnel_type = ctx->udp_tunnel ? "CONNECT-UDP" : "CONNECT"; if(cf->connected) { *done = TRUE; return CURLE_OK; } - CURL_TRC_CF(data, cf, "connect"); + CURL_TRC_CF(data, cf, "%s", tunnel_type); connect_sub: - result = cf->next->cft->do_connect(cf->next, data, done); - if(result || !*done) - return result; + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } *done = FALSE; if(!ctx->sub_filter_installed) { - const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + const char *alpn = NULL; + + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + } if(alpn) - infof(data, "CONNECT: '%s' negotiated", alpn); + infof(data, "%s: '%s' negotiated", tunnel_type, alpn); else if(!alpn) { /* No ALPN, proxytype rules. Fake ALPN */ - infof(data, "CONNECT: no ALPN negotiated"); + infof(data, "%s: no ALPN negotiated", tunnel_type); switch(ctx->proxytype) { case CURLPROXY_HTTP_1_0: alpn = "http/1.0"; @@ -276,6 +596,9 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, case CURLPROXY_HTTPS2: alpn = "h2"; break; + case CURLPROXY_HTTPS3: + alpn = "h3"; + break; default: alpn = "http/1.1"; break; @@ -284,7 +607,8 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); - result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10, + (bool)ctx->udp_tunnel); if(result) goto out; } @@ -292,20 +616,32 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", httpversion % 10); - result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion, + (bool)ctx->udp_tunnel); if(result) goto out; } #ifdef USE_NGHTTP2 else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); - result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest); + result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest, + (bool)ctx->udp_tunnel); + if(result) + goto out; + } +#endif /* USE_NGHTTP2 */ +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + else if(!strcmp(alpn, "h3")) { + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3"); + result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->dest, + (bool)ctx->udp_tunnel); if(result) goto out; } -#endif +#endif /* USE_PROXY_HTTP3 && USE_NGHTTP3 && USE_NGTCP2 && USE_OPENSSL */ else { - failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn); + failf(data, "%s: negotiated ALPN '%s' not supported", tunnel_type, alpn); result = CURLE_COULDNT_CONNECT; goto out; } @@ -321,6 +657,19 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->sub_filter_installed); + if(ctx->udp_tunnel) { +#ifdef USE_PROXY_HTTP3 + /* Insert capsule filter between us and the protocol sub-filter. + * This handles encap/decap of UDP datagrams in capsule format. */ + result = Curl_cf_capsule_insert_after(cf, data); + if(result) + goto out; + CURL_TRC_CF(data, cf, "installed capsule filter for UDP tunnel"); +#else + result = CURLE_NOT_BUILT_IN; + goto out; +#endif /* USE_PROXY_HTTP3 */ + } result = CURLE_OK; } @@ -404,7 +753,8 @@ struct Curl_cftype Curl_cft_http_proxy = { CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - uint8_t proxytype) + uint8_t proxytype, + bool udp_tunnel) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; @@ -421,6 +771,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, } Curl_peer_link(&ctx->dest, dest); ctx->proxytype = proxytype; + ctx->udp_tunnel = udp_tunnel; result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) diff --git a/lib/http_proxy.h b/lib/http_proxy.h index c122aa6dd87f..b0becedf03f8 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -32,14 +32,47 @@ enum Curl_proxy_use { HEADER_SERVER, /* direct to server */ HEADER_PROXY, /* regular request to proxy */ - HEADER_CONNECT /* sending CONNECT to a proxy */ + HEADER_CONNECT, /* sending CONNECT to a proxy */ + HEADER_CONNECT_UDP /* sending CONNECT-UDP to a proxy */ }; +/* HTTP version for proxy tunnel request creation */ +typedef enum { + PROXY_HTTP_V1 = 1, + PROXY_HTTP_V2 = 2, + PROXY_HTTP_V3 = 3 +} proxy_http_ver; + +/* Result from inspecting a proxy tunnel response */ +typedef enum { + PROXY_INSPECT_OK, /* Tunnel established */ + PROXY_INSPECT_FAILED, /* Tunnel failed */ + PROXY_INSPECT_AUTH_RETRY /* Retry with auth */ +} proxy_inspect_result; + CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, struct Curl_peer *dest, - int httpversion); + proxy_http_ver ver); +CURLcode Curl_http_proxy_create_CONNECTUDP(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver); + +/* Create CONNECT or CONNECT-UDP request */ +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel); + +/* Inspect tunnel response for H2/H3 proxy (capsule-protocol, auth) */ +struct http_resp; +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600 * 1000) @@ -47,13 +80,17 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - uint8_t proxytype); + uint8_t proxytype, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_http_proxy; #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ #define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ - ((t) == CURLPROXY_HTTPS2)) + ((t) == CURLPROXY_HTTPS2) || \ + ((t) == CURLPROXY_HTTPS3)) + +#define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3) #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/lib/peer.c b/lib/peer.c index 43e5aef0f040..5dd3aad372fa 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -536,6 +536,37 @@ CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, #define UNIX_SOCKET_PREFIX "localhost" #endif +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, const char *url) +{ + if(!scheme) + return CURLE_OK; + + if(curl_strequal("https", scheme)) { + if(*proxytype != CURLPROXY_HTTPS2 && *proxytype != CURLPROXY_HTTPS3) + *proxytype = CURLPROXY_HTTPS; + } + else if(curl_strequal("socks5h", scheme)) + *proxytype = CURLPROXY_SOCKS5_HOSTNAME; + else if(curl_strequal("socks5", scheme)) + *proxytype = CURLPROXY_SOCKS5; + else if(curl_strequal("socks4a", scheme)) + *proxytype = CURLPROXY_SOCKS4A; + else if(curl_strequal("socks4", scheme) || curl_strequal("socks", scheme)) + *proxytype = CURLPROXY_SOCKS4; + else if(curl_strequal("http", scheme)) { + if(*proxytype != CURLPROXY_HTTP_1_0) + *proxytype = CURLPROXY_HTTP; + } + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", url); + return CURLE_COULDNT_CONNECT; + } + return CURLE_OK; +} + CURLcode Curl_peer_from_proxy_url(CURLU *uh, struct Curl_easy *data, const char *url, @@ -570,6 +601,7 @@ CURLcode Curl_peer_from_proxy_url(CURLU *uh, break; case CURLPROXY_HTTPS: case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: pp.scheme = &Curl_scheme_https; break; case CURLPROXY_SOCKS4: @@ -592,29 +624,9 @@ CURLcode Curl_peer_from_proxy_url(CURLU *uh, } else { pp.scheme = Curl_get_scheme(scheme); - if(pp.scheme == &Curl_scheme_https) { - proxytype = (proxytype != CURLPROXY_HTTPS2) ? - CURLPROXY_HTTPS : CURLPROXY_HTTPS2; - } - else if(pp.scheme == &Curl_scheme_socks5h) - proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(pp.scheme == &Curl_scheme_socks5) - proxytype = CURLPROXY_SOCKS5; - else if(pp.scheme == &Curl_scheme_socks4a) - proxytype = CURLPROXY_SOCKS4A; - else if((pp.scheme == &Curl_scheme_socks4) || - (pp.scheme == &Curl_scheme_socks)) - proxytype = CURLPROXY_SOCKS4; - else if(pp.scheme == &Curl_scheme_http) { - proxytype = (uint8_t)((proxytype != CURLPROXY_HTTP_1_0) ? - CURLPROXY_HTTP : CURLPROXY_HTTP_1_0); - } - else { - /* Any other xxx:// reject! */ - failf(data, "Unsupported proxy scheme for \'%s\'", url); - result = CURLE_COULDNT_CONNECT; + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, url); + if(result) goto out; - } } DEBUGASSERT(pp.scheme); diff --git a/lib/peer.h b/lib/peer.h index daa01db8ff65..7946735a23e5 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -94,6 +94,11 @@ CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, + const char *url); + CURLcode Curl_peer_from_proxy_url(CURLU *uh, struct Curl_easy *data, const char *url, diff --git a/lib/setopt.c b/lib/setopt.c index 2e08a310ebdc..e67a3c8beb64 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1042,8 +1042,12 @@ static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, case CURLOPT_PROXYAUTH: return httpauth(data, TRUE, (unsigned long)arg); case CURLOPT_PROXYTYPE: - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_HTTPS3)) return CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef USE_PROXY_HTTP3 + if(arg == CURLPROXY_HTTPS3) + return CURLE_NOT_BUILT_IN; +#endif s->proxytype = (unsigned char)arg; break; case CURLOPT_SOCKS5_AUTH: diff --git a/lib/url.c b/lib/url.c index 796d35e2296a..93a5f14f07e4 100644 --- a/lib/url.c +++ b/lib/url.c @@ -99,6 +99,7 @@ #include "headers.h" #include "curlx/strerr.h" #include "curlx/strparse.h" +#include "peer.h" /* Now for the protocols */ #include "ftp.h" @@ -1316,7 +1317,12 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #endif conn->ip_version = data->set.ipver; conn->bits.connect_only = (bool)data->set.connect_only; - conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ +#ifndef CURL_DISABLE_PROXY + if(conn->http_proxy.proxytype == CURLPROXY_HTTPS3) + conn->transport_wanted = TRNSPRT_QUIC; + else +#endif + conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ if(data->set.str[STRING_DEVICE]) { @@ -1793,6 +1799,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, { char *proxyuser = NULL; char *proxypasswd = NULL; + char *scheme = NULL; CURLcode result = CURLE_OK; /* Set the start proxy type for url scheme guessing */ uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; @@ -1807,7 +1814,21 @@ static CURLcode parse_proxy(struct Curl_easy *data, these made up ones for proxies. Guess scheme for URLs without it. */ uc = curl_url_set(uhp, CURLUPART_URL, proxy, CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); - if(uc) { + if(!uc) { + /* parsed okay as a URL - only update proxytype when scheme was explicit */ + uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME); + if(!uc) { + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy); + if(result) + goto error; + } + else if(uc != CURLUE_NO_SCHEME) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* else: no explicit scheme, keep the configured proxytype */ + } + else { failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, curl_url_strerror(uc)); result = CURLE_COULDNT_RESOLVE_PROXY; @@ -1824,6 +1845,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, case CURLPROXY_HTTP_1_0: case CURLPROXY_HTTPS: case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: if(for_pre_proxy) { failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); result = CURLE_COULDNT_RESOLVE_PROXY; @@ -1878,6 +1900,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, proxyinfo->proxytype = proxytype; error: + curlx_free(scheme); curlx_free(proxyuser); curlx_free(proxypasswd); curl_url_cleanup(uhp); diff --git a/lib/version.c b/lib/version.c index b3b0a46abbb1..d5870333abf1 100644 --- a/lib/version.c +++ b/lib/version.c @@ -491,6 +491,9 @@ static const struct feat features_table[] = { #ifdef USE_NTLM FEATURE("NTLM", NULL, CURL_VERSION_NTLM), #endif +#ifdef USE_PROXY_HTTP3 + FEATURE("PROXY-HTTP3", NULL, 0), +#endif #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index fb7fd618893d..6cafda2da05c 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -72,6 +72,7 @@ #define QUIC_MAX_STREAMS (256 * 1024) #define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) +#define QUIC_TUNNEL_INBUF_SIZE (64 * 1024) /* We announce a small window size in transport param to the server, * and grow that immediately to max when no rate limit is in place. @@ -95,6 +96,7 @@ #define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) #define H3_STREAM_SEND_CHUNKS \ (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 /* * Store ngtcp2 version info in this buffer. @@ -139,6 +141,8 @@ struct cf_ngtcp2_ctx { is accepted by peer */ CURLcode tls_vrfy_result; /* result of TLS peer verification */ int qlogfd; + unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ + size_t tunnel_inbuf_len; BIT(initialized); BIT(tls_handshake_complete); /* TLS handshake is done */ BIT(use_earlydata); /* Using 0RTT data */ @@ -156,6 +160,8 @@ static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) { DEBUGASSERT(!ctx->initialized); ctx->qlogfd = -1; + ctx->tunnel_inbuf = NULL; + ctx->tunnel_inbuf_len = 0; ctx->version = NGTCP2_PROTO_VER_MAX; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); @@ -173,6 +179,8 @@ static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) curlx_dyn_free(&ctx->scratch); Curl_uint32_hash_destroy(&ctx->streams); Curl_ssl_peer_cleanup(&ctx->peer); + curlx_safefree(ctx->tunnel_inbuf); + ctx->tunnel_inbuf_len = 0; } curlx_free(ctx); } @@ -493,7 +501,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, struct Curl_easy *data); -static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) +static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; @@ -863,7 +871,7 @@ static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ ngtcp2_crypto_recv_crypto_data_cb, - cf_ngtcp2_handshake_completed, + cb_ngtcp2_handshake_completed, NULL, /* recv_version_negotiation */ ngtcp2_crypto_encrypt_cb, ngtcp2_crypto_decrypt_cb, @@ -982,6 +990,11 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, if(!ctx->qconn) return CURLE_OK; + if(ctx->q.sockfd == CURL_SOCKET_BAD) { + /* Tunneled QUIC, no direct socket - delegate to next filter */ + return cf->next->cft->adjust_pollset(cf->next, data, ps); + } + Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) want_send = TRUE; @@ -1904,8 +1917,72 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, rctx.pktx = pktx; rctx.pkt_count = 0; - return vquic_recv_packets(cf, data, &ctx->q, 1000, + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + return vquic_recv_packets(cf, data, &ctx->q, 1000, cf_ngtcp2_recv_pkts, &rctx); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + unsigned char *buf; + size_t max_udp_payload = QUIC_TUNNEL_INBUF_SIZE; + size_t pkt_limit = QUIC_TUNNEL_INGRESS_PKT_LIMIT; + size_t nread; + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + + if(ctx->qconn) { + size_t max_path_payload; + max_path_payload = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + if(max_path_payload > max_udp_payload) + max_udp_payload = max_path_payload; + } + + if(ctx->tunnel_inbuf_len < max_udp_payload) { + unsigned char *newbuf = + (unsigned char *)curlx_realloc(ctx->tunnel_inbuf, max_udp_payload); + if(!newbuf) + return CURLE_OUT_OF_MEMORY; + ctx->tunnel_inbuf = newbuf; + ctx->tunnel_inbuf_len = max_udp_payload; + } + buf = ctx->tunnel_inbuf; + + while(pkt_limit--) { + result = Curl_conn_cf_recv(cf->next, data, (char *)buf, + ctx->tunnel_inbuf_len, &nread); + if(result == CURLE_AGAIN) { + /* no more data available at the moment */ + return CURLE_OK; + } + if(result) { + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", + result); + return result; + } + if(nread == 0) { + /* tunnel closed */ + return CURLE_OK; + } + + memcpy(&remote_addr, ctx->connected_path.remote.addr, + ctx->connected_path.remote.addrlen); + remote_addrlen = (socklen_t)ctx->connected_path.remote.addrlen; + result = cf_ngtcp2_recv_pkts(buf, nread, nread, &remote_addr, + remote_addrlen, 0, &rctx); + if(result) + return result; + + if(!ctx->q.got_first_byte) { + ctx->q.got_first_byte = TRUE; + ctx->q.first_byte_at = ctx->q.last_op; + } + ctx->q.last_io = ctx->q.last_op; + } + return CURLE_OK; + } } /** @@ -2189,6 +2266,7 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) } ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); + Curl_ssl_peer_cleanup(&ctx->peer); vquic_ctx_free(&ctx->q); if(ctx->h3conn) { nghttp3_conn_del(ctx->h3conn); @@ -2220,6 +2298,12 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, return CURLE_OK; } + if(!cf->next) { + Curl_bufq_reset(&ctx->q.sendbuf); + *done = TRUE; + return CURLE_OK; + } + CF_DATA_SAVE(save, cf, data); *done = FALSE; pktx_init(&pktx, cf, data); @@ -2648,30 +2732,81 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, if(result) return result; - if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) - return CURLE_QUIC_CONNECT_ERROR; - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; + /* Query socket and remote address from sub-chain */ + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) { + /* No direct socket - must be tunneled QUIC (CONNECT-UDP through proxy) */ + ctx->q.sockfd = CURL_SOCKET_BAD; + } + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get local address for ngtcp2 */ + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + } + else { + /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address + from the connected filter below */ + const struct Curl_sockaddr_ex *remote = NULL; + if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&remote))) + return CURLE_QUIC_CONNECT_ERROR; + if(!remote) + return CURLE_QUIC_CONNECT_ERROR; + + memset(&ctx->q.local_addr, 0, sizeof(ctx->q.local_addr)); + switch(remote->family) { + case AF_INET: + ((struct sockaddr_in *)&ctx->q.local_addr)->sin_family = AF_INET; + ctx->q.local_addrlen = sizeof(struct sockaddr_in); + break; +#ifdef USE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)&ctx->q.local_addr)->sin6_family = AF_INET6; + ctx->q.local_addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return CURLE_QUIC_CONNECT_ERROR; + } - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &remote->curl_sa_addr, + (socklen_t)remote->addrlen); - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + } result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, cf_ngtcp2_tls_ctx_setup, &ctx->tls, @@ -2720,8 +2855,8 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, return CURLE_OK; } - /* Connect the UDP filter first */ - if(!cf->next->connected) { + /* Connect the sub-chain */ + if(cf->next && !cf->next->connected) { result = Curl_conn_cf_connect(cf->next, data, done); if(result || !*done) return result; @@ -2803,11 +2938,14 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, #ifdef CURLVERBOSE if(result) { - struct ip_quadruple ip; + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get IP info for error reporting */ + struct ip_quadruple ip; - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } } #endif if(!result && ctx->qconn) { @@ -3003,4 +3141,33 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, return result; } +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_ngtcp2_ctx_init(ctx); + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + cf->conn = cf_at->conn; +out: + if(result) { + curlx_safefree(cf); + cf_ngtcp2_ctx_free(ctx); + } + return result; +} + #endif + +/* Do not leak this filter's call_data accessor in unity builds. */ +#undef CF_CTX_CALL_DATA diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/curl_ngtcp2.h index 185272ace030..d69ae08eaec5 100644 --- a/lib/vquic/curl_ngtcp2.h +++ b/lib/vquic/curl_ngtcp2.h @@ -54,6 +54,8 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr); + +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at); #endif #endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 73f664a65344..43a16958a6ff 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -156,6 +156,7 @@ static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) quiche_config_free(ctx->cfg); ctx->cfg = NULL; } + Curl_ssl_peer_cleanup(&ctx->peer); } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index ad4c713fa9b1..00366b7d309a 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -72,6 +72,8 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, return CURLE_FAILED_INIT; #endif (void)session_reuse_cb; + if(peer->dest) + Curl_ssl_peer_cleanup(peer); result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC); if(result) return result; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index cf4bc5a65fe8..9ac657c2910b 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -261,6 +261,44 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, return result; } +/* Split QUIC payload by datagram (gso) boundaries when sending over a + * non-UDP lower filter (for example CONNECT-UDP proxy tunnel). */ +static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent, len; + CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); + + *psent = 0; + + /* Send one datagram-sized chunk per call into the lower filter. */ + for(p = pkt; p < end; p += len) { + len = CURLMIN(gsolen, (size_t)(end - p)); + result = Curl_conn_cf_send(cf->next, data, p, len, FALSE, &sent); + /* Report forward progress even if we return CURLE_AGAIN later. */ + *psent += sent; + VERBOSE(++calls); + /* Preserve lower-filter errors (including CURLE_AGAIN). */ + if(result) + goto out; + if(sent < len) { + /* We need whole datagrams here. Partial accept means blocked. */ + result = CURLE_AGAIN; + goto out; + } + } + +out: + CURL_TRC_CF(data, cf, "vquic_cf_send(len=%zu, gso=%zu, calls=%zu)" + " -> %d, sent=%zu", + pktlen, gsolen, calls, result, *psent); + return result; +} + static CURLcode vquic_send_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -310,7 +348,22 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len; } - result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + if(qctx->sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + if(gsolen && (blen > gsolen)) { + /* Send one datagram at a time to preserve packet boundaries. */ + result = send_packet_no_gso_cf(cf, data, buf, blen, gsolen, &sent); + } + else { + /* No GSO aggregate to split, regular lower-filter send is enough. */ + result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &sent); + } + } + if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -699,6 +752,16 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_insert_after(cf_at); +#else + (void)cf_at; + return CURLE_NOT_BUILT_IN; +#endif +} + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, @@ -737,10 +800,6 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, failf(data, "HTTP/3 is not supported over a SOCKS proxy"); return CURLE_URL_MALFORMAT; } - if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over an HTTP proxy"); - return CURLE_URL_MALFORMAT; - } #endif return CURLE_OK; diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 1f0a1ab5e51b..59178acd9405 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -39,6 +39,8 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, size_t scidlen, int *qlogfdp); +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at); + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index b4a0f9684f01..fde151590b93 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3713,8 +3713,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; } - if(data->set.fdebug && data->set.verbose) { - /* the SSL trace callback is only used for verbose logging */ + if(data->set.fdebug && data->set.verbose && + (peer->transport != TRNSPRT_QUIC)) { + /* the SSL trace callback is only used for verbose logging; + * QUIC connections use a different TLS record format that + * ossl_trace cannot handle */ SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); } @@ -4007,12 +4010,20 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + char tls_id[80]; BIO *bio; CURLcode result; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(octx); + if(!connssl->peer.dest) { + Curl_ossl_version(tls_id, sizeof(tls_id)); + result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); + if(result) + return result; + } + result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, connssl->alpn, NULL, NULL, ossl_new_session_cb, cf, diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index e7dbad09c618..d640df4f0311 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1197,6 +1197,7 @@ void Curl_ssl_peer_cleanup(struct ssl_peer *peer) Curl_peer_unlink(&peer->dest); curlx_safefree(peer->sni); curlx_safefree(peer->scache_key); + peer->transport = TRNSPRT_NONE; peer->type = CURL_SSL_PEER_DNS; } @@ -1206,6 +1207,8 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl) { connssl->ssl_impl->close(cf, data); connssl->state = ssl_connection_none; + connssl->connecting_state = ssl_connect_1; + connssl->prefs_checked = FALSE; Curl_ssl_peer_cleanup(&connssl->peer); } cf->connected = FALSE; diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 6700ee74cbe1..a0d8159a5879 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -133,9 +133,6 @@ struct ssl_connect_data { BIT(input_pending); /* data for SSL_read() may be available */ }; -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data - /* Definitions for SSL Implementations */ struct Curl_ssl { @@ -209,3 +206,9 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ + +#ifdef USE_SSL +/* Restore the default SSL filter call_data accessor for unity builds. */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data +#endif diff --git a/src/tool_getparam.c b/src/tool_getparam.c index a7458a3b5fcf..7e776ea6b645 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -250,6 +250,7 @@ static const struct LongShort aliases[]= { {"proxy-digest", ARG_BOOL, ' ', C_PROXY_DIGEST}, {"proxy-header", ARG_STRG, ' ', C_PROXY_HEADER}, {"proxy-http2", ARG_BOOL, ' ', C_PROXY_HTTP2}, + {"proxy-http3", ARG_BOOL, ' ', C_PROXY_HTTP3}, {"proxy-insecure", ARG_BOOL, ' ', C_PROXY_INSECURE}, {"proxy-key", ARG_FILE|ARG_TLS, ' ', C_PROXY_KEY}, {"proxy-key-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_KEY_TYPE}, @@ -2024,6 +2025,18 @@ static ParameterError opt_bool(struct OperationConfig *config, config->proxyver = toggle ? CURLPROXY_HTTPS2 : CURLPROXY_HTTPS; break; + case C_PROXY_HTTP3: /* --proxy-http3 */ +#ifndef USE_PROXY_HTTP3 + if(toggle) + return PARAM_LIBCURL_DOESNT_SUPPORT; + config->proxyver = CURLPROXY_HTTPS; +#else + if(!feature_httpsproxy || !feature_http3) + return PARAM_LIBCURL_DOESNT_SUPPORT; + + config->proxyver = toggle ? CURLPROXY_HTTPS3 : CURLPROXY_HTTPS; +#endif + break; case C_APPEND: /* --append */ config->ftp_append = toggle; break; @@ -2895,7 +2908,8 @@ static ParameterError opt_string(struct OperationConfig *config, case C_PROXY: /* --proxy */ /* --proxy */ err = getstr(&config->proxy, nextarg, ALLOW_BLANK); - if(config->proxyver != CURLPROXY_HTTPS2) + if(config->proxyver != CURLPROXY_HTTPS2 && + config->proxyver != CURLPROXY_HTTPS3) config->proxyver = CURLPROXY_HTTP; break; case C_REQUEST: /* --request */ diff --git a/src/tool_getparam.h b/src/tool_getparam.h index e137cc322f98..32476d377691 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -201,6 +201,7 @@ typedef enum { C_PROXY_DIGEST, C_PROXY_HEADER, C_PROXY_HTTP2, + C_PROXY_HTTP3, C_PROXY_INSECURE, C_PROXY_KEY, C_PROXY_KEY_TYPE, diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 864771bfba88..c0b0af792f9e 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -542,6 +542,9 @@ const struct helptxt helptext[] = { { " --proxy-http2", "Use HTTP/2 with HTTPS proxy", CURLHELP_HTTP | CURLHELP_PROXY }, + { " --proxy-http3", + "Use HTTP/3 with HTTPS proxy", + CURLHELP_HTTP | CURLHELP_PROXY }, { " --proxy-insecure", "Skip HTTPS proxy cert verification", CURLHELP_PROXY | CURLHELP_TLS }, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 78779a55188e..4887a3594a38 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -289,6 +289,8 @@ test3216 test3217 test3218 test3219 test3220 \ \ test3300 test3301 test3302 test3303 test3304 \ \ +test3400 \ +\ test4000 test4001 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 data320.html \ diff --git a/tests/data/test3400 b/tests/data/test3400 new file mode 100644 index 000000000000..12d014bffd21 --- /dev/null +++ b/tests/data/test3400 @@ -0,0 +1,19 @@ + + + + +unittest +capsule + + + + + +unittest + + +capsule protocol encode and decode unit tests + + + + diff --git a/tests/http/CMakeLists.txt b/tests/http/CMakeLists.txt index 3373f8af2b39..9801d51907c5 100644 --- a/tests/http/CMakeLists.txt +++ b/tests/http/CMakeLists.txt @@ -28,6 +28,12 @@ if(NOT CADDY) endif() mark_as_advanced(CADDY) +find_program(H2O "h2o") # /usr/local/bin/h2o +if(NOT H2O) + set(H2O "") +endif() +mark_as_advanced(H2O) + find_program(VSFTPD "vsftpd") # /usr/sbin/vsftpd if(NOT VSFTPD) set(VSFTPD "") diff --git a/tests/http/Makefile.am b/tests/http/Makefile.am index f4dc92f61b06..7232c1e8aa15 100644 --- a/tests/http/Makefile.am +++ b/tests/http/Makefile.am @@ -31,6 +31,7 @@ TESTENV = \ testenv/dnsd.py \ testenv/dante.py \ testenv/env.py \ + testenv/h2o.py \ testenv/httpd.py \ testenv/mod_curltest/mod_curltest.c \ testenv/nghttpx.py \ @@ -72,6 +73,7 @@ EXTRA_DIST = \ test_40_socks.py \ test_50_scp.py \ test_51_sftp.py \ + test_60_h3_proxy.py \ $(TESTENV) clean-local: diff --git a/tests/http/config.ini.in b/tests/http/config.ini.in index 78808e966db9..daf9869b7cda 100644 --- a/tests/http/config.ini.in +++ b/tests/http/config.ini.in @@ -44,3 +44,6 @@ danted = @DANTED@ [sshd] sshd = @SSHD@ sftpd = @SFTPD@ + +[h2o] +h2o = @H2O@ diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 08da73ac0fdf..0de5c1a8b9e0 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -1,4 +1,4 @@ -#*************************************************************************** +# *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | @@ -31,9 +31,10 @@ import pytest from testenv.env import EnvConfig -sys.path.append(os.path.join(os.path.dirname(__file__), '.')) +sys.path.append(os.path.join(os.path.dirname(__file__), ".")) from testenv import Env, Httpd, Nghttpx, NghttpxFwd, NghttpxQuic, Sshd +from testenv.h2o import H2oProxy, H2oServer log = logging.getLogger(__name__) @@ -42,51 +43,47 @@ def pytest_report_header(config): # Env inits its base properties only once, we can report them here env = Env() report = [ - f'Testing curl {env.curl_version()}', - f' platform: {platform.platform()}', - f' curl: Version: {env.curl_version_string()}', - f' curl: Features: {env.curl_features_string()}', - f' curl: Protocols: {env.curl_protocols_string()}', - f' httpd: {env.httpd_version()}', - f' httpd-proxy: {env.httpd_version()}' + f"Testing curl {env.curl_version()}", + f" platform: {platform.platform()}", + f" curl: Version: {env.curl_version_string()}", + f" curl: Features: {env.curl_features_string()}", + f" curl: Protocols: {env.curl_protocols_string()}", + f" httpd: {env.httpd_version()}", + f" httpd-proxy: {env.httpd_version()}", ] if env.have_h3(): - report.extend([ - f' nghttpx: {env.nghttpx_version()}' - ]) + report.extend([f" nghttpx: {env.nghttpx_version()}"]) + if env.have_h2o(): + report.extend([f" h2o: {env.h2o_version()}"]) if env.has_caddy(): - report.extend([ - f' Caddy: {env.caddy_version()}' - ]) + report.extend([f" Caddy: {env.caddy_version()}"]) if env.has_vsftpd(): - report.extend([ - f' VsFTPD: {env.vsftpd_version()}' - ]) - buildinfo_fn = os.path.join(env.build_dir, 'buildinfo.txt') + report.extend([f" VsFTPD: {env.vsftpd_version()}"]) + buildinfo_fn = os.path.join(env.build_dir, "buildinfo.txt") if os.path.exists(buildinfo_fn): - with open(buildinfo_fn, 'r') as file_in: + with open(buildinfo_fn, "r") as file_in: for line in file_in: line = line.strip() - if line and not line.startswith('#'): + if line and not line.startswith("#"): report.extend([line]) - return '\n'.join(report) + return "\n".join(report) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def env_config(pytestconfig, testrun_uid, worker_id) -> EnvConfig: - return EnvConfig(pytestconfig=pytestconfig, - testrun_uid=testrun_uid, - worker_id=worker_id) + return EnvConfig( + pytestconfig=pytestconfig, testrun_uid=testrun_uid, worker_id=worker_id + ) -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def env(pytestconfig, env_config) -> Env: env = Env(pytestconfig=pytestconfig, env_config=env_config) level = logging.DEBUG if env.verbose > 0 else logging.INFO - logging.getLogger('').setLevel(level=level) - if not env.curl_has_protocol('http'): + logging.getLogger("").setLevel(level=level) + if not env.curl_has_protocol("http"): pytest.skip("curl built without HTTP support") - if not env.curl_has_protocol('https'): + if not env.curl_has_protocol("https"): pytest.skip("curl built without HTTPS support") if env.setup_incomplete(): pytest.skip(env.incomplete_reason()) @@ -95,23 +92,23 @@ def env(pytestconfig, env_config) -> Env: return env -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def httpd(env) -> Generator[Httpd, None, None]: httpd = Httpd(env=env) if not httpd.exists(): - pytest.skip(f'httpd not found: {env.httpd}') + pytest.skip(f"httpd not found: {env.httpd}") httpd.clear_logs() assert httpd.initial_start() yield httpd httpd.stop() -@pytest.fixture(scope='session') -def nghttpx(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: +@pytest.fixture(scope="session") +def nghttpx(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]: nghttpx = NghttpxQuic(env=env) if nghttpx.exists(): if not nghttpx.supports_h3() and env.have_h3_curl(): - log.warning('nghttpx does not support QUIC, but curl does') + log.warning("nghttpx does not support QUIC, but curl does") nghttpx.clear_logs() assert nghttpx.initial_start() yield nghttpx @@ -120,8 +117,8 @@ def nghttpx(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: yield False -@pytest.fixture(scope='session') -def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: +@pytest.fixture(scope="session") +def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]: nghttpx = NghttpxFwd(env=env) if nghttpx.exists(): nghttpx.clear_logs() @@ -132,37 +129,63 @@ def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: yield False -@pytest.fixture(scope='session') -def sshd(env: Env) -> Generator[Union[Sshd,bool], None, None]: +@pytest.fixture(scope="session") +def sshd(env: Env) -> Generator[Union[Sshd, bool], None, None]: if env.has_sshd(): sshd = Sshd(env=env) - assert sshd.initial_start(), f'{sshd.dump_log()}' + assert sshd.initial_start(), f"{sshd.dump_log()}" yield sshd sshd.stop() else: yield False -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def configures_httpd(env, httpd) -> Generator[bool, None, None]: # include this fixture as test parameter if the test configures httpd itself yield True -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def configures_nghttpx(env, httpd) -> Generator[bool, None, None]: # include this fixture as test parameter if the test configures nghttpx itself yield True -@pytest.fixture(autouse=True, scope='function') +@pytest.fixture(autouse=True, scope="function") def server_reset(request, env, httpd, nghttpx): # make sure httpd is in default configuration when a test starts - if 'configures_httpd' not in request.node._fixtureinfo.argnames: + if "configures_httpd" not in request.node._fixtureinfo.argnames: httpd.reset_config() httpd.reload_if_config_changed() - if env.have_h3() and \ - 'nghttpx' in request.node._fixtureinfo.argnames and \ - 'configures_nghttpx' not in request.node._fixtureinfo.argnames: + if ( + env.have_h3() + and "nghttpx" in request.node._fixtureinfo.argnames + and "configures_nghttpx" not in request.node._fixtureinfo.argnames + ): nghttpx.reset_config() nghttpx.reload_if_config_changed() + + +@pytest.fixture(scope="session") +def h2o_server(env) -> Generator[Union[H2oServer, bool], None, None]: + h2o = H2oServer(env=env) + if env.have_h2o(): + h2o.clear_logs() + assert h2o.initial_start() + yield h2o + h2o.stop() + else: + yield False + + +@pytest.fixture(scope="session") +def h2o_proxy(env) -> Generator[Union[H2oProxy, bool], None, None]: + h2o = H2oProxy(env=env) + if env.have_h2o(): + h2o.clear_logs() + assert h2o.initial_start() + yield h2o + h2o.stop() + else: + yield False diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py new file mode 100644 index 000000000000..def32a6fe775 --- /dev/null +++ b/tests/http/test_60_h3_proxy.py @@ -0,0 +1,689 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# *************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# +import os +import subprocess +import time + +import pytest +from testenv import CurlClient, Env + +MARK_NEEDS_HTTPS_PROXY = pytest.mark.skipif( + condition=not Env.curl_has_feature("HTTPS-proxy"), + reason="curl lacks HTTPS-proxy support" +) +MARK_NEEDS_HTTP3 = pytest.mark.skipif( + condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" +) +MARK_NEEDS_PROXY_HTTP3 = pytest.mark.skipif( + condition=not Env.curl_has_feature("PROXY-HTTP3"), + reason="curl lacks experimental HTTP/3 proxy support" +) +MARK_NEEDS_NGHTTP3 = pytest.mark.skipif( + condition=not Env.curl_uses_lib("nghttp3"), reason="only supported with nghttp3" +) +MARK_NEEDS_NGHTTP2 = pytest.mark.skipif( + condition=not Env.curl_uses_lib("nghttp2"), reason="only supported with nghttp2" +) +MARK_NEEDS_H2O = pytest.mark.skipif( + condition=not Env.have_h2o(), reason="no h2o available" +) +MARK_NEEDS_NGHTTPX = pytest.mark.skipif( + condition=not Env.have_nghttpx(), reason="no nghttpx available" +) + +H3_PROXY_COMMON_MARKS = [ + MARK_NEEDS_HTTPS_PROXY, + MARK_NEEDS_HTTP3, + MARK_NEEDS_PROXY_HTTP3, + MARK_NEEDS_NGHTTP3, +] + +NGTCP2_ONLY_MSG = "only supported with the ngtcp2 quic stack" +UNSUPPORTED_OPT_MSG = "does not support this" +H2O_HELLO_MSG = '"message": "Hello from h2o HTTP/3 server"' + + +def _require_available(**items): + missing = [name for name, value in items.items() if not value] + if missing: + pytest.skip(f"{' or '.join(missing)} not available") + + +def _download_path(curl: CurlClient) -> str: + return os.path.join(curl.run_dir, "download_#1.data") + + +def _check_download_message(curl: CurlClient, expected: str): + dpath = _download_path(curl) + assert os.path.exists(dpath), f"Download file not found: {dpath}" + with open(dpath, "r") as fd: + content = fd.read() + assert expected in content, f"Unexpected response content: {content}" + + +def _check_download_size(curl: CurlClient, expected_size: int): + dpath = _download_path(curl) + assert os.path.exists(dpath), f"Download file not found: {dpath}" + actual = os.path.getsize(dpath) + assert actual == expected_size, f"expected {expected_size}B download, got {actual}B" + + +def _nghttpx_proxy_args( + env: Env, + nghttpx, + proxy_proto: str, + tunnel: bool, + insecure: bool = False, +): + xargs = [ + "--proxy", + f"https://{env.proxy_domain}:{nghttpx._port}/", + "--resolve", + f"{env.proxy_domain}:{nghttpx._port}:127.0.0.1", + "--proxy-cacert", + env.ca.cert_file, + ] + if proxy_proto == "h3": + xargs.append("--proxy-http3") + elif proxy_proto == "h2": + xargs.append("--proxy-http2") + + if tunnel: + xargs.append("--proxytunnel") + + xargs.extend(["--cacert", env.ca.cert_file, "--proxy-insecure"]) + if insecure: + xargs.append("--insecure") + return xargs + + +def _h2o_proxy_args( + env: Env, + h2o_proxy, + proxy_proto: str, + tunnel: bool, + insecure: bool = False, +): + if proxy_proto == "h3": + pport = h2o_proxy.port + elif proxy_proto == "h2": + pport = h2o_proxy.h2_port + else: + pport = h2o_proxy.h1_port + + xargs = [ + "--proxy", + f"https://{env.proxy_domain}:{pport}/", + "--resolve", + f"{env.proxy_domain}:{pport}:127.0.0.1", + "--proxy-cacert", + env.ca.cert_file, + ] + if proxy_proto == "h2": + xargs.append("--proxy-http2") + elif proxy_proto == "h3": + xargs.append("--proxy-http3") + + if tunnel: + xargs.append("--proxytunnel") + + xargs.extend(["--cacert", env.ca.cert_file, "--proxy-insecure"]) + if insecure: + xargs.append("--insecure") + return xargs + + +class TestH3ProxySuccess: + """Success matrix for HTTP/3 proxy CONNECT / CONNECT-UDP.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + @pytest.mark.parametrize( + ["alpn_proto", "proxy_proto"], + [ + pytest.param("http/1.1", "h3", id="h1_over_h3_proxytunnel"), + pytest.param( + "h2", + "h3", + marks=MARK_NEEDS_NGHTTP2, + id="h2_over_h3_proxytunnel", + ), + pytest.param("h3", "h3", id="h3_over_h3_proxytunnel"), + pytest.param( + "h3", + "h2", + marks=MARK_NEEDS_NGHTTP2, + id="h3_over_h2_proxytunnel", + ), + pytest.param("h3", "http/1.1", id="h3_over_h1_proxytunnel"), + ], + ) + def test_60_01_connect_tunnel( + self, + env: Env, + h2o_server, + h2o_proxy, + alpn_proto, + proxy_proto, + ): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args( + env, h2o_proxy, proxy_proto, tunnel=True, insecure=True + ) + + r = curl.http_download( + urls=[url], alpn_proto=alpn_proto, with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_message(curl, H2O_HELLO_MSG) + + +class TestH3ProxyFailure: + """Failure matrix when proxy side does not support requested mode.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_NGHTTPX] + + @pytest.mark.parametrize( + ["alpn_proto", "proxy_proto", "exp_err"], + [ + pytest.param( + "http/1.1", + "h3", + "could not connect to server", + id="fail_h1_over_h3_proxytunnel", + ), + pytest.param( + "h2", + "h3", + "could not connect to server", + marks=MARK_NEEDS_NGHTTP2, + id="fail_h2_over_h3_proxytunnel", + ), + pytest.param( + "h3", + "h3", + "could not connect to server", + id="fail_h3_over_h3_proxytunnel", + ), + pytest.param( + "h3", + "h2", + "connect-udp response status 400", + marks=MARK_NEEDS_NGHTTP2, + id="fail_h3_over_h2_proxytunnel", + ), + pytest.param( + "h3", + "http/1.1", + "connect-udp tunnel failed, response 404", + id="fail_h3_over_h1_proxytunnel", + ), + ], + ) + def test_60_02_connect_tunnel_fail( + self, + env: Env, + httpd, + nghttpx, + alpn_proto, + proxy_proto, + exp_err, + ): + _require_available(httpd=httpd, nghttpx=nghttpx) + + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _nghttpx_proxy_args(env, nghttpx, proxy_proto, tunnel=True) + r = curl.http_download( + urls=[url], alpn_proto=alpn_proto, with_stats=True, extra_args=proxy_args + ) + assert r.exit_code != 0, f"Expected failure but curl succeeded: {r}" + assert exp_err in r.stderr.lower(), ( + f"Expected protocol/proxy error but got: {r.stderr}" + ) + + +class TestH3ProxyModeSelection: + """Behavior checks for tunnel vs non-tunnel proxy mode selection.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_NGHTTPX] + + @pytest.mark.parametrize( + ["proxy_proto"], + [ + pytest.param("h3", id="proxy_h3"), + pytest.param("h2", marks=MARK_NEEDS_NGHTTP2, id="proxy_h2"), + pytest.param("http/1.1", id="proxy_h1"), + ], + ) + def test_60_03_h3_target_auto_connect_udp( + self, env: Env, httpd, nghttpx, proxy_proto + ): + _require_available(httpd=httpd, nghttpx=nghttpx) + + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _nghttpx_proxy_args( + env, nghttpx, proxy_proto, tunnel=False + ) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + + # An HTTP/3 target auto-triggers CONNECT-UDP even without --proxytunnel, + # just as HTTPS targets auto-trigger CONNECT. nghttpx does not support + # CONNECT-UDP so this fails, which confirms auto-CONNECT-UDP is active. + assert r.exit_code != 0, ( + "expected failure: h3 target auto-triggers CONNECT-UDP " + "which nghttpx does not support" + ) + assert "connect-udp" in r.stderr.lower(), ( + f"expected CONNECT-UDP attempt in output, got: {r.stderr}" + ) + + +class TestH3ProxyRuntimeGuards: + """Guard checks for unsupported HTTP/3 proxy options.""" + + pytestmark = [ + MARK_NEEDS_HTTPS_PROXY, + MARK_NEEDS_PROXY_HTTP3, + pytest.mark.skipif( + condition=Env.curl_uses_lib("ngtcp2"), + reason="guard only applies to non-ngtcp2 builds", + ), + ] + + @pytest.mark.skipif( + condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" + ) + def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = [ + "--proxy", + "https://127.0.0.1:1/", + "--proxy-http3", + "--proxytunnel", + "--proxy-insecure", + "--cacert", + env.ca.cert_file, + ] + + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + if not env.curl_has_feature("PROXY-HTTP3"): + r.check_exit_code(2) + assert UNSUPPORTED_OPT_MSG in r.stderr.lower(), ( + f"Expected unsupported option failure but got: {r.stderr}" + ) + return + + r.check_exit_code(1) + assert NGTCP2_ONLY_MSG in r.stderr.lower(), ( + f"Expected ngtcp2 guard failure but got: {r.stderr}" + ) + + +class TestH3ProxyRobustness: + """Robustness checks for shutdown and proxy loss during transfer.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + doc_root = os.path.join(env.gen_dir, "docs") + env.make_data_file( + indir=doc_root, fname="proxy-drop-20m", fsize=20 * 1024 * 1024 + ) + + def test_60_05_graceful_shutdown( + self, env: Env, h2o_server, h2o_proxy + ): + if not env.curl_is_debug(): + pytest.skip("needs debug curl for shutdown trace lines") + if not env.curl_is_verbose(): + pytest.skip("needs verbose-strings curl build") + + curl = CurlClient(env=env, run_env={"CURL_DEBUG": "all"}) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = curl.get_proxy_args(proto="h3", tunnel=True) + proxy_args.extend(["--cacert", env.ca.cert_file, "--insecure"]) + + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + + shutdown_lines = [ + line + for line in r.trace_lines + if ("start shutdown(" in line.lower()) + or ("shutdown completely sent off" in line.lower()) + ] + assert shutdown_lines, f"No shutdown trace lines found:\n{r.stderr}" + + def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + + proxy_port = h2o_proxy.port + url = f"https://localhost:{h2o_server.port}/proxy-drop-20m" + out_path = os.path.join(env.gen_dir, "proxy-drop.out") + args = [ + env.curl, + "--http1.1", + "--proxy", + f"https://{env.proxy_domain}:{proxy_port}/", + "--resolve", + f"{env.proxy_domain}:{proxy_port}:127.0.0.1", + "--proxy-cacert", + env.ca.cert_file, + "--proxy-http3", + "--proxytunnel", + "--proxy-insecure", + "--cacert", + env.ca.cert_file, + "--limit-rate", + "100k", + "--max-time", + "20", + "-o", + out_path, + url, + ] + + proc = None + try: + proc = subprocess.Popen( + args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + time.sleep(1.0) + assert h2o_proxy.stop(), "failed to stop h2o proxy" + _, stderr = proc.communicate(timeout=30) + assert proc.returncode != 0, ( + "curl should fail when proxy is terminated mid-transfer" + ) + serr = stderr.lower() + assert ( + "failed" in serr + or "transfer closed" in serr + or "recv failure" in serr + or "connection" in serr + ), f"Unexpected error output: {stderr}" + finally: + if proc and (proc.poll() is None): + proc.kill() + proc.wait(timeout=5) + assert h2o_proxy.start(), "failed to restart h2o proxy" + + +class TestH3ProxyDataTransfer: + """Large file transfers and multiplexing through HTTP/3 proxy.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + doc_root = os.path.join(env.gen_dir, "docs") + env.make_data_file(indir=doc_root, fname="download-1m", fsize=1 * 1024 * 1024) + env.make_data_file(indir=doc_root, fname="download-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-2m", fsize=2 * 1024 * 1024) + + def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/download-10m" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_size(curl, 10 * 1024 * 1024) + + def test_60_08_large_upload(self, env: Env, httpd, h2o_server, h2o_proxy): + _require_available(h2o_proxy=h2o_proxy) + fdata = os.path.join(env.gen_dir, "upload-2m") + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/curltest/echo?id=[0-0]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r = curl.http_upload( + urls=[url], + data=f"@{fdata}", + alpn_proto="http/1.1", + with_stats=True, + extra_args=proxy_args, + ) + r.check_response(count=1, http_status=200) + + def test_60_09_parallel_downloads(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + count = 5 + curl = CurlClient(env=env) + urln = f"https://localhost:{h2o_server.port}/download-1m?[0-{count - 1}]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args.extend(["--parallel", "--parallel-max", f"{count}"]) + r = curl.http_download( + urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=count, http_status=200) + + +class TestH3ProxyConnectionManagement: + """Proxy authentication, connection reuse, and session resumption.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + def test_60_10_proxy_basic_auth(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args.extend(["--proxy-user", "testuser:testpass"]) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_message(curl, H2O_HELLO_MSG) + + def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + urln = f"https://localhost:{h2o_server.port}/data.json?[0-2]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r = curl.http_download( + urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=3, http_status=200) + assert r.total_connects <= 3, ( + f"expected proxy connection reuse, got {r.total_connects} connects" + ) + + def test_60_12_quic_session_resumption(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + # First request establishes QUIC session + curl1 = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r1 = curl1.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r1.check_response(count=1, http_status=200) + # Second request from a fresh CurlClient; session may be reused + # by the TLS session cache if supported + curl2 = CurlClient(env=env) + r2 = curl2.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r2.check_response(count=1, http_status=200) + # Third request from a fresh CurlClient; session may be reused + # by the TLS session cache if supported + curl3 = CurlClient(env=env) + r3 = curl3.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r3.check_response(count=1, http_status=200) + + +class TestH3ProxyUdpTunnel: + """CONNECT-UDP tunnel payload size and capsule-protocol tests.""" + + pytestmark = H3_PROXY_COMMON_MARKS + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + doc_root = os.path.join(env.gen_dir, "docs") + env.make_data_file(indir=doc_root, fname="download-1400", fsize=1400) + env.make_data_file(indir=doc_root, fname="download-1m", fsize=1 * 1024 * 1024) + env.make_data_file(indir=doc_root, fname="download-10m", fsize=10 * 1024 * 1024) + + @MARK_NEEDS_H2O + @pytest.mark.parametrize( + "fname,fsize", + [ + ("download-1400", 1400), + ("download-1m", 1 * 1024 * 1024), + ("download-10m", 10 * 1024 * 1024), + ], + ) + def test_60_13_udp_tunnel_payload_sizes( + self, env: Env, h2o_server, h2o_proxy, fname, fsize + ): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/{fname}" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_size(curl, fsize) + + @MARK_NEEDS_NGHTTPX + def test_60_14_udp_tunnel_capsule_absent(self, env: Env, httpd, nghttpx): + _require_available(httpd=httpd, nghttpx=nghttpx) + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _nghttpx_proxy_args(env, nghttpx, "h3", tunnel=True) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + assert r.exit_code != 0, ( + "expected failure: nghttpx does not support CONNECT-UDP / Capsule-Protocol" + ) + + +class TestH3ProxyEdgeCases: + """Timeout and protocol-mismatch edge cases.""" + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + def test_60_15_connect_timeout(self, env: Env, h2o_server): + _require_available(h2o_server=h2o_server) + curl = CurlClient(env=env, timeout=15) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = [ + "--proxy", + "https://192.0.2.1:1/", + "--proxy-http3", + "--proxytunnel", + "--proxy-insecure", + "--connect-timeout", + "3", + "--cacert", + env.ca.cert_file, + ] + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + assert r.exit_code != 0, "expected timeout connecting to unreachable proxy" + assert r.duration.total_seconds() < 10, ( + f"timeout not respected: took {r.duration.total_seconds():.1f}s" + ) + + @MARK_NEEDS_NGHTTP2 + def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): + _require_available(httpd=httpd, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + # h2 inner traffic always uses CONNECT (TCP), never CONNECT-UDP, + # even through an HTTP/3 proxy with --proxytunnel. h2o supports + # CONNECT TCP tunneling, so this request succeeds. + r = curl.http_download( + urls=[url], alpn_proto="h2", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + + +class TestH3ProxyHappyEyeballs: + """ + Verify that happy eyeballs is active for HTTP/3 proxy connections. + + With the H3-PROXY filter sitting above HAPPY-EYEBALLS -> UDP, address + family selection to the proxy is done by happy eyeballs. + """ + + pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + + def test_60_17_h3_proxy_happy_eyeballs_filter_present(self, env: Env, h2o_server, h2o_proxy): + """Verbose trace confirms HAPPY-EYEBALLS filter is in the H3 proxy chain.""" + if not env.curl_is_debug(): + pytest.skip("needs debug curl for filter trace") + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env, run_env={"CURL_DEBUG": "HAPPY-EYEBALLS,H3-PROXY"}) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + assert "happy-eyeballs" in r.stderr.lower(), ( + f"expected HAPPY-EYEBALLS trace for H3 proxy, got: {r.stderr}" + ) + + @MARK_NEEDS_NGHTTP2 + def test_60_18_h3_proxy_ipv4_all_proto(self, env: Env, h2o_server, h2o_proxy): + """IPv4-forced H3 proxy works for h1/h2/h3 inner protocols.""" + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + for alpn_proto in ["http/1.1", "h2", "h3"]: + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args( + env, h2o_proxy, "h3", tunnel=True, insecure=True + ) + proxy_args.append("--ipv4") + r = curl.http_download( + urls=[url], + alpn_proto=alpn_proto, + with_stats=True, + extra_args=proxy_args, + ) + r.check_response(count=1, http_status=200) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 99aa649bc0fd..272b6045cbf8 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -688,7 +688,12 @@ def get_proxy_args(self, proto: str = 'http/1.1', proxy_name = '[::1]' if use_ipv6 else \ self._server_addr if use_ip else self.env.proxy_domain if proxys: - pport = self.env.pts_port(proto) if tunnel else self.env.proxys_port + if tunnel: + pport = self.env.pts_port(proto) + elif proto == 'h3': + pport = self.env.h3proxys_port + else: + pport = self.env.proxys_port xargs = [ '--proxy', f'https://{proxy_name}:{pport}/', '--proxy-cacert', self.env.ca.cert_file, @@ -697,6 +702,8 @@ def get_proxy_args(self, proto: str = 'http/1.1', xargs.extend(['--resolve', f'{proxy_name}:{pport}:{self._server_addr}']) if proto == 'h2': xargs.append('--proxy-http2') + elif proto == 'h3': + xargs.append('--proxy-http3') else: xargs = [ '--proxy', f'http://{proxy_name}:{self.env.proxy_port}/', diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index c7bbfc4c5461..a2032f82ce5f 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -#*************************************************************************** +# *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | @@ -54,20 +54,21 @@ def init_config_from(conf_path): TESTS_HTTPD_PATH = os.path.dirname(os.path.dirname(__file__)) PROJ_PATH = os.path.dirname(os.path.dirname(TESTS_HTTPD_PATH)) TOP_PATH = os.path.join(os.getcwd(), os.path.pardir) -CONFIG_PATH = os.path.join(TOP_PATH, 'tests', 'http', 'config.ini') +CONFIG_PATH = os.path.join(TOP_PATH, "tests", "http", "config.ini") if not os.path.exists(CONFIG_PATH): - ALT_CONFIG_PATH = os.path.join(PROJ_PATH, 'tests', 'http', 'config.ini') + ALT_CONFIG_PATH = os.path.join(PROJ_PATH, "tests", "http", "config.ini") if not os.path.exists(ALT_CONFIG_PATH): - raise Exception(f'unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}') + raise Exception( + f"unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}" + ) TOP_PATH = PROJ_PATH CONFIG_PATH = ALT_CONFIG_PATH DEF_CONFIG = init_config_from(CONFIG_PATH) -CURL = os.path.join(TOP_PATH, 'src', 'curl') -CURLINFO = os.path.join(TOP_PATH, 'src', 'curlinfo') +CURL = os.path.join(TOP_PATH, "src", "curl") +CURLINFO = os.path.join(TOP_PATH, "src", "curlinfo") class NghttpxUtil: - CMD = None VERSION_FULL = None @@ -76,34 +77,37 @@ def version(cls, cmd): if cmd is None: return None if cls.VERSION_FULL is None or cmd != cls.CMD: - p = subprocess.run(args=[cmd, '--version'], - capture_output=True, text=True) + p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{cmd} --version failed with exit code: {p.returncode}') + raise RuntimeError( + f"{cmd} --version failed with exit code: {p.returncode}" + ) cls.CMD = cmd for line in p.stdout.splitlines(keepends=False): - if line.startswith('nghttpx '): + if line.startswith("nghttpx "): cls.VERSION_FULL = line if cls.VERSION_FULL is None: - raise RuntimeError(f'{cmd}: unable to determine version') + raise RuntimeError(f"{cmd}: unable to determine version") return cls.VERSION_FULL @staticmethod def version_with_h3(version): - return re.match(r'.* ngtcp2/\d+\.\d+\.\d+.*', version) is not None + return re.match(r".* ngtcp2/\d+\.\d+\.\d+.*", version) is not None class EnvConfig: - - def __init__(self, pytestconfig: Optional[pytest.Config] = None, - testrun_uid=None, - worker_id=None): + def __init__( + self, + pytestconfig: Optional[pytest.Config] = None, + testrun_uid=None, + worker_id=None, + ): self.pytestconfig = pytestconfig self.testrun_uid = testrun_uid - self.worker_id = worker_id if worker_id is not None else 'master' + self.worker_id = worker_id if worker_id is not None else "master" self.tests_dir = TESTS_HTTPD_PATH - self.gen_root = self.gen_dir = os.path.join(self.tests_dir, 'gen') - if self.worker_id != 'master': + self.gen_root = self.gen_dir = os.path.join(self.tests_dir, "gen") + if self.worker_id != "master": self.gen_dir = os.path.join(self.gen_dir, self.worker_id) self.project_dir = os.path.dirname(os.path.dirname(self.tests_dir)) self.build_dir = TOP_PATH @@ -111,57 +115,56 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, # check cur and its features self.curl = CURL self.curlinfo = CURLINFO - if 'CURL' in os.environ: - self.curl = os.environ['CURL'] + if "CURL" in os.environ: + self.curl = os.environ["CURL"] self.curl_props = { - 'version_string': '', - 'version': '', - 'os': '', - 'fullname': '', - 'features_string': '', - 'features': set(), - 'protocols_string': '', - 'protocols': set(), - 'libs': set(), - 'lib_versions': set(), + "version_string": "", + "version": "", + "os": "", + "fullname": "", + "features_string": "", + "features": set(), + "protocols_string": "", + "protocols": set(), + "libs": set(), + "lib_versions": set(), } self.curl_is_debug = False self.curl_protos = [] - p = subprocess.run(args=[self.curl, '-V'], - capture_output=True, text=True) + p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{self.curl} -V failed with exit code: {p.returncode}') - if p.stderr.startswith('WARNING:'): + raise RuntimeError(f"{self.curl} -V failed with exit code: {p.returncode}") + if p.stderr.startswith("WARNING:"): self.curl_is_debug = True for line in p.stdout.splitlines(keepends=False): - if line.startswith('curl '): - self.curl_props['version_string'] = line - m = re.match(r'^curl (?P\S+) (?P\S+) (?P.*)$', line) + if line.startswith("curl "): + self.curl_props["version_string"] = line + m = re.match(r"^curl (?P\S+) (?P\S+) (?P.*)$", line) if m: - self.curl_props['fullname'] = m.group(0) - self.curl_props['version'] = m.group('version') - self.curl_props['os'] = m.group('os') - self.curl_props['lib_versions'] = { - lib.lower() for lib in m.group('libs').split(' ') + self.curl_props["fullname"] = m.group(0) + self.curl_props["version"] = m.group("version") + self.curl_props["os"] = m.group("os") + self.curl_props["lib_versions"] = { + lib.lower() for lib in m.group("libs").split(" ") } - self.curl_props['libs'] = { - re.sub(r'/[a-z0-9.-]*', '', lib) for lib in self.curl_props['lib_versions'] + self.curl_props["libs"] = { + re.sub(r"/[a-z0-9.-]*", "", lib) + for lib in self.curl_props["lib_versions"] } - if line.startswith('Features: '): - self.curl_props['features_string'] = line[10:] - self.curl_props['features'] = { - feat.lower() for feat in line[10:].split(' ') + if line.startswith("Features: "): + self.curl_props["features_string"] = line[10:] + self.curl_props["features"] = { + feat.lower() for feat in line[10:].split(" ") } - if line.startswith('Protocols: '): - self.curl_props['protocols_string'] = line[11:] - self.curl_props['protocols'] = { - prot.lower() for prot in line[11:].split(' ') + if line.startswith("Protocols: "): + self.curl_props["protocols_string"] = line[11:] + self.curl_props["protocols"] = { + prot.lower() for prot in line[11:].split(" ") } - p = subprocess.run(args=[self.curlinfo], - capture_output=True, text=True) + p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{self.curlinfo} failed with exit code: {p.returncode}') + raise RuntimeError(f"{self.curlinfo} failed with exit code: {p.returncode}") self.curl_is_verbose = 'verbose-strings: ON' in p.stdout self.curl_can_cert_status = 'cert-status: ON' in p.stdout self.curl_override_dns = 'override-dns: ON' in p.stdout @@ -169,18 +172,18 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self.ports = {} - self.httpd = self.config['httpd']['httpd'] - self.apxs = self.config['httpd']['apxs'] + self.httpd = self.config["httpd"]["httpd"] + self.apxs = self.config["httpd"]["apxs"] if len(self.apxs) == 0: self.apxs = None self._httpd_version = None self.examples_pem = { - 'key': 'xxx', - 'cert': 'xxx', + "key": "xxx", + "cert": "xxx", } - self.htdocs_dir = os.path.join(self.gen_dir, 'htdocs') - self.tld = 'http.curl.se' + self.htdocs_dir = os.path.join(self.gen_dir, "htdocs") + self.tld = "http.curl.se" self.domain1 = f"one.{self.tld}" self.domain1brotli = f"brotli.one.{self.tld}" self.domain2 = f"two.{self.tld}" @@ -188,22 +191,43 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self.proxy_domain = f"proxy.{self.tld}" self.expired_domain = f"expired.{self.tld}" self.cert_specs = [ - CertificateSpec(domains=[self.domain1, self.domain1brotli, 'localhost', '127.0.0.1'], key_type='rsa2048'), - CertificateSpec(name='domain1-no-ip', domains=[self.domain1, self.domain1brotli], key_type='rsa2048'), - CertificateSpec(name='domain1-very-bad', domains=[self.domain1, 'dns:127.0.0.1'], key_type='rsa2048'), - CertificateSpec(domains=[self.domain2], key_type='rsa2048'), - CertificateSpec(domains=[self.ftp_domain], key_type='rsa2048'), - CertificateSpec(domains=[self.proxy_domain, '127.0.0.1'], key_type='rsa2048'), - CertificateSpec(domains=[self.expired_domain], key_type='rsa2048', - valid_from=timedelta(days=-100), valid_to=timedelta(days=-10)), - CertificateSpec(name="clientsX", sub_specs=[ - CertificateSpec(name="user1", client=True), - ]), + CertificateSpec( + domains=[self.domain1, self.domain1brotli, "localhost", "127.0.0.1"], + key_type="rsa2048", + ), + CertificateSpec( + name="domain1-no-ip", + domains=[self.domain1, self.domain1brotli], + key_type="rsa2048", + ), + CertificateSpec( + name="domain1-very-bad", + domains=[self.domain1, "dns:127.0.0.1"], + key_type="rsa2048", + ), + CertificateSpec(domains=[self.domain2], key_type="rsa2048"), + CertificateSpec(domains=[self.ftp_domain], key_type="rsa2048"), + CertificateSpec( + domains=[self.proxy_domain, "127.0.0.1"], key_type="rsa2048" + ), + CertificateSpec( + domains=[self.expired_domain], + key_type="rsa2048", + valid_from=timedelta(days=-100), + valid_to=timedelta(days=-10), + ), + CertificateSpec( + name="clientsX", + sub_specs=[ + CertificateSpec(name="user1", client=True), + ], + ), ] - self.openssl = 'openssl' - p = subprocess.run(args=[self.openssl, 'version'], - capture_output=True, text=True) + self.openssl = "openssl" + p = subprocess.run( + args=[self.openssl, "version"], capture_output=True, text=True + ) if p.returncode != 0: # no openssl in path self.openssl = None @@ -211,7 +235,7 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, else: self.openssl_version = p.stdout.strip() - self.nghttpx = self.config['nghttpx']['nghttpx'] + self.nghttpx = self.config["nghttpx"]["nghttpx"] if len(self.nghttpx.strip()) == 0: self.nghttpx = None self._nghttpx_version = None @@ -220,30 +244,58 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self._nghttpx_version = NghttpxUtil.version(self.nghttpx) self.nghttpx_with_h3 = NghttpxUtil.version_with_h3(self._nghttpx_version) - self.caddy = self.config['caddy']['caddy'] + self.caddy = self.config["caddy"]["caddy"] self._caddy_version = None if len(self.caddy.strip()) == 0: self.caddy = None + + self.h2o = self.config["h2o"]["h2o"] + if len(self.h2o.strip()) == 0: + self.h2o = None + self._h2o_version = None + if self.h2o is not None: + try: + p = subprocess.run( + args=[self.h2o, "--version"], capture_output=True, text=True + ) + if p.returncode != 0: + # not a working h2o + self.h2o = None + else: + # h2o --version output format: "h2o version 2.3.0" + m = re.search(r"h2o version (\S+)", p.stdout) + if m: + self._h2o_version = m.group(1) + else: + self.h2o = None + except Exception: + log.exception("checking h2o version") + self.h2o = None + if self.caddy is not None: - p = subprocess.run(args=[self.caddy, 'version'], - capture_output=True, text=True) + p = subprocess.run( + args=[self.caddy, "version"], capture_output=True, text=True + ) if p.returncode != 0: # not a working caddy self.caddy = None - m = re.match(r'v?(\d+\.\d+\.\d+).*', p.stdout) + m = re.match(r"v?(\d+\.\d+\.\d+).*", p.stdout) if m: self._caddy_version = m.group(1) else: - raise RuntimeError(f'Unable to determine caddy version from: {p.stdout}') + raise RuntimeError( + f"Unable to determine caddy version from: {p.stdout}" + ) - self.vsftpd = self.config['vsftpd']['vsftpd'] - if self.vsftpd == '': + self.vsftpd = self.config["vsftpd"]["vsftpd"] + if self.vsftpd == "": self.vsftpd = None self._vsftpd_version = None if self.vsftpd is not None: - with tempfile.TemporaryFile('w+') as tmp: - p = subprocess.run(args=[self.vsftpd, '-v'], - capture_output=True, text=True, stdin=tmp) + with tempfile.TemporaryFile("w+") as tmp: + p = subprocess.run( + args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp + ) if p.returncode != 0: # not a working vsftpd self.vsftpd = None @@ -256,80 +308,83 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, # any data there instead. tmp.seek(0) ver_text = tmp.read() - m = re.match(r'vsftpd: version (\d+\.\d+\.\d+)', ver_text) + m = re.match(r"vsftpd: version (\d+\.\d+\.\d+)", ver_text) if m: self._vsftpd_version = m.group(1) elif len(p.stderr) == 0: # vsftp does not use stdout or stderr for printing its version... -.- - self._vsftpd_version = 'unknown' + self._vsftpd_version = "unknown" else: - raise Exception(f'Unable to determine VsFTPD version from: {p.stderr}') + raise Exception(f"Unable to determine VsFTPD version from: {p.stderr}") - self.danted = self.config['danted']['danted'] - if self.danted == '': + self.danted = self.config["danted"]["danted"] + if self.danted == "": self.danted = None self._danted_version = None if self.danted is not None: - p = subprocess.run(args=[self.danted, '-v'], - capture_output=True, text=True) + p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True) assert p.returncode == 0 if p.returncode != 0: # not a working vsftpd self.danted = None - m = re.match(r'^Dante v(\d+\.\d+\.\d+).*', p.stdout) + m = re.match(r"^Dante v(\d+\.\d+\.\d+).*", p.stdout) if not m: - m = re.match(r'^Dante v(\d+\.\d+\.\d+).*', p.stderr) + m = re.match(r"^Dante v(\d+\.\d+\.\d+).*", p.stderr) if m: self._danted_version = m.group(1) else: self.danted = None - raise Exception(f'Unable to determine danted version from: {p.stderr}') + raise Exception(f"Unable to determine danted version from: {p.stderr}") - self.sshd = self.config['sshd']['sshd'] - if self.sshd == '': + self.sshd = self.config["sshd"]["sshd"] + if self.sshd == "": self.sshd = None self._sshd_version = None if self.sshd is not None: - p = subprocess.run(args=[self.sshd, '-V'], - capture_output=True, text=True) + p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True) assert p.returncode == 0 if p.returncode != 0: self.sshd = None else: - m = re.match(r'^OpenSSH_(\d+\.\d+.*),.*', p.stderr) - assert m, f'version: {p.stderr}' + m = re.match(r"^OpenSSH_(\d+\.\d+.*),.*", p.stderr) + assert m, f"version: {p.stderr}" if m: self._sshd_version = m.group(1) else: self.sshd = None - raise Exception(f'Unable to determine sshd version from: {p.stderr}') + raise Exception( + f"Unable to determine sshd version from: {p.stderr}" + ) if self.sshd: - self.sftpd = self.config['sshd']['sftpd'] - if self.sftpd == '': + self.sftpd = self.config["sshd"]["sftpd"] + if self.sftpd == "": self.sftpd = None else: self.sftpd = None - self._tcpdump = shutil.which('tcpdump') + self._tcpdump = shutil.which("tcpdump") @property def httpd_version(self): if self._httpd_version is None and self.apxs is not None: try: - p = subprocess.run(args=[self.apxs, '-q', 'HTTPD_VERSION'], - capture_output=True, text=True) + p = subprocess.run( + args=[self.apxs, "-q", "HTTPD_VERSION"], + capture_output=True, + text=True, + ) if p.returncode != 0: - log.error(f'{self.apxs} failed to query HTTPD_VERSION: {p}') + log.error(f"{self.apxs} failed to query HTTPD_VERSION: {p}") else: self._httpd_version = p.stdout.strip() except Exception: - log.exception(f'{self.apxs} failed to run') + log.exception(f"{self.apxs} failed to run") return self._httpd_version def versiontuple(self, v): - v = re.sub(r'(\d+\.\d+(\.\d+)?)(-\S+)?', r'\1', v) - return tuple(map(int, v.split('.'))) + v = re.sub(r"(\d+\.\d+(\.\d+)?)(-\S+)?", r"\1", v) + return tuple(map(int, v.split("."))) def httpd_is_at_least(self, minv): if self.httpd_version is None: @@ -344,15 +399,17 @@ def caddy_is_at_least(self, minv): return hv >= self.versiontuple(minv) def is_complete(self) -> bool: - return os.path.isfile(self.httpd) and \ - self.apxs is not None and \ - os.path.isfile(self.apxs) + return ( + os.path.isfile(self.httpd) + and self.apxs is not None + and os.path.isfile(self.apxs) + ) def get_incomplete_reason(self) -> Optional[str]: if self.httpd is None or len(self.httpd.strip()) == 0: - return 'httpd not configured, see `--with-test-httpd=`' + return "httpd not configured, see `--with-test-httpd=`" if not os.path.isfile(self.httpd): - return f'httpd ({self.httpd}) not found' + return f"httpd ({self.httpd}) not found" if self.apxs is None: return "command apxs not found (commonly provided in apache2-dev)" if not os.path.isfile(self.apxs): @@ -371,18 +428,21 @@ def caddy_version(self): def vsftpd_version(self): return self._vsftpd_version + @property + def h2o_version(self): + return self._h2o_version + @property def tcpdmp(self) -> Optional[str]: return self._tcpdump def clear_locks(self): - ca_lock = os.path.join(self.gen_root, 'ca/ca.lock') + ca_lock = os.path.join(self.gen_root, "ca/ca.lock") if os.path.exists(ca_lock): os.remove(ca_lock) class Env: - SERVER_TIMEOUT = 30 # seconds to wait for server to come up/reload CONFIG = EnvConfig() @@ -407,98 +467,106 @@ def have_nghttpx() -> bool: def have_h3_server() -> bool: return Env.CONFIG.nghttpx_with_h3 + @staticmethod + def have_h2o() -> bool: + return Env.CONFIG.h2o is not None + @staticmethod def have_ssl_curl() -> bool: - return Env.curl_has_feature('ssl') or Env.curl_has_feature('multissl') + return Env.curl_has_feature("ssl") or Env.curl_has_feature("multissl") @staticmethod def have_h2_curl() -> bool: - return 'http2' in Env.CONFIG.curl_props['features'] + return "http2" in Env.CONFIG.curl_props["features"] @staticmethod def have_h3_curl() -> bool: - return 'http3' in Env.CONFIG.curl_props['features'] + return "http3" in Env.CONFIG.curl_props["features"] @staticmethod def have_compressed_curl() -> bool: - return 'brotli' in Env.CONFIG.curl_props['libs'] or \ - 'zlib' in Env.CONFIG.curl_props['libs'] or \ - 'zstd' in Env.CONFIG.curl_props['libs'] + return ( + "brotli" in Env.CONFIG.curl_props["libs"] + or "zlib" in Env.CONFIG.curl_props["libs"] + or "zstd" in Env.CONFIG.curl_props["libs"] + ) @staticmethod def curl_uses_lib(libname: str) -> bool: - return libname.lower() in Env.CONFIG.curl_props['libs'] + return libname.lower() in Env.CONFIG.curl_props["libs"] @staticmethod def curl_uses_any_libs(libs: List[str]) -> bool: for libname in libs: - if libname.lower() in Env.CONFIG.curl_props['libs']: + if libname.lower() in Env.CONFIG.curl_props["libs"]: return True return False @staticmethod def curl_uses_ossl_quic() -> bool: if Env.have_h3_curl(): - return not Env.curl_uses_lib('ngtcp2') and Env.curl_uses_lib('nghttp3') + return not Env.curl_uses_lib("ngtcp2") and Env.curl_uses_lib("nghttp3") return False @staticmethod def curl_version_string() -> str: - return Env.CONFIG.curl_props['version_string'] + return Env.CONFIG.curl_props["version_string"] @staticmethod def curl_features_string() -> str: - return Env.CONFIG.curl_props['features_string'] + return Env.CONFIG.curl_props["features_string"] @staticmethod def curl_has_feature(feature: str) -> bool: - return feature.lower() in Env.CONFIG.curl_props['features'] + return feature.lower() in Env.CONFIG.curl_props["features"] @staticmethod def curl_protocols_string() -> str: - return Env.CONFIG.curl_props['protocols_string'] + return Env.CONFIG.curl_props["protocols_string"] @staticmethod def curl_has_protocol(protocol: str) -> bool: - return protocol.lower() in Env.CONFIG.curl_props['protocols'] + return protocol.lower() in Env.CONFIG.curl_props["protocols"] @staticmethod def curl_lib_version(libname: str) -> str: - prefix = f'{libname.lower()}/' - for lversion in Env.CONFIG.curl_props['lib_versions']: + prefix = f"{libname.lower()}/" + for lversion in Env.CONFIG.curl_props["lib_versions"]: if lversion.startswith(prefix): - return lversion[len(prefix):] - return 'unknown' + return lversion[len(prefix) :] + return "unknown" @staticmethod def curl_lib_version_at_least(libname: str, min_version) -> bool: lversion = Env.curl_lib_version(libname) - if lversion != 'unknown': - return Env.CONFIG.versiontuple(min_version) <= \ - Env.CONFIG.versiontuple(lversion) + if lversion != "unknown": + return Env.CONFIG.versiontuple(min_version) <= Env.CONFIG.versiontuple( + lversion + ) return False @staticmethod def curl_lib_version_before(libname: str, lib_version) -> bool: lversion = Env.curl_lib_version(libname) - if lversion != 'unknown': - if m := re.match(r'(\d+\.\d+\.\d+).*', lversion): + if lversion != "unknown": + if m := re.match(r"(\d+\.\d+\.\d+).*", lversion): lversion = m.group(1) - return Env.CONFIG.versiontuple(lib_version) > \ - Env.CONFIG.versiontuple(lversion) + return Env.CONFIG.versiontuple(lib_version) > Env.CONFIG.versiontuple( + lversion + ) return False @staticmethod def curl_os() -> str: - return Env.CONFIG.curl_props['os'] + return Env.CONFIG.curl_props["os"] @staticmethod def curl_fullname() -> str: - return Env.CONFIG.curl_props['fullname'] + return Env.CONFIG.curl_props["fullname"] @staticmethod def curl_version() -> str: - return Env.CONFIG.curl_props['version'] + return Env.CONFIG.curl_props["version"] @staticmethod def curl_is_debug() -> bool: @@ -528,32 +596,31 @@ def curl_can_early_data() -> bool: @staticmethod def curl_can_h3_early_data() -> bool: - return Env.curl_can_early_data() and \ - Env.curl_uses_lib('ngtcp2') + return Env.curl_can_early_data() and Env.curl_uses_lib("ngtcp2") @staticmethod def http_protos() -> List[str]: # http protocols we can test if Env.have_h2_curl(): if Env.have_h3(): - return ['http/1.1', 'h2', 'h3'] - return ['http/1.1', 'h2'] - return ['http/1.1'] + return ["http/1.1", "h2", "h3"] + return ["http/1.1", "h2"] + return ["http/1.1"] @staticmethod def http_h1_h2_protos() -> List[str]: # http 1+2 protocols we can test if Env.have_h2_curl(): - return ['http/1.1', 'h2'] - return ['http/1.1'] + return ["http/1.1", "h2"] + return ["http/1.1"] @staticmethod def http_mplx_protos() -> List[str]: # http multiplexing protocols we can test if Env.have_h2_curl(): if Env.have_h3(): - return ['h2', 'h3'] - return ['h2'] + return ["h2", "h3"] + return ["h2"] return [] @staticmethod @@ -572,6 +639,10 @@ def nghttpx_version() -> str: def caddy_version() -> str: return Env.CONFIG.caddy_version + @staticmethod + def h2o_version() -> str: + return Env.CONFIG.h2o_version + @staticmethod def caddy_is_at_least(minv) -> bool: return Env.CONFIG.caddy_is_at_least(minv) @@ -611,21 +682,20 @@ def tcpdump() -> Optional[str]: def __init__(self, pytestconfig=None, env_config=None): if env_config: Env.CONFIG = env_config - self._verbose = pytestconfig.option.verbose \ - if pytestconfig is not None else 0 + self._verbose = pytestconfig.option.verbose if pytestconfig is not None else 0 self._ca = None self._test_timeout = 300.0 if self._verbose > 1 else 60.0 # seconds def issue_certs(self): if self._ca is None: # ca_dir = os.path.join(self.CONFIG.gen_root, 'ca') - ca_dir = os.path.join(self.gen_dir, 'ca') + ca_dir = os.path.join(self.gen_dir, "ca") os.makedirs(ca_dir, exist_ok=True) - lock_file = os.path.join(ca_dir, 'ca.lock') + lock_file = os.path.join(ca_dir, "ca.lock") with FileLock(lock_file): - self._ca = TestCA.create_root(name=self.CONFIG.tld, - store_dir=ca_dir, - key_type="rsa2048") + self._ca = TestCA.create_root( + name=self.CONFIG.tld, store_dir=ca_dir, key_type="rsa2048" + ) self._ca.issue_certs(self.CONFIG.cert_specs) if self.have_openssl(): self._ca.create_hashdir(self.openssl) @@ -714,19 +784,19 @@ def update_ports(self, ports: Dict[str, int]): @property def http_port(self) -> int: - return self.CONFIG.ports.get('http', 0) + return self.CONFIG.ports.get("http", 0) @property def https_port(self) -> int: - return self.CONFIG.ports['https'] + return self.CONFIG.ports["https"] @property def https_only_tcp_port(self) -> int: - return self.CONFIG.ports['https-tcp-only'] + return self.CONFIG.ports["https-tcp-only"] @property def nghttpx_https_port(self) -> int: - return self.CONFIG.ports['nghttpx_https'] + return self.CONFIG.ports["nghttpx_https"] @property def h3_port(self) -> int: @@ -734,27 +804,35 @@ def h3_port(self) -> int: @property def proxy_port(self) -> int: - return self.CONFIG.ports['proxy'] + return self.CONFIG.ports["proxy"] @property def proxys_port(self) -> int: - return self.CONFIG.ports['proxys'] + return self.CONFIG.ports["proxys"] @property def ftp_port(self) -> int: - return self.CONFIG.ports['ftp'] + return self.CONFIG.ports["ftp"] @property def ftps_port(self) -> int: - return self.CONFIG.ports['ftps'] + return self.CONFIG.ports["ftps"] @property def h2proxys_port(self) -> int: - return self.CONFIG.ports['h2proxys'] + return self.CONFIG.ports["h2proxys"] + + @property + def h3proxys_port(self) -> int: + return self.CONFIG.ports["h3proxys"] - def pts_port(self, proto: str = 'http/1.1') -> int: + def pts_port(self, proto: str = "http/1.1") -> int: # proxy tunnel port - return self.CONFIG.ports['h2proxys' if proto == 'h2' else 'proxys'] + if proto == "h3": + return self.CONFIG.ports["h3proxys"] + if proto == "h2": + return self.CONFIG.ports["h2proxys"] + return self.CONFIG.ports["proxys"] @property def caddy(self) -> str: @@ -762,11 +840,11 @@ def caddy(self) -> str: @property def caddy_https_port(self) -> int: - return self.CONFIG.ports['caddys'] + return self.CONFIG.ports["caddys"] @property def caddy_http_port(self) -> int: - return self.CONFIG.ports['caddy'] + return self.CONFIG.ports["caddy"] @property def danted(self) -> str: @@ -778,7 +856,7 @@ def vsftpd(self) -> str: @property def ws_port(self) -> int: - return self.CONFIG.ports['ws'] + return self.CONFIG.ports["ws"] @property def curl(self) -> str: @@ -802,58 +880,65 @@ def nghttpx(self) -> Optional[str]: @property def slow_network(self) -> bool: - return "CURL_DBG_SOCK_WBLOCK" in os.environ or \ - "CURL_DBG_SOCK_WPARTIAL" in os.environ + return ( + "CURL_DBG_SOCK_WBLOCK" in os.environ + or "CURL_DBG_SOCK_WPARTIAL" in os.environ + ) @property def ci_run(self) -> bool: return "CURL_CI" in os.environ def port_for(self, alpn_proto: Optional[str] = None): - if alpn_proto is None or \ - alpn_proto in ['h2', 'http/1.1', 'http/1.0', 'http/0.9']: + if alpn_proto is None or alpn_proto in [ + "h2", + "http/1.1", + "http/1.0", + "http/0.9", + ]: return self.https_port - if alpn_proto in ['h3']: + if alpn_proto in ["h3"]: return self.h3_port return self.http_port def authority_for(self, domain: str, alpn_proto: Optional[str] = None): - return f'{domain}:{self.port_for(alpn_proto=alpn_proto)}' + return f"{domain}:{self.port_for(alpn_proto=alpn_proto)}" - def make_data_file(self, indir: str, fname: str, fsize: int, - line_length: int = 1024) -> str: + def make_data_file( + self, indir: str, fname: str, fsize: int, line_length: int = 1024 + ) -> str: if line_length < 11: - raise RuntimeError('line_length less than 11 not supported') + raise RuntimeError("line_length less than 11 not supported") fpath = os.path.join(indir, fname) s10 = "0123456789" s = round((line_length / 10) + 1) * s10 - s = s[0:line_length-11] - with open(fpath, 'w') as fd: + s = s[0 : line_length - 11] + with open(fpath, "w") as fd: for i in range(int(fsize / line_length)): fd.write(f"{i:09d}-{s}\n") remain = int(fsize % line_length) if remain != 0: i = int(fsize / line_length) + 1 - fd.write(f"{i:09d}-{s}"[0:remain-1] + "\n") + fd.write(f"{i:09d}-{s}"[0 : remain - 1] + "\n") return fpath def make_data_gzipbomb(self, indir: str, fname: str, fsize: int) -> str: fpath = os.path.join(indir, fname) - gzpath = f'{fpath}.gz' - varpath = f'{fpath}.var' + gzpath = f"{fpath}.gz" + varpath = f"{fpath}.var" - with open(fpath, 'w') as fd: - fd.write('not what we are looking for!\n') + with open(fpath, "w") as fd: + fd.write("not what we are looking for!\n") count = int(fsize / 1024) zero1k = bytearray(1024) - with gzip.open(gzpath, 'wb') as fd: + with gzip.open(gzpath, "wb") as fd: for _ in range(count): fd.write(zero1k) - with open(varpath, 'w') as fd: - fd.write(f'URI: {fname}\n') - fd.write('\n') - fd.write(f'URI: {fname}.gz\n') - fd.write('Content-Type: text/plain\n') - fd.write('Content-Encoding: x-gzip\n') - fd.write('\n') + with open(varpath, "w") as fd: + fd.write(f"URI: {fname}\n") + fd.write("\n") + fd.write(f"URI: {fname}.gz\n") + fd.write("Content-Type: text/plain\n") + fd.write("Content-Encoding: x-gzip\n") + fd.write("\n") return fpath diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py new file mode 100644 index 000000000000..6a55f4882bf7 --- /dev/null +++ b/tests/http/testenv/h2o.py @@ -0,0 +1,428 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# *************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# +import logging +import os +import signal +import socket +import subprocess +import time +from datetime import datetime, timedelta +from typing import Dict, Optional + +from .curl import CurlClient +from .env import Env +from .ports import alloc_ports_and_do + +log = logging.getLogger(__name__) + + +class H2o: + def __init__(self, env: Env, name: str, domain: str, cred_name: str): + self.env = env + self._name = name + self._domain = domain + self._port = 0 # defaults to h3_port + self._cred_name = cred_name + self._loaded_cred_name = None + self._process = None + self._tmp_dir = os.path.join(self.env.gen_dir, self._name) + self._run_dir = os.path.join(self._tmp_dir, "run") + self._conf_file = os.path.join(self._run_dir, "h2o.conf") + self._error_log = os.path.join(self._run_dir, "h2o.log") + self._pid_file = os.path.join(self._run_dir, "h2o.pid") + self._stderr = os.path.join(self._run_dir, "h2o.stderr") + self._cmd = env.CONFIG.h2o + # For proxy subclasses + self._h1_port = None + self._h2_port = None + + @property + def port(self) -> int: + return self._port + + @property + def h1_port(self) -> Optional[int]: + return getattr(self, "_h1_port", None) + + @property + def h2_port(self) -> Optional[int]: + return getattr(self, "_h2_port", None) + + def clear_logs(self): + self._rmf(self._error_log) + self._rmf(self._stderr) + + def dump_logs(self): + lines = [] + lines.append(f"stderr of {self._name}") + lines.append("-------------------------------------------") + self._dump_file(self._stderr, lines) + lines.append("") + lines.append(f"errorlog of {self._name}") + lines.append("-------------------------------------------") + self._dump_file(self._error_log, lines) + lines.append("") + return lines + + def _rmf(self, path): + if os.path.isfile(path): + os.remove(path) + return + + def _dump_file(self, path, lines): + if os.path.isfile(path): + with open(path) as fd: + for line in fd: + lines.append(line.rstrip()) + + def _mkpath(self, path): + if not os.path.exists(path): + os.makedirs(path) + return + + def _log(self, level, msg): + getattr(log, level)(f"[{self._name}] {msg}") + + def is_running(self): + if self._process: + self._process.poll() + return self._process.returncode is None + return False + + def initial_start(self): + self._rmf(self._pid_file) + self._rmf(self._error_log) + self._mkpath(self._run_dir) + self.write_config() + + def start(self, wait_live=True): + self._mkpath(self._tmp_dir) + self._mkpath(self._run_dir) + if self._process: + self.stop() + self._loaded_cred_name = self._cred_name + self.write_config() + args = [self._cmd, "-c", self._conf_file] + ngerr = open(self._stderr, "a") + self._process = subprocess.Popen(args=args, stderr=ngerr) + if self._process.returncode is not None: + return False + if wait_live: + time.sleep(1) + # fail fast if h2o rejected the config and already exited + self._process.poll() + if self._process.returncode is not None: + self._log("error", + f"h2o exited early (rc={self._process.returncode})" + f" - check {self._stderr} for details") + self._process = None + return False + return not wait_live or self.wait_for_state( + live=True, timeout=timedelta(seconds=Env.SERVER_TIMEOUT) + ) + + def stop(self, wait_dead=True): + self._mkpath(self._tmp_dir) + if self._process: + self._process.terminate() + try: + self._process.wait(timeout=5) + except subprocess.TimeoutExpired: + self._process.kill() + self._process.wait(timeout=2) + self._process = None + return not wait_dead or self.wait_for_state( + live=False, timeout=timedelta(seconds=5) + ) + return True + + def restart(self): + self.stop() + return self.start() + + def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): + if self._process: + running = self._process + self._process = None + os.kill(running.pid, signal.SIGQUIT) + end_wait = datetime.now() + timedelta(seconds=5) + exited = False + if not self.start(wait_live=False): + self._process = running + return False + while datetime.now() < end_wait: + try: + self._log("debug", f"waiting for h2o({running.pid}) to exit.") + running.wait(1) + self._log( + "debug", + f"h2o({running.pid}) terminated -> {running.returncode}", + ) + exited = True + break + except subprocess.TimeoutExpired: + self._log("warning", f"h2o({running.pid}), not shut down yet.") + os.kill(running.pid, signal.SIGQUIT) + if not exited and datetime.now() >= end_wait: + self._log("error", f"h2o({running.pid}), terminate forcefully.") + os.kill(running.pid, signal.SIGKILL) + running.terminate() + running.wait(1) + return self.wait_for_state(live=True, timeout=timeout) + return False + + def wait_for_state( + self, + live: bool, + timeout: timedelta, + url: Optional[str] = None, + log_prefix: str = "h2o", + ): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + try_until = datetime.now() + timeout + if url is None: + url = f"https://{self._domain}:{self._port}/" + while datetime.now() < try_until: + if live: + r = curl.http_get( + url=url, extra_args=["--trace", "curl.trace", "--trace-time"] + ) + if r.exit_code == 0: + return True + else: + r = curl.http_get(url=url) + if r.exit_code != 0: + return True + time.sleep(0.1) + if live: + self._log("error", f"Server still not responding after {timeout}") + else: + self._log("debug", f"Server still responding after {timeout}") + return False + + def write_config(self): + # To be overridden by subclasses + with open(self._conf_file, "w") as fd: + fd.write("# h2o test config\n") + + +class H2oServer(H2o): + """h2o HTTP/3 server for testing.""" + + PORT_SPECS = { + "h2o_https": socket.SOCK_STREAM, + } + + def __init__(self, env: Env): + super().__init__( + env=env, name="h2o-server", domain=env.domain1, cred_name=env.domain1 + ) + + def initial_start(self): + super().initial_start() + + def startup(ports: Dict[str, int]) -> bool: + self._port = ports["h2o_https"] + if self.start(): + self.env.update_ports(ports) + return True + self.stop() + self._port = 0 + return False + + return alloc_ports_and_do( + H2oServer.PORT_SPECS, startup, self.env.gen_root, max_tries=3 + ) + + def write_config(self): + creds = self.env.get_credentials(self._cred_name) + assert creds # convince pytype this is not None + doc_root = os.path.join(self.env.gen_dir, "docs") + self._mkpath(doc_root) + self._mkpath(self._run_dir) + # Create a simple test file + with open(os.path.join(doc_root, "data.json"), "w") as f: + f.write('{"message": "Hello from h2o HTTP/3 server"}\n') + with open(self._conf_file, "w") as fd: + fd.write(f"""# h2o HTTP/3 server configuration +server-name: "h2o-test-server" +num-threads: 1 + +listen: &ssl_listen + port: {self._port} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +listen: + <<: *ssl_listen + type: quic + +hosts: + "{self._domain}": + paths: + "/": + file.dir: {doc_root} + +http2-reprioritize-blocking-assets: ON + +access-log: {self._run_dir}/access.log +error-log: {self._error_log} +""") + + +class H2oProxy(H2o): + """h2o MASQUE proxy for testing.""" + + def __init__(self, env: Env): + super().__init__( + env=env, + name="h2o-proxy", + domain=env.proxy_domain, + cred_name=env.proxy_domain, + ) + + def initial_start(self): + super().initial_start() + + def startup(ports: Dict[str, int]) -> bool: + self._port = ports["h3proxys"] + self._h2_port = ports["h2proxys"] + self._h1_port = ports["proxys"] + if self.start(): + self.env.update_ports(ports) + return True + self.stop() + self._port = 0 + self._h2_port = 0 + self._h1_port = 0 + return False + + return alloc_ports_and_do( + { + "h3proxys": socket.SOCK_DGRAM, + "h2proxys": socket.SOCK_STREAM, + "proxys": socket.SOCK_STREAM, + }, + startup, + self.env.gen_root, + max_tries=3, + ) + + def write_config(self): + creds = self.env.get_credentials(self._cred_name) + assert creds # convince pytype this is not None + self._mkpath(self._run_dir) + with open(self._conf_file, "w") as fd: + fd.write(f"""# h2o MASQUE proxy configuration +server-name: "h2o-test-proxy" +num-threads: 1 + +proxy.tunnel: ON + +# HTTP/1.1 proxy listener +listen: &h1_listen + port: {getattr(self, "_h1_port", self._port)} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# HTTP/2 proxy listener +listen: &h2_listen + port: {getattr(self, "_h2_port", self._port)} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# HTTP/3 proxy listener (main port) +listen: &h3_listen + port: {self._port} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# QUIC listener for HTTP/3 +listen: + <<: *h3_listen + type: quic + +hosts: + "{self._domain}": + paths: + "/": + proxy.connect: [+*] + proxy.ssl.verify-peer: OFF + "/.well-known/masque/udp": + proxy.connect-udp: [+*] + proxy.ssl.verify-peer: OFF + +http2-reprioritize-blocking-assets: ON + +access-log: {self._run_dir}/access.log +error-log: {self._error_log} +""") + + def wait_for_state( + self, + live: bool, + timeout: timedelta, + url: Optional[str] = None, + log_prefix: str = "h2o", + ): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + try_until = datetime.now() + timeout + if url is None: + url = f"https://{self.env.proxy_domain}:{self._port}/" + while datetime.now() < try_until: + if live: + r = curl.http_get( + url=url, extra_args=["--trace", "curl.trace", "--trace-time"] + ) + if r.exit_code == 0: + return True + else: + r = curl.http_get(url=url) + if r.exit_code != 0: + return True + time.sleep(0.1) + if live: + self._log("error", f"Proxy still not responding after {timeout}") + else: + self._log("debug", f"Proxy still responding after {timeout}") + return False diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index c8eccd27ad4d..c6c75c781d00 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -47,4 +47,4 @@ TESTS_C = \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ - unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c + unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c unit3400.c diff --git a/tests/unit/unit3400.c b/tests/unit/unit3400.c new file mode 100644 index 000000000000..e48dd3c1a72f --- /dev/null +++ b/tests/unit/unit3400.c @@ -0,0 +1,268 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "unitcheck.h" + +#include "bufq.h" +#include "capsule.h" + +#if defined(USE_PROXY_HTTP3) && defined(USE_NGTCP2) && \ + !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +static void queue_bytes(struct bufq *q, const unsigned char *src, size_t len) +{ + size_t nwritten = 0; + CURLcode result = Curl_bufq_write(q, src, len, &nwritten); + fail_unless(result == CURLE_OK, "queue failed"); + fail_unless(nwritten == len, "queue short write"); +} +#endif + +#if defined(USE_PROXY_HTTP3) && \ + !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +static void check_capsule_hdr(size_t payload_len, + const unsigned char *expected, + size_t expected_len) +{ + unsigned char hdr[HTTP_CAPSULE_HEADER_MAX_SIZE]; + size_t hdr_len; + + memset(hdr, 0xA5, sizeof(hdr)); + hdr_len = Curl_capsule_encap_udp_hdr(hdr, sizeof(hdr), payload_len); + fail_unless(hdr_len == expected_len, "capsule header length mismatch"); + fail_unless(!memcmp(hdr, expected, expected_len), + "capsule header bytes mismatch"); +} + +static void test_capsule_encap_udp_hdr_boundaries(void) +{ + const unsigned char p0[] = { 0x00, 0x01, 0x00 }; + const unsigned char p62[] = { 0x00, 0x3F, 0x00 }; + const unsigned char p63[] = { 0x00, 0x40, 0x40, 0x00 }; + const unsigned char p64[] = { 0x00, 0x40, 0x41, 0x00 }; + const unsigned char p16382[] = { 0x00, 0x7F, 0xFF, 0x00 }; + const unsigned char p16383[] = { 0x00, 0x80, 0x00, 0x40, 0x00, 0x00 }; + const unsigned char p16384[] = { 0x00, 0x80, 0x00, 0x40, 0x01, 0x00 }; + + check_capsule_hdr(0, p0, sizeof(p0)); + check_capsule_hdr(62, p62, sizeof(p62)); + check_capsule_hdr(63, p63, sizeof(p63)); + check_capsule_hdr(64, p64, sizeof(p64)); + check_capsule_hdr(16382, p16382, sizeof(p16382)); + check_capsule_hdr(16383, p16383, sizeof(p16383)); + check_capsule_hdr(16384, p16384, sizeof(p16384)); +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#if defined(USE_PROXY_HTTP3) && defined(USE_NGTCP2) && \ + !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +static void check_capsule_result(struct bufq *q, + const unsigned char *capsule, size_t capslen, + size_t outlen, CURLcode expect_err, + size_t expect_nread) +{ + unsigned char out[32]; + CURLcode err = CURLE_OK; + size_t nread; + + memset(out, 0, sizeof(out)); + Curl_bufq_reset(q); + if(capsule && capslen) + queue_bytes(q, capsule, capslen); + + nread = Curl_capsule_process_udp_raw(NULL, NULL, q, out, outlen, &err); + fail_unless(err == expect_err, "unexpected capsule error"); + fail_unless(nread == expect_nread, "unexpected capsule read size"); +} + +static void test_capsule_encode_decode_roundtrip(void) +{ + struct dynbuf dyn; + struct bufq q; + unsigned char payload[128]; + unsigned char out[128]; + CURLcode result, err; + size_t payload_len; + size_t i, nread; + + for(i = 0; i < sizeof(payload); ++i) + payload[i] = (unsigned char)i; + + for(i = 0; i < 2; ++i) { + payload_len = i ? 64 : 7; + memset(out, 0, sizeof(out)); + + result = Curl_capsule_encap_udp_datagram(&dyn, payload, payload_len); + fail_unless(result == CURLE_OK, "failed to encapsulate UDP datagram"); + + Curl_bufq_init2(&q, 32, 8, BUFQ_OPT_NONE); + queue_bytes(&q, (const unsigned char *)curlx_dyn_ptr(&dyn), + curlx_dyn_len(&dyn)); + + err = CURLE_OK; + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), + &err); + fail_unless(err == CURLE_OK, "failed to decode UDP datagram"); + fail_unless(nread == payload_len, "decoded payload length mismatch"); + fail_unless(!memcmp(out, payload, payload_len), + "decoded payload bytes mismatch"); + fail_unless(Curl_bufq_is_empty(&q), "decoded capsule must be consumed"); + + Curl_bufq_free(&q); + curlx_dyn_free(&dyn); + } +} + +static void test_capsule_sequential_decode(void) +{ + /* Verify that multiple back-to-back capsules in the same bufq are + each decoded in turn and the buffer is fully consumed. */ + struct bufq q; + unsigned char out[8]; + CURLcode err; + size_t nread; + /* Two back-to-back 3-byte UDP capsules */ + const unsigned char two_caps[] = { + 0x00, 0x04, 0x00, 0x11, 0x22, 0x33, /* capsule 1: [0x11,0x22,0x33] */ + 0x00, 0x04, 0x00, 0xAA, 0xBB, 0xCC /* capsule 2: [0xAA,0xBB,0xCC] */ + }; + + Curl_bufq_init2(&q, 32, 4, BUFQ_OPT_NONE); + + queue_bytes(&q, two_caps, sizeof(two_caps)); + + /* First capsule */ + memset(out, 0, sizeof(out)); + err = CURLE_OK; + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), &err); + fail_unless(err == CURLE_OK, "sequential: first capsule decode failed"); + fail_unless(nread == 3, "sequential: first capsule size mismatch"); + fail_unless(out[0] == 0x11 && out[1] == 0x22 && out[2] == 0x33, + "sequential: first capsule bytes mismatch"); + + /* Second capsule */ + memset(out, 0, sizeof(out)); + err = CURLE_OK; + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), &err); + fail_unless(err == CURLE_OK, "sequential: second capsule decode failed"); + fail_unless(nread == 3, "sequential: second capsule size mismatch"); + fail_unless(out[0] == 0xAA && out[1] == 0xBB && out[2] == 0xCC, + "sequential: second capsule bytes mismatch"); + + /* Buffer must be empty after both capsules */ + fail_unless(Curl_bufq_is_empty(&q), + "sequential: buffer must be empty after two capsules"); + + /* No more data - CURLE_AGAIN expected */ + err = CURLE_OK; + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), &err); + fail_unless(err == CURLE_AGAIN, + "sequential: empty queue should return AGAIN"); + fail_unless(nread == 0, "sequential: empty queue should read zero bytes"); + + Curl_bufq_free(&q); +} + +static void test_capsule_decode_paths(void) +{ + struct bufq q; + unsigned char out[8]; + CURLcode err = CURLE_OK; + size_t nread; + const unsigned char invalid_type[] = { 0x01 }; + const unsigned char partial_len[] = { 0x00, 0x40 }; + const unsigned char invalid_context[] = { 0x00, 0x01, 0x01 }; + const unsigned char invalid_caps_len[] = { 0x00, 0x00, 0x00 }; + const unsigned char partial_payload[] = { 0x00, 0x04, 0x00, 0x11, 0x22 }; + const unsigned char payload_3b[] = { 0x00, 0x04, 0x00, 0x11, 0x22, 0x33 }; + const unsigned char payload_empty[] = { 0x00, 0x01, 0x00 }; + + Curl_bufq_init2(&q, 32, 4, BUFQ_OPT_NONE); + + check_capsule_result(&q, NULL, 0, 0, CURLE_BAD_FUNCTION_ARGUMENT, 0); + check_capsule_result(&q, NULL, 0, sizeof(out), CURLE_AGAIN, 0); + check_capsule_result(&q, invalid_type, sizeof(invalid_type), sizeof(out), + CURLE_RECV_ERROR, 0); + check_capsule_result(&q, partial_len, sizeof(partial_len), sizeof(out), + CURLE_AGAIN, 0); + check_capsule_result(&q, invalid_context, sizeof(invalid_context), + sizeof(out), CURLE_RECV_ERROR, 0); + check_capsule_result(&q, invalid_caps_len, sizeof(invalid_caps_len), + sizeof(out), CURLE_RECV_ERROR, 0); + check_capsule_result(&q, partial_payload, sizeof(partial_payload), + sizeof(out), CURLE_AGAIN, 0); + + /* oversized payload is rejected and discarded */ + Curl_bufq_reset(&q); + queue_bytes(&q, payload_3b, sizeof(payload_3b)); + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, 2, &err); + fail_unless(err == CURLE_RECV_ERROR, + "expected RECV_ERROR for short output buffer"); + fail_unless(nread == 0, "expected zero read on short output buffer"); + fail_unless(Curl_bufq_is_empty(&q), + "oversized capsule must be discarded"); + + /* zero-length UDP payload is accepted and consumed */ + Curl_bufq_reset(&q); + queue_bytes(&q, payload_empty, sizeof(payload_empty)); + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), &err); + fail_unless(err == CURLE_OK, "zero-length UDP payload should succeed"); + fail_unless(nread == 0, "zero-length UDP payload should read zero"); + fail_unless(Curl_bufq_is_empty(&q), "zero-length capsule must be consumed"); + + /* normal payload decode */ + Curl_bufq_reset(&q); + queue_bytes(&q, payload_3b, sizeof(payload_3b)); + memset(out, 0, sizeof(out)); + nread = Curl_capsule_process_udp_raw(NULL, NULL, &q, out, sizeof(out), &err); + fail_unless(err == CURLE_OK, "payload decode should succeed"); + fail_unless(nread == 3, "payload decode size mismatch"); + fail_unless(out[0] == 0x11 && out[1] == 0x22 && out[2] == 0x33, + "payload decode bytes mismatch"); + fail_unless(Curl_bufq_is_empty(&q), "payload capsule must be consumed"); + + Curl_bufq_free(&q); +} +#endif /* USE_NGTCP2 && !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +static CURLcode test_unit3400(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + + (void)arg; + +#if defined(USE_PROXY_HTTP3) && \ + !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + test_capsule_encap_udp_hdr_boundaries(); +#endif + +#if defined(USE_PROXY_HTTP3) && defined(USE_NGTCP2) && \ + !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + test_capsule_encode_decode_roundtrip(); + test_capsule_decode_paths(); + test_capsule_sequential_decode(); +#endif + + UNITTEST_END_SIMPLE +} From f21b5d4e6628370e1400d9503ee243f5150b5cea Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 16:16:12 +0200 Subject: [PATCH 0215/1062] gtls: fix ignored return and uninitialized status in OCSP check gnutls_ocsp_resp_get_single() was called with (void) discarding its return value, so a failure (e.g. an OCSP response with no SingleResponse entries) went undetected. The following switch() then read an uninitialized gnutls_ocsp_cert_status_t, which is undefined behaviour and could yield GNUTLS_OCSP_CERT_GOOD (0) depending on stack contents, causing gtls_verify_ocsp_status to return CURLE_OK for a response that was never successfully parsed. Fix by initializing status to GNUTLS_OCSP_CERT_UNKNOWN and treating a negative return from gnutls_ocsp_resp_get_single as an error. Closes #21679 --- lib/vtls/gtls.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 22001c339125..d0b851e0eb1d 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1429,7 +1429,7 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, { gnutls_ocsp_resp_t ocsp_resp = NULL; gnutls_datum_t status_request; - gnutls_ocsp_cert_status_t status; + gnutls_ocsp_cert_status_t status = GNUTLS_OCSP_CERT_UNKNOWN; gnutls_x509_crl_reason_t reason; CURLcode result = CURLE_OK; int rc; @@ -1461,8 +1461,13 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } - (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); + rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } switch(status) { case GNUTLS_OCSP_CERT_GOOD: From 23e4bd9602acb8aad0583b88d8b74734d6a31f1b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 09:06:55 +0200 Subject: [PATCH 0216/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 53 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 0d41fffc7dba..bfc453e30458 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,15 +1,16 @@ curl and libcurl 8.21.0 Public curl releases: 275 - Command line options: 273 + Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1477 - Contributors: 3688 + Authors: 1479 + Contributors: 3691 This release includes the following changes: o curl: named globs in output file name for upload glob references [77] + o HTTP/3: add proxy CONNECT and MASQUE CONNECT-UDP support (ngtcp2 QUIC) [53] o http2: remove stream dependency tracking [40] o lib: drop support for CURLAUTH_DIGEST_IE [4] o libssh: add support for SHA256 host public keys [57] @@ -23,6 +24,7 @@ This release includes the following bugfixes: o cf-h2-prox: fix peer leak [132] o cf-h2-proxy: drop interim responses [47] o cfilters: fix busy loop on blocked transfers [72] + o CIPHERS.md: fix the example that uses only TLS 1.3 [137] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] o cmake: fix three issues generating lib options in config files [126] @@ -50,6 +52,7 @@ This release includes the following bugfixes: o docs: fix --follow doc typo [97] o docs: fix a couple of typos [62] o docs: fix grammar and wording in FAQ [66] + o docs: fix odd wording in CONTRIBUTE.md [107] o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] o ECH: cleanups [20] o event: fix wakeup consumption [93] @@ -58,29 +61,37 @@ This release includes the following bugfixes: o ftp: remove bits.ftp_use_control_ssl [28] o gnutls: allow building with nettle 4.0 [96] o gnutls: fix more nettle 4+ compatibility issues [94] + o GnuTLS: require 3.7.2 for earlydata [103] o gsasl: fix potential double free [56] + o gtls: fix ignored return and uninitialized status in OCSP check [49] o gtls: fix some typos [15] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] o idn: replace header guards with forward declaration [100] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] + o KNOWN_BUGS: remove stale Threads::Threads entry [135] o ldap: fix minor leak on write callback error [24] o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] + o ldap: switch of chasing referrals [114] o lib678: fix to not be perma-skipped [10] o lib: make `__STDC_VERSION__` literals `L` (where missing) o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] + o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] o managen: apply minor fixes and improvements [115] o mbedtls: null-terminate the private key blob [36] o mk-unity.pl: `#include`, and not concatenate input headers [124] o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o multi: handle pause in multi socket callback [109] o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] o netrc: scanner refactor [121] + o ngtcp2: fail handshake directly [138] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] o rustls: error on CURLOPT_CRLFILE with native CA store [59] o schannel: enforce Extended Key Usage for custom CA roots [29] + o schannel: error on TLS 1.3-only with cipher list [136] o schannel: fix revoke_best_effort setting for proxy [70] o schannel_verify: avoid out of blob access [11] o scripts: catch Credits-to contributors [127] @@ -96,6 +107,7 @@ This release includes the following bugfixes: o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] o test1588: use %TESTNUMBER, not hard-coded number [118] + o test1981: explicitly set the locale [85] o tests: add an assert to avoid IPC blocking [69] o tests: fix unit1636 with --disable-progress-meter [37] o tftp: stricter option name checks [90] @@ -107,6 +119,7 @@ This release includes the following bugfixes: o tool_urlglob: avoid overflow at end of range [22] o tool_urlglob: better 'Duplicate glob name' position [82] o tool_urlglob: make globbing error reported for correct position [91] + o transfer: clear referer when set to NULL [112] o unix-sockets: ignore proxy settings [6] o url: compare full origin when setting credentials [42] o url: detect proxy changes read from environment [110] @@ -124,6 +137,7 @@ This release includes the following bugfixes: o user-agent.md: mention double quotes too [3] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] o vtls_scache: include signature_algorithms in the SSL peer cache key [123] + o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] o websockets: auto-tunnel through http proxy [102] o windows: update MS SDK versions in comments [60] @@ -151,15 +165,16 @@ This release would not have looked like this without help, code, reports and advice from friends like these: 0xN3R3K3, 11soda11, Alan De Smet, amitbidlan, Andrei Rybak, Andrew Nesbitt, - Bastian Jesuiter, Bill Mill, chrizilla on github, co-authors in libssh2, - Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, - dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, - Fabian Keil, Harry Sintonen, jeffhuang, Jeremy Nicoll, Joshua Rogers, - Kai Pastor, Mark Esler, mulan_dh on hackerone, parasol-aser, penpal, - Raymond Steen, Ray Satiro, renovate[bot], Sergio Correia, sfan5 on github, - Shintomon Mathew, Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, - Viktor Szakats, Will Cosgrove, Xi Ruoyao, x-xiang on github - (42 contributors) + Aritra Basu, Bastian Jesuiter, Bill Mill, chrizilla on github, + co-authors in libssh2, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, + Dario Vinella, dependabot[bot], Earnestly on github, Elise Vance, + Emanuel Krollmann, Fabian Keil, Harry Sintonen, jeffhuang, Jeremy Nicoll, + Johannes Schlatow, Joshua Rogers, Kai Pastor, Mark Esler, Max Dymond, mik, + mulan_dh on hackerone, parasol-aser, penpal, Peter Krefting, Raymond Steen, + Ray Satiro, renovate[bot], Sergio Correia, sfan5 on github, Shintomon Mathew, + Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, Viktor Szakats, + Will Cosgrove, Xi Ruoyao, x-xiang on github + (47 contributors) References to bug reports and discussions on issues: @@ -211,9 +226,11 @@ References to bug reports and discussions on issues: [46] = https://curl.se/bug/?i=21721 [47] = https://curl.se/bug/?i=21626 [48] = https://curl.se/bug/?i=21606 + [49] = https://curl.se/bug/?i=21679 [50] = https://curl.se/bug/?i=21635 [51] = https://curl.se/bug/?i=21633 [52] = https://curl.se/bug/?i=21616 + [53] = https://curl.se/bug/?i=21153 [54] = https://curl.se/bug/?i=21707 [55] = https://curl.se/bug/?i=21714 [56] = https://curl.se/bug/?i=21609 @@ -241,6 +258,7 @@ References to bug reports and discussions on issues: [82] = https://curl.se/bug/?i=21567 [83] = https://curl.se/bug/?i=21570 [84] = https://curl.se/bug/?i=21569 + [85] = https://curl.se/bug/?i=21749 [87] = https://curl.se/bug/?i=21562 [88] = https://curl.se/bug/?i=21563 [89] = https://curl.se/bug/?i=21528 @@ -257,11 +275,17 @@ References to bug reports and discussions on issues: [100] = https://curl.se/bug/?i=21551 [101] = https://curl.se/bug/?i=21550 [102] = https://curl.se/bug/?i=21663 + [103] = https://curl.se/bug/?i=21750 [105] = https://curl.se/bug/?i=21672 [106] = https://curl.se/bug/?i=21646 + [107] = https://curl.se/bug/?i=21705 [108] = https://curl.se/bug/?i=21667 + [109] = https://curl.se/bug/?i=21748 [110] = https://curl.se/bug/?i=21666 [111] = https://curl.se/bug/?i=21678 + [112] = https://curl.se/bug/?i=21741 + [113] = https://curl.se/bug/?i=21747 + [114] = https://curl.se/bug/?i=21732 [115] = https://curl.se/bug/?i=21670 [116] = https://curl.se/bug/?i=21668 [117] = https://curl.se/bug/?i=21659 @@ -272,9 +296,14 @@ References to bug reports and discussions on issues: [122] = https://curl.se/bug/?i=21604 [123] = https://curl.se/bug/?i=21651 [124] = https://curl.se/bug/?i=21656 + [125] = https://curl.se/bug/?i=21724 [126] = https://curl.se/bug/?i=21654 [127] = https://curl.se/bug/?i=21653 [128] = https://curl.se/bug/?i=21649 [130] = https://curl.se/bug/?i=21647 [131] = https://curl.se/bug/?i=21650 [132] = https://curl.se/bug/?i=21602 + [135] = https://curl.se/bug/?i=21734 + [136] = https://curl.se/bug/?i=21702 + [137] = https://curl.se/bug/?i=21719 + [138] = https://curl.se/bug/?i=21712 From efdf733baebc475af485b582551844f20bed546b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 09:26:41 +0200 Subject: [PATCH 0217/1062] gtls: use the correct return code in trace output Instead of using a hard-coded zero. Spotted by Copilot Closes #21766 --- lib/vtls/gtls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index d0b851e0eb1d..70edfc629c1c 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -2249,7 +2249,8 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zd", blen, nread); + CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> %d, %zd", blen, + (int)result, nread); return result; } From a8e6f90a6980a70839823b7fe5f6e0faeeec4833 Mon Sep 17 00:00:00 2001 From: tiymat <138939221+tiymat@users.noreply.github.com> Date: Tue, 26 May 2026 22:35:27 -0230 Subject: [PATCH 0218/1062] urlapi: forbid '|' in host Closes #21762 --- lib/urlapi.c | 2 +- tests/libtest/lib1560.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index ef5b2b48e909..589a400834b1 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -471,7 +471,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return ipv6_parse(u, hostname, hlen); else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%|"); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 3eeed6b6943d..69c7fd857b92 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -326,6 +326,7 @@ static const struct testcase get_parts_list[] = { {"https://user@example.net?he l lo", "https | user | [12] | [13] | example.net | [15] | / | he l lo | [17]", CURLU_ALLOW_SPACE, 0, CURLUE_OK}, + {"https://exam|ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam{}[]ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam{ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam}ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, From 049ec8a3631b72e834a4a87dcd04759885138f7c Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 15:27:22 +0200 Subject: [PATCH 0219/1062] content_encoding: fix limit failure message The message triggered earlier than intended and did not take the transfer/content type into account. Ref #21603 Reported-by: Joshua Rogers Closes #21756 --- lib/content_encoding.c | 7 ++++--- tests/data/test387 | 2 +- tests/data/test418 | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 0224a8bfe9e8..aaef92a7d950 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -750,9 +750,10 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_OK; } - if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %d content encodings", - MAX_ENCODE_STACK); + if(Curl_cwriter_count(data, phase) >= MAX_ENCODE_STACK) { + failf(data, "Reject response exceeding limit of %d %s encodings", + MAX_ENCODE_STACK, + is_transfer ? "transfer" : "content"); return CURLE_BAD_CONTENT_ENCODING; } diff --git a/tests/data/test387 b/tests/data/test387 index ebdd4b5d116f..5872cd692e11 100644 --- a/tests/data/test387 +++ b/tests/data/test387 @@ -51,7 +51,7 @@ Connection: TE 61 -curl: (61) Reject response due to more than 5 content encodings +curl: (61) Reject response exceeding limit of 5 transfer encodings diff --git a/tests/data/test418 b/tests/data/test418 index ccda8912298d..cdd25f4ba9bc 100644 --- a/tests/data/test418 +++ b/tests/data/test418 @@ -59,7 +59,7 @@ Connection: TE 61 -curl: (61) Reject response due to more than 5 content encodings +curl: (61) Reject response exceeding limit of 5 transfer encodings From 1791a087079e90769d52e7a797a59ecaf2d1bd6d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 15:59:09 +0200 Subject: [PATCH 0220/1062] content_encoding: timeout during slow decoding Check during transfer/content decoding for every MB or so, if the transfer has reached its overall time limit. Error out if so. This is mainly a protectin against compression bombs using way more time than the transfer is allowed to. Normal compression ratios are unlikely to benefit as they need more upstream data where the timeout handling is already in place. Fixes #21603 Reported-by: Joshua Rogers Closes #21758 --- lib/content_encoding.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/content_encoding.c b/lib/content_encoding.c index aaef92a7d950..b106fc813bb5 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -46,6 +46,7 @@ #include #endif +#include "connect.h" #include "sendf.h" #include "curl_trc.h" #include "content_encoding.h" @@ -154,6 +155,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, z_const Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ + int i = 0; /* Check state. */ if(zp->zlib_init != ZLIB_INIT && @@ -167,6 +169,15 @@ static CURLcode inflate_stream(struct Curl_easy *data, int status; /* zlib status */ done = TRUE; + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return exit_zlib(data, z, &zp->zlib_init, CURLE_OPERATION_TIMEDOUT); + } + } + /* (re)set buffer for decompressed output for every iteration */ z->next_out = (Bytef *)zp->buffer; z->avail_out = DECOMPRESS_BUFFER_SIZE; @@ -412,6 +423,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data, size_t dstleft; CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -421,6 +433,16 @@ static CURLcode brotli_do_write(struct Curl_easy *data, while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { + + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + dst = (uint8_t *)bp->buffer; dstleft = DECOMPRESS_BUFFER_SIZE; r = BrotliDecoderDecompressStream(bp->br, @@ -520,6 +542,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -529,6 +552,15 @@ static CURLcode zstd_do_write(struct Curl_easy *data, in.size = nbytes; for(;;) { + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + out.pos = 0; out.dst = zp->buffer; out.size = DECOMPRESS_BUFFER_SIZE; From a7d4abb0cfcc5b155174675c51d0edecc15eccbb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 27 May 2026 10:36:22 +0200 Subject: [PATCH 0221/1062] cf-h3-proxy: add SSL flag Since the proxy filter does TLS, it needs to set the SSL flag. Follow-up to e78b1b3eccfa6a2e3 Closes #21770 Spotted by Codex Security --- lib/cf-h3-proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 1896ba6302dc..6af81d6d5908 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -3421,7 +3421,7 @@ static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_h3_proxy = { "H3-PROXY", - CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SSL, CURL_LOG_LVL_NONE, cf_h3_proxy_destroy, cf_h3_proxy_connect, From 7e1001bcd69967707cf6fee9e71fc10dd244d509 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 20 May 2026 12:20:10 +0200 Subject: [PATCH 0222/1062] tidy-up: miscellaneous - H3 proxy: re-sync code with original source `curl_ngtcp2.c` to reduce differences, and to apply missed minor fixes. Also apply clang-format. Drop redundant `#undef`s, casts, `#endif` comments, includes, drop intermediate variables, sync include and macro order. Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 - INSTALL-CMAKE.md: move `CURL_ENABLE_SMB` to the enable section. - tests/http/env: rename `tcpdmp` to `tcpdump` to match object variable. - mbedtls: drop incorrect `mbedTLS 4+` comments. (features are also supported by 3+, meaning it's always supported.) - lib1648: rename a variable to match purpose. - CIPHERS.md: alpha-sort link list. - replace rare `X''` hex markup with `0x`. - `IP v4/6` -> `IPv4/6`. - 'version X.Y' -> 'vX.Y', where sensible. - 'VX.Y' -> 'vX.Y', where sensible. - fix indents, casing, newlines, typos. Closes #21772 --- .github/workflows/linux-old.yml | 6 +- RELEASE-NOTES | 4 +- configure.ac | 2 +- docs/CIPHERS.md | 8 +- docs/CONTRIBUTE.md | 2 +- docs/INSTALL-CMAKE.md | 4 +- docs/RUSTLS.md | 2 +- docs/internals/CHECKSRC.md | 28 +- include/curl/curlver.h | 2 +- lib/capsule.c | 6 +- lib/cf-capsule.c | 2 +- lib/cf-h2-proxy.c | 3 - lib/cf-h3-proxy.c | 380 ++++++++++------------ lib/cf-h3-proxy.h | 4 +- lib/curl_sha512_256.c | 4 +- lib/http.c | 1 - lib/http2.c | 3 - lib/md5.c | 2 +- lib/netrc.c | 8 +- lib/sha256.c | 2 +- lib/socks.c | 8 +- lib/vquic/curl_ngtcp2.c | 111 +++---- lib/vquic/vquic.c | 29 +- lib/vtls/gtls.c | 2 +- lib/vtls/mbedtls.c | 2 +- lib/vtls/schannel_verify.c | 12 +- projects/vms/build_gnv_curl_pcsi_desc.com | 2 +- projects/vms/build_vms.com | 2 +- projects/vms/curl_gnv_build_steps.txt | 2 +- projects/vms/readme | 6 +- src/tool_getpass.c | 2 +- tests/http/test_20_websockets.py | 2 +- tests/http/testenv/env.py | 4 +- tests/libtest/lib1560.c | 2 +- tests/libtest/lib1648.c | 4 +- tests/server/socksd.c | 36 +- 36 files changed, 329 insertions(+), 370 deletions(-) diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index f0513646fbac..46fa9acffde5 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -116,10 +116,8 @@ jobs: echo '::group::raw'; cat bld-1/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld-1/lib/curl_config.h | sort || true - # when this job can get a libssh version 0.9.0 or later, this should get - # that enabled again - # when this job can get c-ares 1.16.0 or later, we can enable that - # again + # when this job can get libssh 0.9.0 or greater, this should get that enabled again + # when this job can get c-ares 1.16.0 or greater, this should get that enabled again - name: 'CM configure (out-of-tree, zstd, gssapi)' run: | diff --git a/RELEASE-NOTES b/RELEASE-NOTES index bfc453e30458..14726d1213c5 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -71,7 +71,7 @@ This release includes the following bugfixes: o KNOWN_BUGS: remove stale Threads::Threads entry [135] o ldap: fix minor leak on write callback error [24] o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] - o ldap: switch of chasing referrals [114] + o ldap: switch off chasing referrals [114] o lib678: fix to not be perma-skipped [10] o lib: make `__STDC_VERSION__` literals `L` (where missing) o lib: two minor typos [16] @@ -101,7 +101,7 @@ This release includes the following bugfixes: o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] o show-headers.md: mention bold headers and --no-styled-output [17] - o snpego_sspi: preserve distinction btw policy-only and uncond delegation [74] + o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] o src: fix comment typos [83] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] diff --git a/configure.ac b/configure.ac index 0601371baacc..445ab29c9632 100644 --- a/configure.ac +++ b/configure.ac @@ -2458,7 +2458,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then CPPFLAGS="$CPPFLAGS $CPP_SSH2" LIBS="$LIB_SSH2 $LIBS" - dnl check for function added in libssh2 version 1.9.0 + dnl check for function added in libssh2 v1.9.0 AC_CHECK_LIB(ssh2, libssh2_agent_get_identity_path) AC_CHECK_HEADER(libssh2.h, diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index f0ece576e3bf..0b70bda7d9e1 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -270,10 +270,10 @@ Restrict to only TLS 1.2 with the `CAMELLIA-128-GCM` cipher. ## Further reading -- [OpenSSL cipher suite names documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-suite-names) -- [wolfSSL cipher support documentation](https://www.wolfssl.com/documentation/manuals/wolfssl/chapter04.html#cipher-support) +- [GnuTLS Priority Strings](https://gnutls.org/manual/html_node/Priority-Strings.html) +- [IANA cipher suites list](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4) - [mbedTLS cipher suites reference](https://mbed-tls.readthedocs.io/projects/api/en/development/api/file/ssl__ciphersuites_8h/) +- [OpenSSL cipher suite names documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-suite-names) - [Schannel cipher suites documentation](https://learn.microsoft.com/windows/win32/secauthn/cipher-suites-in-schannel) -- [IANA cipher suites list](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4) - [Wikipedia cipher suite article](https://en.wikipedia.org/wiki/Cipher_suite) -- [GnuTLS Priority Strings](https://gnutls.org/manual/html_node/Priority-Strings.html) +- [wolfSSL cipher support documentation](https://www.wolfssl.com/documentation/manuals/wolfssl/chapter04.html#cipher-support) diff --git a/docs/CONTRIBUTE.md b/docs/CONTRIBUTE.md index bfbc9220f674..f9b473c09a35 100644 --- a/docs/CONTRIBUTE.md +++ b/docs/CONTRIBUTE.md @@ -261,7 +261,7 @@ work. has already been closed. - `Ref: URL` to more information about the commit; use `Bug:` instead for a - reference to a bug on another bug tracker] + reference to a bug on another bug tracker. - `Fixes #1234` - if this fixes a GitHub issue; GitHub closes the issue once this commit is merged. diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 83eb9df68e8f..e6b52a17ee57 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -242,6 +242,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) ## Enabling features - `CURL_ENABLE_NTLM`: Enable NTLM support. Default: `OFF` +- `CURL_ENABLE_SMB`: Enable SMB. Default: `OFF` - `CURL_ENABLE_SSL`: Enable SSL support. Default: `ON` - `CURL_WINDOWS_SSPI`: Enable SSPI on Windows. Default: =`CURL_USE_SCHANNEL` - `ENABLE_IPV6`: Enable IPv6 support. Default: `ON` if target supports IPv6. @@ -296,7 +297,6 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_RTSP`: Disable RTSP. Default: `OFF` - `CURL_DISABLE_SHA512_256`: Disable SHA-512/256 hash algorithm. Default: `OFF` - `CURL_DISABLE_SHUFFLE_DNS`: Disable shuffle DNS feature. Default: `OFF` -- `CURL_ENABLE_SMB`: Enable SMB. Default: `OFF` - `CURL_DISABLE_SMTP`: Disable SMTP. Default: `OFF` - `CURL_DISABLE_SOCKETPAIR`: Disable use of socketpair for curl_multi_poll(). Default: `OFF` - `CURL_DISABLE_SRP`: Disable TLS-SRP support. Default: `OFF` @@ -486,7 +486,7 @@ Examples: - `HTTPD`: Absolute path. Default: search for `apache2` - `DANTED`: Absolute path. Default: search for `danted` - `TEST_NGHTTPX`: Absolute path. Default: search for `nghttpx` -- `VSFTPD`: Absolute path. Default: search for `vsftps` +- `VSFTPD`: Absolute path. Default: search for `vsftpd` - `SSHD`: Absolute path. Default: search for `sshd` - `SFTPD`: Absolute path. Default: search for `sftp-server` diff --git a/docs/RUSTLS.md b/docs/RUSTLS.md index b1c8044e8e3e..ed032f7fb628 100644 --- a/docs/RUSTLS.md +++ b/docs/RUSTLS.md @@ -56,7 +56,7 @@ Once downloaded, build `curl` using `--with-rustls` and the path to the extracte Building `rustls-ffi` from source requires both a rust compiler, and the [cargo-c] cargo plugin. To install a Rust compiler, use [rustup] or your package manager to install -the **1.73+** or newer toolchain. +the **1.73** or newer toolchain. To install `cargo-c`, use your [package manager][cargo-c pkg], download [a pre-built archive][cargo-c prebuilt], or build it from source with `cargo install cargo-c`. diff --git a/docs/internals/CHECKSRC.md b/docs/internals/CHECKSRC.md index b94adc3c072e..4719dd6ba6f5 100644 --- a/docs/internals/CHECKSRC.md +++ b/docs/internals/CHECKSRC.md @@ -40,21 +40,21 @@ warnings are: code style mandates the assignment to be done outside of it. - `ASTERISKNOSPACE`: A pointer was declared like `char* name` instead of the - more appropriate `char *name` style. The asterisk should sit next to the - name. + more appropriate `char *name` style. The asterisk should sit next to the + name. - `ASTERISKSPACE`: A pointer was declared like `char * name` instead of the - more appropriate `char *name` style. The asterisk should sit right next to - the name without a space in between. + more appropriate `char *name` style. The asterisk should sit right next to + the name without a space in between. - `BADCOMMAND`: There is a bad `checksrc` instruction in the code. See the - **Ignore certain warnings** section below for details. + **Ignore certain warnings** section below for details. - `BANNEDFUNC`: A banned function was used. The functions sprintf, vsprintf, - strcat, strncat, gets are **never** allowed in curl source code. + strcat, strncat, gets are **never** allowed in curl source code. - `BRACEELSE`: '} else' on the same line. The else is supposed to be on the - following line. + following line. - `BRACEPOS`: wrong position for an open brace (`{`). @@ -80,8 +80,8 @@ warnings are: string, use it - `INDENTATION`: detected a wrong start column for code. Note that this - warning only checks some specific places and can certainly miss many bad - indentations. + warning only checks some specific places and can certainly miss many bad + indentations. - `LONGLINE`: A line is longer than 79 columns. @@ -99,7 +99,7 @@ warnings are: - `PARENBRACE`: `){` was used without sufficient space in between. - `RETURNNOSPACE`: `return` was used without space between the keyword and the - following value. + following value. - `SEMINOSPACE`: There was no space (or newline) following a semicolon. @@ -107,7 +107,7 @@ warnings are: `sizeof(int)` style. - `SNPRINTF` - Found use of `snprintf()`. Since we use an internal replacement - with a different return code etc, we prefer `curl_msnprintf()`. + with a different return code etc, we prefer `curl_msnprintf()`. - `SPACEAFTERPAREN`: there was a space after open parenthesis, `( text`. @@ -116,7 +116,7 @@ warnings are: - `SPACEBEFORECOMMA`: there was a space before a comma, `one , two`. - `SPACEBEFOREPAREN`: there was a space before an open parenthesis, `if (`, - where one was not expected + where one was not expected - `SPACESEMICOLON`: there was a space before semicolon, ` ;`. @@ -127,7 +127,7 @@ warnings are: - `TYPEDEFSTRUCT`: we frown upon (most) typedefed structs - `UNUSEDIGNORE`: a `checksrc` inlined warning ignore was asked for but not - used, that is an ignore that should be removed or changed to get used. + used, that is an ignore that should be removed or changed to get used. - `USESAFEFREE`: there was a `curlx_free(var)` call made right before assigning NULL to `var`. We prefer replacing that with `curlx_safefree()`, which is @@ -144,7 +144,7 @@ so: `enable ` Currently these are the extended warnings which can be enabled: - `COPYRIGHTYEAR`: the current changeset has not updated the copyright year in - the source file + the source file - `STRERROR`: use of banned function strerror() diff --git a/include/curl/curlver.h b/include/curl/curlver.h index f93ca6fd0326..5cc0e27c61dd 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -47,7 +47,7 @@ Where XX, YY and ZZ are the main version, release and patch numbers in hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + using two digits. Version 1.2 would appear as "0x010200" while 9.11.7 appears as "0x090b07". This 6-digit (24 bits) hexadecimal number does not show pre-release number, diff --git a/lib/capsule.c b/lib/capsule.c index 698cdcdb5669..f8dfcc050cc0 100644 --- a/lib/capsule.c +++ b/lib/capsule.c @@ -43,12 +43,12 @@ static uint64_t capsule_ntohll(uint64_t value) #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) return value; #elif (defined(__GNUC__) || defined(__clang__)) && \ - defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) return __builtin_bswap64(value); #else union { - uint64_t u64; - uint32_t u32[2]; + uint64_t u64; + uint32_t u32[2]; } src, dst; src.u64 = value; diff --git a/lib/cf-capsule.c b/lib/cf-capsule.c index dd740c0f157f..afa0ae713ad5 100644 --- a/lib/cf-capsule.c +++ b/lib/cf-capsule.c @@ -238,7 +238,7 @@ CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, return CURLE_OUT_OF_MEMORY; Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); + BUFQ_OPT_SOFT_LIMIT); result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx); if(result) { diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index b2cc49896fb9..5eaa9571e646 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -1518,6 +1518,3 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, } #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY && USE_NGHTTP2 */ - -/* Do not leak this filter's call_data accessor in unity builds. */ -#undef CF_CTX_CALL_DATA diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 6af81d6d5908..4c0c21462e31 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -21,18 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ - defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ - defined(USE_NGTCP2) && defined(USE_OPENSSL) + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) #include -#include + #ifdef USE_OPENSSL #include -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #include #elif defined(OPENSSL_QUIC_API2) #include @@ -40,11 +39,15 @@ #include #endif #include "vtls/openssl.h" -#endif /* USE_OPENSSL */ +#endif #include #include "urldata.h" +#include "url.h" +#include "uint-hash.h" +#include "curl_trc.h" +#include "rand.h" #include "hash.h" #include "sendf.h" #include "multiif.h" @@ -57,17 +60,13 @@ #include "dynhds.h" #include "http_proxy.h" #include "select.h" -#include "uint-hash.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" -#include "curl_trc.h" #include "cf-h3-proxy.h" -#include "url.h" #include "capsule.h" -#include "rand.h" /* A stream window is the maximum amount we need to buffer for * each active transfer. We use HTTP/3 flow control and only ACK @@ -79,7 +78,7 @@ /* The pool keeps spares around and half of a full stream window * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep + * The benefit of the pool is that stream buffers do not keep * spares. Memory consumption goes down when streams run empty, * have a large upload done, etc. */ #define PROXY_H3_STREAM_POOL_SPARES \ @@ -90,11 +89,10 @@ #define PROXY_H3_STREAM_SEND_CHUNKS \ (PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) -#define PROXY_QUIC_MAX_STREAMS (256*1024) -#define PROXY_QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) +#define PROXY_QUIC_MAX_STREAMS (256 * 1024) +#define PROXY_QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) -typedef enum -{ +typedef enum { H3_TUNNEL_INIT, /* init/default/no tunnel state */ H3_TUNNEL_CONNECT, /* CONNECT request is being sent */ H3_TUNNEL_RESPONSE, /* CONNECT response received completely */ @@ -104,8 +102,7 @@ typedef enum struct h3_proxy_stream_ctx; -struct h3_tunnel_stream -{ +struct h3_tunnel_stream { struct http_resp *resp; char *authority; struct h3_proxy_stream_ctx *stream; @@ -218,7 +215,7 @@ struct cf_ngtcp2_proxy_ctx { struct curl_tls_ctx tls; #ifdef OPENSSL_QUIC_API2 ngtcp2_crypto_ossl_ctx *ossl_ctx; -#endif /* OPENSSL_QUIC_API2 */ +#endif ngtcp2_path connected_path; ngtcp2_conn *qconn; ngtcp2_cid dcid; @@ -231,33 +228,31 @@ struct cf_ngtcp2_proxy_ctx { struct cf_call_data call_data; nghttp3_conn *h3conn; nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; - /* hash `data->mid` to `h3_proxy_stream_ctx` */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; /* hash data->mid to h3_proxy_stream_ctx */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ int qlogfd; BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ }; -struct cf_h3_proxy_ctx -{ +struct cf_h3_proxy_ctx { struct cf_ngtcp2_proxy_ctx *ngtcp2_ctx; - struct cf_call_data call_data; /* fallback before backend ctx exists */ - struct bufq inbufq; /* network receive buffer */ - struct Curl_peer *dest; /* where to tunnel to */ + struct cf_call_data call_data; /* fallback before backend ctx exists */ + struct bufq inbufq; /* network receive buffer */ + struct Curl_peer *dest; /* where to tunnel to */ struct h3_tunnel_stream tunnel; /* our tunnel CONNECT stream */ BIT(connected); BIT(udp_tunnel); @@ -266,12 +261,11 @@ struct cf_h3_proxy_ctx /** * All about the H3 internals of a stream */ -struct h3_proxy_stream_ctx -{ - int64_t id; /* HTTP/3 stream identifier */ +struct h3_proxy_stream_ctx { + int64_t id; /* HTTP/3 stream identifier */ struct bufq sendbuf; /* h3 request body */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - uint64_t error3; /* HTTP/3 stream error code */ + uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ curl_off_t tun_data_recvd; /* number of bytes received over tunnel */ uint64_t rx_offset; /* current receive offset */ @@ -286,7 +280,7 @@ struct h3_proxy_stream_ctx BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; -#define H3_PROXY_STREAM_CTX(ctx, data) \ +#define H3_PROXY_STREAM_CTX(ctx, data) \ ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) #define H3_STREAM_ID(stream) ((stream)->id) @@ -356,12 +350,12 @@ static void cf_ngtcp2_proxy_ctx_close(struct cf_ngtcp2_proxy_ctx *ctx) ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); ctx->ossl_ctx = NULL; } -#endif /* OPENSSL_QUIC_API2 */ +#endif ctx->call_data = save; } static void cf_ngtcp2_proxy_setup_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -393,8 +387,8 @@ static void cf_ngtcp2_proxy_setup_keep_alive(struct Curl_cfilter *cf, ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " "set keep-alive to %" PRIu64 " ms.", - (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS), - (uint64_t)(keep_ns / NGTCP2_MILLISECONDS)); + rp->max_idle_timeout / NGTCP2_MILLISECONDS, + keep_ns / NGTCP2_MILLISECONDS); } } @@ -451,10 +445,10 @@ static void proxy_quic_printf(void *user_data, const char *fmt, ...) va_end(ap); curl_mfprintf(stderr, "\n"); } -#endif /* DEBUG_NGTCP2 */ +#endif static void proxy_qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) + const void *data, size_t datalen) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -483,7 +477,7 @@ static void quic_settings_proxy(struct cf_ngtcp2_proxy_ctx *ctx, s->log_printf = proxy_quic_printf; #else s->log_printf = NULL; -#endif /* DEBUG_NGTCP2 */ +#endif s->initial_ts = pktx->ts; s->handshake_timeout = (data->set.connecttimeout > 0) ? @@ -496,7 +490,7 @@ static void quic_settings_proxy(struct cf_ngtcp2_proxy_ctx *ctx, /* try ten times the ngtcp2 defaults here for problems with Caddy */ s->glitch_ratelim_burst = 1000 * 10; s->glitch_ratelim_rate = 33 * 10; -#endif /* NGTCP2_SETTINGS_V3 */ +#endif t->initial_max_data = 10 * PROXY_H3_STREAM_WINDOW_SIZE; t->initial_max_stream_data_bidi_local = PROXY_H3_STREAM_WINDOW_SIZE; t->initial_max_stream_data_bidi_remote = PROXY_H3_STREAM_WINDOW_SIZE; @@ -510,7 +504,7 @@ static void quic_settings_proxy(struct cf_ngtcp2_proxy_ctx *ctx, } static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data); static bool cf_ngtcp2_proxy_err_is_fatal(int code) { @@ -520,7 +514,7 @@ static bool cf_ngtcp2_proxy_err_is_fatal(int code) } static void cf_ngtcp2_proxy_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) + struct Curl_easy *data, int code) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -545,7 +539,7 @@ static bool cf_ngtcp2_proxy_h3_err_is_fatal(int code) } static void cf_ngtcp2_proxy_h3_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) + struct Curl_easy *data, int code) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -558,7 +552,6 @@ static void cf_ngtcp2_proxy_h3_err_set(struct Curl_cfilter *cf, } /* How to access `call_data` from a cf_h3_proxy filter */ -#undef CF_CTX_CALL_DATA static struct cf_call_data *cf_h3_proxy_call_data(struct Curl_cfilter *cf) { struct cf_h3_proxy_ctx *ctx = cf ? cf->ctx : NULL; @@ -571,6 +564,7 @@ static struct cf_call_data *cf_h3_proxy_call_data(struct Curl_cfilter *cf) return &ctx->call_data; } +#undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) (*cf_h3_proxy_call_data(cf)) static void cf_h3_proxy_ctx_clear(struct cf_h3_proxy_ctx *ctx) @@ -590,7 +584,7 @@ static void cf_h3_proxy_ctx_free(struct cf_h3_proxy_ctx *ctx) } static CURLcode h3_proxy_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -631,8 +625,8 @@ static CURLcode h3_proxy_data_setup(struct Curl_cfilter *cf, } static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) + uint64_t datalen, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -667,8 +661,8 @@ static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, } static int cb_h3_proxy_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) + uint64_t app_error_code, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -726,8 +720,7 @@ static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, if(!stream->rx_offset) return; - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); if(avail <= 0) { /* nothing available, do not extend the rx offset */ CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 @@ -762,8 +755,8 @@ static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, } static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -808,8 +801,8 @@ static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, } static int cb_h3_proxy_deferred_consume(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *user_data, - void *stream_user_data) + size_t consumed, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -828,13 +821,12 @@ static int cb_h3_proxy_deferred_consume(nghttp3_conn *conn, int64_t stream_id, return 0; } -static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t sid, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) +static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - int64_t stream_id = sid; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); @@ -888,12 +880,11 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t sid, } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base); + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, + (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); result = Curl_dynhds_add(&proxy_ctx->tunnel.resp->headers, - (const char *)h3name.base, h3name.len, - (const char *)h3val.base, h3val.len); + (const char *)h3name.base, h3name.len, + (const char *)h3val.base, h3val.len); if(result) { return -1; } @@ -901,14 +892,14 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t sid, return 0; } -static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t sid, - int fin, void *user_data, void *stream_user_data) +static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t stream_id, + int fin, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; struct Curl_easy *data = stream_user_data; - int64_t stream_id = sid; struct h3_proxy_stream_ctx *stream; (void)conn; (void)stream_id; @@ -932,8 +923,8 @@ static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t sid, if(!stream) return 0; - CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", - stream_id, stream->status_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, + stream->status_code); if(!proxy_ctx->tunnel.has_final_response) { if(stream->status_code / 100 != 1) { @@ -949,9 +940,9 @@ static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t sid, return 0; } -static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t sid, - uint64_t app_error_code, void *user_data, - void *stream_user_data) +static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -961,7 +952,7 @@ static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t sid, (void)stream_user_data; if(ctx) { - int rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, sid, + int rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, app_error_code); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { @@ -972,15 +963,14 @@ static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t sid, return 0; } -static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t sid, - uint64_t app_error_code, void *user_data, - void *stream_user_data) +static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; struct Curl_easy *data = stream_user_data; - int64_t stream_id = sid; int rv; (void)conn; @@ -1001,11 +991,13 @@ static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t sid, return 0; } -static nghttp3_ssize -cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) +static nghttp3_ssize cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, + int64_t stream_id, + nghttp3_vec *vec, + size_t veccnt, + uint32_t *pflags, + void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; @@ -1094,14 +1086,14 @@ static nghttp3_callbacks ngh3_proxy_callbacks = { cb_h3_proxy_reset_stream, NULL, /* shutdown */ NULL, /* recv_settings (deprecated) */ -#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ NULL, /* recv_origin */ NULL, /* end_origin */ NULL, /* rand */ -#endif /* NGHTTP3_CALLBACKS_V2 */ +#endif #ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ NULL, /* recv_settings2 */ -#endif /* NGHTTP3_CALLBACKS_V3 */ +#endif }; #if NGTCP2_VERSION_NUM < 0x011100 @@ -1111,8 +1103,7 @@ struct cf_ngtcp2_proxy_sfind_ctx { uint32_t mid; }; -static bool cf_ngtcp2_proxy_sfind(uint32_t mid, void *value, - void *user_data) +static bool cf_ngtcp2_proxy_sfind(uint32_t mid, void *value, void *user_data) { struct cf_ngtcp2_proxy_sfind_ctx *fctx = user_data; struct h3_proxy_stream_ctx *stream = value; @@ -1125,8 +1116,8 @@ static bool cf_ngtcp2_proxy_sfind(uint32_t mid, void *value, return TRUE; /* continue */ } -static struct h3_proxy_stream_ctx * -cf_ngtcp2_proxy_get_stream(struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) +static struct h3_proxy_stream_ctx *cf_ngtcp2_proxy_get_stream( + struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) { struct cf_ngtcp2_proxy_sfind_ctx fctx; fctx.stream_id = stream_id; @@ -1135,8 +1126,8 @@ cf_ngtcp2_proxy_get_stream(struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) return fctx.stream; } #else -static struct h3_proxy_stream_ctx * -cf_ngtcp2_proxy_get_stream(struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) +static struct h3_proxy_stream_ctx *cf_ngtcp2_proxy_get_stream( + struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) { struct Curl_easy *data = ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); @@ -1204,8 +1195,7 @@ static CURLcode cf_ngtcp2_h3conn_init(struct Curl_cfilter *cf, rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, qpack_dec_stream_id); if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", - ngtcp2_strerror(rc)); + failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); return CURLE_QUIC_CONNECT_ERROR; } @@ -1260,7 +1250,7 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, #ifdef USE_GNUTLS int flags = gnutls_session_get_flags(ctx->tls.gtls.session); ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); -#endif /* USE_GNUTLS */ +#endif #ifdef USE_WOLFSSL #ifdef WOLFSSL_EARLY_DATA ctx->earlydata_accepted = @@ -1270,7 +1260,7 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, DEBUGASSERT(0); /* should not come here if ED is disabled. */ ctx->earlydata_accepted = FALSE; #endif /* WOLFSSL_EARLY_DATA */ -#endif /* USE_WOLFSSL */ +#endif CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? @@ -1291,14 +1281,13 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, } static int cb_ngtcp2_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t sid, uint64_t offset, + int64_t stream_id, uint64_t offset, const uint8_t *buf, size_t buflen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - int64_t stream_id = (int64_t)sid; nghttp3_ssize nconsumed; int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; struct Curl_easy *data = stream_user_data; @@ -1326,9 +1315,8 @@ static int cb_ngtcp2_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, * including QPACK HEADERS. In other words, it does not consume payload of * DATA frame. */ if(nconsumed) { - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, - (uint64_t)nconsumed); - ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); } return 0; @@ -1359,14 +1347,13 @@ static int cb_ngtcp2_acked_stream_data_offset(ngtcp2_conn *tconn, } static int cb_ngtcp2_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t sid, uint64_t app_error_code, + int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; struct Curl_easy *data = stream_user_data; - int64_t stream_id = (int64_t)sid; int rv; (void)tconn; @@ -1382,8 +1369,7 @@ static int cb_ngtcp2_stream_close(ngtcp2_conn *tconn, uint32_t flags, rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" - PRIu64 ") -> %d", stream_id, (uint64_t)app_error_code, - rv); + PRIu64 ") -> %d", stream_id, app_error_code, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { cf_ngtcp2_proxy_h3_err_set(cf, data, rv); return NGTCP2_ERR_CALLBACK_FAILURE; @@ -1403,9 +1389,8 @@ static int cb_ngtcp2_extend_max_local_streams_bidi(ngtcp2_conn *tconn, (void)tconn; ctx->max_bidi_streams = max_streams; if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 - ", used %" PRIu64, (uint64_t)ctx->max_bidi_streams, - (uint64_t)ctx->used_bidi_streams); + CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 ", used %" PRIu64, + ctx->max_bidi_streams, ctx->used_bidi_streams); return 0; } @@ -1466,14 +1451,13 @@ static int cb_ngtcp2_get_new_connection_id2(ngtcp2_conn *tconn, } #endif -static int cb_ngtcp2_stream_reset(ngtcp2_conn *tconn, int64_t sid, +static int cb_ngtcp2_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - int64_t stream_id = (int64_t)sid; struct Curl_easy *data = stream_user_data; int rv; (void)tconn; @@ -1514,7 +1498,7 @@ static int cb_ngtcp2_extend_max_stream_data(ngtcp2_conn *tconn, stream = H3_PROXY_STREAM_CTX(ctx, s_data); if(stream && stream->quic_flow_blocked) { CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", - (int64_t)stream_id); + stream_id); stream->quic_flow_blocked = FALSE; Curl_multi_mark_dirty(s_data); } @@ -1611,13 +1595,13 @@ static ngtcp2_callbacks ngtcp2_proxy_callbacks = { NULL, /* early_data_rejected */ #ifdef NGTCP2_CALLBACKS_V2 /* ngtcp2 v1.14.0+ */ NULL, /* begin_path_validation */ -#endif /* NGTCP2_CALLBACKS_V2 */ +#endif #ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ NULL, /* recv_stateless_reset2 */ cb_ngtcp2_get_new_connection_id2, /* get_new_connection_id2 */ NULL, /* dcid_status2 */ ngtcp2_crypto_get_path_challenge_data2_cb, /* get_path_challenge_data2 */ -#endif /* NGTCP2_CALLBACKS_V3 */ +#endif }; #if defined(_MSC_VER) && defined(_DLL) @@ -1642,7 +1626,7 @@ static CURLcode cf_ngtcp2_recv_pkts_proxy(const unsigned char *buf, CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", buflen, gso_size, ecn); ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - (socklen_t)ctx->q.local_addrlen); + ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, remote_addrlen); pi.ecn = (uint8_t)ecn; @@ -1705,8 +1689,8 @@ static CURLcode proxy_h3_progress_ingress_ngtcp2(struct Curl_cfilter *cf, * Return number of bytes written or -1 with *err set. */ static CURLcode proxy_read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - size_t *pnread) + unsigned char *buf, size_t buflen, + size_t *pnread) { struct proxy_pkt_io_ctx *x = userp; struct cf_h3_proxy_ctx *proxy_ctx = x->cf->ctx; @@ -1757,11 +1741,11 @@ static CURLcode proxy_read_pkt_to_send(void *userp, else if(n < 0) { switch(n) { case NGTCP2_ERR_STREAM_DATA_BLOCKED: { - struct h3_proxy_stream_ctx *stream = NULL; + struct h3_proxy_stream_ctx *stream; DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", - (int64_t)stream_id); + stream_id); stream = cf_ngtcp2_proxy_get_stream(ctx, stream_id); if(stream) /* it might be not one of our h3 streams? */ stream->quic_flow_blocked = TRUE; @@ -1851,7 +1835,7 @@ static CURLcode proxy_h3_progress_egress_ngtcp2(struct Curl_cfilter *cf, */ max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); path_max_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", send_quantum); @@ -1867,7 +1851,7 @@ static CURLcode proxy_h3_progress_egress_ngtcp2(struct Curl_cfilter *cf, size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); if((buflen >= send_quantum) || ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) - break; + break; DEBUGASSERT(nread > 0); ++pktcnt; if(pktcnt == 1) { @@ -1918,7 +1902,7 @@ static CURLcode proxy_h3_progress_egress_ngtcp2(struct Curl_cfilter *cf, } static CURLcode cf_ngtcp2_proxy_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) + struct Curl_easy *data, bool *done) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -1969,7 +1953,7 @@ static CURLcode cf_ngtcp2_proxy_shutdown(struct Curl_cfilter *cf, &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" PRIu64 ") -> %zd", ctx->last_error.type, - (uint64_t)ctx->last_error.error_code, (ssize_t)nwritten); + ctx->last_error.error_code, (ssize_t)nwritten); /* there are cases listed in ngtcp2 documentation where this call * may fail. Since we are doing a connection shutdown as graceful * as we can, such an error is ignored here. */ @@ -2020,7 +2004,7 @@ static CURLcode cf_ngtcp2_proxy_shutdown(struct Curl_cfilter *cf, } static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { bool done; cf_ngtcp2_proxy_shutdown(cf, data, &done); @@ -2044,8 +2028,8 @@ static void cf_ngtcp2_proxy_close(struct Curl_cfilter *cf, } static void cf_ngtcp2_proxy_stream_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream) + struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -2136,9 +2120,9 @@ static CURLcode check_and_set_expiry_ngtcp2(struct Curl_cfilter *cf, } static ssize_t proxy_recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream, - CURLcode *err) + struct Curl_easy *data, + struct h3_proxy_stream_ctx *stream, + CURLcode *err) { ssize_t nread = -1; *err = CURLE_OK; @@ -2180,10 +2164,10 @@ static ssize_t proxy_recv_closed_stream(struct Curl_cfilter *cf, return nread; } -static struct h3_proxy_stream_ctx * -h3_proxy_resolve_send_stream(struct cf_h3_proxy_ctx *proxy_ctx, - struct cf_ngtcp2_proxy_ctx *ctx, - struct Curl_easy *data) +static struct h3_proxy_stream_ctx *h3_proxy_resolve_send_stream( + struct cf_h3_proxy_ctx *proxy_ctx, + struct cf_ngtcp2_proxy_ctx *ctx, + struct Curl_easy *data) { struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); @@ -2328,8 +2312,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, } if(!Curl_bufq_is_empty(&proxy_ctx->inbufq)) { - result = Curl_bufq_cread(&proxy_ctx->inbufq, - buf, len, pnread); + result = Curl_bufq_cread(&proxy_ctx->inbufq, buf, len, pnread); if(result) goto out; } @@ -2340,8 +2323,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, /* inbufq had nothing before, maybe after progressing ingress? */ if(!*pnread && !Curl_bufq_is_empty(&proxy_ctx->inbufq)) { - result = Curl_bufq_cread(&proxy_ctx->inbufq, - buf, len, pnread); + result = Curl_bufq_cread(&proxy_ctx->inbufq, buf, len, pnread); if(result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) " "-> %zd, %d", @@ -2355,8 +2337,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, } else { if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", - stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); cf_ngtcp2_proxy_stream_close(cf, data, stream); result = stream->xfer_result; goto out; @@ -2451,7 +2432,7 @@ static void proxy_h3_submit(int64_t *pstream_id, *err = CURLE_SEND_ERROR; goto out; } - stream->id = (int64_t)sid; + stream->id = sid; ++ctx->used_bidi_streams; /* Set stream user data in ngtcp2 connection for callbacks */ @@ -2668,8 +2649,9 @@ static CURLcode cf_ngtcp2_proxy_adjust_pollset(struct Curl_cfilter *cf, bool c_exhaust, s_exhaust; CF_DATA_SAVE(save, cf, data); - c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || - !ngtcp2_conn_get_max_data_left(ctx->qconn)); + c_exhaust = want_send && + (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); s_exhaust = want_send && stream && H3_STREAM_ID(stream) >= 0 && stream->quic_flow_blocked; want_recv = (want_recv || c_exhaust || s_exhaust); @@ -2726,8 +2708,8 @@ static int proxy_quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { ngtcp2_crypto_conn_ref *cref; struct Curl_cfilter *cf; - struct cf_h3_proxy_ctx *proxy_ctx; struct cf_ngtcp2_proxy_ctx *ctx; + struct cf_h3_proxy_ctx *proxy_ctx; struct Curl_easy *data; cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); @@ -2751,7 +2733,7 @@ static int proxy_quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) quic_tp = (unsigned char *)tpbuf; quic_tp_len = (size_t)tplen; } -#endif /* HAVE_OPENSSL_EARLYDATA */ +#endif Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, SSL_version(ssl), "h3", quic_tp, quic_tp_len); } @@ -2766,7 +2748,7 @@ static CURLcode cf_ngtcp2_proxy_tls_ctx_setup(struct Curl_cfilter *cf, struct curl_tls_ctx *ctx = user_data; #ifdef USE_OPENSSL -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); @@ -2779,7 +2761,7 @@ static CURLcode cf_ngtcp2_proxy_tls_ctx_setup(struct Curl_cfilter *cf, failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); return CURLE_FAILED_INIT; } -#endif +#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ if(Curl_ssl_scache_use(cf, data)) { SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, SSL_SESS_CACHE_CLIENT | @@ -2795,10 +2777,10 @@ static CURLcode cf_ngtcp2_proxy_tls_ctx_setup(struct Curl_cfilter *cf, } static CURLcode cf_ngtcp2_proxy_on_session_reuse(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct alpn_spec *alpns, - struct Curl_ssl_session *scs, - bool *do_early_data) + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; @@ -2811,24 +2793,24 @@ static CURLcode cf_ngtcp2_proxy_on_session_reuse(struct Curl_cfilter *cf, #ifdef USE_GNUTLS ctx->earlydata_max = gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); -#endif /* USE_GNUTLS */ +#endif #ifdef USE_WOLFSSL #ifdef WOLFSSL_EARLY_DATA ctx->earlydata_max = scs->earlydata_max; #else ctx->earlydata_max = 0; #endif /* WOLFSSL_EARLY_DATA */ -#endif /* USE_WOLFSSL */ +#endif #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if((!ctx->earlydata_max)) { + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if(!ctx->earlydata_max) { CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); } else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); } else { int rv; @@ -2867,7 +2849,7 @@ static CURLcode cf_h3_proxy_ctx_init(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; const struct Curl_sockaddr_ex *sockaddr = NULL; int qfd; - static const struct alpn_spec ALPN_SPEC_H3 = {{ "h3", "h3-29" }, 2}; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; struct proxy_pkt_io_ctx pktx; ctx = curlx_calloc(1, sizeof(struct cf_ngtcp2_proxy_ctx)); @@ -2913,7 +2895,7 @@ static CURLcode cf_h3_proxy_ctx_init(struct Curl_cfilter *cf, goto out; /* Get remote address from the socket filter below */ if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, - CURL_UNCONST(&sockaddr))) + CURL_UNCONST(&sockaddr))) goto out; if(!sockaddr) goto out; @@ -2996,8 +2978,8 @@ static CURLcode cf_h3_proxy_ctx_init(struct Curl_cfilter *cf, } static CURLcode h3_submit_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_tunnel_stream *ts) + struct Curl_easy *data, + struct h3_tunnel_stream *ts) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; CURLcode result; @@ -3023,10 +3005,9 @@ static CURLcode h3_submit_CONNECT(struct Curl_cfilter *cf, return result; } -static CURLcode -h3_proxy_inspect_response(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_tunnel_stream *ts) +static CURLcode h3_proxy_inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; proxy_inspect_result res; @@ -3126,9 +3107,8 @@ static CURLcode cf_h3_proxy_quic_connect(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; if(cerr) { - CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" - PRIu64, - cerr->type, (uint64_t)cerr->error_code); + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, + cerr->type, cerr->error_code); switch(cerr->type) { case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: CURL_TRC_CF(data, cf, "error in version negotiation"); @@ -3242,13 +3222,11 @@ static CURLcode H3_CONNECT(struct Curl_cfilter *cf, return result; } -static CURLcode -cf_h3_proxy_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode cf_h3_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_call_data save = {0}; + struct cf_call_data save = { 0 }; CURLcode result = CURLE_OK; timediff_t check; struct h3_tunnel_stream *ts = &proxy_ctx->tunnel; @@ -3318,8 +3296,7 @@ static void h3_proxy_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) stream = H3_PROXY_STREAM_CTX(ctx, data); if(stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", - stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); cf_ngtcp2_proxy_stream_close(cf, data, stream); Curl_uint32_hash_remove(&ctx->streams, data->mid); if(!Curl_uint32_hash_count(&ctx->streams)) @@ -3420,21 +3397,21 @@ static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, } struct Curl_cftype Curl_cft_h3_proxy = { - "H3-PROXY", - CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SSL, - CURL_LOG_LVL_NONE, - cf_h3_proxy_destroy, - cf_h3_proxy_connect, - cf_h3_proxy_close, - cf_h3_proxy_shutdown, - cf_h3_proxy_adjust_pollset, - cf_h3_proxy_data_pending, - cf_h3_proxy_send, - cf_h3_proxy_recv, - cf_h3_proxy_cntrl, - cf_h3_proxy_is_alive, - Curl_cf_def_conn_keep_alive, - cf_h3_proxy_query, + "H3-PROXY", + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SSL, + CURL_LOG_LVL_NONE, + cf_h3_proxy_destroy, + cf_h3_proxy_connect, + cf_h3_proxy_close, + cf_h3_proxy_shutdown, + cf_h3_proxy_adjust_pollset, + cf_h3_proxy_data_pending, + cf_h3_proxy_send, + cf_h3_proxy_recv, + cf_h3_proxy_cntrl, + cf_h3_proxy_is_alive, + Curl_cf_def_conn_keep_alive, + cf_h3_proxy_query, }; CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, @@ -3473,6 +3450,3 @@ CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, } #endif - -/* Do not leak this filter's call_data accessor in unity builds. */ -#undef CF_CTX_CALL_DATA diff --git a/lib/cf-h3-proxy.h b/lib/cf-h3-proxy.h index c1d5dd151144..b2f16acc0eeb 100644 --- a/lib/cf-h3-proxy.h +++ b/lib/cf-h3-proxy.h @@ -27,8 +27,8 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ - defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ - defined(USE_NGTCP2) && defined(USE_OPENSSL) + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 7b851788ad8d..cb2381f3033f 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -168,8 +168,8 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; - result = EVP_DigestFinal_ex(*ctx, - tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? + CURLE_OK : CURLE_SSL_CIPHER; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); curlx_memzero(tmp_digest, sizeof(tmp_digest)); diff --git a/lib/http.c b/lib/http.c index c935d4f69f20..7b9fad95df4e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -338,7 +338,6 @@ static CURLcode http_output_bearer(struct Curl_easy *data) fail: return result; } - #endif #endif diff --git a/lib/http2.c b/lib/http2.c index 9e755a0e1da5..9eb1e0aeaa41 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -3021,6 +3021,3 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) } #endif /* !CURL_DISABLE_HTTP && USE_NGHTTP2 */ - -/* Do not leak this filter's call_data accessor in unity builds. */ -#undef CF_CTX_CALL_DATA diff --git a/lib/md5.c b/lib/md5.c index 9d339becfa80..1f1b4f8ad610 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -121,7 +121,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 #include typedef psa_hash_operation_t my_md5_ctx; diff --git a/lib/netrc.c b/lib/netrc.c index 48aaa7681617..599d8c69966b 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -46,8 +46,7 @@ /* .netrc is not really a standard. The GNU definition can be found here: - * https://www.gnu.org/software/inetutils/manual/\ - * html_node/The-_002enetrc-file.html + * https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html * This gives grammar like: * * LITERAL := \S+ | QUOTED @@ -81,8 +80,8 @@ #define NETRC_DEBUG 0 /* convert a dynbuf call CURLcode error to a NETRCcode error */ -#define curl2netrc(r) \ - ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \ +#define curl2netrc(r) \ + ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \ NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)) typedef enum { @@ -132,7 +131,6 @@ static const char *netrc_tokenstr(curl_netrc_token token) return "[???]"; } } - #endif static void netrc_lexer_init(struct netrc_lexer *lexer, diff --git a/lib/sha256.c b/lib/sha256.c index 6211d04cd008..047119044bb8 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -141,7 +141,7 @@ static void my_sha256_final(unsigned char *digest, void *ctx) } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 #include typedef psa_hash_operation_t my_sha256_ctx; diff --git a/lib/socks.c b/lib/socks.c index 0acc21d7badf..387ebfc168e5 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -958,13 +958,13 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | + | 1 | 1 | 0x00 | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ ATYP: - o IP v4 address: X'01', BND.ADDR = 4 byte - o domain name: X'03', BND.ADDR = [ 1 byte length, string ] - o IP v6 address: X'04', BND.ADDR = 16 byte + o IPv4 address: 0x01, BND.ADDR = 4 byte + o domain name: 0x03, BND.ADDR = [ 1 byte length, string ] + o IPv6 address: 0x04, BND.ADDR = 16 byte */ if(resp[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 6cafda2da05c..4b02c217be1f 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -24,8 +24,8 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + #include -#include #ifdef USE_OPENSSL #include @@ -45,6 +45,8 @@ #include "vtls/wolfssl.h" #endif +#include + #include "urldata.h" #include "url.h" #include "uint-hash.h" @@ -86,9 +88,9 @@ #error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE #endif -/* The pool keeps spares around and half of a full stream windows +/* The pool keeps spares around and half of a full stream window * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep + * The benefit of the pool is that stream buffers do not keep * spares. Memory consumption goes down when streams run empty, * have a large upload done, etc. */ #define H3_STREAM_POOL_SPARES 2 @@ -128,26 +130,26 @@ struct cf_ngtcp2_ctx { struct cf_call_data call_data; nghttp3_conn *h3conn; nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; /* hash data->mid to h3_stream_ctx */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ int qlogfd; - unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ + unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ size_t tunnel_inbuf_len; BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ }; /* How to access `call_data` from a cf_ngtcp2 filter */ @@ -432,11 +434,8 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) #ifdef DEBUG_NGTCP2 static void quic_printf(void *user_data, const char *fmt, ...) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - (void)ctx; /* need an easy handle to infof() message */ va_list ap; + (void)user_data; va_start(ap, fmt); curl_mvfprintf(stderr, fmt, ap); va_end(ap); @@ -529,10 +528,9 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms, remote transport[max_udp_payload=%" PRIu64 - ", initial_max_data=%" PRIu64 - "]", - curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), - rp->max_udp_payload_size, rp->initial_max_data); + ", initial_max_data=%" PRIu64 "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); } #endif @@ -822,7 +820,8 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, } #ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ -static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid, +static int cb_get_new_connection_id2( + ngtcp2_conn *tconn, ngtcp2_cid *cid, struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) { CURLcode result; @@ -1052,7 +1051,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) + const char *buf, size_t buflen, bool eos) { /* This function returns no error intentionally, but records * the result at the stream, skipping further writes once the @@ -1061,17 +1060,17 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, * send/recv callbacks. Closing the stream here leads to SEND/RECV * errors in other places that then overwrite the transfer's result. */ if(!stream->xfer_result) { - stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, buflen, eos); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, blen); + "bytes of headers", stream->id, stream->xfer_result, buflen); } } static void h3_xfer_write_resp(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) + const char *buf, size_t buflen, bool eos) { /* This function returns no error intentionally, but records * the result at the stream, skipping further writes once the @@ -1080,11 +1079,11 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, * send/recv callbacks. Closing the stream here leads to SEND/RECV * errors in other places that then overwrite the transfer's result. */ if(!stream->xfer_result) { - stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); + stream->xfer_result = Curl_xfer_write_resp(data, buf, buflen, eos); /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", - stream->id, stream->xfer_result, blen); + stream->id, stream->xfer_result, buflen); } } } @@ -1104,8 +1103,7 @@ static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, if(!stream->rx_offset) return; - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); if(avail <= 0) { /* nothing available, do not extend the rx offset */ CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 @@ -1136,7 +1134,7 @@ static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, } static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t blen, + const uint8_t *buf, size_t buflen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; @@ -1150,15 +1148,15 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, if(!stream) return NGHTTP3_ERR_CALLBACK_FAILURE; - h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE); + h3_xfer_write_resp(cf, data, stream, (const char *)buf, buflen, FALSE); - ngtcp2_conn_extend_max_offset(ctx->qconn, blen); - stream->rx_offset += blen; + ngtcp2_conn_extend_max_offset(ctx->qconn, buflen); + stream->rx_offset += buflen; if(stream->rx_offset_max < stream->rx_offset) stream->rx_offset_max = stream->rx_offset; CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, - stream->id, blen, stream->rx_offset_max - stream->rx_offset); + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); cf_ngtcp2_upd_rx_win(cf, data, stream); return 0; } @@ -1397,8 +1395,7 @@ static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, qpack_dec_stream_id); if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", - ngtcp2_strerror(rc)); + failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); return CURLE_QUIC_CONNECT_ERROR; } @@ -1444,7 +1441,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, /* incoming data frames on the h3 stream */ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t blen, size_t *pnread) + char *buf, size_t buflen, size_t *pnread) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1455,7 +1452,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, (void)ctx; (void)buf; - NOVERBOSE((void)blen); + NOVERBOSE((void)buflen); CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); @@ -1510,8 +1507,8 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(ctx->tls_vrfy_result) result = ctx->tls_vrfy_result; denied: - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", - stream ? stream->id : -1, blen, result, *pnread); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(buflen=%zu) -> %d, %zu", + stream ? stream->id : -1, buflen, result, *pnread); CF_DATA_RESTORE(cf, save); return result; } @@ -1680,7 +1677,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); if(rc) { - failf(data, "can get bidi streams"); + failf(data, "cannot open bidi streams"); result = CURLE_SEND_ERROR; goto out; } @@ -1958,8 +1955,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, return CURLE_OK; } if(result) { - CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", - result); + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", result); return result; } if(nread == 0) { @@ -2657,7 +2653,7 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); } else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); } else { int rv; @@ -2742,15 +2738,15 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, /* Direct UDP socket - get local address for ngtcp2 */ ctx->q.local_addrlen = sizeof(ctx->q.local_addr); rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); + &ctx->q.local_addrlen); if(rv == -1) return CURLE_QUIC_CONNECT_ERROR; ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, &ctx->connected_path, @@ -3168,6 +3164,3 @@ CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at) } #endif - -/* Do not leak this filter's call_data accessor in unity builds. */ -#undef CF_CTX_CALL_DATA diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 9ac657c2910b..a35abfb2c97a 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -255,8 +255,8 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, VERBOSE(++calls); } out: - CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu)" - " -> %d, sent=%zu", + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", VQUIC_SEND_METHOD, pktlen, gsolen, calls, result, *psent); return result; } @@ -293,8 +293,8 @@ static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "vquic_cf_send(len=%zu, gso=%zu, calls=%zu)" - " -> %d, sent=%zu", + CURL_TRC_CF(data, cf, + "vquic_cf_send(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", pktlen, gsolen, calls, result, *psent); return result; } @@ -323,8 +323,8 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } else { result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); - CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=1)" - " -> %d, sent=%zu", + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", VQUIC_SEND_METHOD, pktlen, gsolen, result, *psent); } if(!result) @@ -499,7 +499,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)", - mcount, SOCKERRNO, errstr); + mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -526,8 +526,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, result); Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -607,8 +608,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, result); return result; } @@ -671,8 +673,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 70edfc629c1c..37cf96e08ceb 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -26,7 +26,7 @@ * but vtls.c should ever call or use these functions. * * Note: do not use the GnuTLS' *_t variable type names in this source code, - * since they were not present in 1.0.X. + * since they were not present in 1.0.x. */ #include "curl_setup.h" diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 51c19267bd8e..9a15534252ec 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -1563,7 +1563,7 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { -#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 psa_status_t status; size_t sha256len_actual; status = psa_hash_compute(PSA_ALG_SHA_256, input, inputlen, diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 25b13955f264..d8edec9b2eab 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -720,15 +720,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(ca_info_blob) { result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); } else { result = add_certs_file_to_store(trust_store, - conn_config->CAfile, - data); + conn_config->CAfile, + data); } if(result == CURLE_OK) { if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { diff --git a/projects/vms/build_gnv_curl_pcsi_desc.com b/projects/vms/build_gnv_curl_pcsi_desc.com index 566384de0257..cbdd6379693b 100644 --- a/projects/vms/build_gnv_curl_pcsi_desc.com +++ b/projects/vms/build_gnv_curl_pcsi_desc.com @@ -134,7 +134,7 @@ $ write pdsc " end if;" $! $write pdsc " software VMSPORTS ''base' ZLIB ;" $write pdsc - - " if (not ) ;" + " if (not ) ;" $write pdsc " error NEED_ZLIB;" $write pdsc " end if;" $! diff --git a/projects/vms/build_vms.com b/projects/vms/build_vms.com index 253fccbc8074..283e39ecf493 100644 --- a/projects/vms/build_vms.com +++ b/projects/vms/build_vms.com @@ -38,7 +38,7 @@ $! Always link a debug image. $! NOIEEE Do not use IEEE floating point. (Alpha/I64) $! VAX must always use DFLOAT $! NOLARGE Disable large-file support if large file support available. -$! (Non-VAX, VMS >= V7.2.) +$! (Non-VAX, VMS >= v7.2.) $! NOLDAP Disable LDAP support if LDAP is available. $! NOKERBEROS Disable Kerberos support if Kerberos is available. $! LIST Create C compiler listings and linker maps. diff --git a/projects/vms/curl_gnv_build_steps.txt b/projects/vms/curl_gnv_build_steps.txt index c02c898dad7c..687b7b63f498 100644 --- a/projects/vms/curl_gnv_build_steps.txt +++ b/projects/vms/curl_gnv_build_steps.txt @@ -20,7 +20,7 @@ Currently building curl using GNV takes longer than building curl via DCL. The GNV procedure actually uses the same configure and makefiles that Unix builds use. -Building curl on OpenVMS using GNV requires GNV V2.1-2 or the updated +Building curl on OpenVMS using GNV requires GNV v2.1-2 or the updated images that are available via anonymous FTP at encompasserve.org in the gnv directory. It also requires the GNV Bash 4.2.45 kit as an update from the same location or from the sourceforge.net GNV project. diff --git a/projects/vms/readme b/projects/vms/readme index 661dc9b471d3..9db0ee38735b 100644 --- a/projects/vms/readme +++ b/projects/vms/readme @@ -19,9 +19,9 @@ curl_gnv_build_steps.txt and other useful information. Prerequisites: -OpenVMS V7.0 or later (any platform) -DECC V6.5 or later -OpenSSL or hp SSL, if you want SSL support +OpenVMS v7.0 or later (any platform) +DECC v6.5 or later +OpenSSL or HP SSL, if you want SSL support What is Here: diff --git a/src/tool_getpass.c b/src/tool_getpass.c index 68a16cab3e61..f0eeebcc4164 100644 --- a/src/tool_getpass.c +++ b/src/tool_getpass.c @@ -58,7 +58,7 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) long sts; short chan; - /* iosbdef.h was not in VAX V7.2 or CC 6.4 */ + /* iosbdef.h was not in VAX v7.2 or CC 6.4 */ struct _isb { short int iosb$w_status; /* status */ short int iosb$w_bcnt; /* byte count */ diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 416c342a6005..3a55d41b2bcf 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -176,7 +176,7 @@ def test_20_07_data_large_small_recv(self, env: Env, ws_echo, model): r.check_exit_code(0) # Send large frames and simulate send blocking on 8192 bytes chunks - # Simlates error reported in #15865 + # Simulates error reported in #15865 @pytest.mark.parametrize("model", [ pytest.param(1, id='multi_perform'), pytest.param(2, id='curl_ws_send+recv'), diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index a2032f82ce5f..3b43cfce0d10 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -433,7 +433,7 @@ def h2o_version(self): return self._h2o_version @property - def tcpdmp(self) -> Optional[str]: + def tcpdump(self) -> Optional[str]: return self._tcpdump def clear_locks(self): @@ -677,7 +677,7 @@ def has_sftpd() -> bool: @staticmethod def tcpdump() -> Optional[str]: - return Env.CONFIG.tcpdmp + return Env.CONFIG.tcpdump def __init__(self, pytestconfig=None, env_config=None): if env_config: diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 69c7fd857b92..af759ce0d64b 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -205,7 +205,7 @@ static const struct testcase get_parts_list[] = { "http://-atest/", "http | [11] | [12] | [13] | -atest | [15] | / | [16] | [17]", 0, 0, CURLUE_OK }, - { /* Multiple trailing dots is not okey */ + { /* Multiple trailing dots is not okay */ "http://example.com../", "", 0, 0, CURLUE_BAD_HOSTNAME }, diff --git a/tests/libtest/lib1648.c b/tests/libtest/lib1648.c index e97b2bdc88ec..048068985b53 100644 --- a/tests/libtest/lib1648.c +++ b/tests/libtest/lib1648.c @@ -51,11 +51,11 @@ static CURLcode init1648(CURL *curl, const char *url, const char *proxy) return result; /* failure */ } -static CURLcode run1648(CURL *curl, const char *url, const char *userpwd) +static CURLcode run1648(CURL *curl, const char *url, const char *proxy) { CURLcode result = CURLE_OK; - result = init1648(curl, url, userpwd); + result = init1648(curl, url, proxy); if(result) return result; diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 8a4840ce69b4..20fdffa1bacb 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -44,7 +44,7 @@ * "password [string]" - the password that must match (if method is 2) * "backend [IPv4]" - numerical IPv4 address of backend to connect to * "backendport [number:0]" - TCP port of backend to connect to. 0 means use - the client's specified port number. + * the client's specified port number. * "method [number: 0]" - connect method to respond with: * 0 - no auth * 1 - GSSAPI (not supported) @@ -164,9 +164,9 @@ static void socksd_getconfig(void) logmsg("password [%s] set", s_config.password); } /* Methods: - o X'00' NO AUTHENTICATION REQUIRED - o X'01' GSSAPI - o X'02' USERNAME/PASSWORD + o 0x00 NO AUTHENTICATION REQUIRED + o 0x01 GSSAPI + o 0x02 USERNAME/PASSWORD */ else if(!strcmp(key, "method")) { pval = value; @@ -441,9 +441,9 @@ static curl_socket_t sockit(curl_socket_t fd) return CURL_SOCKET_BAD; } /* ATYP: - o IP V4 address: X'01' - o DOMAINNAME: X'03' - o IP V6 address: X'04' + o IPv4 address: 0x01 + o domain name: 0x03 + o IPv6 address: 0x04 */ type = buffer[SOCKS5_ATYP]; address = &buffer[SOCKS5_DSTADDR]; @@ -522,17 +522,17 @@ static curl_socket_t sockit(curl_socket_t fd) response[SOCKS5_VERSION] = s_config.responseversion; /* - o REP Reply field: - o X'00' succeeded - o X'01' general SOCKS server failure - o X'02' connection not allowed by ruleset - o X'03' Network unreachable - o X'04' Host unreachable - o X'05' Connection refused - o X'06' TTL expired - o X'07' Command not supported - o X'08' Address type not supported - o X'09' to X'FF' unassigned + o REP Reply field: + o 0x00 succeeded + o 0x01 general SOCKS server failure + o 0x02 connection not allowed by ruleset + o 0x03 Network unreachable + o 0x04 Host unreachable + o 0x05 Connection refused + o 0x06 TTL expired + o 0x07 Command not supported + o 0x08 Address type not supported + o 0x09 to 0xFF unassigned */ response[SOCKS5_REP] = rep; response[SOCKS5_RESERVED] = 0; /* must be zero */ From 4f31f076c2b4470fa74c359f7bd85fa74a4af9a6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 10:30:18 +0200 Subject: [PATCH 0223/1062] INSTALL-CMAKE.md: document `H2O` config variable Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21769 --- docs/INSTALL-CMAKE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index e6b52a17ee57..bfc1e451f2b7 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -482,6 +482,7 @@ Examples: - `APXS`: Absolute path. Default: search for `apxs` - `CADDY`: Absolute path. Default: search for `caddy` +- `H2O`: Absolute path. Default: search for `h2o` - `HTTPD_NGHTTPX`: Absolute path. Default: search for `nghttpx` - `HTTPD`: Absolute path. Default: search for `apache2` - `DANTED`: Absolute path. Default: search for `danted` From 7bcf34672d059313edb6f6bbab83c4285ef0a5ca Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 10:24:40 +0200 Subject: [PATCH 0224/1062] vtls_spack: drop redundant macro fallbacks For `UINT16_MAX` and `UINT32_MAX`. They are used in other sources without this fallback. Closes #21768 --- lib/vtls/vtls_spack.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index d633dcba4aef..c6f0921311ab 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -31,13 +31,6 @@ #include "vtls/vtls_spack.h" #include "curlx/strdup.h" -#ifndef UINT16_MAX -#define UINT16_MAX 0xffff -#endif -#ifndef UINT32_MAX -#define UINT32_MAX 0xffffffff -#endif - #define CURL_SPACK_VERSION 0x01 #define CURL_SPACK_IETF_ID 0x02 #define CURL_SPACK_VALID_UNTIL 0x03 From c7f0267eb774104173d49625d98d2da6f91fdf81 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 10:21:24 +0200 Subject: [PATCH 0225/1062] curl_sha512_256: fix result code on error Replace result code `CURLE_SSL_CIPHER` with `CURLE_BAD_FUNCTION_ARGUMENT` in case of a low-level digest function fails. Functionality is related to vauth, not SSL, and the operation is a digest, not a cipher. Also fix a indentation. Follow-up to 05268cf801a193b68411cfa298413c3e5ca79d4f #13070 Closes #21767 --- lib/curl_sha512_256.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index cb2381f3033f..3780dd42b4da 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -145,7 +145,7 @@ static CURLcode Curl_sha512_256_update(void *context, Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; if(!EVP_DigestUpdate(*ctx, data, length)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } @@ -169,13 +169,13 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? - CURLE_OK : CURLE_SSL_CIPHER; + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); curlx_memzero(tmp_digest, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? - CURLE_OK : CURLE_SSL_CIPHER; + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); @@ -206,7 +206,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, do { word32 ilen = (word32)CURLMIN(length, UINT_MAX); if(wc_Sha512_256Update(ctx, data, ilen)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; length -= ilen; data += ilen; } while(length); @@ -216,7 +216,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) { if(wc_Sha512_256Final(ctx, digest)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } From a1baacc670127ece13d8d4664ca4f768b283ae99 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 24 May 2026 14:55:05 +0200 Subject: [PATCH 0226/1062] schannel: check `schannel_sha256sum()` success, and more Also: - support 4GiB+ SHA-256 digest inputs. - check `CryptGetHashParam()` output size. - avoid overwriting existing digest when new digest calculation fails. - avoid adding digest hash element on failure. Closes #21739 --- lib/vtls/schannel.c | 121 ++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 84a078a5e22f..0298b2b65fc6 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -2640,16 +2640,24 @@ static CURLcode schannel_random(struct Curl_easy *data, return Curl_win32_random(entropy, length); } -static void schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) +static CURLcode schannel_checksum(const unsigned char *input, + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) { + CURLcode result = CURLE_FAILED_INIT; + HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; + size_t off; + + DWORD cbHashSize; + DWORD dwHashSizeLen; + DWORD dwChecksumLen; + /* since this can fail in multiple ways, zero memory first so we never * return old data */ @@ -2657,37 +2665,45 @@ static void schannel_checksum(const unsigned char *input, if(!CryptAcquireContext(&hProv, NULL, NULL, provType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return; /* failed */ + goto out; - do { - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) + goto out; - if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) - break; /* failed */ + result = CURLE_BAD_FUNCTION_ARGUMENT; - if(!CryptHashData(hHash, input, (DWORD)inputlen, 0)) - break; /* failed */ + off = 0; + while(off < inputlen) { + DWORD chunk = (DWORD)CURLMIN(inputlen - off, 0xffffffffUL); + if(!CryptHashData(hHash, input + off, chunk, 0)) + goto out; + off += chunk; + } - /* get hash size */ - if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, - &dwHashSizeLen, 0)) - break; /* failed */ + /* get hash size */ + cbHashSize = 0; + dwHashSizeLen = (DWORD)sizeof(cbHashSize); + if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, + &dwHashSizeLen, 0)) + goto out; - /* check hash size */ - if(checksumlen < cbHashSize) - break; /* failed */ + /* check if hash fits into the return buffer */ + if(checksumlen < cbHashSize) + goto out; - if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) - break; /* failed */ - } while(0); + dwChecksumLen = (DWORD)checksumlen; + if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0) && + dwChecksumLen == cbHashSize) + result = CURLE_OK; +out: if(hHash) CryptDestroyHash(hHash); if(hProv) CryptReleaseContext(hProv, 0); + + return result; } static CURLcode schannel_sha256sum(const unsigned char *input, @@ -2695,9 +2711,8 @@ static CURLcode schannel_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { - schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; + return schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); } static void *schannel_get_internals(struct ssl_connect_data *connssl, @@ -2755,10 +2770,11 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, if(share->CAinfo_blob_size != ca_info_blob->len) { return NULL; } - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - info_blob_digest, - CURL_SHA256_DIGEST_LENGTH); + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) + return NULL; if(memcmp(share->CAinfo_blob_digest, info_blob_digest, CURL_SHA256_DIGEST_LENGTH)) { return NULL; @@ -2796,7 +2812,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct schannel_cert_share *share; - size_t CAinfo_blob_size = 0; + unsigned char digest[CURL_SHA256_DIGEST_LENGTH]; char *CAfile = NULL; DEBUGASSERT(multi); @@ -2805,12 +2821,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, return FALSE; } + if(ca_info_blob) { + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, digest, sizeof(digest))) { + return FALSE; + } + } + else if(conn_config->CAfile) { + CAfile = curlx_strdup(conn_config->CAfile); + if(!CAfile) { + return FALSE; + } + } + share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); if(!share) { share = curlx_calloc(1, sizeof(*share)); if(!share) { + curlx_free(CAfile); return FALSE; } if(!Curl_hash_add2(&multi->proto_hash, @@ -2818,35 +2848,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1, share, schannel_cert_share_free)) { curlx_free(share); + curlx_free(CAfile); return FALSE; } } - if(ca_info_blob) { - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - share->CAinfo_blob_digest, - CURL_SHA256_DIGEST_LENGTH); - CAinfo_blob_size = ca_info_blob->len; - } - else { - if(conn_config->CAfile) { - CAfile = curlx_strdup(conn_config->CAfile); - if(!CAfile) { - return FALSE; - } - } - } - /* free old cache data */ if(share->cert_store) { CertCloseStore(share->cert_store, 0); } curlx_free(share->CAfile); + if(ca_info_blob) { + memcpy(share->CAinfo_blob_digest, digest, sizeof(digest)); + share->CAinfo_blob_size = ca_info_blob->len; + } + else + share->CAinfo_blob_size = 0; + share->time = curlx_now(); share->cert_store = cert_store; - share->CAinfo_blob_size = CAinfo_blob_size; share->CAfile = CAfile; return TRUE; } From 40f2da6ec3d032b1645c17cfa8dffeaaf7052c2a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 10:06:34 +0200 Subject: [PATCH 0227/1062] vtls: more large buffer support and error checks for SHA-256 - gnutls: support 4GiB+ SHA-256 digest inputs. - openssl: check success of low-level update/finish digest calls. - openssl: pass NULL to `EVP_DigestFinal_ex()` instead of discarding returned value. - wolfssl: support 4GiB+ SHA-256 digest inputs. - wolfssl: check success of low-level update/finish digest calls. - sync and tidy up argument names in low-level sha256_sum functions. Closes #21771 --- lib/vtls/gtls.c | 11 ++++++++--- lib/vtls/openssl.c | 18 ++++++++++-------- lib/vtls/wolfssl.c | 15 +++++++++++---- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 37cf96e08ceb..dcda203bb757 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -2269,14 +2269,19 @@ static CURLcode gtls_random(struct Curl_easy *data, return rc ? CURLE_FAILED_INIT : CURLE_OK; } -static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode gtls_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum, /* output */ size_t sha256len) { struct sha256_ctx SHA256pw; sha256_init(&SHA256pw); - sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + sha256_update(&SHA256pw, ilen, input); + len -= ilen; + input += ilen; + } while(len); #if NETTLE_VERSION_MAJOR >= 4 (void)sha256len; sha256_digest(&SHA256pw, sha256sum); diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index fde151590b93..8789dedc79f8 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5454,26 +5454,28 @@ static CURLcode ossl_random(struct Curl_easy *data, return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } -static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode ossl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { + CURLcode result = CURLE_OK; EVP_MD_CTX *mdctx; - unsigned int len = 0; (void)unused; mdctx = EVP_MD_CTX_create(); if(!mdctx) return CURLE_OUT_OF_MEMORY; if(!EVP_DigestInit(mdctx, EVP_sha256())) { - EVP_MD_CTX_destroy(mdctx); - return CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; + goto out; } - EVP_DigestUpdate(mdctx, tmp, tmplen); - EVP_DigestFinal_ex(mdctx, sha256sum, &len); + if(!EVP_DigestUpdate(mdctx, input, len) || + !EVP_DigestFinal_ex(mdctx, sha256sum, NULL)) + result = CURLE_BAD_FUNCTION_ARGUMENT; +out: EVP_MD_CTX_destroy(mdctx); - return CURLE_OK; + return result; } static bool ossl_cert_status_request(void) diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 26d260ae0fa7..96ad6554f4a6 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -2284,8 +2284,8 @@ static CURLcode wssl_random(struct Curl_easy *data, return CURLE_OK; } -static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode wssl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { @@ -2293,8 +2293,15 @@ static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ (void)unused; if(wc_InitSha256(&SHA256pw)) return CURLE_FAILED_INIT; - wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); - wc_Sha256Final(&SHA256pw, sha256sum); + do { + word32 ilen = (word32)CURLMIN(len, UINT32_MAX); + if(wc_Sha256Update(&SHA256pw, input, ilen)) + return CURLE_BAD_FUNCTION_ARGUMENT; + len -= ilen; + input += ilen; + } while(len); + if(wc_Sha256Final(&SHA256pw, sha256sum)) + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } From 50b1408f97d9e8fc585c5351cbf86bf60a30eb59 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 23 May 2026 01:05:10 +0200 Subject: [PATCH 0228/1062] autotools: mbedtls detection fixes - fix symbol used for first-round detection. - skip detecting mbedtls on custom path if custom path was not supplied. Reported-by: Ross Burton Fixes #21727 Closes #21729 --- m4/curl-mbedtls.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m4/curl-mbedtls.m4 b/m4/curl-mbedtls.m4 index 7c5bccd22983..6887302592d6 100644 --- a/m4/curl-mbedtls.m4 +++ b/m4/curl-mbedtls.m4 @@ -42,7 +42,7 @@ if test "x$OPT_MBEDTLS" != "xno"; then if test -z "$OPT_MBEDTLS"; then dnl check for lib first without setting any new path - AC_CHECK_LIB(mbedtls, mbedtls_havege_init, + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, dnl libmbedtls found, set the variable [ AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) @@ -58,7 +58,7 @@ if test "x$OPT_MBEDTLS" != "xno"; then addcflags="" mbedtlslib="" - if test "$USE_MBEDTLS" != "yes"; then + if test "$USE_MBEDTLS" != "yes" && test -n "$OPT_MBEDTLS"; then dnl add the path and test again addld=-L$OPT_MBEDTLS/lib$libsuff addcflags=-I$OPT_MBEDTLS/include From 1c302362e01672fa272da0b1186b159a4ece64b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 19:51:17 +0000 Subject: [PATCH 0229/1062] GHA: update dependency cloudflare/quiche to v0.29.1 Closes #21783 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 91a0e735aab7..cb46b879119a 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -52,7 +52,7 @@ env: OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.29.0 + QUICHE_VERSION: 0.29.1 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com From 4f8ed62c49c426fd7e06d4799f5b4e6533bcebe8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 22:56:01 +0200 Subject: [PATCH 0230/1062] cf-h3-proxy.c: bring back include Without it, it breaks regular (non-unity) builds. Fix regression from 7e1001bcd69967707c Closes #21785 --- lib/cf-h3-proxy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 4c0c21462e31..10f9e38e808b 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -28,6 +28,7 @@ defined(USE_NGTCP2) && defined(USE_OPENSSL) #include +#include #ifdef USE_OPENSSL #include From ead2e13a8e26b517f29e90bf953e298de3cb2eb0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 15:40:12 +0200 Subject: [PATCH 0231/1062] dnscache: remove Curl_dns_entry_link Unused function Closes #21774 --- lib/dnscache.c | 14 -------------- lib/dnscache.h | 5 ----- 2 files changed, 19 deletions(-) diff --git a/lib/dnscache.c b/lib/dnscache.c index 20f6b2171441..82114b492025 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -616,20 +616,6 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } -struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, - struct Curl_dns_entry *dns) -{ - if(!dns) - return NULL; - else { - struct Curl_dnscache *dnscache = dnscache_get(data); - dnscache_lock(data, dnscache); - dns->refcount++; - dnscache_unlock(data, dnscache); - return dns; - } -} - /* * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry. * When the reference count reaches 0, the entry is destroyed. It is important diff --git a/lib/dnscache.h b/lib/dnscache.h index ebe25f6dd416..9239977cf8c9 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -80,11 +80,6 @@ void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, struct Curl_https_rrinfo *hinfo); #endif /* USE_HTTPSRR */ -/* 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, From 7bc2bf7917afbc4a0abb836f942e1a55e95494e4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 15:42:27 +0200 Subject: [PATCH 0232/1062] http_proxy: make two proxy_create functions static And drop their `Curl_` prefixes. They are only used within this file. Closes #21775 --- lib/http_proxy.c | 24 ++++++++++++------------ lib/http_proxy.h | 11 ----------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 373865272bd7..fd85e8a0faf3 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -192,11 +192,11 @@ static int proxy_http_ver_major(proxy_http_ver ver) return 0; } -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *dest, - proxy_http_ver ver) +static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) { char *authority = NULL; int httpversion = proxy_http_ver_major(ver); @@ -268,11 +268,11 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, return result; } -CURLcode Curl_http_proxy_create_CONNECTUDP(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *dest, - proxy_http_ver ver) +static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) { const char *proxy_scheme = "http"; const char *proxy_host = cf->conn->http_proxy.peer->hostname; @@ -443,9 +443,9 @@ CURLcode Curl_http_proxy_create_tunnel_request( CURLcode result; if(udp_tunnel) - result = Curl_http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver); + result = http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver); else - result = Curl_http_proxy_create_CONNECT(preq, cf, data, dest, ver); + result = http_proxy_create_CONNECT(preq, cf, data, dest, ver); if(result) return result; diff --git a/lib/http_proxy.h b/lib/http_proxy.h index b0becedf03f8..0e44161375c2 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -50,17 +50,6 @@ typedef enum { PROXY_INSPECT_AUTH_RETRY /* Retry with auth */ } proxy_inspect_result; -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *dest, - proxy_http_ver ver); -CURLcode Curl_http_proxy_create_CONNECTUDP(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *dest, - proxy_http_ver ver); - /* Create CONNECT or CONNECT-UDP request */ CURLcode Curl_http_proxy_create_tunnel_request( struct httpreq **preq, struct Curl_cfilter *cf, From 98431e89bbfdae6ea096403fcd0cd1eb7599e3bc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 15:45:50 +0200 Subject: [PATCH 0233/1062] creds: remove two unused functions Curl_creds_same_user and Curl_creds_same_password Closes #21776 --- lib/creds.c | 10 ---------- lib/creds.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index e59c601b9585..779ae2f13167 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -149,16 +149,6 @@ void Curl_creds_unlink(struct Curl_creds **pcreds) } } -bool Curl_creds_same_user(struct Curl_creds *creds, const char *user) -{ - return creds && !Curl_timestrcmp(creds->user, user); -} - -bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd) -{ - return creds && !Curl_timestrcmp(creds->passwd, passwd); -} - bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) { return (c1 == c2) || diff --git a/lib/creds.h b/lib/creds.h index f979629f4938..fc978285dbb6 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -66,8 +66,6 @@ void Curl_creds_unlink(struct Curl_creds **pcreds); /* TRUE if both creds are NULL or have same username and password. */ bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); -bool Curl_creds_same_user(struct Curl_creds *creds, const char *user); -bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd); /* Provides properties for creds or, if creds is NULL, the empty string */ #define Curl_creds_has_user(c) ((c) && (c)->user[0]) From 73c2b4b4355aab31561af4b74d2f9a7732f75ae8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 15:50:19 +0200 Subject: [PATCH 0234/1062] capsule: make capsule_encap_udp_hdr static And drop the Curl_ prefix. Closes #21777 --- lib/capsule.c | 13 ++++++++++--- lib/capsule.h | 10 ---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/capsule.c b/lib/capsule.c index f8dfcc050cc0..2d9af5cea58f 100644 --- a/lib/capsule.c +++ b/lib/capsule.c @@ -135,8 +135,15 @@ static CURLcode capsule_decode_varint_at(struct bufq *recvbufq, return CURLE_OK; } -size_t Curl_capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, - size_t payload_len) +/** + * Write the capsule header (type + varint length + context ID) into `hdr`. + * @param hdr Output buffer (must be >= HTTP_CAPSULE_HEADER_MAX_SIZE) + * @param hdrlen Size of `hdr` in bytes + * @param payload_len Length of the UDP payload that follows + * @return Number of header bytes written, or 0 on error + */ +static size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len) { size_t off = 0; DEBUGASSERT(hdrlen >= HTTP_CAPSULE_HEADER_MAX_SIZE); @@ -156,7 +163,7 @@ CURLcode Curl_capsule_encap_udp_datagram(struct dynbuf *dyn, size_t hdr_len; curlx_dyn_init(dyn, HTTP_CAPSULE_HEADER_MAX_SIZE + blen); - hdr_len = Curl_capsule_encap_udp_hdr(hdr, sizeof(hdr), blen); + hdr_len = capsule_encap_udp_hdr(hdr, sizeof(hdr), blen); DEBUGASSERT(hdr_len); if(!hdr_len) return CURLE_FAILED_INIT; diff --git a/lib/capsule.h b/lib/capsule.h index fa7dec19cb0d..4d50f783600d 100644 --- a/lib/capsule.h +++ b/lib/capsule.h @@ -36,16 +36,6 @@ /* HTTP Capsule function prototypes */ -/** - * Write the capsule header (type + varint length + context ID) into `hdr`. - * @param hdr Output buffer (must be >= HTTP_CAPSULE_HEADER_MAX_SIZE) - * @param hdrlen Size of `hdr` in bytes - * @param payload_len Length of the UDP payload that follows - * @return Number of header bytes written, or 0 on error - */ -size_t Curl_capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, - size_t payload_len); - /** * Encapsulate UDP payload into HTTP Datagram capsule format * @param dyn Dynamic buffer to write capsule to From 15356f0d3651dc044423bcd361d58ac6ba8843bb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 18:06:51 +0200 Subject: [PATCH 0235/1062] lib1560: fix to propagate failure from `get_nothing()` Also: - check `curl_url()` for NULL where missing. - free memory `curl_url_get()` return pointer where missing. - propagate `curl_url_set()` errors in `clear_url()`, where missing. - add missing NULL-check before `strcmp()` in `clear_url()`. Closes #21780 --- tests/libtest/lib1560.c | 148 +++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 56 deletions(-) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index af759ce0d64b..c9363fe7782b 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1514,8 +1514,10 @@ static int set_url(void) for(i = 0; set_url_list[i].in && !error; i++) { CURLUcode rc; CURLU *urlp = curl_url(); - if(!urlp) + if(!urlp) { + error++; break; + } rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].in, set_url_list[i].urlflags); if(!rc) { @@ -1810,6 +1812,8 @@ static int scopeid(void) int error = 0; CURLUcode rc; char *url; + if(!u) + return 1; rc = curl_url_set(u, CURLUPART_URL, "https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0); @@ -1937,52 +1941,80 @@ static int scopeid(void) static int get_nothing(void) { CURLU *u = curl_url(); - if(u) { - char *p; - CURLUcode rc; - - rc = curl_url_get(u, CURLUPART_SCHEME, &p, 0); - if(rc != CURLUE_NO_SCHEME) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + int error = 0; + CURLUcode rc; + char *p = NULL; + if(!u) + return 1; - rc = curl_url_get(u, CURLUPART_HOST, &p, 0); - if(rc != CURLUE_NO_HOST) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_SCHEME, &p, 0); + if(rc != CURLUE_NO_SCHEME) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_USER, &p, 0); - if(rc != CURLUE_NO_USER) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_HOST, &p, 0); + if(rc != CURLUE_NO_HOST) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_PASSWORD, &p, 0); - if(rc != CURLUE_NO_PASSWORD) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_USER, &p, 0); + if(rc != CURLUE_NO_USER) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_OPTIONS, &p, 0); - if(rc != CURLUE_NO_OPTIONS) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_PASSWORD, &p, 0); + if(rc != CURLUE_NO_PASSWORD) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_PATH, &p, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); - else - curl_free(p); + rc = curl_url_get(u, CURLUPART_OPTIONS, &p, 0); + if(rc != CURLUE_NO_OPTIONS) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_QUERY, &p, 0); - if(rc != CURLUE_NO_QUERY) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_PATH, &p, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } + else + curl_free(p); - rc = curl_url_get(u, CURLUPART_FRAGMENT, &p, 0); - if(rc != CURLUE_NO_FRAGMENT) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_QUERY, &p, 0); + if(rc != CURLUE_NO_QUERY) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0); - if(rc != CURLUE_NO_ZONEID) - curl_mfprintf(stderr, "unexpected return code %d on line %d\n", rc, - __LINE__); + rc = curl_url_get(u, CURLUPART_FRAGMENT, &p, 0); + if(rc != CURLUE_NO_FRAGMENT) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - curl_url_cleanup(u); + rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0); + if(rc != CURLUE_NO_ZONEID) { + curl_mfprintf(stderr, "unexpected return code %d on line %d\n", rc, + __LINE__); + error++; + curl_free(p); } - return 0; + + curl_url_cleanup(u); + + return error; } static const struct clearurlcase clear_url_list[] = { @@ -2003,29 +2035,33 @@ static int clear_url(void) { CURLU *u = curl_url(); int i, error = 0; - if(u) { - char *p = NULL; - CURLUcode rc; + CURLUcode rc; + char *p = NULL; + if(!u) + return 1; - for(i = 0; clear_url_list[i].in && !error; i++) { - rc = curl_url_set(u, clear_url_list[i].part, clear_url_list[i].in, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + for(i = 0; clear_url_list[i].in && !error; i++) { + rc = curl_url_set(u, clear_url_list[i].part, clear_url_list[i].in, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } - rc = curl_url_set(u, CURLUPART_URL, NULL, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_set(u, CURLUPART_URL, NULL, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } - rc = curl_url_get(u, clear_url_list[i].part, &p, 0); - if(rc != clear_url_list[i].ucode || - (clear_url_list[i].out && strcmp(p, clear_url_list[i].out) != 0)) { + rc = curl_url_get(u, clear_url_list[i].part, &p, 0); + if(rc != clear_url_list[i].ucode || + (p && clear_url_list[i].out && strcmp(p, clear_url_list[i].out) != 0)) { - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); - error++; - } - if(rc == CURLUE_OK) - curl_free(p); + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; } + if(rc == CURLUE_OK) + curl_free(p); } curl_url_cleanup(u); @@ -2160,7 +2196,7 @@ static int urldup(void) static int test_api_errors(void) { CURLU *u = curl_url(); - char *p; + char *p = NULL; CURLUcode rc; if(!u) return 1; From a5fcaa85536603d765234d9e97156458bf95c485 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 18:39:10 +0200 Subject: [PATCH 0236/1062] m4: drop redundant conditions in TLS library detections Omit checking `OPT_` against `no` twice. Also: - openssl: drop stray `OPT_OPENSSL=off` check. Follow-up to 68d89f242cf9f6326e3b2f6fe119b7c74ef41c66 #6897 - rustls: drop no-op line. Follow-up to 9c4209837094781d5eef69ae6bcad0e86b64bf99 #13202 - gnutls: fix casing in comment. - merge `if` branches where possible after these changes. Closes #21781 --- m4/curl-amissl.m4 | 1 - m4/curl-gnutls.m4 | 184 +++++++++++++++++----------------- m4/curl-mbedtls.m4 | 149 ++++++++++++++-------------- m4/curl-openssl.m4 | 20 ++-- m4/curl-rustls.m4 | 5 +- m4/curl-schannel.m4 | 3 +- m4/curl-wolfssl.m4 | 233 ++++++++++++++++++++++---------------------- 7 files changed, 285 insertions(+), 310 deletions(-) diff --git a/m4/curl-amissl.m4 b/m4/curl-amissl.m4 index da90cc412d7a..4048037fd016 100644 --- a/m4/curl-amissl.m4 +++ b/m4/curl-amissl.m4 @@ -63,5 +63,4 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then else AC_MSG_RESULT(no) fi - ]) diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index 222386e0d9a7..93e1e1b9c0da 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -30,114 +30,110 @@ AC_DEFUN([CURL_WITH_GNUTLS], [ if test "x$OPT_GNUTLS" != "xno"; then ssl_msg= - if test "x$OPT_GNUTLS" != "xno"; then - - addld="" - addlib="" - gtlslib="" - version="" - addcflags="" - - if test "x$OPT_GNUTLS" = "xyes"; then - dnl this is with no particular path given - CURL_CHECK_PKGCONFIG(gnutls) - - if test "$PKGCONFIG" != "no"; then - addlib=`$PKGCONFIG --libs-only-l gnutls` - addld=`$PKGCONFIG --libs-only-L gnutls` - addcflags=`$PKGCONFIG --cflags-only-I gnutls` - version=`$PKGCONFIG --modversion gnutls` - gtlslib=`echo $addld | $SED -e 's/^-L//'` - else - dnl without pkg-config, we try libgnutls-config as that was how it - dnl used to be done - check=`libgnutls-config --version 2>/dev/null` - if test -n "$check"; then - addlib=`libgnutls-config --libs` - addcflags=`libgnutls-config --cflags` - version=`libgnutls-config --version` - gtlslib=`libgnutls-config --prefix`/lib$libsuff - fi - fi + addld="" + addlib="" + gtlslib="" + version="" + addcflags="" + + if test "x$OPT_GNUTLS" = "xyes"; then + dnl this is with no particular path given + CURL_CHECK_PKGCONFIG(gnutls) + + if test "$PKGCONFIG" != "no"; then + addlib=`$PKGCONFIG --libs-only-l gnutls` + addld=`$PKGCONFIG --libs-only-L gnutls` + addcflags=`$PKGCONFIG --cflags-only-I gnutls` + version=`$PKGCONFIG --modversion gnutls` + gtlslib=`echo $addld | $SED -e 's/^-L//'` else - dnl this is with a given path, first check if there is a libgnutls-config - dnl there and if not, make an educated guess - cfg=$OPT_GNUTLS/bin/libgnutls-config - check=`$cfg --version 2>/dev/null` + dnl without pkg-config, we try libgnutls-config as that was how it + dnl used to be done + check=`libgnutls-config --version 2>/dev/null` if test -n "$check"; then - addlib=`$cfg --libs` - addcflags=`$cfg --cflags` - version=`$cfg --version` - gtlslib=`$cfg --prefix`/lib$libsuff - else - dnl without pkg-config and libgnutls-config, we guess a lot! - addlib=-lgnutls - addld=-L$OPT_GNUTLS/lib$libsuff - addcflags=-I$OPT_GNUTLS/include - dnl we just do not know - version="" - gtlslib=$OPT_GNUTLS/lib$libsuff + addlib=`libgnutls-config --libs` + addcflags=`libgnutls-config --cflags` + version=`libgnutls-config --version` + gtlslib=`libgnutls-config --prefix`/lib$libsuff fi fi - - if test -z "$version"; then - dnl lots of efforts, still no go - version="unknown" + else + dnl this is with a given path, first check if there is a libgnutls-config + dnl there and if not, make an educated guess + cfg=$OPT_GNUTLS/bin/libgnutls-config + check=`$cfg --version 2>/dev/null` + if test -n "$check"; then + addlib=`$cfg --libs` + addcflags=`$cfg --cflags` + version=`$cfg --version` + gtlslib=`$cfg --prefix`/lib$libsuff + else + dnl without pkg-config and libgnutls-config, we guess a lot! + addlib=-lgnutls + addld=-L$OPT_GNUTLS/lib$libsuff + addcflags=-I$OPT_GNUTLS/include + dnl we just do not know + version="" + gtlslib=$OPT_GNUTLS/lib$libsuff fi + fi - if test -n "$addlib"; then + if test -z "$version"; then + dnl lots of efforts, still no go + version="unknown" + fi - CLEANLIBS="$LIBS" - CLEANCPPFLAGS="$CPPFLAGS" - CLEANLDFLAGS="$LDFLAGS" - CLEANLDFLAGSPC="$LDFLAGSPC" + if test -n "$addlib"; then - LIBS="$addlib $LIBS" - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" - fi + CLEANLIBS="$LIBS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" - dnl this function is selected since it was introduced in 3.1.10 - AC_CHECK_LIB(gnutls, gnutls_x509_crt_get_dn2, - [ - AC_DEFINE(USE_GNUTLS, 1, [if GnuTLS is enabled]) - GNUTLS_ENABLED=1 - USE_GNUTLS="yes" - ssl_msg="GnuTLS" - QUIC_ENABLED=yes - test "gnutls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - LIBS="$CLEANLIBS" - CPPFLAGS="$CLEANCPPFLAGS" - LDFLAGS="$CLEANLDFLAGS" - LDFLAGSPC="$CLEANLDFLAGSPC" - ]) - - if test "$USE_GNUTLS" = "yes"; then - AC_MSG_NOTICE([detected GnuTLS version $version]) - check_for_ca_bundle=1 - if test -n "$gtlslib"; then - dnl when shared libs were found in a path that the runtime - dnl linker does not search through, we need to add it to - dnl CURL_LIBRARY_PATH to prevent further configure tests to fail - dnl due to this - if test "$cross_compiling" != "yes"; then - CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$gtlslib" - export CURL_LIBRARY_PATH - AC_MSG_NOTICE([Added $gtlslib to CURL_LIBRARY_PATH]) - fi + LIBS="$addlib $LIBS" + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + fi + + dnl this function is selected since it was introduced in 3.1.10 + AC_CHECK_LIB(gnutls, gnutls_x509_crt_get_dn2, + [ + AC_DEFINE(USE_GNUTLS, 1, [if GnuTLS is enabled]) + GNUTLS_ENABLED=1 + USE_GNUTLS="yes" + ssl_msg="GnuTLS" + QUIC_ENABLED=yes + test "gnutls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + LIBS="$CLEANLIBS" + CPPFLAGS="$CLEANCPPFLAGS" + LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" + ]) + + if test "$USE_GNUTLS" = "yes"; then + AC_MSG_NOTICE([detected GnuTLS version $version]) + check_for_ca_bundle=1 + if test -n "$gtlslib"; then + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to + dnl CURL_LIBRARY_PATH to prevent further configure tests to fail + dnl due to this + if test "$cross_compiling" != "yes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$gtlslib" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $gtlslib to CURL_LIBRARY_PATH]) fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls" fi + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls" fi - - fi dnl GNUTLS not disabled + fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi +fi dnl GnuTLS not disabled dnl dnl Check which crypto backend GnuTLS uses diff --git a/m4/curl-mbedtls.m4 b/m4/curl-mbedtls.m4 index 6887302592d6..0c19f0723717 100644 --- a/m4/curl-mbedtls.m4 +++ b/m4/curl-mbedtls.m4 @@ -33,90 +33,85 @@ if test "x$OPT_MBEDTLS" != "xno"; then _ldflagspc=$LDFLAGSPC ssl_msg= - if test "x$OPT_MBEDTLS" != "xno"; then - - if test "x$OPT_MBEDTLS" = "xyes"; then - OPT_MBEDTLS="" + if test "x$OPT_MBEDTLS" = "xyes"; then + OPT_MBEDTLS="" + fi + + if test -z "$OPT_MBEDTLS"; then + dnl check for lib first without setting any new path + + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, + dnl libmbedtls found, set the variable + [ + AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) + MBEDTLS_ENABLED=1 + USE_MBEDTLS="yes" + ssl_msg="mbedTLS" + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], [], -lmbedx509 -lmbedcrypto) + fi + + addld="" + addlib="" + addcflags="" + mbedtlslib="" + + if test "$USE_MBEDTLS" != "yes" && test -n "$OPT_MBEDTLS"; then + dnl add the path and test again + addld=-L$OPT_MBEDTLS/lib$libsuff + addcflags=-I$OPT_MBEDTLS/include + mbedtlslib=$OPT_MBEDTLS/lib$libsuff + + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" fi - if test -z "$OPT_MBEDTLS"; then - dnl check for lib first without setting any new path - - AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, - dnl libmbedtls found, set the variable + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, [ - AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) - MBEDTLS_ENABLED=1 - USE_MBEDTLS="yes" - ssl_msg="mbedTLS" - test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], [], -lmbedx509 -lmbedcrypto) - fi - - addld="" - addlib="" - addcflags="" - mbedtlslib="" - - if test "$USE_MBEDTLS" != "yes" && test -n "$OPT_MBEDTLS"; then - dnl add the path and test again - addld=-L$OPT_MBEDTLS/lib$libsuff - addcflags=-I$OPT_MBEDTLS/include - mbedtlslib=$OPT_MBEDTLS/lib$libsuff - - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" + AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) + MBEDTLS_ENABLED=1 + USE_MBEDTLS="yes" + ssl_msg="mbedTLS" + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc + ], -lmbedx509 -lmbedcrypto) + fi + + if test "$USE_MBEDTLS" = "yes"; then + AC_MSG_NOTICE([detected mbedTLS]) + check_for_ca_bundle=1 + + LIBS="-lmbedtls -lmbedx509 -lmbedcrypto $LIBS" + + if test -n "$mbedtlslib"; then + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to + dnl CURL_LIBRARY_PATH to prevent further configure tests to fail + dnl due to this + if test "$cross_compiling" != "yes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$mbedtlslib" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) fi - - AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, - [ - AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) - MBEDTLS_ENABLED=1 - USE_MBEDTLS="yes" - ssl_msg="mbedTLS" - test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - CPPFLAGS=$_cppflags - LDFLAGS=$_ldflags - LDFLAGSPC=$_ldflagspc - ], -lmbedx509 -lmbedcrypto) fi - - if test "$USE_MBEDTLS" = "yes"; then - AC_MSG_NOTICE([detected mbedTLS]) - check_for_ca_bundle=1 - - LIBS="-lmbedtls -lmbedx509 -lmbedcrypto $LIBS" - - if test -n "$mbedtlslib"; then - dnl when shared libs were found in a path that the runtime - dnl linker does not search through, we need to add it to - dnl CURL_LIBRARY_PATH to prevent further configure tests to fail - dnl due to this - if test "$cross_compiling" != "yes"; then - CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$mbedtlslib" - export CURL_LIBRARY_PATH - AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) - fi - fi - dnl FIXME: Enable when mbedTLS was detected via pkg-config - if false; then - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls mbedx509 mbedcrypto" - fi - - dnl Check DES support in mbedTLS <4. - AC_CHECK_FUNCS(mbedtls_des_crypt_ecb) - if test "$ac_cv_func_mbedtls_des_crypt_ecb" = 'yes'; then - HAVE_MBEDTLS_DES_CRYPT_ECB=1 - fi + dnl FIXME: Enable when mbedTLS was detected via pkg-config + if false; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls mbedx509 mbedcrypto" fi - fi dnl mbedTLS not disabled + dnl Check DES support in mbedTLS <4. + AC_CHECK_FUNCS(mbedtls_des_crypt_ecb) + if test "$ac_cv_func_mbedtls_des_crypt_ecb" = 'yes'; then + HAVE_MBEDTLS_DES_CRYPT_ECB=1 + fi + fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi - +fi dnl mbedTLS not disabled ]) diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index aa9274fd25bc..816e7631fae5 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -220,10 +220,6 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" != "1"; then LIBS="$CLEANLIBS" - fi - - if test "x$OPT_OPENSSL" != "xoff" && - test "$OPENSSL_ENABLED" != "1"; then AC_MSG_ERROR([OpenSSL libs and/or directories were not found where specified!]) fi fi @@ -330,15 +326,14 @@ if test "x$OPT_OPENSSL" != "xno"; then fi fi - test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi + if test "$OPENSSL_ENABLED" != "1"; then + AC_MSG_NOTICE([OPT_OPENSSL: $OPT_OPENSSL]) + AC_MSG_NOTICE([OPENSSL_ENABLED: $OPENSSL_ENABLED]) + AC_MSG_ERROR([--with-openssl was given but OpenSSL could not be detected]) + fi -if test "x$OPT_OPENSSL" != "xno" && - test "$OPENSSL_ENABLED" != "1"; then - AC_MSG_NOTICE([OPT_OPENSSL: $OPT_OPENSSL]) - AC_MSG_NOTICE([OPENSSL_ENABLED: $OPENSSL_ENABLED]) - AC_MSG_ERROR([--with-openssl was given but OpenSSL could not be detected]) -fi + test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" +fi dnl OpenSSL not disabled if test "$OPENSSL_ENABLED" = "1"; then dnl --- @@ -395,6 +390,5 @@ AS_HELP_STRING([--disable-openssl-auto-load-config],[Disable automatic loading o AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration is not loaded automatically]) fi ]) - fi ]) diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index cf682e43d350..86bed3c32315 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -185,13 +185,10 @@ if test "x$OPT_RUSTLS" != "xno"; then test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" - if test "x$OPT_RUSTLS" != "xno" && - test "$RUSTLS_ENABLED" != "1"; then + if test "$RUSTLS_ENABLED" != "1"; then AC_MSG_NOTICE([OPT_RUSTLS: $OPT_RUSTLS]) AC_MSG_NOTICE([RUSTLS_ENABLED: $RUSTLS_ENABLED]) AC_MSG_ERROR([--with-rustls was given but Rustls could not be detected]) fi fi ]) - -RUSTLS_ENABLED diff --git a/m4/curl-schannel.m4 b/m4/curl-schannel.m4 index 3d0385347c10..e7358fbf62f2 100644 --- a/m4/curl-schannel.m4 +++ b/m4/curl-schannel.m4 @@ -26,8 +26,7 @@ AC_DEFUN([CURL_WITH_SCHANNEL], [ AC_MSG_CHECKING([whether to enable Windows native SSL/TLS]) if test "x$OPT_SCHANNEL" != "xno"; then ssl_msg= - if test "x$OPT_SCHANNEL" != "xno" && - test "$curl_cv_native_windows" = "yes"; then + if test "$curl_cv_native_windows" = "yes"; then if test "$curl_cv_winuwp" = "yes"; then AC_MSG_ERROR([UWP does not support Schannel.]) fi diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index 1d7b46721b32..a18659605e77 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -43,134 +43,129 @@ if test "$OPT_WOLFSSL" != "no"; then ssl_msg= - if test "$OPT_WOLFSSL" != "no"; then - - if test "$OPT_WOLFSSL" = "yes"; then - OPT_WOLFSSL="" + if test "$OPT_WOLFSSL" = "yes"; then + OPT_WOLFSSL="" + fi + + dnl try pkg-config magic + CURL_CHECK_PKGCONFIG(wolfssl, [$wolfpkg]) + AC_MSG_NOTICE([Check directory $wolfpkg]) + + addld="" + addlib="" + addcflags="" + if test "$PKGCONFIG" != "no"; then + addlib=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --libs-only-l wolfssl` + addld=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --libs-only-L wolfssl` + addcflags=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --cflags-only-I wolfssl` + version=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --modversion wolfssl` + wolfssllibpath=`echo $addld | $SED -e 's/^-L//'` + else + addlib=-lwolfssl + dnl use system defaults if user does not supply a path + if test -n "$OPT_WOLFSSL"; then + addld=-L$OPT_WOLFSSL/lib$libsuff + addcflags=-I$OPT_WOLFSSL/include + wolfssllibpath=$OPT_WOLFSSL/lib$libsuff fi - - dnl try pkg-config magic - CURL_CHECK_PKGCONFIG(wolfssl, [$wolfpkg]) - AC_MSG_NOTICE([Check directory $wolfpkg]) - - addld="" - addlib="" - addcflags="" - if test "$PKGCONFIG" != "no"; then - addlib=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --libs-only-l wolfssl` - addld=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --libs-only-L wolfssl` - addcflags=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --cflags-only-I wolfssl` - version=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --modversion wolfssl` - wolfssllibpath=`echo $addld | $SED -e 's/^-L//'` - else - addlib=-lwolfssl - dnl use system defaults if user does not supply a path - if test -n "$OPT_WOLFSSL"; then - addld=-L$OPT_WOLFSSL/lib$libsuff - addcflags=-I$OPT_WOLFSSL/include - wolfssllibpath=$OPT_WOLFSSL/lib$libsuff - fi + fi + + if test "$curl_cv_apple" = "yes"; then + addlib="$addlib -framework Security -framework CoreFoundation" + else + addlib="$addlib -lm" + fi + + if test "$USE_WOLFSSL" != "yes"; then + CPPFLAGS="$CPPFLAGS -DWOLFSSL_OPTIONS_IGNORE_SYS" + + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + AC_MSG_NOTICE([Add $addld to LDFLAGS]) + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + AC_MSG_NOTICE([Add $addcflags to CPPFLAGS]) fi - if test "$curl_cv_apple" = "yes"; then - addlib="$addlib -framework Security -framework CoreFoundation" - else - addlib="$addlib -lm" + my_ac_save_LIBS="$LIBS" + LIBS="$addlib $LIBS" + AC_MSG_NOTICE([Add $addlib to LIBS]) + + AC_MSG_CHECKING([for wolfSSL_Init in -lwolfssl]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + return wolfSSL_Init(); + ]]) + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(USE_WOLFSSL, 1, [if wolfSSL is enabled]) + WOLFSSL_ENABLED=1 + USE_WOLFSSL="yes" + ssl_msg="wolfSSL" + test "wolfssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + AC_MSG_RESULT(no) + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc + wolfssllibpath="" + ]) + LIBS="$my_ac_save_LIBS" + fi + + if test "$USE_WOLFSSL" = "yes"; then + AC_MSG_NOTICE([detected wolfSSL]) + check_for_ca_bundle=1 + + LIBS="$addlib $LIBS" + + dnl is this wolfSSL providing the original QUIC API? + AC_CHECK_FUNCS([wolfSSL_set_quic_use_legacy_codepoint], [QUIC_ENABLED=yes]) + + dnl wolfSSL needs configure --enable-opensslextra to have *get_peer* + dnl wc_Des_EcbEncrypt is needed for NTLM support. + dnl if wolfSSL_BIO_set_shutdown is present, we have the full BIO feature set + AC_CHECK_FUNCS(wolfSSL_get_peer_certificate \ + wolfSSL_UseALPN \ + wolfSSL_BIO_new \ + wolfSSL_BIO_set_shutdown \ + wc_Des_EcbEncrypt) + + dnl if this symbol is present, we want the include path to include the + dnl OpenSSL API root as well + if test "$ac_cv_func_wc_Des_EcbEncrypt" = "yes"; then + HAVE_WC_DES_ECBENCRYPT=1 fi - if test "$USE_WOLFSSL" != "yes"; then - CPPFLAGS="$CPPFLAGS -DWOLFSSL_OPTIONS_IGNORE_SYS" - - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - AC_MSG_NOTICE([Add $addld to LDFLAGS]) - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" - AC_MSG_NOTICE([Add $addcflags to CPPFLAGS]) - fi - - my_ac_save_LIBS="$LIBS" - LIBS="$addlib $LIBS" - AC_MSG_NOTICE([Add $addlib to LIBS]) - - AC_MSG_CHECKING([for wolfSSL_Init in -lwolfssl]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - return wolfSSL_Init(); - ]]) - ],[ - AC_MSG_RESULT(yes) - AC_DEFINE(USE_WOLFSSL, 1, [if wolfSSL is enabled]) - WOLFSSL_ENABLED=1 - USE_WOLFSSL="yes" - ssl_msg="wolfSSL" - test "wolfssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - AC_MSG_RESULT(no) - CPPFLAGS=$_cppflags - LDFLAGS=$_ldflags - LDFLAGSPC=$_ldflagspc - wolfssllibpath="" - ]) - LIBS="$my_ac_save_LIBS" + dnl if this symbol is present, we can make use of BIO filter chains + if test "$ac_cv_func_wolfSSL_BIO_new" = "yes"; then + HAVE_WOLFSSL_BIO_NEW=1 fi - if test "$USE_WOLFSSL" = "yes"; then - AC_MSG_NOTICE([detected wolfSSL]) - check_for_ca_bundle=1 - - LIBS="$addlib $LIBS" - - dnl is this wolfSSL providing the original QUIC API? - AC_CHECK_FUNCS([wolfSSL_set_quic_use_legacy_codepoint], [QUIC_ENABLED=yes]) - - dnl wolfSSL needs configure --enable-opensslextra to have *get_peer* - dnl wc_Des_EcbEncrypt is needed for NTLM support. - dnl if wolfSSL_BIO_set_shutdown is present, we have the full BIO feature set - AC_CHECK_FUNCS(wolfSSL_get_peer_certificate \ - wolfSSL_UseALPN \ - wolfSSL_BIO_new \ - wolfSSL_BIO_set_shutdown \ - wc_Des_EcbEncrypt) - - dnl if this symbol is present, we want the include path to include the - dnl OpenSSL API root as well - if test "$ac_cv_func_wc_Des_EcbEncrypt" = "yes"; then - HAVE_WC_DES_ECBENCRYPT=1 - fi - - dnl if this symbol is present, we can make use of BIO filter chains - if test "$ac_cv_func_wolfSSL_BIO_new" = "yes"; then - HAVE_WOLFSSL_BIO_NEW=1 + if test -n "$wolfssllibpath"; then + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to + dnl CURL_LIBRARY_PATH to prevent further configure tests to fail + dnl due to this + if test "$cross_compiling" != "yes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$wolfssllibpath" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $wolfssllibpath to CURL_LIBRARY_PATH]) fi - - if test -n "$wolfssllibpath"; then - dnl when shared libs were found in a path that the runtime - dnl linker does not search through, we need to add it to - dnl CURL_LIBRARY_PATH to prevent further configure tests to fail - dnl due to this - if test "$cross_compiling" != "yes"; then - CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$wolfssllibpath" - export CURL_LIBRARY_PATH - AC_MSG_NOTICE([Added $wolfssllibpath to CURL_LIBRARY_PATH]) - fi - fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE wolfssl" - else - AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or does not work]) fi - - fi dnl wolfSSL not disabled + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE wolfssl" + else + AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or does not work]) + fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi - +fi dnl wolfSSL not disabled ]) From cdb266738b9057cf73a241f267271a6a5e47d1ca Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 22:40:50 +0200 Subject: [PATCH 0237/1062] pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ The upstream issue seems to have been fixed or mitigated in quiche v0.29.0. Though the original upstream report and patch remain open at the time of writing this. Ref: https://github.com/cloudflare/quiche/issues/2277 Ref: https://github.com/cloudflare/quiche/pull/2278 Follow-up to 252b82f693574e884fb36dfde9371b409716a0fc #21730 Follow-up to 91b422d356a52d32708c02514d8ede66363e8847 #20952 Follow-up to 14478429e71ef0eee6d12b73113e9ff8e3ae9e75 #19916 Closes #21784 --- tests/http/test_05_errors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index 8d82c093eb60..3483a28e8a26 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -43,7 +43,7 @@ class TestErrors: @pytest.mark.parametrize("proto", Env.http_protos()) def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.29.1'): + not env.curl_lib_version_at_least('quiche', '0.29.0'): pytest.skip("quiche issue #2277 not fixed") count = 1 curl = CurlClient(env=env) @@ -64,7 +64,7 @@ def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_mplx_protos()) def test_05_02_partial_20(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.29.1'): + not env.curl_lib_version_at_least('quiche', '0.29.0'): pytest.skip("quiche issue #2277 not fixed") count = 20 curl = CurlClient(env=env) From 59320082b06e6bf9b6063c9fe5d2a447671533dc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 18:00:46 +0200 Subject: [PATCH 0238/1062] tidy-up: apply clang-format fixes To lib, vtls/vauth, vtls/vquic, lib/vtls. Also: - unit3400: drop redundant `(void)arg`. Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 - fix comment typos. Closes #21786 --- lib/arpa_telnet.h | 1 + lib/asyn-ares.c | 4 +-- lib/cf-capsule.c | 3 +- lib/cf-h3-proxy.c | 3 +- lib/cf-https-connect.c | 2 +- lib/cf-ip-happy.h | 2 +- lib/cf-socket.c | 7 ++--- lib/cfilters.c | 8 ++--- lib/conncache.c | 6 ++-- lib/connect.c | 20 ++++++------- lib/connect.h | 12 ++++---- lib/content_encoding.c | 31 ++++++++++--------- lib/cookie.c | 2 +- lib/curl_addrinfo.c | 66 ++++++++++++++++++++--------------------- lib/curl_fopen.c | 6 ++-- lib/curl_hmac.h | 25 ++++++++-------- lib/curl_md5.h | 14 ++++----- lib/curl_ntlm_core.h | 2 +- lib/curl_sasl.c | 2 +- lib/curl_sha512_256.c | 14 ++++----- lib/curl_share.h | 6 ++-- lib/easy.c | 4 +-- lib/ftp.c | 6 ++-- lib/hsts.c | 3 +- lib/http.c | 6 ++-- lib/http2.c | 12 ++++---- lib/http_proxy.c | 18 +++++------ lib/http_proxy.h | 7 +++-- lib/imap.c | 14 ++++----- lib/mprintf.c | 5 ++-- lib/socks.c | 12 +++----- lib/uint-bset.c | 10 +++---- lib/uint-spbset.c | 2 +- lib/url.c | 4 +-- lib/vauth/digest_sspi.c | 14 ++++----- lib/vauth/vauth.h | 3 +- lib/vquic/curl_ngtcp2.c | 2 +- lib/vtls/gtls.c | 12 ++++---- lib/vtls/keylog.c | 7 +++-- lib/vtls/keylog.h | 7 +++-- lib/vtls/mbedtls.c | 22 +++++++------- lib/vtls/openssl.c | 14 ++++----- lib/vtls/openssl.h | 6 ++-- lib/vtls/vtls.c | 8 ++--- lib/vtls/vtls.h | 4 +-- lib/vtls/vtls_int.h | 18 +++++------ lib/vtls/vtls_scache.c | 32 ++++++++++---------- lib/vtls/wolfssl.c | 45 ++++++++++++++-------------- tests/libtest/lib1560.c | 14 +++------ tests/unit/unit3400.c | 5 +--- 50 files changed, 263 insertions(+), 289 deletions(-) diff --git a/lib/arpa_telnet.h b/lib/arpa_telnet.h index b5faab419c26..826f937537c0 100644 --- a/lib/arpa_telnet.h +++ b/lib/arpa_telnet.h @@ -35,6 +35,7 @@ #define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ #define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ #define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ + #define CURL_NEW_ENV_VAR 0 #define CURL_NEW_ENV_VALUE 1 diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index a4a74642859a..4b6dd02182cc 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -281,8 +281,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(ares->ares_status == ARES_SUCCESS && !result) { struct Curl_dns_entry *dns = Curl_dnscache_mk_entry2(data, async->dns_queries, - &ares->res_AAAA, &ares->res_A, - async->hostname, async->port); + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/cf-capsule.c b/lib/cf-capsule.c index afa0ae713ad5..55a550954c4a 100644 --- a/lib/cf-capsule.c +++ b/lib/cf-capsule.c @@ -153,8 +153,7 @@ static CURLcode capsule_cf_send(struct Curl_cfilter *cf, curlx_dyn_free(&dyn); return CURLE_OUT_OF_MEMORY; } - memcpy(ctx->pending, - curlx_dyn_ptr(&dyn) + nwritten, remaining); + memcpy(ctx->pending, curlx_dyn_ptr(&dyn) + nwritten, remaining); ctx->pending_len = remaining; ctx->pending_offset = 0; ctx->pending_payload = len; diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 10f9e38e808b..b37060349351 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -1498,8 +1498,7 @@ static int cb_ngtcp2_extend_max_stream_data(ngtcp2_conn *tconn, } stream = H3_PROXY_STREAM_CTX(ctx, s_data); if(stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", - stream_id); + CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", stream_id); stream->quic_flow_blocked = FALSE; Curl_multi_mark_dirty(s_data); } diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index d1d8f51076e3..8e8f73138c05 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -824,7 +824,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, if((conn->scheme->protocol != CURLPROTO_HTTPS) || !conn->bits.tls_enable_alpn) - goto out; + goto out; result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 5805d6397c59..970ec248818b 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -53,7 +53,7 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, uint8_t transport); #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) + defined(USE_PROXY_HTTP3) /* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw UDP sockets with TRNSPRT_QUIC transport so the socket is connected to the proxy peer. H3-PROXY manages its own ngtcp2 QUIC stack on top. */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 83534d7bdf2c..ec158bddb579 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -81,8 +81,7 @@ static void tcpnodelay(struct Curl_cfilter *cf, int level = IPPROTO_TCP; VERBOSE(char buffer[STRERROR_LEN]); - if(setsockopt(sockfd, level, TCP_NODELAY, - (void *)&onoff, sizeof(onoff)) < 0) + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); #else @@ -92,8 +91,8 @@ static void tcpnodelay(struct Curl_cfilter *cf, #endif } -#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ - defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ +#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ + defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD) #if defined(USE_WINSOCK) || \ (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ diff --git a/lib/cfilters.c b/lib/cfilters.c index 6f5793c833c1..3946c7231c3e 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -1028,8 +1028,8 @@ const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, return NULL; } -static const struct Curl_sockaddr_ex * -cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data) +static const struct Curl_sockaddr_ex *cf_get_remote_addr( + struct Curl_cfilter *cf, struct Curl_easy *data) { const struct Curl_sockaddr_ex *remote_addr = NULL; if(cf && @@ -1067,8 +1067,8 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data) return data->conn->sock[FIRSTSOCKET]; } -const struct Curl_sockaddr_ex * -Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) +const struct Curl_sockaddr_ex *Curl_conn_get_remote_addr( + struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf = (data->conn && CONN_SOCK_IDX_VALID(sockindex)) ? diff --git a/lib/conncache.c b/lib/conncache.c index fe180be1f53d..5ba236862200 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -305,9 +305,9 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool, return bundle; } -static struct connectdata * -cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle, - const struct curltime *pnow) +static struct connectdata *cpool_bundle_get_oldest_idle( + struct cpool_bundle *bundle, + const struct curltime *pnow) { struct Curl_llist_node *curr; timediff_t highscore = -1; diff --git a/lib/connect.c b/lib/connect.c index 64ec2ff941f6..c2038f4ee4dd 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -366,9 +366,9 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, } } else { - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) - && !Curl_conn_is_ssl(cf->conn, cf->sockindex) - && !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { + if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && + !Curl_conn_is_ssl(cf->conn, cf->sockindex) && + !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { result = Curl_cf_ssl_proxy_insert_after(cf, data); if(result) return result; @@ -427,7 +427,7 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { #ifndef CURL_DISABLE_PROXY #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) + defined(USE_PROXY_HTTP3) if(IS_QUIC_PROXY(cf->conn->http_proxy.proxytype) && cf->conn->bits.tunnel_proxy) { /* For HTTPS3 proxy tunnels, H3-PROXY manages the QUIC connection @@ -447,8 +447,8 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, the underlying conn to the proxy is TCP. */ else #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ - if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy - && !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) + if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && + !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) result = cf_ip_happy_insert_after(cf, data, TRNSPRT_TCP); else #endif /* !CURL_DISABLE_PROXY */ @@ -521,7 +521,7 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, /* Adding Curl_cf_quic_insert_after() because now we need the next filter to be QUIC/HTTP/3 (which has SSL) */ #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) + defined(USE_PROXY_HTTP3) if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy && (data->state.http_neg.wanted == CURL_HTTP_V3x)) { @@ -540,9 +540,9 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, if(ctx->state < CF_SETUP_CNNCT_SSL) { #ifdef USE_SSL if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || - (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->scheme->flags & PROTOPT_SSL)) /* we want SSL */ - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + (ctx->ssl_mode != CURL_CF_SSL_DISABLE && + cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ result = Curl_cf_ssl_insert_after(cf, data); if(result) return result; diff --git a/lib/connect.h b/lib/connect.h index 8aa130e8865c..65e1ab1ea76c 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -87,9 +87,9 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, * when the connection will close. */ -#define CONNCTRL_KEEP 0 /* undo a marked closure */ +#define CONNCTRL_KEEP 0 /* undo a marked closure */ #define CONNCTRL_CONNECTION 1 -#define CONNCTRL_STREAM 2 +#define CONNCTRL_STREAM 2 void Curl_conncontrol(struct connectdata *conn, int ctrl @@ -100,12 +100,12 @@ void Curl_conncontrol(struct connectdata *conn, #if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) +#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) +#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) #else /* !DEBUGBUILD || !CURLVERBOSE */ #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) +#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) +#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) #endif CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, diff --git a/lib/content_encoding.c b/lib/content_encoding.c index b106fc813bb5..889271bdffce 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -101,8 +101,7 @@ static void zfree_cb(voidpf opaque, voidpf ptr) static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) - failf(data, "Error while processing content unencoding: %s", - z->msg); + failf(data, "Error while processing content unencoding: %s", z->msg); else failf(data, "Error while processing content unencoding: " "Unknown failure within decompression software."); @@ -166,7 +165,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* because the buffer size is fixed, iteratively decompress and transfer to the client via next_write function. */ while(!done) { - int status; /* zlib status */ + int status; /* zlib status */ done = TRUE; if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { @@ -187,7 +186,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Flush output data if some. */ if(z->avail_out != DECOMPRESS_BUFFER_SIZE) { if(status == Z_OK || status == Z_STREAM_END) { - zp->zlib_init = started; /* Data started. */ + zp->zlib_init = started; /* Data started. */ result = Curl_cwriter_write(data, writer->next, type, zp->buffer, DECOMPRESS_BUFFER_SIZE - z->avail_out); if(result) { @@ -221,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = FALSE; break; } - zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ + zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; @@ -235,7 +234,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) - zp->zlib_init = started; /* Cannot restart anymore. */ + zp->zlib_init = started; /* Cannot restart anymore. */ return result; } @@ -245,7 +244,7 @@ static CURLcode deflate_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -262,7 +261,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -282,7 +281,7 @@ static void deflate_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -304,7 +303,7 @@ static CURLcode gzip_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -322,7 +321,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -343,7 +342,7 @@ static void gzip_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -364,7 +363,7 @@ static const struct Curl_cwtype gzip_encoding = { struct brotli_writer { struct Curl_cwriter super; char buffer[DECOMPRESS_BUFFER_SIZE]; - BrotliDecoderState *br; /* State structure for brotli. */ + BrotliDecoderState *br; /* State structure for brotli. */ }; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) @@ -429,7 +428,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data, return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(!bp->br) - return CURLE_WRITE_ERROR; /* Stream already ended. */ + return CURLE_WRITE_ERROR; /* Stream already ended. */ while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { @@ -495,7 +494,7 @@ static const struct Curl_cwtype brotli_encoding = { /* Zstd writer. */ struct zstd_writer { struct Curl_cwriter super; - ZSTD_DStream *zds; /* State structure for zstd. */ + ZSTD_DStream *zds; /* State structure for zstd. */ char buffer[DECOMPRESS_BUFFER_SIZE]; }; @@ -817,7 +816,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, } if(!cwt) - cwt = &error_writer; /* Defer error at use. */ + cwt = &error_writer; /* Defer error at use. */ result = Curl_cwriter_create(&writer, data, cwt, phase); CURL_TRC_WRITE(data, "added %s decoder %s -> %d", diff --git a/lib/cookie.c b/lib/cookie.c index 0b45798fca2a..63615a496c91 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -1153,7 +1153,7 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, curlx_fclose(handle); } data->state.cookie_engine = TRUE; - ci->running = TRUE; /* now, we are running */ + ci->running = TRUE; /* now, we are running */ return result; } diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 1efb4b701757..52d1e96a5462 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -60,7 +60,7 @@ * any function call which actually allocates a Curl_addrinfo struct. */ -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ +#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) /* workaround icc 9.1 optimizer issue */ # define vqualifier volatile @@ -447,43 +447,43 @@ bool Curl_is_ipaddr(const char *address) bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, struct Curl_str *host, struct Curl_str *zone) { - const char *zonep = NULL; - size_t i = 0, hlen = 0, zlen = 0; + const char *zonep = NULL; + size_t i = 0, hlen = 0, zlen = 0; - if(host) - memset(host, 0, sizeof(*host)); - if(zone) - memset(zone, 0, sizeof(*zone)); + if(host) + memset(host, 0, sizeof(*host)); + if(zone) + memset(zone, 0, sizeof(*zone)); - for(i = 0; i < len; ++i, ++hlen) { - if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) - break; - } + for(i = 0; i < len; ++i, ++hlen) { + if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) + break; + } - if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ - i += 1; - if(maybe_url_encoded && !strncmp("25", s + i, 2)) - i += 2; - zonep = s + i; - for(; i < len; ++i, ++zlen) { - /* Allow unreserved characters as defined in RFC 3986 */ - if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || - (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) - break; - } + if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ + i += 1; + if(maybe_url_encoded && !strncmp("25", s + i, 2)) + i += 2; + zonep = s + i; + for(; i < len; ++i, ++zlen) { + /* Allow unreserved characters as defined in RFC 3986 */ + if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || + (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) + break; } + } - if(i != len) - return FALSE; /* invalid chars in zone */ - if(host && hlen) { - host->str = s; - host->len = hlen; - } - if(zone && zlen) { - zone->str = zonep; - zone->len = zlen; - } - return TRUE; + if(i != len) + return FALSE; /* invalid chars in zone */ + if(host && hlen) { + host->str = s; + host->len = hlen; + } + if(zone && zlen) { + zone->str = zonep; + zone->len = zlen; + } + return TRUE; } #ifdef USE_UNIX_SOCKETS diff --git a/lib/curl_fopen.c b/lib/curl_fopen.c index cc888f761648..abb17eab4b87 100644 --- a/lib/curl_fopen.c +++ b/lib/curl_fopen.c @@ -42,13 +42,13 @@ */ #ifdef _WIN32 -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) (((x) == '/') || ((x) == '\\')) #elif defined(MSDOS) || defined(OS2) -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) ((x) == '\\') #else -#define PATHSEP "/" +#define PATHSEP "/" #define IS_SEP(x) ((x) == '/') #endif diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index 301d44fee809..d9a697a0a595 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -24,26 +24,26 @@ * ***************************************************************************/ -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ - !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) #define HMAC_MD5_LENGTH 16 typedef CURLcode (*HMAC_hinit)(void *context); -typedef void (*HMAC_hupdate)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (*HMAC_hfinal)(unsigned char *result, void *context); +typedef void (*HMAC_hupdate)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (*HMAC_hfinal)(unsigned char *result, void *context); /* Per-hash function HMAC parameters. */ struct HMAC_params { - HMAC_hinit hinit; /* Initialize context procedure. */ - HMAC_hupdate hupdate; /* Update context with data. */ - HMAC_hfinal hfinal; /* Get final result procedure. */ - unsigned int ctxtsize; /* Context structure size. */ - unsigned int maxkeylen; /* Maximum key length (bytes). */ - unsigned int resultlen; /* Result length (bytes). */ + HMAC_hinit hinit; /* Initialize context procedure. */ + HMAC_hupdate hupdate; /* Update context with data. */ + HMAC_hfinal hfinal; /* Get final result procedure. */ + unsigned int ctxtsize; /* Context structure size. */ + unsigned int maxkeylen; /* Maximum key length (bytes). */ + unsigned int resultlen; /* Result length (bytes). */ }; /* HMAC computation context. */ @@ -66,7 +66,6 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, const unsigned char *data, size_t datalen, unsigned char *output); - #endif #endif /* HEADER_CURL_HMAC_H */ diff --git a/lib/curl_md5.h b/lib/curl_md5.h index 8a0cc2623e38..1beaed5e66c6 100644 --- a/lib/curl_md5.h +++ b/lib/curl_md5.h @@ -38,16 +38,16 @@ typedef void (*Curl_MD5_update_func)(void *context, typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context); struct MD5_params { - Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ - Curl_MD5_update_func md5_update_func; /* Update context with data */ - Curl_MD5_final_func md5_final_func; /* Get final result procedure */ - unsigned int md5_ctxtsize; /* Context structure size */ - unsigned int md5_resultlen; /* Result length (bytes) */ + Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ + Curl_MD5_update_func md5_update_func; /* Update context with data */ + Curl_MD5_final_func md5_final_func; /* Get final result procedure */ + unsigned int md5_ctxtsize; /* Context structure size */ + unsigned int md5_resultlen; /* Result length (bytes) */ }; struct MD5_context { - const struct MD5_params *md5_hash; /* Hash function definition */ - void *md5_hashctx; /* Hash function context */ + const struct MD5_params *md5_hash; /* Hash function definition */ + void *md5_hashctx; /* Hash function context */ }; extern const struct MD5_params Curl_DIGEST_MD5; diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index f96bf0ada5ba..df24158f8e30 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -30,7 +30,7 @@ struct ntlmdata; /* Helpers to generate function byte arguments in little endian order */ -#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) +#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ ((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff)) diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index d8c088dda2ff..7e867d753fe7 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -35,7 +35,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ + !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) #include "urldata.h" diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 3780dd42b4da..eb8bc66fc8e0 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -498,14 +498,10 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ -#define SIG0(x) \ - (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) -#define SIG1(x) \ - (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) -#define sig0(x) \ - (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) -#define sig1(x) \ - (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) +#define SIG0(x) (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) +#define SIG1(x) (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) +#define sig0(x) (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) +#define sig1(x) (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) if(1) { unsigned int t; @@ -715,7 +711,7 @@ static CURLcode Curl_sha512_256_update(void *context, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; - uint64_t num_bits; /* Number of processed bits */ + uint64_t num_bits; /* Number of processed bits */ unsigned int bytes_have; /* Number of bytes in the context buffer */ /* the void pointer here is required to mute Intel compiler warning */ void * const ctx_buf = ctx->buffer; diff --git a/lib/curl_share.h b/lib/curl_share.h index 69001be705a5..1d49a8d1dd3c 100644 --- a/lib/curl_share.h +++ b/lib/curl_share.h @@ -81,9 +81,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); /* convenience macro to check if this handle is using a shared SSL spool */ -#define CURL_SHARE_ssl_scache(data) ((data)->share && \ - ((data)->share->specifier & \ - (1 << CURL_LOCK_DATA_SSL_SESSION))) +#define CURL_SHARE_ssl_scache(data) \ + ((data)->share && \ + ((data)->share->specifier & (1 << CURL_LOCK_DATA_SSL_SESSION))) CURLcode Curl_share_easy_unlink(struct Curl_easy *data); CURLcode Curl_share_easy_link(struct Curl_easy *data, diff --git a/lib/easy.c b/lib/easy.c index a472d6ddc52e..ce3d00200a26 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -78,8 +78,8 @@ #include "easy_lock.h" /* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized; -static long easy_init_flags; +static unsigned int initialized; +static long easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE diff --git a/lib/ftp.c b/lib/ftp.c index 836bc96e8998..3723e7e96817 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1188,8 +1188,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", - port); + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", port); return CURLE_OK; } @@ -1276,8 +1275,7 @@ static CURLcode ftp_port_send_command(struct Curl_easy *data, * EPRT |2|1080::8:800:200C:417A|5282| */ result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], - sa->sa_family == AF_INET ? 1 : 2, - myhost, port); + sa->sa_family == AF_INET ? 1 : 2, myhost, port); if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); diff --git a/lib/hsts.c b/lib/hsts.c index 94738874b5d6..a8e6bcae43bc 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -617,8 +617,7 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) { - return !!hsts_check(h, dest->hostname, - strlen(dest->hostname), TRUE); + return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE); } #if defined(DEBUGBUILD) || defined(UNITTESTS) diff --git a/lib/http.c b/lib/http.c index 7b9fad95df4e..c9fb9995c09b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1132,9 +1132,9 @@ static void http_switch_to_get(struct Curl_easy *data, int code) Curl_creader_set_rewind(data, FALSE); } -#define HTTPREQ_IS_POST(data) \ - ((data)->state.httpreq == HTTPREQ_POST || \ - (data)->state.httpreq == HTTPREQ_POST_FORM || \ +#define HTTPREQ_IS_POST(data) \ + ((data)->state.httpreq == HTTPREQ_POST || \ + (data)->state.httpreq == HTTPREQ_POST_FORM || \ (data)->state.httpreq == HTTPREQ_POST_MIME) CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, diff --git a/lib/http2.c b/lib/http2.c index 9eb1e0aeaa41..ab70455faee9 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1701,12 +1701,12 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } else if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " - "not want a body anyway, ignore: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - stream->close_handled = TRUE; - return CURLE_OK; + CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " + "not want a body anyway, ignore: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + stream->close_handled = TRUE; + return CURLE_OK; } failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", stream->id, stream->reset_by_server ? "server" : "curl", diff --git a/lib/http_proxy.c b/lib/http_proxy.c index fd85e8a0faf3..a52a1c3713dc 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -366,7 +366,7 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, /* If user is not overriding Host: header, we add for HTTP/1.x */ if(ver == PROXY_HTTP_V1 && - !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { result = Curl_dynhds_cadd(&req->headers, "Host", authority); if(result) goto out; @@ -380,8 +380,8 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, } if(ver == PROXY_HTTP_V1 && - !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { result = Curl_dynhds_cadd(&req->headers, "User-Agent", data->set.str[STRING_USERAGENT]); if(result) @@ -389,7 +389,7 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, } if(ver == PROXY_HTTP_V1 && - !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; @@ -557,7 +557,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; - const char *tunnel_type; /* Determine tunnel type once and reuse */ + const char *tunnel_type; /* Determine tunnel type once and reuse */ tunnel_type = ctx->udp_tunnel ? "CONNECT-UDP" : "CONNECT"; @@ -608,7 +608,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10, - (bool)ctx->udp_tunnel); + (bool)ctx->udp_tunnel); if(result) goto out; } @@ -617,7 +617,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", httpversion % 10); result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion, - (bool)ctx->udp_tunnel); + (bool)ctx->udp_tunnel); if(result) goto out; } @@ -625,13 +625,13 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest, - (bool)ctx->udp_tunnel); + (bool)ctx->udp_tunnel); if(result) goto out; } #endif /* USE_NGHTTP2 */ #if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ - defined(USE_NGTCP2) && defined(USE_OPENSSL) + defined(USE_NGTCP2) && defined(USE_OPENSSL) else if(!strcmp(alpn, "h3")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3"); result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->dest, diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 0e44161375c2..0a5734e3d8a2 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -76,9 +76,10 @@ extern struct Curl_cftype Curl_cft_http_proxy; #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ -#define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ - ((t) == CURLPROXY_HTTPS2) || \ - ((t) == CURLPROXY_HTTPS3)) +#define IS_HTTPS_PROXY(t) \ + (((t) == CURLPROXY_HTTPS) || \ + ((t) == CURLPROXY_HTTPS2) || \ + ((t) == CURLPROXY_HTTPS3)) #define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3) diff --git a/lib/imap.c b/lib/imap.c index 9eb79e5aefad..1898e33adbcc 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -910,12 +910,12 @@ static CURLcode imap_perform_append(struct Curl_easy *data, if(data->set.upload_flags) { int i; struct ulbits ulflag[] = { - {CURLULFLAG_ANSWERED, "Answered"}, - {CURLULFLAG_DELETED, "Deleted"}, - {CURLULFLAG_DRAFT, "Draft"}, - {CURLULFLAG_FLAGGED, "Flagged"}, - {CURLULFLAG_SEEN, "Seen"}, - {0, NULL} + { CURLULFLAG_ANSWERED, "Answered" }, + { CURLULFLAG_DELETED, "Deleted" }, + { CURLULFLAG_DRAFT, "Draft" }, + { CURLULFLAG_FLAGGED, "Flagged" }, + { CURLULFLAG_SEEN, "Seen" }, + { 0, NULL } }; result = CURLE_OUT_OF_MEMORY; @@ -1043,7 +1043,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, /* Extract the word */ for(wordlen = 0; line[wordlen] && !ISBLANK(line[wordlen]) && - !ISNEWLINE(line[wordlen]);) + !ISNEWLINE(line[wordlen]);) wordlen++; /* Does the server support the STARTTLS capability? */ diff --git a/lib/mprintf.c b/lib/mprintf.c index 1a7958f91399..06f6129c4b3e 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -1073,9 +1073,8 @@ static int formatf(void *userp, /* untouched by format(), sent to the /* Answer the count of characters written. */ if(p.flags & FLAGS_LONGLONG) *(int64_t *)iptr->val.ptr = (int64_t)done; - else - if(p.flags & FLAGS_LONG) - *(long *)iptr->val.ptr = (long)done; + else if(p.flags & FLAGS_LONG) + *(long *)iptr->val.ptr = (long)done; else if(!(p.flags & FLAGS_SHORT)) *(int *)iptr->val.ptr = done; else diff --git a/lib/socks.c b/lib/socks.c index 387ebfc168e5..27c3c714f53a 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -656,8 +656,7 @@ static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT); return CURLPX_OK; } - failf(data, - "SOCKS5 GSSAPI per-message authentication is not enabled."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not enabled."); return CURLPX_GSSAPI_PERMSG; case 2: /* regular name + password authentication */ @@ -712,8 +711,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, - &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -722,8 +720,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, - &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -1086,8 +1083,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); goto process_state; #else - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG); #endif } diff --git a/lib/uint-bset.c b/lib/uint-bset.c index 5469174944d9..55aedb234b05 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -157,7 +157,7 @@ bool Curl_uint32_bset_next(struct uint32_bset *bset, uint32_t last, /* shift away the bits we already iterated in this slot */ x = (bset->slots[islot] >> (last % 64)); if(x) { - /* more bits set, next is `last` + trailing0s of the shifted slot */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } @@ -179,10 +179,10 @@ uint32_t Curl_popcount64(uint64_t x) /* Compute the "Hamming Distance" between 'x' and 0, * which is the number of set bits in 'x'. * See: https://en.wikipedia.org/wiki/Hamming_weight */ - const uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ - const uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ - const uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ - /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ + const uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ + const uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ + const uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ + /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ const uint64_t h01 = 0x0101010101010101LL; x -= (x >> 1) & m1; /* replace every 2 bits with bits present */ x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */ diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index 3daa2eea758e..d4e6c8e70bf5 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -203,7 +203,7 @@ static bool uint32_spbset_chunk_next(struct uint32_spbset_chunk *chunk, if(i < CURL_UINT32_SPBSET_CH_SLOTS) { x = (chunk->slots[i] >> (last % 64)); if(x) { - /* more bits set, next is `last` + trailing0s of the shifted slot */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } diff --git a/lib/url.c b/lib/url.c index 93a5f14f07e4..926d29ed5f77 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1014,8 +1014,8 @@ static bool url_match_auth_ntlm(struct connectdata *conn, that can be reused and "upgraded" to NTLM if it does not have any auth ongoing. */ #ifdef USE_SPNEGO - if((conn->http_ntlm_state == NTLMSTATE_NONE) - && (conn->http_negotiate_state == GSS_AUTHNONE)) { + if((conn->http_ntlm_state == NTLMSTATE_NONE) && + (conn->http_negotiate_state == GSS_AUTHNONE)) { #else if(conn->http_ntlm_state == NTLMSTATE_NONE) { #endif diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 6ca00d799890..74d654fc46e5 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -476,8 +476,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - if(Curl_create_sspi_identity(creds->user, creds->passwd, - &identity)) { + if(Curl_create_sspi_identity(creds->user, creds->passwd, &identity)) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -557,11 +556,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Generate our response message */ status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, - spn, - ISC_REQ_USE_HTTP_STYLE, 0, 0, - &chlg_desc, 0, - digest->http_context, - &resp_desc, &attrs, NULL); + spn, + ISC_REQ_USE_HTTP_STYLE, + 0, 0, + &chlg_desc, 0, + digest->http_context, + &resp_desc, &attrs, NULL); curlx_free(spn); if(status == SEC_I_COMPLETE_NEEDED || diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index c21b3495715d..0f82f929453d 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -317,8 +317,7 @@ struct negotiatedata { BIT(havemultiplerequests); }; -struct negotiatedata * -Curl_auth_nego_get(struct connectdata *conn, bool proxy); +struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy); /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 4b02c217be1f..8693ed16ee74 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1918,7 +1918,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, if(ctx->q.sockfd != CURL_SOCKET_BAD) { /* Direct UDP socket (via happy eyeballs) */ return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts, &rctx); + cf_ngtcp2_recv_pkts, &rctx); } else { /* Tunneled QUIC (CONNECT-UDP through proxy) */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index dcda203bb757..0b0744517d58 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -323,12 +323,12 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) "+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ "%DISABLE_TLS13_COMPAT_MODE" -static CURLcode -gnutls_set_ssl_version_min_max(struct Curl_easy *data, - struct ssl_peer *peer, - struct ssl_primary_config *conn_config, - const char **prioritylist, - bool tls13support) +static CURLcode gnutls_set_ssl_version_min_max( + struct Curl_easy *data, + struct ssl_peer *peer, + struct ssl_primary_config *conn_config, + const char **prioritylist, + bool tls13support) { long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 4ae2387a7a72..23c74de04f5f 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -102,9 +102,10 @@ bool Curl_tls_keylog_write_line(const char *line) return TRUE; } -bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen) +bool Curl_tls_keylog_write( + const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen) { size_t pos, i; unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h index 68ded4769d10..b09fcc6f4d4c 100644 --- a/lib/vtls/keylog.h +++ b/lib/vtls/keylog.h @@ -61,9 +61,10 @@ const char *Curl_tls_keylog_file_name(void); * Appends a key log file entry. * Returns true iff the key log file is open and a valid entry was provided. */ -bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen); +bool Curl_tls_keylog_write( + const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF. diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 9a15534252ec..b750313084cf 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -179,10 +179,10 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) -static CURLcode -mbed_set_ssl_version_min_max(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - struct ssl_primary_config *conn_config) +static CURLcode mbed_set_ssl_version_min_max( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + struct ssl_primary_config *conn_config) { mbedtls_ssl_protocol_version ver_min = #ifdef MBEDTLS_SSL_PROTO_TLS1_2 @@ -275,15 +275,15 @@ static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) return id; } #else -#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str +#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str #define mbed_cipher_suite_walk_str Curl_cipher_suite_walk_str #endif -static CURLcode -mbed_set_selected_ciphers(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - const char *ciphers12, - const char *ciphers13) +static CURLcode mbed_set_selected_ciphers( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + const char *ciphers12, + const char *ciphers13) { const char *ciphers = ciphers12; const int *supported; @@ -745,7 +745,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, } static CURLcode mbed_load_crl(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 8789dedc79f8..64c904dbf684 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3324,13 +3324,13 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, return result; } -static CURLcode -ossl_init_session_and_alpns(struct ossl_ctx *octx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns_requested, - Curl_ossl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode ossl_init_session_and_alpns( + struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const struct alpn_spec *alpns_requested, + Curl_ossl_init_session_reuse_cb *sess_reuse_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 44a0218ff5c4..4fa466b36789 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -107,12 +107,12 @@ struct Curl_ssl_session; /* Struct to hold a curl OpenSSL instance */ struct ossl_ctx { /* these ones requires specific SSL-types */ - SSL_CTX* ssl_ctx; - SSL* ssl; + SSL_CTX *ssl_ctx; + SSL *ssl; BIO_METHOD *bio_method; CURLcode io_result; /* result of last BIO cfilter operation */ /* blocked writes need to retry with same length, remember it */ - int blocked_ssl_write_len; + int blocked_ssl_write_len; #if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* Set to true once a valid keylog entry has been created to avoid dupes. This is a bool and not a bitfield because it is passed by address. */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index d640df4f0311..eb0aa4277f9a 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1908,8 +1908,8 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); } -struct ssl_config_data * -Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data) { #ifdef CURL_DISABLE_PROXY (void)cf; @@ -1919,8 +1919,8 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) #endif } -struct ssl_primary_config * -Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf) { #ifdef CURL_DISABLE_PROXY return &cf->conn->ssl_config; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index f15f2956d614..dfda7d0f2ed1 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -241,8 +241,8 @@ struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, /** * Get the primary config relevant for the filter from its connection. */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf); extern struct Curl_cftype Curl_cft_ssl; #ifndef CURL_DISABLE_PROXY diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index a0d8159a5879..fa5da6e4d595 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -38,20 +38,20 @@ struct Curl_ssl_session; /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_0_LENGTH 8 -#define ALPN_HTTP_1_0 "http/1.0" +#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_H2_LENGTH 2 -#define ALPN_H2 "h2" -#define ALPN_H3_LENGTH 2 -#define ALPN_H3 "h3" +#define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_H2_LENGTH 2 +#define ALPN_H2 "h2" +#define ALPN_H3_LENGTH 2 +#define ALPN_H3 "h3" /* conservative sizes on the ALPN entries and count we are handling, * we can increase these if we ever feel the need or have to accommodate * ALPN strings from the "outside". */ -#define ALPN_NAME_MAX 10 -#define ALPN_ENTRIES_MAX 3 -#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) +#define ALPN_NAME_MAX 10 +#define ALPN_ENTRIES_MAX 3 +#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) struct alpn_spec { char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 2fc563e800bd..fe30091bfb56 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -495,15 +495,14 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) cf_ssl_peer_key_is_global(peer->ssl_peer_key))); } -static CURLcode -cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, - const char *ssl_peer_key, - const char *clientcert, - const char *key_passwd, - const char *srp_username, - const char *srp_password, - const unsigned char *salt, - const unsigned char *hmac) +static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, + const char *ssl_peer_key, + const char *clientcert, + const char *key_passwd, + const char *srp_username, + const char *srp_password, + const unsigned char *salt, + const unsigned char *hmac) { CURLcode result = CURLE_OUT_OF_MEMORY; @@ -758,8 +757,8 @@ static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data, return result; } -static struct Curl_ssl_scache_peer * -cf_ssl_get_free_peer(struct Curl_ssl_scache *scache) +static struct Curl_ssl_scache_peer *cf_ssl_get_free_peer( + struct Curl_ssl_scache *scache) { struct Curl_ssl_scache_peer *peer = NULL; size_t i; @@ -1107,11 +1106,10 @@ static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) return result; } -static CURLcode -cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, - const unsigned char *salt, - const unsigned char *hmac, - struct Curl_ssl_scache_peer **ppeer) +static CURLcode cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, + const unsigned char *salt, + const unsigned char *hmac, + struct Curl_ssl_scache_peer **ppeer) { size_t i; CURLcode result = CURLE_OK; @@ -1255,7 +1253,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, for(i = 0; scache && i < scache->peer_count; i++) { peer = &scache->peers[i]; if(!peer->ssl_peer_key && !peer->hmac_set) - continue; /* skip free entry */ + continue; /* skip free entry */ if(!peer->exportable) continue; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 96ad6554f4a6..836cd2688aa7 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -81,7 +81,7 @@ options.h. */ #ifndef KEEP_PEER_CERT #if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ - (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) + (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) #define KEEP_PEER_CERT #endif #endif @@ -221,7 +221,7 @@ static int wssl_do_file_type(const char *type) #ifdef WOLFSSL_HAVE_KYBER struct group_name_map { const word16 group; - const char *name; + const char *name; }; static const struct group_name_map gnm[] = { @@ -316,7 +316,7 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) * sending during shutdown. */ CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d" " to %d bytes", blen, wssl->io_send_blocked_len); - skiplen = (ssize_t)(blen - wssl->io_send_blocked_len); + skiplen = (size_t)(blen - wssl->io_send_blocked_len); blen = wssl->io_send_blocked_len; } result = Curl_conn_cf_send(cf->next, data, @@ -517,13 +517,13 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, connssl->earlydata_max); } -static CURLcode -wssl_setup_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct wssl_ctx *wss, - struct alpn_spec *alpns, - const char *ssl_peer_key, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode wssl_setup_session( + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wssl_ctx *wss, + struct alpn_spec *alpns, + const char *ssl_peer_key, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *scs = NULL; @@ -1172,18 +1172,18 @@ static CURLcode wssl_init_curves(struct Curl_easy *data, return CURLE_OK; } -static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - struct alpn_spec *alpns, - void *ssl_user_data, - unsigned char transport, +static CURLcode wssl_init_ssl_handle( + struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + void *ssl_user_data, + unsigned char transport, #ifdef WOLFSSL_HAVE_KYBER - word16 pqkem, + word16 pqkem, #endif - Curl_wssl_init_session_reuse_cb - *sess_reuse_cb) + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { /* Let's make an SSL structure */ wctx->ssl = wolfSSL_new(wctx->ssl_ctx); @@ -1206,8 +1206,7 @@ static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, #ifdef WOLFSSL_HAVE_KYBER if(pqkem) { - if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != - WOLFSSL_SUCCESS) { + if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) { failf(data, "unable to use PQ KEM"); } } @@ -2210,7 +2209,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, wssl->hs_result = result; goto out; } - /* handhshake was done without errors */ + /* handshake was done without errors */ #ifdef HAVE_ALPN if(connssl->alpn) { int rc; diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index c9363fe7782b..cd2cbec589c8 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1537,10 +1537,8 @@ static int set_url(void) __FILE__, __LINE__, rc, curl_url_strerror(rc)); error++; } - else { - if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) { - error++; - } + else if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) { + error++; } curl_free(url); } @@ -1640,7 +1638,6 @@ static int set_parts(void) if(!uc) { /* only do this if it worked */ rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); - if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", __FILE__, __LINE__, rc, curl_url_strerror(rc)); @@ -1680,17 +1677,14 @@ static int get_url(bool has_utf8) if(!rc) { char *url = NULL; rc = curl_url_get(urlp, CURLUPART_URL, &url, get_url_list[i].getflags); - if(rc) { curl_mfprintf(stderr, "%s:%d returned %d (%s). URL: '%s'\n", __FILE__, __LINE__, rc, curl_url_strerror(rc), get_url_list[i].in); error++; } - else { - if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) { - error++; - } + else if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) { + error++; } curl_free(url); } diff --git a/tests/unit/unit3400.c b/tests/unit/unit3400.c index e48dd3c1a72f..2e083ddc5aee 100644 --- a/tests/unit/unit3400.c +++ b/tests/unit/unit3400.c @@ -220,8 +220,7 @@ static void test_capsule_decode_paths(void) fail_unless(err == CURLE_RECV_ERROR, "expected RECV_ERROR for short output buffer"); fail_unless(nread == 0, "expected zero read on short output buffer"); - fail_unless(Curl_bufq_is_empty(&q), - "oversized capsule must be discarded"); + fail_unless(Curl_bufq_is_empty(&q), "oversized capsule must be discarded"); /* zero-length UDP payload is accepted and consumed */ Curl_bufq_reset(&q); @@ -250,8 +249,6 @@ static CURLcode test_unit3400(const char *arg) { UNITTEST_BEGIN_SIMPLE - (void)arg; - #if defined(USE_PROXY_HTTP3) && \ !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) test_capsule_encap_udp_hdr_boundaries(); From d1b482caec886b3215340cd7374c5befb04d930e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 23:55:14 +0200 Subject: [PATCH 0239/1062] unit3400: repair after capsule_encap_udp_hdr went static Access the static function with UNITTEST as designed. Follow-up to 73c2b4b4355aab3156 Closes #21788 --- lib/capsule.c | 8 ++++++-- lib/capsule.h | 3 +++ tests/unit/unit3400.c | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/capsule.c b/lib/capsule.c index 2d9af5cea58f..4f5d0e7e2497 100644 --- a/lib/capsule.c +++ b/lib/capsule.c @@ -141,9 +141,13 @@ static CURLcode capsule_decode_varint_at(struct bufq *recvbufq, * @param hdrlen Size of `hdr` in bytes * @param payload_len Length of the UDP payload that follows * @return Number of header bytes written, or 0 on error + * + * @unittest 3400 */ -static size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, - size_t payload_len) +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len); +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len) { size_t off = 0; DEBUGASSERT(hdrlen >= HTTP_CAPSULE_HEADER_MAX_SIZE); diff --git a/lib/capsule.h b/lib/capsule.h index 4d50f783600d..315ec8a61eab 100644 --- a/lib/capsule.h +++ b/lib/capsule.h @@ -46,6 +46,9 @@ CURLcode Curl_capsule_encap_udp_datagram(struct dynbuf *dyn, const void *buf, size_t blen); +struct Curl_easy; +struct Curl_cfilter; + /** * Process one UDP capsule from buffer into raw datagram payload bytes. * @param cf Connection filter diff --git a/tests/unit/unit3400.c b/tests/unit/unit3400.c index 2e083ddc5aee..8df86c19f400 100644 --- a/tests/unit/unit3400.c +++ b/tests/unit/unit3400.c @@ -48,7 +48,7 @@ static void check_capsule_hdr(size_t payload_len, size_t hdr_len; memset(hdr, 0xA5, sizeof(hdr)); - hdr_len = Curl_capsule_encap_udp_hdr(hdr, sizeof(hdr), payload_len); + hdr_len = capsule_encap_udp_hdr(hdr, sizeof(hdr), payload_len); fail_unless(hdr_len == expected_len, "capsule header length mismatch"); fail_unless(!memcmp(hdr, expected, expected_len), "capsule header bytes mismatch"); From 6597e6d4610d95cada3f3b2768b39705ae158e2d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2026 23:37:12 +0200 Subject: [PATCH 0240/1062] tftp: avoid the timeout calc if the timeout is crazy Avoids integer overflow when a silly value is set. Fixes #21782 Reported-by: Mike-menny on github Closes #21787 --- lib/tftp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tftp.c b/lib/tftp.c index 7aaf882d9b5e..039b7dd393d0 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -167,7 +167,8 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) } /* Set per-block timeout to total */ - if(timeout_ms > 0) + if((timeout_ms > 0) && (timeout_ms < 3600000)) + /* do the calculation only if the timeout is "reasonable" */ timeout = (time_t)(timeout_ms + 500) / 1000; else timeout = 15; From e2ca8408c43275b3cf36ed08f4503732cd8df3a5 Mon Sep 17 00:00:00 2001 From: ambikeesshh Date: Sat, 23 May 2026 00:27:18 +0530 Subject: [PATCH 0241/1062] cf-socket: set scope_id for IPv6 link-local addresses When connecting to an mDNS hostname that resolves to an IPv6 link-local address, connect() fails with EINVAL because sin6_scope_id is 0. This is a regression since 8.20.0 where the threaded resolver started splitting A and AAAA queries into separate getaddrinfo calls. The AAAA-only call with PF_INET6 may not set scope_id on systems where the same call with PF_UNSPEC did. When the resolver does not provide scope_id for a link-local address, try to determine it from the system's network interfaces using getifaddrs(). Also add scope_id to verbose connect output so the value can be seen in curl -v logs. Built and tested locally on Linux. checksrc passes. Fixes #21669 Reported-by: Bartel Sielski Closes #21728 --- lib/cf-socket.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index ec158bddb579..eb782b65dca6 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -44,6 +44,12 @@ #include #endif +#ifdef HAVE_IFADDRS_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif #ifdef __VMS #include #include @@ -297,6 +303,49 @@ int Curl_sock_nosigpipe(curl_socket_t sockfd) } #endif /* USE_SO_NOSIGPIPE */ +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +static uint32_t get_scope_id(struct Curl_easy *data, + struct sockaddr_in6 *sa6) +{ + uint32_t scope_id = 0; + if(data->conn->scope_id) + return data->conn->scope_id; + /* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */ + scope_id = sa6->sin6_scope_id; + if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + /* The resolver did not set scope_id for this link-local address. + * Try to determine it from the system's network interfaces. + * Without a scope_id, connect() to a link-local address fails + * with EINVAL on Linux. + * NOTE: On multi-homed hosts with several interfaces having + * link-local addresses, this picks the first one found, which + * may not be the correct outgoing interface. */ +#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H) + struct ifaddrs *ifa, *ifa_list; + if(getifaddrs(&ifa_list) == 0) { + for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) { + if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 && + (ifa->ifa_flags & IFF_UP) && + !(ifa->ifa_flags & IFF_LOOPBACK)) { + struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr; + if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) { + scope_id = s6->sin6_scope_id; + infof(data, + "determined scope_id=%lu for link-local address " + "from local interface", + (unsigned long)scope_id); + break; + } + } + } + freeifaddrs(ifa_list); + } +#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */ + } + return scope_id; +} +#endif + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) @@ -366,9 +415,9 @@ static CURLcode socket_open(struct Curl_easy *data, #endif #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(data->conn->scope_id && (addr->family == AF_INET6)) { + if(addr->family == AF_INET6) { struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr; - sa6->sin6_scope_id = data->conn->scope_id; + sa6->sin6_scope_id = get_scope_id(data, sa6); } #endif return CURLE_OK; @@ -1085,7 +1134,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); #endif - infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + { + struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr; + if(sa6->sin6_scope_id) + infof(data, " Trying [%s]:%d scope_id=%lu...", + ctx->ip.remote_ip, ctx->ip.remote_port, + (unsigned long)sa6->sin6_scope_id); + else +#endif + infof(data, " Trying [%s]:%d...", + ctx->ip.remote_ip, ctx->ip.remote_port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + } +#endif } else #endif From f1959ae9621af4473f7daa831d5318efdaccd97f Mon Sep 17 00:00:00 2001 From: tiymat <138939221+tiymat@users.noreply.github.com> Date: Wed, 27 May 2026 00:44:31 -0230 Subject: [PATCH 0242/1062] urlapi: fix an issue parsing file URLs Fixes #21743 Closes #21764 --- lib/urlapi.c | 8 +++++++- tests/libtest/lib1560.c | 6 +++++- tests/unit/unit1675.c | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 589a400834b1..a5ec95032b46 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -876,13 +876,19 @@ UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, path = &url[5]; pathlen = urllen - 5; + /* RFC 8089: file-hier-part = ( "//" auth-path ) / local-path, where + local-path also starts with a "/". So reject anything that doesn't + start with at least one "/" */ + if(path[0] != '/') + return CURLUE_BAD_FILE_URL; + /* Extra handling URLs with an authority component (i.e. that start with * "file://") * * We allow omitted hostname (e.g. file:/) -- valid according to * RFC 8089, but not the (current) WHAT-WG URL spec. */ - if(path[0] == '/' && path[1] == '/') { + if(path[1] == '/') { /* swallow the two slashes */ const char *ptr = &path[2]; diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index cd2cbec589c8..bdf8b56cad5b 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -886,7 +886,11 @@ static const struct urltestcase get_url_list[] = { {"file:///.", "file:///", 0, 0, CURLUE_OK}, {"file:///./", "file:///", 0, 0, CURLUE_OK}, {"file:///a", "file:///a", 0, 0, CURLUE_OK}, - {"file:./", "file://", 0, 0, CURLUE_OK}, + {"file:./", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:foo", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:foo/bar", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:?q", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:#f", "", 0, 0, CURLUE_BAD_FILE_URL}, {"http://example.com/hello/../here", "http://example.com/hello/../here", CURLU_PATH_AS_IS, 0, CURLUE_OK}, diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index 024c7ff40028..b5b372336af3 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -267,6 +267,10 @@ static CURLcode test_unit1675(const char *arg) {"file:///etc/hosts", "/etc/hosts", TRUE}, {"file://localhost/etc/hosts", "/etc/hosts", TRUE}, {"file://apple/etc/hosts", "/etc/hosts", FALSE}, + {"file:foo", NULL, FALSE}, + {"file:./", NULL, FALSE}, + {"file:?q", NULL, FALSE}, + {"file:#f", NULL, FALSE}, #ifdef _WIN32 {"file:///c:/windows/system32", "c:/windows/system32", TRUE}, {"file://localhost/c:/windows/system32", "c:/windows/system32", TRUE}, From de9bb509d16f28f9cd43705349bf4b6642ac3a4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 05:06:20 +0000 Subject: [PATCH 0243/1062] GHA: update dependency google/boringssl to v0.20260526.0 Closes #21790 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index cb46b879119a..a18d4dd8bab1 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 1.73.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260508.0 + BORINGSSL_VERSION: 0.20260526.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9546aa2cb5aa..09acc3eab9a6 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -37,7 +37,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 1.73.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260508.0 + BORINGSSL_VERSION: 0.20260526.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.678 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com From 9591ff123ded454d9c45c946c286123b4a1c0a5d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 09:23:27 +0200 Subject: [PATCH 0244/1062] tidy-up: add space around operators, where missing Closes #21793 --- .github/scripts/randcurl.pl | 4 ++-- .github/scripts/verify-examples.pl | 2 +- include/curl/curl.h | 4 ++-- scripts/checksrc.pl | 2 +- scripts/contributors.sh | 4 ++-- scripts/managen | 8 +++---- src/tool_cb_dbg.c | 2 +- tests/data/test1063 | 2 +- tests/ech_combos.py | 2 +- tests/ech_tests.sh | 2 +- tests/ftpserver.pl | 2 +- tests/http/scorecard.py | 36 +++++++++++++++--------------- tests/http/test_02_download.py | 26 ++++++++++----------- tests/http/test_07_upload.py | 28 +++++++++++------------ tests/http/test_08_caddy.py | 12 +++++----- tests/http/test_09_push.py | 6 ++--- tests/http/test_10_proxy.py | 8 +++---- tests/http/test_14_auth.py | 2 +- tests/http/test_16_info.py | 8 +++---- tests/http/test_17_ssl_use.py | 2 +- tests/http/test_18_methods.py | 6 ++--- tests/http/test_19_shutdown.py | 6 ++--- tests/http/test_30_vsftpd.py | 10 ++++----- tests/http/test_31_vsftpds.py | 12 +++++----- tests/http/test_32_ftps_vsftpd.py | 12 +++++----- tests/http/test_40_socks.py | 4 ++-- tests/http/test_50_scp.py | 8 +++---- tests/http/test_51_sftp.py | 8 +++---- tests/negtelnetserver.py | 4 ++-- tests/processhelp.pm | 4 ++-- tests/runtests.pl | 12 +++++----- tests/server/rtspd.c | 2 +- tests/test1276.pl | 4 ++-- tests/testutil.pm | 2 +- 34 files changed, 128 insertions(+), 128 deletions(-) diff --git a/.github/scripts/randcurl.pl b/.github/scripts/randcurl.pl index f9c24d90db6c..83fc0e795f8f 100755 --- a/.github/scripts/randcurl.pl +++ b/.github/scripts/randcurl.pl @@ -218,7 +218,7 @@ sub runconfig { } # run curl command lines using -K -my $end = time() + $seconds/2; +my $end = time() + $seconds / 2; my $c = 0; print "Running command lines\n"; do { @@ -228,7 +228,7 @@ sub runconfig { print "$c command lines\n"; # run curl command lines -$end = time() + $seconds/2; +$end = time() + $seconds / 2; $c = 0; print "Running config lines\n"; do { diff --git a/.github/scripts/verify-examples.pl b/.github/scripts/verify-examples.pl index 007369b4abbc..a23dc412f08b 100755 --- a/.github/scripts/verify-examples.pl +++ b/.github/scripts/verify-examples.pl @@ -68,7 +68,7 @@ sub extract { print O "/* !checksrc! disable BANNEDFUNC all */\n"; # for fopen() print O "/* !checksrc! disable COPYRIGHT all */\n"; print O "/* !checksrc! disable UNUSEDIGNORE all */\n"; - printf O "#line %d \"$f\"\n", $iline+1; + printf O "#line %d \"$f\"\n", $iline + 1; } } elsif($syn == 2) { diff --git a/include/curl/curl.h b/include/curl/curl.h index c790760b88bd..31c1bfb988ec 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -252,7 +252,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE (10*1024*1024) +#define CURL_MAX_READ_SIZE (10 * 1024 * 1024) #endif #ifndef CURL_MAX_WRITE_SIZE @@ -269,7 +269,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, /* The only reason to have a max limit for this is to avoid the risk of a bad server feeding libcurl with a never-ending header that causes reallocs infinitely */ -#define CURL_MAX_HTTP_HEADER (100*1024) +#define CURL_MAX_HTTP_HEADER (100 * 1024) #endif /* This is a magic return code for the write callback that, when returned, diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 748941d5c97c..64b1da9a8553 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -754,7 +754,7 @@ sub scanfile { my $cond = $4; if($cond =~ / = /) { checkwarn("ASSIGNWITHINCONDITION", - $line, $pos+1, $file, $l, + $line, $pos + 1, $file, $l, "assignment within conditional expression"); } my $temp = $cond; diff --git a/scripts/contributors.sh b/scripts/contributors.sh index 44dce7e2ab1f..a3a11d52432a 100755 --- a/scripts/contributors.sh +++ b/scripts/contributors.sh @@ -82,7 +82,7 @@ awk ' { if(length($0)) { num++; - n = sprintf("%s%s%s,", n, length(n)?" ":"", $0); + n = sprintf("%s%s%s,", n, length(n) ? " " : "", $0); #print n; if(length(n) > 77) { printf(" %s\n", p); @@ -93,7 +93,7 @@ awk ' } END { - pp=substr(p,1,length(p)-1); + pp = substr(p, 1, length(p) - 1); printf(" %s\n", pp); printf(" (%d contributors)\n", num); } diff --git a/scripts/managen b/scripts/managen index 554f4c984e8f..e9b5d6157e67 100755 --- a/scripts/managen +++ b/scripts/managen @@ -89,8 +89,8 @@ my $colwidth=79; # max number of columns sub prefixline { my ($num) = @_; - print "\t" x ($num/8); - print ' ' x ($num%8); + print "\t" x ($num / 8); + print ' ' x ($num % 8); } sub justline { @@ -868,7 +868,7 @@ sub single { if($count == ($num -1)) { $sep = " and "; } - $mstr .= sprintf "%s$l", $mstr?$sep:""; + $mstr .= sprintf "%s$l", $mstr ? $sep : ""; $count++; } push @foot, overrides($standalone, @@ -1202,7 +1202,7 @@ sub listglobals { close(F); } for my $e (0 .. $#globalopts) { - $globals .= sprintf "%s--%s", $e?($globalopts[$e+1] ? ", " : " and "):"", + $globals .= sprintf "%s--%s", $e ? ($globalopts[$e + 1] ? ", " : " and ") : "", $globalopts[$e],; } } diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index c9c14e6d1311..cd046dcbad08 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -134,7 +134,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, struct timeval tv; char timebuf[20]; /* largest signed 64-bit is: 9,223,372,036,854,775,807 - * max length in decimal: 1 + (6*3) = 19 + * max length in decimal: 1 + (6 * 3) = 19 * formatted via TRC_IDS_FORMAT_IDS_2 this becomes 2 + 19 + 1 + 19 + 2 = 43 * negative xfer-id are not printed, negative conn-ids use TRC_IDS_FORMAT_1 */ diff --git a/tests/data/test1063 b/tests/data/test1063 index 63a2949716de..0e95511d2748 100644 --- a/tests/data/test1063 +++ b/tests/data/test1063 @@ -21,7 +21,7 @@ Largefile Invalid large X- range on a file:// -# This range value is 2**32+7, which will be truncated to the valid value 7 +# This range value is 2**32 + 7, which will be truncated to the valid value 7 # if the large file support is not working correctly -r 4294967303- file://localhost%FILE_PWD/%LOGDIR/test%TESTNUMBER.txt diff --git a/tests/ech_combos.py b/tests/ech_combos.py index 586619be33fc..8b15eff41fe1 100755 --- a/tests/ech_combos.py +++ b/tests/ech_combos.py @@ -65,7 +65,7 @@ def CombinationRepetitionUtil(chosen, arr, badarr, index, chosen[index] = arr[start] # Current is excluded, replace it - # with next (Note that i+1 is passed, + # with next (Note that i + 1 is passed, # but index is not changed) CombinationRepetitionUtil(chosen, arr, badarr, index + 1, r, start, end) diff --git a/tests/ech_tests.sh b/tests/ech_tests.sh index 4de58e4bc5f0..e1246dae187b 100755 --- a/tests/ech_tests.sh +++ b/tests/ech_tests.sh @@ -1090,7 +1090,7 @@ age_of_news=0 if [ -f "$LTOP"/bad_runs ]; then age_of_news=$(fileage "$LTOP"/bad_runs) # only consider news "new" if we have not mailed today - if ((age_of_news < 24*3600)); then + if ((age_of_news < 24 * 3600)); then itsnews="no" fi fi diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index a15c69e5fb83..88533019c8b1 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -2567,7 +2567,7 @@ sub PASV_ftp { $p="1,2,3,4"; } sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\r\n", - int($pasvport/256), int($pasvport%256)); + int($pasvport / 256), int($pasvport % 256)); } else { # EPSV reply diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 205b556f61e2..1326ce28ff9c 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -55,14 +55,14 @@ class ScoreCardError(Exception): class Card: @classmethod def fmt_ms(cls, tval): - return f'{int(tval*1000)} ms' if tval >= 0 else '--' + return f'{int(tval * 1000)} ms' if tval >= 0 else '--' @classmethod def fmt_size(cls, val): - if val >= (1024*1024*1024): - return f'{val / (1024*1024*1024):0.000f}GB' + if val >= (1024 * 1024 * 1024): + return f'{val / (1024 * 1024 * 1024):0.000f}GB' if val >= (1024 * 1024): - return f'{val / (1024*1024):0.000f}MB' + return f'{val / (1024 * 1024):0.000f}MB' if val >= 1024: return f'{val / 1024:0.000f}KB' return f'{val:0.000f}B' @@ -71,8 +71,8 @@ def fmt_size(cls, val): def fmt_mbs(cls, val): if val is None or val < 0: return '--' - if val >= (1024*1024): - return f'{val/(1024*1024):.3g} MB/s' + if val >= (1024 * 1024): + return f'{val / (1024 * 1024):.3g} MB/s' if val >= 1024: return f'{val / 1024:.3g} KB/s' return f'{val:.3g} B/s' @@ -81,10 +81,10 @@ def fmt_mbs(cls, val): def fmt_speed(cls, val): if val is None or val < 0: return '--' - if val >= (10*1024*1024): - return f'{(val/(1024*1024)):.3f} MB/s' - if val >= (10*1024): - return f'{val/1024:.3f} KB/s' + if val >= (10 * 1024 * 1024): + return f'{(val / (1024 * 1024)):.3f} MB/s' + if val >= (10 * 1024): + return f'{val / 1024:.3f} KB/s' return f'{val:.3f} B/s' @classmethod @@ -92,10 +92,10 @@ def fmt_speed_result(cls, val, limit): if val is None or val < 0: return '--' pct = ((val / limit) * 100) - 100 - if val >= (10*1024*1024): - return f'{(val/(1024*1024)):.3f} MB/s, {pct:+.1f}%' - if val >= (10*1024): - return f'{val/1024:.3f} KB/s, {pct:+.1f}%' + if val >= (10 * 1024 * 1024): + return f'{(val / (1024 * 1024)):.3f} MB/s, {pct:+.1f}%' + if val >= (10 * 1024): + return f'{val / 1024:.3f} KB/s, {pct:+.1f}%' return f'{val:.3f} B/s, {pct:+.1f}%' @classmethod @@ -260,9 +260,9 @@ def __init__(self, env: Env, raise Exception(f'unrecognised limit-rate: {self._limit_rate}') self._limit_rate_num = float(m.group(1)) if m.group(3) == 'g': - self._limit_rate_num *= 1024*1024*1024 + self._limit_rate_num *= 1024 * 1024 * 1024 elif m.group(3) == 'm': - self._limit_rate_num *= 1024*1024 + self._limit_rate_num *= 1024 * 1024 elif m.group(3) == 'k': self._limit_rate_num *= 1024 elif m.group(3) == 'b': @@ -335,7 +335,7 @@ def setup_resources(self, server_docs: str, self._make_docs_file(docs_dir=server_docs, fname=fname, fsize=fsize) self._make_docs_file(docs_dir=server_docs, - fname='reqs10.data', fsize=10*1024) + fname='reqs10.data', fsize=10 * 1024) def _check_downloads(self, r: ExecResult, count: int): error = '' @@ -636,7 +636,7 @@ def do_requests(self, url: str, count: int, max_parallel: int = 1, nsamples: int def requests(self, count: int, meta: Dict[str, Any]) -> Dict[str, Any]: url = f'https://{self.env.domain1}:{self.server_port}/reqs10.data' - fsize = 10*1024 + fsize = 10 * 1024 cols = ['size', 'total'] rows = [] mparallel = meta['request_parallels'] diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index dc51c25f52dd..6e1352ec3a08 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -43,12 +43,12 @@ class TestDownload: def _class_scope(self, env, httpd): indir = httpd.docs_dir env.make_data_file(indir=indir, fname="data-0k", fsize=0) - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) - env.make_data_file(indir=indir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=indir, fname="data-50m", fsize=50*1024*1024) - env.make_data_gzipbomb(indir=indir, fname="bomb-100m.txt", fsize=100*1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) + env.make_data_file(indir=indir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=indir, fname="data-50m", fsize=50 * 1024 * 1024) + env.make_data_gzipbomb(indir=indir, fname="bomb-100m.txt", fsize=100 * 1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) @@ -275,7 +275,7 @@ def test_02_20_h2_small_frames(self, env: Env, httpd, configures_httpd): self.check_downloads(curl, srcfile, count) # download serial via lib client, pause/resume at different offsets - @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) + @pytest.mark.parametrize("pause_offset", [0, 10 * 1024, 100 * 1023, 640000]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -293,7 +293,7 @@ def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): self.check_downloads(client, srcfile, count) # download via lib client, several at a time, pause/resume - @pytest.mark.parametrize("pause_offset", [100*1023]) + @pytest.mark.parametrize("pause_offset", [100 * 1023]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_22_lib_parallel_resume(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -481,7 +481,7 @@ def check_downloads(self, client, srcfile: str, count: int, assert False, f'download {dfile} differs:\n{diff}' # download via lib client, 1 at a time, pause/resume at different offsets - @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) + @pytest.mark.parametrize("pause_offset", [0, 10 * 1024, 100 * 1023, 640000]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_29_h2_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -688,7 +688,7 @@ def test_02_35_pause_bomb(self, env: Env, httpd, nghttpx, proto): # download with looong urls @pytest.mark.parametrize("proto", Env.http_protos()) - @pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024, 80*1024, 96*1024]) + @pytest.mark.parametrize("url_junk", [1024, 16 * 1024, 32 * 1024, 64 * 1024, 80 * 1024, 96 * 1024]) def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): if proto == 'h3' and env.curl_uses_lib('quiche'): pytest.skip("quiche fails from 16k onwards") @@ -699,11 +699,11 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): if url_junk <= 1024: r.check_exit_code(0) r.check_response(http_status=200) - elif url_junk <= 16*1024: + elif url_junk <= 16 * 1024: r.check_exit_code(0) # server replies with 414, Request URL too long r.check_response(http_status=414) - elif url_junk <= 32*1024: + elif url_junk <= 32 * 1024: r.check_exit_code(0) # server replies with 414, Request URL too long r.check_response(http_status=414) @@ -716,7 +716,7 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): # h2 is unable to send such large headers (frame limits) r.check_exit_code(55) elif proto == 'h3': - if url_junk <= 64*1024: + if url_junk <= 64 * 1024: r.check_exit_code(0) # nghttpx reports 431 Request Header Field too Large r.check_response(http_status=431) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 49f5213aa152..fee50387c59d 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -42,12 +42,12 @@ class TestUpload: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-63k", fsize=63*1024) - env.make_data_file(indir=env.gen_dir, fname="data-64k", fsize=64*1024) - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="data-1m+", fsize=(1024*1024)+1) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-63k", fsize=63 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-64k", fsize=64 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-1m+", fsize=(1024 * 1024) + 1) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # upload small data, check that this is what was echoed @pytest.mark.parametrize("proto", Env.http_protos()) @@ -147,7 +147,7 @@ def test_07_14_upload_stdin(self, env: Env, httpd, nghttpx, proto, indata): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_15_hx_put(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -161,7 +161,7 @@ def test_07_15_hx_put(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_16_hx_put_reuse(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -175,7 +175,7 @@ def test_07_16_hx_put_reuse(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_17_hx_post_reuse(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/echo' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -519,7 +519,7 @@ def test_07_43_upload_denied(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("httpcode", [301, 302, 307, 308]) def test_07_44_put_redir(self, env: Env, httpd, nghttpx, proto, httpcode): count = 1 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put-redir-{httpcode}' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -639,10 +639,10 @@ def test_07_63_upload_exp100_paused(self, env: Env, httpd, nghttpx, proto): @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx") @pytest.mark.parametrize("proto,upload_size", [ pytest.param('http/1.1', 100, id='h1-small-body'), - pytest.param('http/1.1', 10*1024, id='h1-medium-body'), - pytest.param('http/1.1', 32*1024, id='h1-limited-body'), - pytest.param('h2', 10*1024, id='h2-medium-body'), - pytest.param('h2', 32*1024, id='h2-limited-body'), + pytest.param('http/1.1', 10 * 1024, id='h1-medium-body'), + pytest.param('http/1.1', 32 * 1024, id='h1-limited-body'), + pytest.param('h2', 10 * 1024, id='h2-medium-body'), + pytest.param('h2', 32 * 1024, id='h2-limited-body'), pytest.param('h3', 1024, id='h3-small-body'), pytest.param('h3', 1024 * 1024, id='h3-limited-body'), ]) diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index c7dd25a8eb5d..efee4bc48b76 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -59,12 +59,12 @@ def _make_docs_file(self, docs_dir: str, fname: str, fsize: int): @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, caddy): - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10k.data', fsize=10*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data1.data', fsize=1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data5.data', fsize=5*1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10.data', fsize=10*1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data100.data', fsize=100*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10k.data', fsize=10 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data1.data', fsize=1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data5.data', fsize=5 * 1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10.data', fsize=10 * 1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data100.data', fsize=100 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_09_push.py b/tests/http/test_09_push.py index 8f4714be0708..386289b39e97 100644 --- a/tests/http/test_09_push.py +++ b/tests/http/test_09_push.py @@ -40,9 +40,9 @@ def _class_scope(self, env, httpd): push_dir = os.path.join(httpd.docs_dir, 'push') if not os.path.exists(push_dir): os.makedirs(push_dir) - env.make_data_file(indir=push_dir, fname="data1", fsize=1*1024) - env.make_data_file(indir=push_dir, fname="data2", fsize=1*1024) - env.make_data_file(indir=push_dir, fname="data3", fsize=1*1024) + env.make_data_file(indir=push_dir, fname="data1", fsize=1 * 1024) + env.make_data_file(indir=push_dir, fname="data2", fsize=1 * 1024) + env.make_data_file(indir=push_dir, fname="data3", fsize=1 * 1024) def httpd_configure(self, env, httpd): httpd.set_extra_config(env.domain1, [ diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index 89c66278d485..f81260a28f21 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -45,11 +45,11 @@ def _class_scope(self, env, httpd, nghttpx_fwd): os.makedirs(push_dir) if env.have_nghttpx(): nghttpx_fwd.start_if_needed() - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) def get_tunnel_proto_used(self, r: ExecResult): for line in r.trace_lines: diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 288e7b5d28de..7a1bc25c0fdf 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -37,7 +37,7 @@ class TestAuth: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # download 1 file, not authenticated @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_16_info.py b/tests/http/test_16_info.py index 5be5e31b92ca..977342eb55f8 100644 --- a/tests/http/test_16_info.py +++ b/tests/http/test_16_info.py @@ -38,10 +38,10 @@ class TestInfo: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) # download plain file @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 4a4dd0bf7e04..0f3b2ccb623f 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -64,7 +64,7 @@ class TestSSLUse: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=httpd.docs_dir, fname="data-10k", fsize=10*1024) + env.make_data_file(indir=httpd.docs_dir, fname="data-10k", fsize=10 * 1024) def test_17_01_sslinfo_plain(self, env: Env, httpd): proto = 'http/1.1' diff --git a/tests/http/test_18_methods.py b/tests/http/test_18_methods.py index 006347092833..faa31a638e7f 100644 --- a/tests/http/test_18_methods.py +++ b/tests/http/test_18_methods.py @@ -37,9 +37,9 @@ class TestMethods: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_19_shutdown.py b/tests/http/test_19_shutdown.py index d72ed5a2ece2..4ed9ad27fb01 100644 --- a/tests/http/test_19_shutdown.py +++ b/tests/http/test_19_shutdown.py @@ -39,9 +39,9 @@ class TestShutdown: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) # check with `tcpdump` that we see curl TCP RST packets @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index 5991c4555343..adbd6ecc597a 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -64,13 +64,13 @@ def _class_scope(self, env, vsftpd): os.makedirs(vsftpd.docs_dir) self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-0k', fsize=0) self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-0k", fsize=0) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_30_01_list_dir(self, env: Env, vsftpd: VsFTPD): curl = CurlClient(env=env) diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 0f8f23e9b6fa..f36bb1715a46 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -70,12 +70,12 @@ def _class_scope(self, env, vsftpds): if not os.path.exists(vsftpds.docs_dir): os.makedirs(vsftpds.docs_dir) self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_31_01_list_dir(self, env: Env, vsftpds: VsFTPD): curl = CurlClient(env=env) @@ -191,7 +191,7 @@ def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): line_length = 21 srcfile = os.path.join(env.gen_dir, docname) dstfile = os.path.join(vsftpds.docs_dir, docname) - env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100*1024, + env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100 * 1024, line_length=line_length) srcsize = os.path.getsize(srcfile) self._rmf(dstfile) diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index 5433081d9822..19eec643c66b 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -70,12 +70,12 @@ def _class_scope(self, env, vsftpds): if not os.path.exists(vsftpds.docs_dir): os.makedirs(vsftpds.docs_dir) self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_32_01_list_dir(self, env: Env, vsftpds: VsFTPD): curl = CurlClient(env=env) @@ -204,7 +204,7 @@ def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): line_length = 21 srcfile = os.path.join(env.gen_dir, docname) dstfile = os.path.join(vsftpds.docs_dir, docname) - env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100*1024, + env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100 * 1024, line_length=line_length) srcsize = os.path.getsize(srcfile) self._rmf(dstfile) diff --git a/tests/http/test_40_socks.py b/tests/http/test_40_socks.py index 0e1d117399c4..9702aa111060 100644 --- a/tests/http/test_40_socks.py +++ b/tests/http/test_40_socks.py @@ -49,8 +49,8 @@ def danted(self, env: Env) -> Generator[Dante, None, None]: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=indir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) def test_40_01_socks_http(self, env: Env, sproto, danted: Dante, httpd): diff --git a/tests/http/test_50_scp.py b/tests/http/test_50_scp.py index 409378a11ee7..5bfba23f338c 100644 --- a/tests/http/test_50_scp.py +++ b/tests/http/test_50_scp.py @@ -41,10 +41,10 @@ class TestScp: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, sshd): - env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) def test_50_01_insecure(self, env: Env, sshd: Sshd): curl = CurlClient(env=env) diff --git a/tests/http/test_51_sftp.py b/tests/http/test_51_sftp.py index 052c7ef46980..76e8727b99ee 100644 --- a/tests/http/test_51_sftp.py +++ b/tests/http/test_51_sftp.py @@ -41,10 +41,10 @@ class TestSftp: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, sshd): - env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) def test_51_01_insecure(self, env: Env, sshd: Sshd): curl = CurlClient(env=env) diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index c31fc033aaa0..ae0e3ae9415f 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -84,7 +84,7 @@ def handle(self): neg.send_wont("NAWS") # Get the data passed through the negotiator - data = neg.recv(4*1024) + data = neg.recv(4 * 1024) log.debug("Incoming data: %r", data) if VERIFIED_REQ.encode('utf-8') in data: @@ -106,7 +106,7 @@ def handle(self): # put some effort into making a clean socket shutdown # that does not give the client ECONNRESET self.request.settimeout(0.1) - self.request.recv(4*1024) + self.request.recv(4 * 1024) self.request.shutdown(socket.SHUT_RDWR) except IOError: diff --git a/tests/processhelp.pm b/tests/processhelp.pm index ee5fc52d6dc4..688c1808d2ab 100644 --- a/tests/processhelp.pm +++ b/tests/processhelp.pm @@ -342,8 +342,8 @@ sub killpid { @requested = sort({$a <=> $b} @requested); } for(my $i = scalar(@requested) - 2; $i >= 0; $i--) { - if($requested[$i] == $requested[$i+1]) { - splice @requested, $i+1, 1; + if($requested[$i] == $requested[$i + 1]) { + splice @requested, $i + 1, 1; } } diff --git a/tests/runtests.pl b/tests/runtests.pl index e6b343c81560..cc8fa51b29e2 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -1928,11 +1928,11 @@ sub singletest_success { my $esttotal = $sofar/$count * $total; my $estleft = $esttotal - $sofar; my $timeleft=sprintf("remaining: %02d:%02d", - $estleft/60, - $estleft%60); + $estleft / 60, + $estleft % 60); my $took = $timevrfyend{$testnum} - $timeprepini{$testnum}; my $duration = sprintf("duration: %02d:%02d", - $sofar/60, $sofar%60); + $sofar / 60, $sofar % 60); if(!$automakestyle) { logmsg sprintf("OK (%-3d out of %-3d, %s, took %.3fs, %s)\n", $count, $total, $timeleft, $took, $duration); @@ -2613,7 +2613,7 @@ sub pickrunner { } } elsif($ARGV[0] =~ /^to$/i) { - $fromnum = $number+1; + $fromnum = $number + 1; } elsif($ARGV[0] =~ /^!(\d+)/) { $fromnum = -1; @@ -2645,7 +2645,7 @@ sub pickrunner { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); # seed of the month. December 2019 becomes 201912 - $randseed = ($year+1900)*100 + $mon+1; + $randseed = ($year + 1900) * 100 + $mon + 1; print "Using curl: $CURL\n"; open(my $curlvh, "-|", exerunner() . shell_quote($CURL) . " --version 2>$dev_null") || die "could not get curl version!"; @@ -3342,7 +3342,7 @@ sub testnumdetails { logmsg "IGNORED: failed tests: $sorted\n"; } logmsg sprintf("TESTDONE: $ok tests out of $total reported OK: %d%%\n", - $ok/$total*100); + $ok / $total * 100); if($failed && ($ok != $total)) { my $failedsorted = numsortwords($failed); diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 31820008f0cc..97b2ba40d9d0 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -76,7 +76,7 @@ struct rtspd_httprequest { - skip bytes. */ int rcmd; /* doing a special command, see defines above */ reqprot_t protocol; /* request protocol, HTTP or RTSP */ - int prot_version; /* HTTP or RTSP version (major*10 + minor) */ + int prot_version; /* HTTP or RTSP version (major * 10 + minor) */ bool pipelining; /* true if request is pipelined */ char *rtp_buffer; size_t rtp_buffersize; diff --git a/tests/test1276.pl b/tests/test1276.pl index 8732ca4b1e53..bdd903953ada 100755 --- a/tests/test1276.pl +++ b/tests/test1276.pl @@ -56,8 +56,8 @@ sub showline { $file[$i] =~ s/[\r\n]//g; if($gen[$i] ne $file[$i]) { printf "File: %u:%s\nGen: %u:%s\n", - $i+1, showline($file[$i]), - $i+1, showline($gen[$i]); + $i + 1, showline($file[$i]), + $i + 1, showline($gen[$i]); $e++; if($e > 10) { # only show 10 lines diff diff --git a/tests/testutil.pm b/tests/testutil.pm index f0a267949212..6e3322332e6b 100644 --- a/tests/testutil.pm +++ b/tests/testutil.pm @@ -164,7 +164,7 @@ sub subbase64 { # boundary. Then provide two alternatives. my $now = time(); my $d = ($1 * 24 * 3600) + $now + 30; - $d = int($d/60) * 60; + $d = int($d / 60) * 60; my $d2 = $d + 60; $$thing =~ s/%%DAYS%%/%alternatives[$d,$d2]/; } From 91facd7bb3bb366525b7cb41221f6359c5e936db Mon Sep 17 00:00:00 2001 From: Aritra Basu Date: Wed, 27 May 2026 20:35:44 -0400 Subject: [PATCH 0245/1062] tests/http: fix HTTP/3 proxy pytest failures with h2o Fix pytest failures in HTTP/3 proxy tests when h2o is not installed, misconfigured, or fails to start at runtime. This prevents: - FileNotFoundError when h2o document root does not exist - Fixture setup errors when h2o is configured but cannot start - Unused test data file creation when h2o is absent or broken - CI aborts on systems where h2o exists but is not runnable Bug: https://github.com/curl/curl/pull/21789#issuecomment-4559098879 Bug: https://github.com/curl/curl/pull/21789#issuecomment-4559161907 Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21791 --- tests/http/conftest.py | 8 ++++++-- tests/http/test_60_h3_proxy.py | 27 +++++++++++++++------------ tests/http/testenv/env.py | 1 + tests/http/testenv/h2o.py | 12 ++++++++---- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 0de5c1a8b9e0..5275b91bf969 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -172,7 +172,9 @@ def h2o_server(env) -> Generator[Union[H2oServer, bool], None, None]: h2o = H2oServer(env=env) if env.have_h2o(): h2o.clear_logs() - assert h2o.initial_start() + if not h2o.initial_start(): + h2o_logs = "\n".join(h2o.dump_logs()) + pytest.skip(f"h2o server failed to start\n{h2o_logs}") yield h2o h2o.stop() else: @@ -184,7 +186,9 @@ def h2o_proxy(env) -> Generator[Union[H2oProxy, bool], None, None]: h2o = H2oProxy(env=env) if env.have_h2o(): h2o.clear_logs() - assert h2o.initial_start() + if not h2o.initial_start(): + h2o_logs = "\n".join(h2o.dump_logs()) + pytest.skip(f"h2o proxy failed to start\n{h2o_logs}") yield h2o h2o.stop() else: diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index def32a6fe775..786c6e7bf274 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -359,10 +359,11 @@ class TestH3ProxyRobustness: pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env): - doc_root = os.path.join(env.gen_dir, "docs") + def _class_scope(self, env, h2o_server): + if not env.have_h2o(): + pytest.skip("h2o not available") env.make_data_file( - indir=doc_root, fname="proxy-drop-20m", fsize=20 * 1024 * 1024 + indir=h2o_server.docs_dir, fname="proxy-drop-20m", fsize=20 * 1024 * 1024 ) def test_60_05_graceful_shutdown( @@ -451,10 +452,11 @@ class TestH3ProxyDataTransfer: pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env): - doc_root = os.path.join(env.gen_dir, "docs") - env.make_data_file(indir=doc_root, fname="download-1m", fsize=1 * 1024 * 1024) - env.make_data_file(indir=doc_root, fname="download-10m", fsize=10 * 1024 * 1024) + def _class_scope(self, env, h2o_server): + if not env.have_h2o(): + pytest.skip("h2o not available") + env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-2m", fsize=2 * 1024 * 1024) def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): @@ -558,11 +560,12 @@ class TestH3ProxyUdpTunnel: pytestmark = H3_PROXY_COMMON_MARKS @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env): - doc_root = os.path.join(env.gen_dir, "docs") - env.make_data_file(indir=doc_root, fname="download-1400", fsize=1400) - env.make_data_file(indir=doc_root, fname="download-1m", fsize=1 * 1024 * 1024) - env.make_data_file(indir=doc_root, fname="download-10m", fsize=10 * 1024 * 1024) + def _class_scope(self, env, h2o_server): + if not env.have_h2o(): + return + env.make_data_file(indir=h2o_server.docs_dir, fname="download-1400", fsize=1400) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) @MARK_NEEDS_H2O @pytest.mark.parametrize( diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 3b43cfce0d10..093092b4c5de 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -909,6 +909,7 @@ def make_data_file( ) -> str: if line_length < 11: raise RuntimeError("line_length less than 11 not supported") + os.makedirs(indir, exist_ok=True) fpath = os.path.join(indir, fname) s10 = "0123456789" s = round((line_length / 10) + 1) * s10 diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index 6a55f4882bf7..c67aaf18886c 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -241,6 +241,11 @@ def __init__(self, env: Env): super().__init__( env=env, name="h2o-server", domain=env.domain1, cred_name=env.domain1 ) + self._docs_dir = os.path.join(self.env.gen_dir, "docs") + + @property + def docs_dir(self): + return self._docs_dir def initial_start(self): super().initial_start() @@ -261,11 +266,10 @@ def startup(ports: Dict[str, int]) -> bool: def write_config(self): creds = self.env.get_credentials(self._cred_name) assert creds # convince pytype this is not None - doc_root = os.path.join(self.env.gen_dir, "docs") - self._mkpath(doc_root) + self._mkpath(self._docs_dir) self._mkpath(self._run_dir) # Create a simple test file - with open(os.path.join(doc_root, "data.json"), "w") as f: + with open(os.path.join(self._docs_dir, "data.json"), "w") as f: f.write('{"message": "Hello from h2o HTTP/3 server"}\n') with open(self._conf_file, "w") as fd: fd.write(f"""# h2o HTTP/3 server configuration @@ -289,7 +293,7 @@ def write_config(self): "{self._domain}": paths: "/": - file.dir: {doc_root} + file.dir: {self._docs_dir} http2-reprioritize-blocking-assets: ON From f2183f51b6651dae759164d064c62fa075d8f695 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 23:43:27 +0200 Subject: [PATCH 0246/1062] build: say 'experimental' in option descriptions Also: - INSTALL-CMAKE.md: alpha-sort enable options. - cmake: sync a description between source and docs. Closes #21795 --- CMakeLists.txt | 10 +++++----- configure.ac | 4 ++-- docs/INSTALL-CMAKE.md | 10 +++++----- m4/curl-confopts.m4 | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a34f8524ef6..d1bce76dd814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -738,7 +738,7 @@ endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) -cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS (experimental)" OFF CURL_ENABLE_SSL OFF) if(WIN32 OR CURL_USE_SCHANNEL OR @@ -1080,8 +1080,8 @@ if(USE_OPENSSL) endif() endif() -option(USE_HTTPSRR "Enable HTTPS RR support" OFF) -option(USE_ECH "Enable ECH support" OFF) +option(USE_HTTPSRR "Enable HTTPS RR support (experimental)" OFF) +option(USE_ECH "Enable ECH support (experimental)" OFF) if(USE_ECH) if(USE_OPENSSL OR USE_WOLFSSL OR USE_RUSTLS) # Be sure that the TLS library actually supports ECH. @@ -1109,7 +1109,7 @@ if(USE_ECH) endif() endif() -option(USE_SSLS_EXPORT "Enable SSL session export support" OFF) +option(USE_SSLS_EXPORT "Enable SSL session import/export (experimental)" OFF) if(USE_SSLS_EXPORT) if(_ssl_enabled) message(STATUS "SSL export enabled.") @@ -1118,7 +1118,7 @@ if(USE_SSLS_EXPORT) endif() endif() -option(USE_PROXY_HTTP3 "Enable experimental HTTP/3 proxy support" OFF) +option(USE_PROXY_HTTP3 "Enable HTTP/3 proxy support (experimental)" OFF) option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) diff --git a/configure.ac b/configure.ac index 445ab29c9632..bb9390e0a1c0 100644 --- a/configure.ac +++ b/configure.ac @@ -57,8 +57,8 @@ CURL_CHECK_OPTION_SSLS_EXPORT AC_MSG_CHECKING([whether to enable HTTP/3 proxy support]) OPT_PROXY_HTTP3="default" AC_ARG_ENABLE(proxy-http3, -AS_HELP_STRING([--enable-proxy-http3],[Enable experimental HTTP/3 proxy support]) -AS_HELP_STRING([--disable-proxy-http3],[Disable experimental HTTP/3 proxy support]), +AS_HELP_STRING([--enable-proxy-http3],[Enable HTTP/3 proxy support (experimental)]) +AS_HELP_STRING([--disable-proxy-http3],[Disable HTTP/3 proxy support (experimental)]), OPT_PROXY_HTTP3=$enableval) case "$OPT_PROXY_HTTP3" in no) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index bfc1e451f2b7..1ea6760ec177 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -250,12 +250,12 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `ENABLE_UNICODE`: Use the Unicode version of the Windows API functions. Default: `OFF` - `ENABLE_UNIX_SOCKETS`: Enable Unix domain sockets support. Default: `ON` - `USE_APPLE_IDN`: Use Apple built-in IDN support. Default: `OFF` -- `USE_ECH`: Enable ECH support. Default: `OFF` -- `USE_HTTPSRR`: Enable HTTPS RR support. Default: `OFF` -- `USE_SSLS_EXPORT`: Enable experimental SSL session import/export. Default: `OFF` +- `USE_ECH`: Enable ECH support (experimental). Default: `OFF` +- `USE_HTTPSRR`: Enable HTTPS RR support (experimental). Default: `OFF` +- `USE_PROXY_HTTP3`: Enable HTTP/3 proxy support (experimental). Default: `OFF` +- `USE_SSLS_EXPORT`: Enable SSL session import/export (experimental). Default: `OFF` - `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` - `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` -- `USE_PROXY_HTTP3`: Enable experimental HTTP/3 proxy support. Default: `OFF` ## Disabling features @@ -348,7 +348,7 @@ Details via CMake - `CURL_USE_OPENSSL`: Enable OpenSSL for SSL/TLS. Default: `ON` if no other TLS backend was enabled. - `CURL_USE_PKGCONFIG`: Enable `pkg-config` to detect dependencies. Default: `ON` for Unix (except Android, Apple devices), vcpkg, MinGW if not cross-compiling. -- `CURL_USE_RUSTLS`: Enable Rustls for SSL/TLS. Default: `OFF` +- `CURL_USE_RUSTLS`: Enable Rustls for SSL/TLS (experimental). Default: `OFF` - `CURL_USE_SCHANNEL`: Enable Windows native SSL/TLS (Schannel). Default: `OFF` - `CURL_USE_WOLFSSL`: Enable wolfSSL for SSL/TLS. Default: `OFF` - `CURL_ZLIB`: Use zlib (`ON`, `OFF` or `AUTO`). Default: `AUTO` diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index 629b923cfa2b..d3e1a33b6bb5 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -475,8 +475,8 @@ AC_DEFUN([CURL_CHECK_OPTION_HTTPSRR], [ AC_MSG_CHECKING([whether to enable HTTPSRR support]) OPT_HTTPSRR="default" AC_ARG_ENABLE(httpsrr, -AS_HELP_STRING([--enable-httpsrr],[Enable HTTPSRR support]) -AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support]), +AS_HELP_STRING([--enable-httpsrr],[Enable HTTPSRR support (experimental)]) +AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support (experimental)]), OPT_HTTPSRR=$enableval) case "$OPT_HTTPSRR" in no) @@ -510,8 +510,8 @@ AC_DEFUN([CURL_CHECK_OPTION_ECH], [ AC_MSG_CHECKING([whether to enable ECH support]) OPT_ECH="default" AC_ARG_ENABLE(ech, -AS_HELP_STRING([--enable-ech],[Enable ECH support]) -AS_HELP_STRING([--disable-ech],[Disable ECH support]), +AS_HELP_STRING([--enable-ech],[Enable ECH support (experimental)]) +AS_HELP_STRING([--disable-ech],[Disable ECH support (experimental)]), OPT_ECH=$enableval) case "$OPT_ECH" in no) @@ -547,9 +547,9 @@ AC_DEFUN([CURL_CHECK_OPTION_SSLS_EXPORT], [ OPT_SSLS_EXPORT="default" AC_ARG_ENABLE(ssls-export, AS_HELP_STRING([--enable-ssls-export], - [Enable SSL session export support]) + [Enable SSL session export support (experimental)]) AS_HELP_STRING([--disable-ssls-export], - [Disable SSL session export support]), + [Disable SSL session export support (experimental)]), OPT_SSLS_EXPORT=$enableval) case "$OPT_SSLS_EXPORT" in no) From cfa735c40a46b19c787f540606495881452384d1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 23:50:45 +0200 Subject: [PATCH 0247/1062] h3-proxy: rename feature 'Proxy/PROXY-HTTP3' to 'proxy-HTTP3' For consistency and to follow existing 'HTTPS-proxy' (with lowercase 'proxy') feature tag more closely. Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21796 --- CMakeLists.txt | 2 +- configure.ac | 6 +++--- docs/libcurl/curl_version_info.md | 2 +- lib/version.c | 2 +- tests/http/test_60_h3_proxy.py | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1bce76dd814..362cc8ab90c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2062,7 +2062,7 @@ curl_add_if("NTLM" CURL_ENABLE_NTLM AND curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) -curl_add_if("PROXY-HTTP3" USE_PROXY_HTTP3) +curl_add_if("proxy-HTTP3" USE_PROXY_HTTP3) curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL) curl_add_if("HTTPS-proxy" NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR diff --git a/configure.ac b/configure.ac index bb9390e0a1c0..20b4a9d91b34 100644 --- a/configure.ac +++ b/configure.ac @@ -5086,7 +5086,7 @@ if test "$want_proxy_http3" = "yes"; then AC_DEFINE(USE_PROXY_HTTP3, 1, [if HTTP/3 proxy support is available]) USE_PROXY_HTTP3=1 AC_MSG_RESULT([yes]) - experimental="$experimental PROXY-HTTP3" + experimental="$experimental proxy-HTTP3" fi fi @@ -5204,7 +5204,7 @@ if test "$curl_psl_msg" = "enabled"; then fi if test "$USE_PROXY_HTTP3" = "1"; then - SUPPORT_FEATURES="$SUPPORT_FEATURES PROXY-HTTP3" + SUPPORT_FEATURES="$SUPPORT_FEATURES proxy-HTTP3" fi if test "$curl_gsasl_msg" = "enabled"; then @@ -5551,7 +5551,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: HTTP1: ${curl_h1_msg} HTTP2: ${curl_h2_msg} HTTP3: ${curl_h3_msg} - Proxy-HTTP3: ${curl_proxy_http3_msg} + proxy-HTTP3: ${curl_proxy_http3_msg} ECH: ${curl_ech_msg} HTTPS RR: ${curl_httpsrr_msg} SSLS-EXPORT: ${curl_ssls_export_msg} diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index ec29fa66e778..5b1822216995 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -298,7 +298,7 @@ supports HTTP NTLM libcurl was built with support for NTLM delegation to a winbind helper. This feature was removed from curl in 8.8.0. -## `PROXY-HTTP3` +## `proxy-HTTP3` *features* mask bit: non-existent diff --git a/lib/version.c b/lib/version.c index d5870333abf1..6522f0951e18 100644 --- a/lib/version.c +++ b/lib/version.c @@ -492,7 +492,7 @@ static const struct feat features_table[] = { FEATURE("NTLM", NULL, CURL_VERSION_NTLM), #endif #ifdef USE_PROXY_HTTP3 - FEATURE("PROXY-HTTP3", NULL, 0), + FEATURE("proxy-HTTP3", NULL, 0), #endif #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 786c6e7bf274..ca4501f6352d 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -39,7 +39,7 @@ condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" ) MARK_NEEDS_PROXY_HTTP3 = pytest.mark.skipif( - condition=not Env.curl_has_feature("PROXY-HTTP3"), + condition=not Env.curl_has_feature("proxy-HTTP3"), reason="curl lacks experimental HTTP/3 proxy support" ) MARK_NEEDS_NGHTTP3 = pytest.mark.skipif( @@ -340,7 +340,7 @@ def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): r = curl.http_download( urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args ) - if not env.curl_has_feature("PROXY-HTTP3"): + if not env.curl_has_feature("proxy-HTTP3"): r.check_exit_code(2) assert UNSUPPORTED_OPT_MSG in r.stderr.lower(), ( f"Expected unsupported option failure but got: {r.stderr}" From 59213f8248cfc10e97a6a23f5e4da9b1e5057400 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 27 May 2026 23:56:50 +0200 Subject: [PATCH 0248/1062] GHA: enable H3 proxy in CI, also enable h2o tests on Linux Also: - GHA/http3-linux: enable deprecated APIs in openssl-prev local OpenSSL builds. Required by h2o and its vendored dependencies. Tried OpenSSL 4, LibreSSL 4.x, BoringSSL: all failed at one point. - GHA/http3-linux: build h2o from source. libuv1-dev may not be stricly required. Tried installing libwslay-dev, but it wasn't recognized. Also disable building h2o libs for a much smaller dist directory and slightly faster build. Sadly, h2o is not versioned, so I pinned to the current latest commit at the master branch. It advertises itself as 2.3.0-DEV in pytest. - drop redundant `libnghttp3` installs. Remains of openssl-quic builds. Follow-up to 6aaac9dd388a64d0f511544496608693e1105d13 #20226 Note GHA/macos pytests may or not not be stable with the H3 proxy tests. Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21789 --- .github/workflows/codeql.yml | 2 +- .github/workflows/http3-linux.yml | 58 +++++++++++++++++++++++++------ .github/workflows/linux.yml | 4 +-- .github/workflows/macos.yml | 16 ++++----- .github/workflows/windows.yml | 4 +-- 5 files changed, 61 insertions(+), 23 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0423966c4988..335fa10abf1c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -129,7 +129,7 @@ jobs: cmake -B _bld2 -G Ninja -DCURL_DISABLE_TYPECHECK=ON -DCURL_WERROR=ON \ -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR="$(brew --prefix openssl)" -DUSE_NGTCP2=ON \ -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON \ - -DCURL_USE_GSASL=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON + -DCURL_USE_GSASL=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON -DUSE_PROXY_HTTP3=ON cmake --build _bld2 cmake --build _bld2 --target testdeps cmake --build _bld2 --target curl-examples-build diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a18d4dd8bab1..1cb2cd8ace02 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -61,6 +61,9 @@ env: NGTCP2_VERSION: 1.22.1 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 + # no tagged releases + H2O_VERSION: 11b0cfa2771e3ccad4a852e72473e4e278ab1de7 # 2026-05-28 + H2O_SHA256: 5ae1bd7b09970d7d49c41fa68193e24da04c2a7ac5581fbe2affc79200b0721f jobs: build-cache: @@ -124,9 +127,9 @@ jobs: - name: 'cache openssl-prev' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-prev-http3-no-deprecated + id: cache-openssl-prev-http3 env: - cache-name: cache-openssl-prev-http3-no-deprecated + cache-name: cache-openssl-prev-http3 with: path: ~/openssl-prev/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} @@ -187,6 +190,15 @@ jobs: key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ ${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }}" + - name: 'cache h2o' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-h2o + env: + cache-name: cache-h2o + with: + path: ~/h2o/build + key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.H2O_VERSION }}-${{ env.OPENSSL_PREV_VERSION }}" + - id: settings if: >- ${{ !steps.cache-awslc.outputs.cache-hit || @@ -195,13 +207,14 @@ jobs: !steps.cache-gnutls.outputs.cache-hit || !steps.cache-libressl.outputs.cache-hit || !steps.cache-openssl-http3-no-deprecated.outputs.cache-hit || - !steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit || + !steps.cache-openssl-prev-http3.outputs.cache-hit || !steps.cache-wolfssl.outputs.cache-hit || !steps.cache-nghttp3.outputs.cache-hit || !steps.cache-ngtcp2-boringssl.outputs.cache-hit || !steps.cache-ngtcp2-openssl-prev.outputs.cache-hit || !steps.cache-ngtcp2.outputs.cache-hit || - !steps.cache-nghttp2.outputs.cache-hit }} + !steps.cache-nghttp2.outputs.cache-hit || + !steps.cache-h2o.outputs.cache-hit }} run: echo 'needs-build=true' >> "$GITHUB_OUTPUT" @@ -216,6 +229,7 @@ jobs: libtool autoconf automake pkgconf \ libbrotli-dev libzstd-dev zlib1g-dev \ libev-dev \ + libuv1-dev \ libc-ares-dev \ libp11-kit-dev autopoint bison gperf gtk-doc-tools libtasn1-bin # for GnuTLS echo 'CC=gcc-12' >> "$GITHUB_ENV" @@ -298,14 +312,14 @@ jobs: make -j1 install_sw - name: 'build openssl-prev' - if: ${{ !steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit }} + if: ${{ !steps.cache-openssl-prev-http3.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --location --proto-redir =https "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin cd "openssl-${OPENSSL_PREV_VERSION}" - ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated + ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests make make -j1 install_sw @@ -399,6 +413,18 @@ jobs: --with-libbrotlienc --with-libbrotlidec make install + - name: 'build h2o' + if: ${{ !steps.cache-h2o.outputs.cache-hit }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location --proto-redir =https "https://github.com/h2o/h2o/archive/${H2O_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${H2O_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin + cd "h2o-${H2O_VERSION}" + cmake -B . -G Ninja -DWITHOUT_LIBS=ON -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DCMAKE_INSTALL_PREFIX=/home/runner/h2o/build + cmake --build . + cmake --install . + linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} needs: build-cache @@ -483,7 +509,7 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/openssl/build/lib PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export + --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export --enable-proxy-http3 - name: 'openssl' install_steps: skipall @@ -491,7 +517,7 @@ jobs: generate: >- -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_NGTCP2=ON -DCURL_DISABLE_LDAP=ON - -DUSE_ECH=ON + -DUSE_ECH=ON -DUSE_PROXY_HTTP3=ON -DCMAKE_UNITY_BUILD=ON - name: 'openssl-prev' @@ -638,9 +664,9 @@ jobs: - name: 'cache openssl-prev' if: ${{ contains(matrix.build.name, 'openssl-prev') }} uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: cache-openssl-prev-http3-no-deprecated + id: cache-openssl-prev-http3 env: - cache-name: cache-openssl-prev-http3-no-deprecated + cache-name: cache-openssl-prev-http3 with: path: ~/openssl-prev/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} @@ -710,6 +736,16 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.OPENSSL_VERSION }}-${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }} fail-on-cache-miss: true + - name: 'cache h2o' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-h2o + env: + cache-name: cache-h2o + with: + path: ~/h2o/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.H2O_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache quiche' if: ${{ contains(matrix.build.name, 'quiche') }} uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -761,6 +797,7 @@ jobs: -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ -DCURL_USE_LIBUV=ON -DCURL_ENABLE_NTLM=ON \ -DTEST_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ + -DH2O=/home/runner/h2o/build/bin/h2o \ -DHTTPD_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ ${MATRIX_GENERATE} ${options} else @@ -768,6 +805,7 @@ jobs: mkdir bld && cd bld && ../configure --enable-warnings --enable-werror --enable-debug --disable-static \ --disable-dependency-tracking --enable-option-checking=fatal \ --with-libuv --enable-ntlm \ + --with-test-h2o=/home/runner/h2o/build/bin/h2o \ --with-test-nghttpx=/home/runner/nghttp2/build/bin/nghttpx \ ${MATRIX_CONFIGURE} fi diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 09acc3eab9a6..61e6470ed32d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -359,7 +359,7 @@ jobs: /home/linuxbrew/.linuxbrew/opt/c-ares/lib/pkgconfig" generate: >- -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON - -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_HTTPSRR=ON -DENABLE_ARES=ON + -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_HTTPSRR=ON -DENABLE_ARES=ON -DUSE_PROXY_HTTP3=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 @@ -400,7 +400,7 @@ jobs: /home/linuxbrew/.linuxbrew/opt/c-ares/lib/pkgconfig" generate: >- -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON - -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON + -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON -DUSE_PROXY_HTTP3=ON - name: 'thread-sanitizer' install_packages: clang-20 libtsan2 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index dec6e5ceed84..b47af0a6aaa0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -244,7 +244,7 @@ jobs: - name: 'OpenSSL libssh' compiler: llvm@18 - install: libssh libnghttp3 + install: libssh generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF - name: '!ssl HTTP-only c-ares' @@ -275,13 +275,13 @@ jobs: install_steps: pytest generate: >- -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_USE_LIBSSH2=OFF - -DCMAKE_C_STANDARD=90 -DCURL_ENABLE_NTLM=ON + -DCMAKE_C_STANDARD=90 -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'OpenSSL SecTrust' compiler: clang install: libnghttp3 libngtcp2 install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm + configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 - name: 'OpenSSL event-based' compiler: clang @@ -293,7 +293,7 @@ jobs: install: openssl@4 libnghttp3 libngtcp2 gsasl generate: >- -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_USE_GSASL=ON -DUSE_APPLE_IDN=ON -DUSE_NGTCP2=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON - -DUSE_APPLE_SECTRUST=ON -DCURL_ENABLE_NTLM=ON + -DUSE_APPLE_SECTRUST=ON -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'MultiSSL AppleIDN clang-tidy +examples' image: macos-26 @@ -326,7 +326,7 @@ jobs: -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy - -DCURL_ENABLE_NTLM=ON + -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'LibreSSL openldap krb5 c-ares +examples' compiler: clang @@ -374,21 +374,21 @@ jobs: - name: 'OpenSSL torture 1' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=480 1 to 500' - name: 'OpenSSL torture 2' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=730 501 to 1250' - name: 'OpenSSL torture 3' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=628 1251 to 9999' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bd8c214d45b5..75753861449e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -301,7 +301,7 @@ jobs: install: 'mingw-w64-clang-aarch64-libssh2' } - { name: 'openssl', type: 'Release', chkprefill: '_chkprefill', build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun' , - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON', + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DUSE_PROXY_HTTP3=ON', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2' } - { name: 'schannel', type: 'Release', test: 'uwp', build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , @@ -950,7 +950,7 @@ jobs: -DNGTCP2_LIBRARY=/ucrt64/lib/libngtcp2.dll.a -DNGTCP2_CRYPTO_OSSL_LIBRARY=/ucrt64/lib/libngtcp2_crypto_ossl.dll.a -DCURL_CA_NATIVE=ON - -DCURL_ENABLE_NTLM=ON + -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'schannel U' install-vcpkg: 'zlib libssh2[core,zlib]' From e4139a73c82d2035142f5ae36196adb4e9831dae Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 27 May 2026 16:50:18 +0200 Subject: [PATCH 0249/1062] h3-proxy: fixes around H3 proxy code: - less exception handling in existing code - true ip happy eyeballing - enable certificate verification - cf-h2-proxy: abort connection when server closed connection tests: - remove all --insecure and --proxy-insecure args - make session reuse test_60_12 a working one - resolve port conflicts between h2o and nghttpx - use proxy args better - make test_60_06 run shorter - kill h2o at the end of tests, normal stop takes too long Ref: 59213f8248cfc10e97a6a23f5e4da9b1e5057400 #21789 Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21798 --- lib/cf-capsule.c | 36 ++++-- lib/cf-capsule.h | 4 + lib/cf-h2-proxy.c | 6 + lib/cf-h3-proxy.c | 62 +++++++++ lib/cf-h3-proxy.h | 7 + lib/cf-ip-happy.c | 116 +++++++++-------- lib/cf-ip-happy.h | 9 +- lib/cf-socket.c | 24 ++-- lib/cf-socket.h | 9 +- lib/cfilters.c | 4 +- lib/connect.c | 145 ++++++++------------- lib/http_proxy.c | 16 ++- lib/http_proxy.h | 6 +- lib/url.c | 7 +- lib/vquic/curl_ngtcp2.c | 3 +- lib/vquic/curl_quiche.c | 3 +- lib/vquic/vquic.c | 8 +- lib/vquic/vquic.h | 3 +- tests/http/conftest.py | 4 +- tests/http/test_60_h3_proxy.py | 229 +++++++++++++++------------------ tests/http/testenv/curl.py | 5 +- tests/http/testenv/env.py | 11 +- tests/http/testenv/h2o.py | 18 ++- tests/http/testenv/nghttpx.py | 63 ++++----- tests/unit/unit2600.c | 9 +- 25 files changed, 442 insertions(+), 365 deletions(-) diff --git a/lib/cf-capsule.c b/lib/cf-capsule.c index 55a550954c4a..333a8d7efe0d 100644 --- a/lib/cf-capsule.c +++ b/lib/cf-capsule.c @@ -224,29 +224,47 @@ struct Curl_cftype Curl_cft_capsule = { Curl_cf_def_query, }; -CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) +CURLcode Curl_cf_capsule_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) { - struct Curl_cfilter *cf; + struct Curl_cfilter *cf = NULL; struct cf_capsule_ctx *ctx; CURLcode result; (void)data; + (void)conn; + *pcf = NULL; ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) - return CURLE_OUT_OF_MEMORY; + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx); - if(result) { + +out: + *pcf = (!result) ? cf : NULL; + if(result && ctx) { Curl_bufq_free(&ctx->recvbuf); curlx_free(ctx); - return result; } - Curl_conn_cf_insert_after(cf_at, cf); - return CURLE_OK; + return result; +} + +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = Curl_cf_capsule_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; } #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/cf-capsule.h b/lib/cf-capsule.h index 437c9681b6cc..e45983543a68 100644 --- a/lib/cf-capsule.h +++ b/lib/cf-capsule.h @@ -27,6 +27,10 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +CURLcode Curl_cf_capsule_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn); + /* Insert a capsule protocol filter after `cf_at` in the filter chain. * The capsule filter encapsulates/decapsulates UDP datagrams using * the HTTP Datagram capsule format (RFC 9297). */ diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 5eaa9571e646..316ed6c75a8a 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -381,6 +381,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, break; } else if(nread == 0) { + CURL_TRC_CF(data, cf, "server closed connection"); ctx->conn_closed = TRUE; break; } @@ -832,6 +833,11 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(ts); DEBUGASSERT(ts->authority); + if(ctx->conn_closed) { + failf(data, "proxy closed connection"); + return CURLE_COULDNT_CONNECT; + } + do { switch(ts->state) { case H2_TUNNEL_INIT: diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index b37060349351..5ca18ace8383 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -53,6 +53,7 @@ #include "sendf.h" #include "multiif.h" #include "cfilters.h" +#include "cf-capsule.h" #include "cf-socket.h" #include "connect.h" #include "progress.h" @@ -3057,6 +3058,10 @@ static CURLcode cf_h3_proxy_quic_connect(struct Curl_cfilter *cf, } *done = FALSE; + if(!proxy_ctx->dest) { + Curl_peer_link(&proxy_ctx->dest, + Curl_conn_get_destination(cf->conn, cf->sockindex)); + } if(!proxy_ctx->ngtcp2_ctx) { result = cf_h3_proxy_ctx_init(cf, data); @@ -3414,6 +3419,63 @@ struct Curl_cftype Curl_cft_h3_proxy = { cf_h3_proxy_query, }; +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if((transport_out != TRNSPRT_QUIC) || (!conn->http_proxy.peer)) + return CURLE_FAILED_INIT; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->udp_tunnel = (transport_in == TRNSPRT_QUIC); + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + cf->conn = conn; + + result = Curl_cf_udp_create(&cf->next, data, conn, addr, + TRNSPRT_QUIC, TRNSPRT_QUIC); + if(result) + goto out; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; + + if(ctx->udp_tunnel) { + struct Curl_cfilter *cf_caps = NULL; + result = Curl_cf_capsule_create(&cf_caps, data, conn); + if(result) + goto out; + cf_caps->conn = conn; + cf_caps->sockindex = cf->sockindex; + cf_caps->next = cf; + cf = cf_caps; + } + +out: + *pcf = (!result) ? cf : NULL; + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + else + CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->udp_tunnel); + return result; +} + CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, diff --git a/lib/cf-h3-proxy.h b/lib/cf-h3-proxy.h index b2f16acc0eeb..40f0fccf0698 100644 --- a/lib/cf-h3-proxy.h +++ b/lib/cf-h3-proxy.h @@ -35,6 +35,13 @@ CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *dest, bool udp_tunnel); +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out); + extern struct Curl_cftype Curl_cft_h3_proxy; #endif diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 965415d45850..cfada937c86b 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -51,6 +51,7 @@ #include "cfilters.h" #include "cf-dns.h" #include "cf-ip-happy.h" +#include "cf-h3-proxy.h" #include "curl_addrinfo.h" #include "curl_trc.h" #include "multiif.h" @@ -60,8 +61,9 @@ struct transport_provider { - uint8_t transport; cf_ip_connect_create *cf_create; + uint8_t transport; + bool tunnel_proxy; }; static @@ -69,23 +71,30 @@ static const #endif struct transport_provider transport_providers[] = { - { TRNSPRT_TCP, Curl_cf_tcp_create }, + { Curl_cf_tcp_create, TRNSPRT_TCP, FALSE }, + { Curl_cf_tcp_create, TRNSPRT_TCP, TRUE }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) - { TRNSPRT_QUIC, Curl_cf_quic_create }, + { Curl_cf_quic_create, TRNSPRT_QUIC, FALSE }, +#endif +#if !defined(CURL_DISABLE_HTTP) && defined(USE_PROXY_HTTP3) + { Curl_cf_h3_proxy_create, TRNSPRT_QUIC, TRUE }, #endif #ifndef CURL_DISABLE_TFTP - { TRNSPRT_UDP, Curl_cf_udp_create }, + { Curl_cf_udp_create, TRNSPRT_UDP, FALSE }, #endif #ifdef USE_UNIX_SOCKETS - { TRNSPRT_UNIX, Curl_cf_unix_create }, + { Curl_cf_unix_create, TRNSPRT_UNIX, FALSE }, + { Curl_cf_unix_create, TRNSPRT_UNIX, TRUE }, #endif }; -static cf_ip_connect_create *get_cf_create(uint8_t transport) +static cf_ip_connect_create *get_cf_create(uint8_t transport, + bool tunnel_proxy) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { - if(transport == transport_providers[i].transport) + if((transport == transport_providers[i].transport) && + (tunnel_proxy == transport_providers[i].tunnel_proxy)) return transport_providers[i].cf_create; } return NULL; @@ -102,7 +111,6 @@ UNITTEST void debug_set_transport_provider( for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { if(transport == transport_providers[i].transport) { transport_providers[i].cf_create = cf_create; - return; } } } @@ -154,7 +162,8 @@ struct cf_ip_attempt { struct curltime started; /* start of current attempt */ CURLcode result; int ai_family; - uint8_t transport; + uint8_t transport_in; + uint8_t transport_out; int error; BIT(connected); /* cf has connected */ BIT(shutdown); /* cf has shutdown */ @@ -177,7 +186,8 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, struct Curl_easy *data, struct Curl_sockaddr_ex *addr, int ai_family, - uint8_t transport, + uint8_t transport_in, + uint8_t transport_out, cf_ip_connect_create *cf_create) { struct Curl_cfilter *wcf; @@ -191,12 +201,14 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, a->addr = *addr; a->ai_family = ai_family; - a->transport = transport; + a->transport_in = transport_in; + a->transport_out = transport_out; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - 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_in, a->transport_out); if(result) goto out; @@ -251,7 +263,8 @@ struct cf_ip_ballers { timediff_t attempt_delay_ms; int last_attempt_ai_family; uint32_t max_concurrent; - uint8_t transport; + uint8_t transport_in; + uint8_t transport_out; }; static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, @@ -269,7 +282,8 @@ 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_in, + a->transport_out); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -299,18 +313,20 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf, static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, struct Curl_cfilter *cf, cf_ip_connect_create *cf_create, - uint8_t transport, + uint8_t transport_in, + uint8_t transport_out, timediff_t attempt_delay_ms, uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); bs->cf_create = cf_create; - bs->transport = transport; + bs->transport_in = transport_in; + bs->transport_out = transport_out; bs->attempt_delay_ms = attempt_delay_ms; bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ - if(transport == TRNSPRT_UNIX) { + if(transport_in == TRNSPRT_UNIX) { #ifdef USE_UNIX_SOCKETS cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); #else @@ -458,12 +474,13 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(bs->max_concurrent) cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); - result = Curl_socket_addr_from_ai(&addr, ai, bs->transport); + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_out); if(result) goto out; result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, - bs->transport, bs->cf_create); + bs->transport_in, bs->transport_out, + 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); @@ -652,11 +669,13 @@ typedef enum { } cf_connect_state; struct cf_ip_happy_ctx { - uint8_t transport; + struct Curl_peer *peer; cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; struct curltime started; + uint8_t transport_in; + uint8_t transport_out; BIT(dns_resolved); }; @@ -732,10 +751,11 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); + CURL_TRC_CF(data, cf, "init ip ballers for transport %u", + ctx->transport_out); ctx->started = *Curl_pgrs_now(data); - return cf_ip_ballers_init(&ctx->ballers, cf, - ctx->cf_create, ctx->transport, + return cf_ip_ballers_init(&ctx->ballers, cf, ctx->cf_create, + ctx->transport_in, ctx->transport_out, data->set.happy_eyeballs_timeout, IP_HE_MAX_CONCURRENT_ATTEMPTS); } @@ -752,8 +772,10 @@ static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) { - if(ctx) + if(ctx) { + Curl_peer_unlink(&ctx->peer); curlx_free(ctx); + } } static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, @@ -973,9 +995,11 @@ struct Curl_cftype Curl_cft_ip_happy = { */ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *peer, struct connectdata *conn, cf_ip_connect_create *cf_create, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { struct cf_ip_happy_ctx *ctx = NULL; CURLcode result; @@ -988,8 +1012,10 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport = transport; + ctx->transport_in = transport_in; + ctx->transport_out = transport_out; ctx->cf_create = cf_create; + Curl_peer_link(&ctx->peer, peer); result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx); @@ -1003,7 +1029,10 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport) + struct Curl_peer *peer, + uint8_t transport_in, + uint8_t transport_out, + bool tunnel_proxy) { cf_ip_connect_create *cf_create; struct Curl_cfilter *cf; @@ -1011,40 +1040,17 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, /* Need to be first */ DEBUGASSERT(cf_at); - cf_create = get_cf_create(transport); + cf_create = get_cf_create(transport_out, tunnel_proxy); if(!cf_create) { - CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport); + CURL_TRC_CF(data, cf_at, "unsupported transport type %u%s", + transport_out, tunnel_proxy ? "to proxy" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } - result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport); - if(result) - return result; - - Curl_conn_cf_insert_after(cf_at, cf); - return CURLE_OK; -} - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) -CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - /* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw - UDP sockets with TRNSPRT_QUIC transport. Using TRNSPRT_QUIC causes - cf_udp_connect() to call cf_udp_setup_quic() which connects the - socket to the peer address, making send() work without an explicit - destination. We use Curl_cf_udp_create (not Curl_cf_quic_create) - because H3-PROXY manages its own ngtcp2 QUIC stack on top. */ - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(cf_at); - result = cf_ip_happy_create(&cf, data, cf_at->conn, - Curl_cf_udp_create, TRNSPRT_QUIC); + result = cf_ip_happy_create(&cf, data, peer, cf_at->conn, cf_create, + transport_in, transport_out); if(result) return result; Curl_conn_cf_insert_after(cf_at, cf); return CURLE_OK; } -#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 970ec248818b..90cecae8894a 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -29,6 +29,7 @@ struct connectdata; struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; +struct Curl_peer; struct Curl_sockaddr_ex; /** @@ -46,11 +47,15 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + uint8_t transport_in, + uint8_t transport_out); CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport); + struct Curl_peer *peer, + uint8_t transport_in, + uint8_t transport_out, + bool tunnel_proxy); #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ defined(USE_PROXY_HTTP3) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index eb782b65dca6..729f8748bfc9 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1768,7 +1768,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1776,7 +1777,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; - DEBUGASSERT(transport == TRNSPRT_TCP); + (void)transport_in; + DEBUGASSERT(transport_out == TRNSPRT_TCP); if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; @@ -1788,7 +1790,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, addr, transport_out); if(result) goto out; @@ -1934,7 +1936,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1942,14 +1945,15 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); + (void)transport_in; + DEBUGASSERT(transport_out == TRNSPRT_UDP || transport_out == TRNSPRT_QUIC); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, addr, transport_out); if(result) goto out; @@ -1988,7 +1992,8 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1996,14 +2001,15 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, (void)data; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UNIX); + (void)transport_in; + DEBUGASSERT(transport_out == TRNSPRT_UNIX); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, addr, transport_out); if(result) goto out; diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 40c001cc14fd..9c1f3bf4b4c0 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -96,7 +96,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + uint8_t transport_in, + uint8_t transport_out); /** * Creates a cfilter that opens a UDP socket to the given address @@ -109,7 +110,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + uint8_t transport_in, + uint8_t transport_out); /** * Creates a cfilter that opens a UNIX socket to the given address @@ -122,7 +124,8 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + uint8_t transport_in, + uint8_t transport_out); /** * Creates a cfilter that keeps a listening socket. diff --git a/lib/cfilters.c b/lib/cfilters.c index 3946c7231c3e..46c17c199d99 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -690,7 +690,9 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) static bool cf_is_ssl(struct Curl_cfilter *cf) { for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_SSL) + /* A tunneling proxy does not offer end2end encryption, even if + * it does SSL itself (e.g. QUIC H3 proxy) */ + if((cf->cft->flags & CF_TYPE_SSL) && !(cf->cft->flags & CF_TYPE_PROXY)) return TRUE; if(cf->cft->flags & CF_TYPE_IP_CONNECT) return FALSE; diff --git a/lib/connect.c b/lib/connect.c index c2038f4ee4dd..0ed7b22a5b48 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -342,72 +342,14 @@ struct cf_setup_ctx { uint8_t transport; }; -#ifndef CURL_DISABLE_PROXY -static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_setup_ctx *ctx) -{ - CURLcode result = CURLE_OK; -#ifndef USE_SSL - (void)cf; - (void)data; - (void)ctx; -#else - /* Skipping the Curl_conn_is_ssl check because SSL is a part of QUIC - For CURLPROXY_HTTPS and CURLPROXY_HTTPS2: - Curl_cft_setup --> Curl_cft_ssl --> Curl_cft_http_proxy --> ... - For CURLPROXY_HTTPS3: - Curl_cft_setup --> Curl_cft_http3 --> Curl_cft_http_proxy --> ... */ - if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy) { - if(!IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; - } - } - else { - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && - !Curl_conn_is_ssl(cf->conn, cf->sockindex) && - !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; - } - } -#endif /* USE_SSL */ - -#ifndef CURL_DISABLE_HTTP - if(cf->conn->bits.tunnel_proxy) { - struct Curl_peer *dest; /* where HTTP should tunnel to */ - bool udp_tun = false; - dest = Curl_conn_get_destination(cf->conn, cf->sockindex); - /* Use CONNECT-UDP only for explicit HTTP/3-only target tunnels. - Do not derive this from proxy transport (for example HTTPS3 proxy). */ - if(data->state.http_neg.wanted == CURL_HTTP_V3x) { -#ifdef USE_PROXY_HTTP3 - udp_tun = TRUE; -#else - failf(data, "HTTP/3 proxy tunnel support not built-in"); - return CURLE_NOT_BUILT_IN; -#endif /* USE_PROXY_HTTP3 */ - } - result = Curl_cf_http_proxy_insert_after(cf, data, dest, - cf->conn->http_proxy.proxytype, - udp_tun); - if(result) - return result; - } -#endif /* !CURL_DISABLE_HTTP */ - return result; -} -#endif /* !CURL_DISABLE_PROXY */ - static CURLcode cf_setup_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + struct Curl_peer *first_peer = + Curl_conn_get_first_peer(cf->conn, cf->sockindex); if(cf->connected) { *done = TRUE; @@ -425,38 +367,38 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, } if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { -#ifndef CURL_DISABLE_PROXY -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) - if(IS_QUIC_PROXY(cf->conn->http_proxy.proxytype) && - cf->conn->bits.tunnel_proxy) { - /* For HTTPS3 proxy tunnels, H3-PROXY manages the QUIC connection - on top of the UDP socket. Let happy eyeballs race IPv4/IPv6 using - QUIC-transport UDP sockets so the socket is connected to the - proxy peer and H3-PROXY can send directly via send(). - Filter chains: - H1/H2 target (CONNECT over QUIC): - SETUP --> HTTP/1.1 or HTTP/2 --> SSL --> HTTP-PROXY --> - H3-PROXY --> HAPPY-EYEBALLS --> UDP - H3 target (MASQUE CONNECT-UDP over QUIC): - SETUP --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> - H3-PROXY --> HAPPY-EYEBALLS --> UDP */ - result = cf_ip_happy_quic_udp_insert_after(cf, data); + /* What type of thing we do connect to first? + * - without a proxy, `ctx->transport` defines it + * - with non-tunneling proxy, `ctx->transport` also applies, but + * for QUIC we need the cf-h3-proxy, not the standard vquic one + * - with tunneling proxy, transport is defined by the proxytype + * chosen and `ctx->transport` is tunneled through it. + */ + uint8_t transport_out = ctx->transport; + bool tunnel_proxy = FALSE; +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + CURL_TRC_CF(data, cf, "happy eyeballing, httpproxy=%d, type=%d, " + "transport=%d", + cf->conn->bits.httpproxy, cf->conn->http_proxy.proxytype, + ctx->transport); + if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { + transport_out = + Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); + tunnel_proxy = TRUE; + if((transport_out == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { + failf(data, "HTTP/3 proxy not possible via SOCKS"); + return CURLE_UNSUPPORTED_PROTOCOL; + } } - /* When tunneling QUIC through an HTTP proxy (CONNECT-UDP), - the underlying conn to the proxy is TCP. */ - else -#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ - if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && - !IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) - result = cf_ip_happy_insert_after(cf, data, TRNSPRT_TCP); - else -#endif /* !CURL_DISABLE_PROXY */ - result = cf_ip_happy_insert_after(cf, data, ctx->transport); +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + result = cf_ip_happy_insert_after(cf, data, first_peer, + ctx->transport, transport_out, + tunnel_proxy); if(result) return result; - ctx->state = CF_SETUP_CNNCT_EYEBALLS; + ctx->state = (tunnel_proxy && (transport_out == TRNSPRT_QUIC)) ? + CF_SETUP_CNNCT_HTTP_PROXY : CF_SETUP_CNNCT_EYEBALLS; if(!cf->next || !cf->next->connected) goto connect_sub_chain; } @@ -491,9 +433,25 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, } if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { - result = cf_setup_add_http_proxy(cf, data, ctx); - if(result) - return result; +#ifdef USE_SSL + if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + result = Curl_cf_ssl_proxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + +#ifndef CURL_DISABLE_HTTP + if(cf->conn->bits.tunnel_proxy) { + struct Curl_peer *dest; /* where HTTP should tunnel to */ + dest = Curl_conn_get_destination(cf->conn, cf->sockindex); + result = Curl_cf_http_proxy_insert_after( + cf, data, dest, ctx->transport, cf->conn->http_proxy.proxytype); + if(result) + return result; + } +#endif /* !CURL_DISABLE_HTTP */ ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; if(!cf->next || !cf->next->connected) goto connect_sub_chain; @@ -503,9 +461,8 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { #ifndef CURL_DISABLE_PROXY if(data->set.haproxyprotocol) { - if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not supported with SSL " - "encryption in place (QUIC?)"); + if(ctx->transport == TRNSPRT_QUIC) { + failf(data, "haproxy protocol not support QUIC"); return CURLE_UNSUPPORTED_PROTOCOL; } result = Curl_cf_haproxy_insert_after(cf, data); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index a52a1c3713dc..39cfb1244681 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -753,8 +753,8 @@ struct Curl_cftype Curl_cft_http_proxy = { CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - uint8_t proxytype, - bool udp_tunnel) + uint8_t transport, + uint8_t proxytype) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; @@ -771,7 +771,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, } Curl_peer_link(&ctx->dest, dest); ctx->proxytype = proxytype; - ctx->udp_tunnel = udp_tunnel; + ctx->udp_tunnel = (transport == TRNSPRT_QUIC); result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) @@ -784,4 +784,14 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, return result; } +uint8_t Curl_http_proxy_transport(uint8_t proxytype) +{ + switch(proxytype) { + case CURLPROXY_HTTPS3: + return TRNSPRT_QUIC; + default: + return TRNSPRT_TCP; + } +} + #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 0a5734e3d8a2..ef4becdacf98 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -69,8 +69,8 @@ CURLcode Curl_http_proxy_inspect_tunnel_response( CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *dest, - uint8_t proxytype, - bool udp_tunnel); + uint8_t transport, + uint8_t proxytype); extern struct Curl_cftype Curl_cft_http_proxy; @@ -83,4 +83,6 @@ extern struct Curl_cftype Curl_cft_http_proxy; #define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3) +uint8_t Curl_http_proxy_transport(uint8_t proxytype); + #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/lib/url.c b/lib/url.c index 926d29ed5f77..b7ba30fe2bb1 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1317,12 +1317,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #endif conn->ip_version = data->set.ipver; conn->bits.connect_only = (bool)data->set.connect_only; -#ifndef CURL_DISABLE_PROXY - if(conn->http_proxy.proxytype == CURLPROXY_HTTPS3) - conn->transport_wanted = TRNSPRT_QUIC; - else -#endif - conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ + conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ if(data->set.str[STRING_DEVICE]) { diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 8693ed16ee74..20996e5a9823 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -3120,7 +3120,8 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, + TRNSPRT_QUIC, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 43a16958a6ff..08b02fec78f6 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1659,7 +1659,8 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, + TRNSPRT_QUIC, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index a35abfb2c97a..dba907bbf090 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -769,10 +769,12 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { - (void)transport; - DEBUGASSERT(transport == TRNSPRT_QUIC); + (void)transport_in; + (void)transport_out; + DEBUGASSERT(transport_out == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) return Curl_cf_ngtcp2_create(pcf, data, conn, addr); #elif defined(USE_QUICHE) diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 59178acd9405..e3d894f8c089 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -45,7 +45,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + uint8_t transport_in, + uint8_t transport_out); extern struct Curl_cftype Curl_cft_http3; diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 5275b91bf969..225d63fe6d19 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -176,7 +176,7 @@ def h2o_server(env) -> Generator[Union[H2oServer, bool], None, None]: h2o_logs = "\n".join(h2o.dump_logs()) pytest.skip(f"h2o server failed to start\n{h2o_logs}") yield h2o - h2o.stop() + h2o.kill() else: yield False @@ -190,6 +190,6 @@ def h2o_proxy(env) -> Generator[Union[H2oProxy, bool], None, None]: h2o_logs = "\n".join(h2o.dump_logs()) pytest.skip(f"h2o proxy failed to start\n{h2o_logs}") yield h2o - h2o.stop() + h2o.kill() else: yield False diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index ca4501f6352d..34d628445d54 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -95,29 +95,29 @@ def _check_download_size(curl: CurlClient, expected_size: int): def _nghttpx_proxy_args( env: Env, nghttpx, + nghttpx_fwd, proxy_proto: str, tunnel: bool, - insecure: bool = False, ): - xargs = [ - "--proxy", - f"https://{env.proxy_domain}:{nghttpx._port}/", - "--resolve", - f"{env.proxy_domain}:{nghttpx._port}:127.0.0.1", - "--proxy-cacert", - env.ca.cert_file, - ] + port = env.pts_port(proxy_proto) + domain = env.proxy_domain + xxarg = None if proxy_proto == "h3": - xargs.append("--proxy-http3") + port = nghttpx.port + domain = env.domain1 + xxarg = "--proxy-http3" elif proxy_proto == "h2": - xargs.append("--proxy-http2") + xxarg = "--proxy-http2" + xargs = [ + "--proxy", f"https://{domain}:{port}/", + "--resolve", f"{domain}:{port}:127.0.0.1", + "--proxy-cacert", env.ca.cert_file + ] + if xxarg: + xargs.append(xxarg) if tunnel: xargs.append("--proxytunnel") - - xargs.extend(["--cacert", env.ca.cert_file, "--proxy-insecure"]) - if insecure: - xargs.append("--insecure") return xargs @@ -126,22 +126,13 @@ def _h2o_proxy_args( h2o_proxy, proxy_proto: str, tunnel: bool, - insecure: bool = False, ): - if proxy_proto == "h3": - pport = h2o_proxy.port - elif proxy_proto == "h2": - pport = h2o_proxy.h2_port - else: - pport = h2o_proxy.h1_port - + pport = env.pts_port(proxy_proto, use_h2o=True) xargs = [ - "--proxy", - f"https://{env.proxy_domain}:{pport}/", - "--resolve", - f"{env.proxy_domain}:{pport}:127.0.0.1", - "--proxy-cacert", - env.ca.cert_file, + "--proxy", f"https://{env.proxy_domain}:{pport}/", + "--resolve", f"{env.proxy_domain}:{pport}:127.0.0.1", + "--proxy-cacert", env.ca.cert_file, + "--cacert", env.ca.cert_file, ] if proxy_proto == "h2": xargs.append("--proxy-http2") @@ -151,9 +142,6 @@ def _h2o_proxy_args( if tunnel: xargs.append("--proxytunnel") - xargs.extend(["--cacert", env.ca.cert_file, "--proxy-insecure"]) - if insecure: - xargs.append("--insecure") return xargs @@ -195,7 +183,7 @@ def test_60_01_connect_tunnel( curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/data.json" proxy_args = _h2o_proxy_args( - env, h2o_proxy, proxy_proto, tunnel=True, insecure=True + env, h2o_proxy, proxy_proto, tunnel=True ) r = curl.http_download( @@ -235,14 +223,14 @@ class TestH3ProxyFailure: pytest.param( "h3", "h2", - "connect-udp response status 400", + "proxy closed connection", marks=MARK_NEEDS_NGHTTP2, id="fail_h3_over_h2_proxytunnel", ), pytest.param( "h3", "http/1.1", - "connect-udp tunnel failed, response 404", + "connect-udp tunnel failed", id="fail_h3_over_h1_proxytunnel", ), ], @@ -252,21 +240,24 @@ def test_60_02_connect_tunnel_fail( env: Env, httpd, nghttpx, + nghttpx_fwd, alpn_proto, proxy_proto, exp_err, ): - _require_available(httpd=httpd, nghttpx=nghttpx) + _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd) curl = CurlClient(env=env) - url = f"https://localhost:{httpd.ports['https']}/data.json" - proxy_args = _nghttpx_proxy_args(env, nghttpx, proxy_proto, tunnel=True) + url = f"https://localhost:{env.https_port}/data.json" + proxy_args = _nghttpx_proxy_args( + env, nghttpx, nghttpx_fwd, proxy_proto, tunnel=True + ) r = curl.http_download( urls=[url], alpn_proto=alpn_proto, with_stats=True, extra_args=proxy_args ) - assert r.exit_code != 0, f"Expected failure but curl succeeded: {r}" + assert r.exit_code != 0, f"Expected failure but curl succeeded: {r.dump_logs()}" assert exp_err in r.stderr.lower(), ( - f"Expected protocol/proxy error but got: {r.stderr}" + f"Expected protocol/proxy error but got: {r.dump_logs()}" ) @@ -284,14 +275,16 @@ class TestH3ProxyModeSelection: ], ) def test_60_03_h3_target_auto_connect_udp( - self, env: Env, httpd, nghttpx, proxy_proto + self, env: Env, httpd, nghttpx, nghttpx_fwd, proxy_proto ): - _require_available(httpd=httpd, nghttpx=nghttpx) + _require_available( + httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd + ) curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/data.json" proxy_args = _nghttpx_proxy_args( - env, nghttpx, proxy_proto, tunnel=False + env, nghttpx, nghttpx_fwd, proxy_proto, tunnel=False ) r = curl.http_download( urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args @@ -305,7 +298,7 @@ def test_60_03_h3_target_auto_connect_udp( "which nghttpx does not support" ) assert "connect-udp" in r.stderr.lower(), ( - f"expected CONNECT-UDP attempt in output, got: {r.stderr}" + f"expected CONNECT-UDP attempt in output, got: {r.dump_logs()}" ) @@ -324,6 +317,9 @@ class TestH3ProxyRuntimeGuards: @pytest.mark.skipif( condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" ) + @pytest.mark.skipif( + condition=Env.curl_has_feature("proxy-HTTP3"), reason="curl has h3 proxy support" + ) def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/data.json" @@ -332,7 +328,6 @@ def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): "https://127.0.0.1:1/", "--proxy-http3", "--proxytunnel", - "--proxy-insecure", "--cacert", env.ca.cert_file, ] @@ -340,16 +335,9 @@ def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): r = curl.http_download( urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args ) - if not env.curl_has_feature("proxy-HTTP3"): - r.check_exit_code(2) - assert UNSUPPORTED_OPT_MSG in r.stderr.lower(), ( - f"Expected unsupported option failure but got: {r.stderr}" - ) - return - - r.check_exit_code(1) - assert NGTCP2_ONLY_MSG in r.stderr.lower(), ( - f"Expected ngtcp2 guard failure but got: {r.stderr}" + r.check_exit_code(2) + assert UNSUPPORTED_OPT_MSG in r.stderr.lower(), ( + f"Expected unsupported option failure but got: {r.stderr}" ) @@ -398,26 +386,21 @@ def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): proxy_port = h2o_proxy.port url = f"https://localhost:{h2o_server.port}/proxy-drop-20m" out_path = os.path.join(env.gen_dir, "proxy-drop.out") + if os.path.exists(out_path): + os.remove(out_path) args = [ env.curl, "--http1.1", - "--proxy", - f"https://{env.proxy_domain}:{proxy_port}/", - "--resolve", - f"{env.proxy_domain}:{proxy_port}:127.0.0.1", - "--proxy-cacert", - env.ca.cert_file, + "--proxy", f"https://{env.proxy_domain}:{proxy_port}/", + "--resolve", f"{env.proxy_domain}:{proxy_port}:127.0.0.1", "--proxy-http3", "--proxytunnel", - "--proxy-insecure", - "--cacert", - env.ca.cert_file, - "--limit-rate", - "100k", - "--max-time", - "20", - "-o", - out_path, + "--proxy-cacert", env.ca.cert_file, + "--cacert", env.ca.cert_file, + "--limit-rate", "10k", + "--max-time", "20", + "-o", out_path, + "-v", url, ] @@ -426,19 +409,14 @@ def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): proc = subprocess.Popen( args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) - time.sleep(1.0) - assert h2o_proxy.stop(), "failed to stop h2o proxy" + while not os.path.exists(out_path): + time.sleep(0.1) + assert h2o_proxy.kill(), "failed to stop h2o proxy" _, stderr = proc.communicate(timeout=30) assert proc.returncode != 0, ( "curl should fail when proxy is terminated mid-transfer" ) - serr = stderr.lower() - assert ( - "failed" in serr - or "transfer closed" in serr - or "recv failure" in serr - or "connection" in serr - ), f"Unexpected error output: {stderr}" + assert proc.returncode == 56, f'{stderr}' finally: if proc and (proc.poll() is None): proc.kill() @@ -463,7 +441,7 @@ def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/download-10m" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) r = curl.http_download( urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args ) @@ -475,7 +453,7 @@ def test_60_08_large_upload(self, env: Env, httpd, h2o_server, h2o_proxy): fdata = os.path.join(env.gen_dir, "upload-2m") curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/curltest/echo?id=[0-0]" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) r = curl.http_upload( urls=[url], data=f"@{fdata}", @@ -490,7 +468,7 @@ def test_60_09_parallel_downloads(self, env: Env, h2o_server, h2o_proxy): count = 5 curl = CurlClient(env=env) urln = f"https://localhost:{h2o_server.port}/download-1m?[0-{count - 1}]" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) proxy_args.extend(["--parallel", "--parallel-max", f"{count}"]) r = curl.http_download( urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args @@ -507,7 +485,7 @@ def test_60_10_proxy_basic_auth(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/data.json" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) proxy_args.extend(["--proxy-user", "testuser:testpass"]) r = curl.http_download( urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args @@ -519,7 +497,7 @@ def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) urln = f"https://localhost:{h2o_server.port}/data.json?[0-2]" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) r = curl.http_download( urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args ) @@ -528,30 +506,29 @@ def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): f"expected proxy connection reuse, got {r.total_connects} connects" ) + @pytest.mark.skipif(condition=not Env.curl_has_feature('SSLS-EXPORT'), + reason='curl lacks SSL session export support') def test_60_12_quic_session_resumption(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) - # First request establishes QUIC session - curl1 = CurlClient(env=env) + curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/data.json" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) - r1 = curl1.http_download( - urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + xargs = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + session_file = os.path.join(env.gen_dir, 'test_60_12.sessions') + if os.path.exists(session_file): + os.remove(session_file) + xargs.extend(['--ssl-sessions', session_file]) + # First request establishes QUIC session + r1 = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs ) r1.check_response(count=1, http_status=200) - # Second request from a fresh CurlClient; session may be reused - # by the TLS session cache if supported - curl2 = CurlClient(env=env) - r2 = curl2.http_download( - urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + xargs.extend(['--trace-config', 'ssls']) + r2 = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs ) r2.check_response(count=1, http_status=200) - # Third request from a fresh CurlClient; session may be reused - # by the TLS session cache if supported - curl3 = CurlClient(env=env) - r3 = curl3.http_download( - urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args - ) - r3.check_response(count=1, http_status=200) + reuses = [line for line in r2.trace_lines if '[SSLS] took session for proxy.http.curl.se' in line] + assert len(reuses), f'{r2.dump_logs()}' class TestH3ProxyUdpTunnel: @@ -582,7 +559,7 @@ def test_60_13_udp_tunnel_payload_sizes( _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/{fname}" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) r = curl.http_download( urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args ) @@ -590,11 +567,17 @@ def test_60_13_udp_tunnel_payload_sizes( _check_download_size(curl, fsize) @MARK_NEEDS_NGHTTPX - def test_60_14_udp_tunnel_capsule_absent(self, env: Env, httpd, nghttpx): - _require_available(httpd=httpd, nghttpx=nghttpx) + def test_60_14_udp_tunnel_capsule_absent( + self, env: Env, httpd, nghttpx, nghttpx_fwd + ): + _require_available( + httpd=httpd, nghttpx=nghttpx, nghttps_fwd=nghttpx_fwd + ) curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/data.json" - proxy_args = _nghttpx_proxy_args(env, nghttpx, "h3", tunnel=True) + proxy_args = _nghttpx_proxy_args( + env, nghttpx, nghttpx_fwd, "h3", tunnel=True + ) r = curl.http_download( urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args ) @@ -608,25 +591,21 @@ class TestH3ProxyEdgeCases: pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - def test_60_15_connect_timeout(self, env: Env, h2o_server): - _require_available(h2o_server=h2o_server) + def test_60_15_connect_timeout(self, env: Env, h2o_proxy): + _require_available(h2o_proxy=h2o_proxy) curl = CurlClient(env=env, timeout=15) - url = f"https://localhost:{h2o_server.port}/data.json" - proxy_args = [ - "--proxy", - "https://192.0.2.1:1/", - "--proxy-http3", - "--proxytunnel", - "--proxy-insecure", - "--connect-timeout", - "3", - "--cacert", - env.ca.cert_file, + url = f"https://localhost:{h2o_proxy.port}/data.json" + # ipv6 0100::/64 is supposed to go into the void (rfc6666) + xargs = [ + '--proxy', 'https://xxx.invalid/', + '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', + '--proxy-http3', '--proxytunnel', + '--connect-timeout', '1', ] r = curl.http_download( - urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs ) - assert r.exit_code != 0, "expected timeout connecting to unreachable proxy" + r.check_exit_code(28) # CURLE_OPERATION_TIMEDOUT assert r.duration.total_seconds() < 10, ( f"timeout not respected: took {r.duration.total_seconds():.1f}s" ) @@ -635,8 +614,8 @@ def test_60_15_connect_timeout(self, env: Env, h2o_server): def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): _require_available(httpd=httpd, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) - url = f"https://localhost:{httpd.ports['https']}/data.json" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + url = f"https://localhost:{env.https_port}/data.json" + proxy_args = curl.get_proxy_args("h3", tunnel=True) # h2 inner traffic always uses CONNECT (TCP), never CONNECT-UDP, # even through an HTTP/3 proxy with --proxytunnel. h2o supports # CONNECT TCP tunneling, so this request succeeds. @@ -663,7 +642,7 @@ def test_60_17_h3_proxy_happy_eyeballs_filter_present(self, env: Env, h2o_server _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env, run_env={"CURL_DEBUG": "HAPPY-EYEBALLS,H3-PROXY"}) url = f"https://localhost:{h2o_server.port}/data.json" - proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True, insecure=True) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) r = curl.http_download( urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args ) @@ -679,9 +658,7 @@ def test_60_18_h3_proxy_ipv4_all_proto(self, env: Env, h2o_server, h2o_proxy): for alpn_proto in ["http/1.1", "h2", "h3"]: curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/data.json" - proxy_args = _h2o_proxy_args( - env, h2o_proxy, "h3", tunnel=True, insecure=True - ) + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) proxy_args.append("--ipv4") r = curl.http_download( urls=[url], diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 272b6045cbf8..5149a8578d7d 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -684,12 +684,13 @@ def _mkpath(self, path): def get_proxy_args(self, proto: str = 'http/1.1', proxys: bool = True, tunnel: bool = False, - use_ip: bool = False, use_ipv6: bool = False): + use_ip: bool = False, use_ipv6: bool = False, + use_h2o: bool = False): proxy_name = '[::1]' if use_ipv6 else \ self._server_addr if use_ip else self.env.proxy_domain if proxys: if tunnel: - pport = self.env.pts_port(proto) + pport = self.env.pts_port(proto, use_h2o=use_h2o) elif proto == 'h3': pport = self.env.h3proxys_port else: diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 093092b4c5de..4a18d65ffab6 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -824,15 +824,16 @@ def h2proxys_port(self) -> int: @property def h3proxys_port(self) -> int: - return self.CONFIG.ports["h3proxys"] + return self.CONFIG.ports["h2o_h3proxys"] - def pts_port(self, proto: str = "http/1.1") -> int: + def pts_port(self, proto: str = "http/1.1", use_h2o: bool = False) -> int: # proxy tunnel port + prefix = 'h2o_' if use_h2o else '' if proto == "h3": - return self.CONFIG.ports["h3proxys"] + return self.CONFIG.ports.get("h2o_h3proxys", 0) if proto == "h2": - return self.CONFIG.ports["h2proxys"] - return self.CONFIG.ports["proxys"] + return self.CONFIG.ports.get(f"{prefix}h2proxys", 0) + return self.CONFIG.ports[f"{prefix}proxys"] @property def caddy(self) -> str: diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index c67aaf18886c..279cff8b90c3 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -160,6 +160,12 @@ def stop(self, wait_dead=True): ) return True + def kill(self, wait_dead=True): + if self._process: + self._process.kill() + return True + return False + def restart(self): self.stop() return self.start() @@ -317,9 +323,9 @@ def initial_start(self): super().initial_start() def startup(ports: Dict[str, int]) -> bool: - self._port = ports["h3proxys"] - self._h2_port = ports["h2proxys"] - self._h1_port = ports["proxys"] + self._port = ports["h2o_h3proxys"] + self._h2_port = ports["h2o_h2proxys"] + self._h1_port = ports["h2o_proxys"] if self.start(): self.env.update_ports(ports) return True @@ -331,9 +337,9 @@ def startup(ports: Dict[str, int]) -> bool: return alloc_ports_and_do( { - "h3proxys": socket.SOCK_DGRAM, - "h2proxys": socket.SOCK_STREAM, - "proxys": socket.SOCK_STREAM, + "h2o_h3proxys": socket.SOCK_DGRAM, + "h2o_h2proxys": socket.SOCK_STREAM, + "h2o_proxys": socket.SOCK_STREAM, }, startup, self.env.gen_root, diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index 0d95a34bceb9..c72a7f7f6de3 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -47,7 +47,7 @@ def __init__(self, env: Env, name: str, domain: str, cred_name: str): self._name = name self._domain = domain self._port = 0 - self._https_port = 0 + self._port_is_quic = False self._cmd = env.nghttpx self._run_dir = os.path.join(env.gen_dir, name) self._pid_file = os.path.join(self._run_dir, 'nghttpx.pid') @@ -76,8 +76,12 @@ def reload_if_config_changed(self): return self.reload() @property - def https_port(self): - return self._https_port + def port(self): + return self._port + + @property + def port_is_quic(self): + return self._port_is_quic def exists(self): return self._cmd and os.path.exists(self._cmd) @@ -150,18 +154,14 @@ def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) try_until = datetime.now() + timeout while datetime.now() < try_until: - if self._https_port > 0: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) - else: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--http3-only', '--connect-timeout', '1' - ]) + xargs = [ + '--trace', 'curl.trace', '--trace-time', + '--connect-timeout', '1' + ] + if self.port_is_quic: + xargs.extend(['--http3-only']) + check_url = f'https://{self._domain}:{self.port}/' + r = curl.http_get(url=check_url, extra_args=xargs) if r.exit_code != 0: return True log.debug(f'waiting for nghttpx to stop responding: {r}') @@ -173,18 +173,14 @@ def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) try_until = datetime.now() + timeout while datetime.now() < try_until: - if self._https_port > 0: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) - else: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--http3-only', '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) + xargs = [ + '--trace', 'curl.trace', '--trace-time', + '--connect-timeout', '1' + ] + if self.port_is_quic: + xargs.extend(['--http3-only']) + check_url = f'https://{self._domain}:{self.port}/' + r = curl.http_get(url=check_url, extra_args=xargs) if r.exit_code == 0: return True time.sleep(.1) @@ -216,13 +212,18 @@ class NghttpxQuic(Nghttpx): def __init__(self, env: Env): super().__init__(env=env, name='nghttpx-quic', domain=env.domain1, cred_name=env.domain1) - self._https_port = env.https_port + self._https_port = 0 def initial_start(self): super().initial_start() def startup(ports: Dict[str, int]) -> bool: - self._port = ports['nghttpx_https'] + self._https_port = ports['nghttpx_https'] + if self.supports_h3(): + self._port = self.env.h3_port + self._port_is_quic = True + else: + self._port = self._https_port if self.start(): self.env.update_ports(ports) return True @@ -240,10 +241,10 @@ def start(self, wait_live=True): creds = self.env.get_credentials(self._cred_name) assert creds # convince pytype this is not None self._loaded_cred_name = self._cred_name - args = [self._cmd, f'--frontend=*,{self._port};tls'] + args = [self._cmd, f'--frontend=*,{self._https_port};tls'] if self.supports_h3(): args.extend([ - f'--frontend=*,{self.env.h3_port};quic', + f'--frontend=*,{self._port};quic', '--frontend-quic-early-data', ]) args.extend([ diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 39bec585607f..47d91ce5530b 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -113,7 +113,8 @@ static int test_idx; struct cf_test_ctx { int idx; int ai_family; - uint8_t transport; + uint8_t transport_in; + uint8_t transport_out; char id[16]; struct curltime started; timediff_t fail_delay_ms; @@ -167,7 +168,8 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + uint8_t transport_in, + uint8_t transport_out) { static const struct Curl_cftype cft_test = { "TEST", @@ -201,7 +203,8 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, } ctx->idx = test_idx++; ctx->ai_family = addr->family; - ctx->transport = transport; + ctx->transport_in = transport_in; + ctx->transport_out = transport_out; ctx->started = curlx_now(); current_tr->ongoing++; if(current_tr->ongoing > current_tr->max_concurrent) From c2ca16f3ff2ad8300e67ea5a3cc4060738473e45 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 16:18:21 +0200 Subject: [PATCH 0250/1062] h3: sync printf masks with types, drop two casts Also fix `nwritten` signedness in `cb_h3_read_req_body()`. Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Ref: #20848 Closes #21799 --- lib/cf-h3-proxy.c | 15 ++++++--------- lib/vquic/curl_ngtcp2.c | 6 +++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 5ca18ace8383..c2fc67e5fb7c 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -779,7 +779,7 @@ static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, stream->tun_data_recvd += (curl_off_t)buflen; CURL_TRC_CF(data, cf, "[cb_h3_proxy_recv_data] " - "[%" PRIu64 "] DATA len=%zu, total=%zd", + "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, H3_STREAM_ID(stream), buflen, stream->tun_data_recvd); result = Curl_bufq_write(&proxy_ctx->inbufq, buf, buflen, &nwritten); @@ -1064,8 +1064,8 @@ static nghttp3_ssize cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", - H3_STREAM_ID(stream), (int)nvecs, + "%zd vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + H3_STREAM_ID(stream), nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), stream->upload_left); @@ -1232,8 +1232,7 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms, remote transport[max_udp_payload=%" PRIu64 - ", initial_max_data=%" PRIu64 - "]", + ", initial_max_data=%" PRIu64 "]", curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), rp->max_udp_payload_size, rp->initial_max_data); } @@ -2326,8 +2325,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, if(!*pnread && !Curl_bufq_is_empty(&proxy_ctx->inbufq)) { result = Curl_bufq_cread(&proxy_ctx->inbufq, buf, len, pnread); if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) " - "-> %zd, %d", + CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) -> %zu, %d", stream->id, len, *pnread, result); goto out; } @@ -2460,8 +2458,7 @@ static void proxy_h3_submit(int64_t *pstream_id, switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " - "connection is closing", - H3_STREAM_ID(stream)); + "connection is closing", H3_STREAM_ID(stream)); break; default: CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 20996e5a9823..552a8c789537 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1555,7 +1555,7 @@ static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nwritten = 0; + size_t nwritten = 0; size_t nvecs = 0; (void)cf; (void)conn; @@ -1602,8 +1602,8 @@ static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", - stream->id, (int)nvecs, + "%zd vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + stream->id, nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), stream->upload_left); From a0c559ff030d859135a32bf8f753166309b0e546 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 17:19:23 +0200 Subject: [PATCH 0251/1062] h3: fix signedness of two printf masks Follow-up to c2ca16f3ff2ad8300e67ea5a3cc4060738473e45 #21799 --- lib/cf-h3-proxy.c | 2 +- lib/vquic/curl_ngtcp2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index c2fc67e5fb7c..7aaa19be3ff9 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -1064,7 +1064,7 @@ static nghttp3_ssize cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%zd vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + "%zu vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", H3_STREAM_ID(stream), nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 552a8c789537..a5e0ea2cf944 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1602,7 +1602,7 @@ static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%zd vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + "%zu vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), From 5e661767339f1c300b01dc7e0cffe5a8290324a5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 28 May 2026 10:14:08 +0200 Subject: [PATCH 0252/1062] http: don't pass on set cookies to new origins Verified by test 2015 Reported-by: azraelxuemo on hackerone Closes #21794 --- lib/http.c | 3 +- tests/data/Makefile.am | 2 +- tests/data/test2015 | 91 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2015 diff --git a/lib/http.c b/lib/http.c index c9fb9995c09b..26f5280502f1 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2530,7 +2530,8 @@ static CURLcode http_cookies(struct Curl_easy *data, char *addcookies = NULL; bool linecap = FALSE; if(data->set.str[STRING_COOKIE] && - !Curl_checkheaders(data, STRCONST("Cookie"))) + !Curl_checkheaders(data, STRCONST("Cookie")) && + Curl_auth_allowed_to_host(data)) addcookies = data->set.str[STRING_COOKIE]; if(data->cookies || addcookies) { diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 4887a3594a38..058be3d73488 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -245,7 +245,7 @@ test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ -test2008 test2009 test2010 test2011 test2012 test2013 test2014 \ +test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ \ test2023 \ test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ diff --git a/tests/data/test2015 b/tests/data/test2015 new file mode 100644 index 000000000000..6cd758471e10 --- /dev/null +++ b/tests/data/test2015 @@ -0,0 +1,91 @@ + + + + +HTTP +HTTP proxy +followlocation +cookies + + +# Server-side + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +contents + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +# Client-side + + +http + + +HTTP with cookie with with -b and redirect to new host + + +http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -b "test=yes" --location + + +proxy + + + +# Verify data after the test has been "shot" + + +GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1 +Host: first.host.it.is +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Cookie: test=yes + +GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1 +Host: goto.second.host.now +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + From c37405cb065e1605acb35700a3bb2ed0f59ff59e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 22:22:32 +0200 Subject: [PATCH 0253/1062] h3-proxy: fix callback return values, and a typo in tests - replace literal -1 with `NGHTTP3_ERR_CALLBACK_FAILURE` in nghttp3 callback. - replace `NGHTTP3_ERR_CALLBACK_FAILURE` with `NGTCP2_ERR_CALLBACK_FAILURE` in ngtcp2 callbacks. - test_60_h3_proxy: fix non-critical typo in symbol. Spotted by GitHub Code Quality Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21802 --- lib/cf-h3-proxy.c | 6 +++--- tests/http/test_60_h3_proxy.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 7aaa19be3ff9..a78561538846 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -888,7 +888,7 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, (const char *)h3name.base, h3name.len, (const char *)h3val.base, h3val.len); if(result) { - return -1; + return NGHTTP3_ERR_CALLBACK_FAILURE; } } return 0; @@ -1218,7 +1218,7 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); if(!ctx || !data) - return NGHTTP3_ERR_CALLBACK_FAILURE; + return NGTCP2_ERR_CALLBACK_FAILURE; ctx->handshake_at = *Curl_pgrs_now(data); ctx->tls_handshake_complete = TRUE; @@ -1274,7 +1274,7 @@ static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, CURLcode result = cf_ngtcp2_h3conn_init(cf, data); if(result) { CURL_TRC_CF(data, cf, "HTTP/3 initialization failed: %d", result); - return NGHTTP3_ERR_CALLBACK_FAILURE; + return NGTCP2_ERR_CALLBACK_FAILURE; } } diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 34d628445d54..626606845ab5 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -571,7 +571,7 @@ def test_60_14_udp_tunnel_capsule_absent( self, env: Env, httpd, nghttpx, nghttpx_fwd ): _require_available( - httpd=httpd, nghttpx=nghttpx, nghttps_fwd=nghttpx_fwd + httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd ) curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/data.json" From 6ac42e569112bfb8fb6bfec02db6d83612b184e3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 22:37:40 +0200 Subject: [PATCH 0254/1062] h3-proxy: disable frequently failing pytests - test_60_02_connect_tunnel_fail[fail_h1_over_h3_proxytunnel] - test_60_02_connect_tunnel_fail[fail_h3_over_h2_proxytunnel] - test_60_02_connect_tunnel_fail[fail_h3_over_h3_proxytunnel] - test_60_03_h3_target_auto_connect_udp[proxy_h3] - test_60_15_connect_timeout Further flaky ones may be disabled in future commits. All to be re-enabled after stabilizing them. Follow-up to 59213f8248cfc10e97a6a23f5e4da9b1e5057400 #21789 Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21803 --- tests/http/test_60_h3_proxy.py | 76 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 626606845ab5..4d1998165049 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -201,12 +201,12 @@ class TestH3ProxyFailure: @pytest.mark.parametrize( ["alpn_proto", "proxy_proto", "exp_err"], [ - pytest.param( - "http/1.1", - "h3", - "could not connect to server", - id="fail_h1_over_h3_proxytunnel", - ), + #pytest.param( + # "http/1.1", + # "h3", + # "could not connect to server", + # id="fail_h1_over_h3_proxytunnel", + #), pytest.param( "h2", "h3", @@ -214,19 +214,19 @@ class TestH3ProxyFailure: marks=MARK_NEEDS_NGHTTP2, id="fail_h2_over_h3_proxytunnel", ), - pytest.param( - "h3", - "h3", - "could not connect to server", - id="fail_h3_over_h3_proxytunnel", - ), - pytest.param( - "h3", - "h2", - "proxy closed connection", - marks=MARK_NEEDS_NGHTTP2, - id="fail_h3_over_h2_proxytunnel", - ), + #pytest.param( + # "h3", + # "h3", + # "could not connect to server", + # id="fail_h3_over_h3_proxytunnel", + #), + #pytest.param( + # "h3", + # "h2", + # "proxy closed connection", + # marks=MARK_NEEDS_NGHTTP2, + # id="fail_h3_over_h2_proxytunnel", + #), pytest.param( "h3", "http/1.1", @@ -269,7 +269,7 @@ class TestH3ProxyModeSelection: @pytest.mark.parametrize( ["proxy_proto"], [ - pytest.param("h3", id="proxy_h3"), + #pytest.param("h3", id="proxy_h3"), pytest.param("h2", marks=MARK_NEEDS_NGHTTP2, id="proxy_h2"), pytest.param("http/1.1", id="proxy_h1"), ], @@ -591,24 +591,24 @@ class TestH3ProxyEdgeCases: pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - def test_60_15_connect_timeout(self, env: Env, h2o_proxy): - _require_available(h2o_proxy=h2o_proxy) - curl = CurlClient(env=env, timeout=15) - url = f"https://localhost:{h2o_proxy.port}/data.json" - # ipv6 0100::/64 is supposed to go into the void (rfc6666) - xargs = [ - '--proxy', 'https://xxx.invalid/', - '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', - '--proxy-http3', '--proxytunnel', - '--connect-timeout', '1', - ] - r = curl.http_download( - urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs - ) - r.check_exit_code(28) # CURLE_OPERATION_TIMEDOUT - assert r.duration.total_seconds() < 10, ( - f"timeout not respected: took {r.duration.total_seconds():.1f}s" - ) + #def test_60_15_connect_timeout(self, env: Env, h2o_proxy): + # _require_available(h2o_proxy=h2o_proxy) + # curl = CurlClient(env=env, timeout=15) + # url = f"https://localhost:{h2o_proxy.port}/data.json" + # # ipv6 0100::/64 is supposed to go into the void (rfc6666) + # xargs = [ + # '--proxy', 'https://xxx.invalid/', + # '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', + # '--proxy-http3', '--proxytunnel', + # '--connect-timeout', '1', + # ] + # r = curl.http_download( + # urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs + # ) + # r.check_exit_code(28) # CURLE_OPERATION_TIMEDOUT + # assert r.duration.total_seconds() < 10, ( + # f"timeout not respected: took {r.duration.total_seconds():.1f}s" + # ) @MARK_NEEDS_NGHTTP2 def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): From 722b59b3ab40efcdf425213b04186f37adc893ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 08:46:34 +0000 Subject: [PATCH 0255/1062] GHA: update dependency ngtcp2/nghttp3 to v1.16.0 Closes #21814 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 1cb2cd8ace02..99a3b8c8322c 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -56,7 +56,7 @@ env: # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - NGHTTP3_VERSION: 1.15.0 + NGHTTP3_VERSION: 1.16.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com NGTCP2_VERSION: 1.22.1 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com From 24874a4f04cbbafd878441ccef11e3caa5897db5 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 29 May 2026 11:06:58 +0200 Subject: [PATCH 0256/1062] scorecard: add support for http: testing Add option `--http-plain` to test against httpd without using TLS. Closes #21805 --- tests/http/scorecard.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 1326ce28ff9c..36947592e28c 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -240,6 +240,7 @@ def __init__(self, env: Env, with_flame: bool = False, socks_args: Optional[List[str]] = None, limit_rate: Optional[str] = None, + http_plain: bool = False, suppress_cl: bool = False): self.verbose = verbose self.env = env @@ -254,6 +255,8 @@ def __init__(self, env: Env, self._socks_args = socks_args self._limit_rate_num = 0 self._limit_rate = limit_rate + self._http_plain = http_plain + self._scheme = 'http' if http_plain else 'https' if self._limit_rate: m = re.match(r'(\d+(\.\d+)?)([gmkb])?', self._limit_rate.lower()) if not m: @@ -300,7 +303,7 @@ def handshakes(self) -> Dict[str, Any]: curl = self.mk_curl_client() args = [ '--http3-only' if self.protocol == 'h3' else '--http2', - f'--{ipv}', f'https://{authority}/' + f'--{ipv}', f'{self._scheme}://{authority}/' ] r = curl.run_direct(args=args, with_stats=True) if r.exit_code == 0 and len(r.stats) == 1: @@ -456,7 +459,7 @@ def downloads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict 'sval': Card.fmt_size(fsize) }] self.info(f'{row[0]["sval"]} downloads...') - url = f'https://{self.env.domain1}:{self.server_port}/score{row[0]["sval"]}.data' + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/score{row[0]["sval"]}.data' if 'single' in cols: row.append(self.dl_single(url=url, nsamples=nsamples)) if count > 1: @@ -578,7 +581,7 @@ def uploads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict[s 'sval': Card.fmt_size(fsize) }] self.info(f'{row[0]["sval"]} uploads...') - url = f'https://{self.env.domain1}:{self.server_port}/curltest/put' + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/curltest/put' fname = f'upload{row[0]["sval"]}.data' fpath = self._make_docs_file(docs_dir=self.env.gen_dir, fname=fname, fsize=fsize) @@ -635,7 +638,7 @@ def do_requests(self, url: str, count: int, max_parallel: int = 1, nsamples: int return Card.mk_reqs_cell(samples, profiles, errors) def requests(self, count: int, meta: Dict[str, Any]) -> Dict[str, Any]: - url = f'https://{self.env.domain1}:{self.server_port}/reqs10.data' + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/reqs10.data' fsize = 10 * 1024 cols = ['size', 'total'] rows = [] @@ -839,7 +842,7 @@ def run_score(args, protocol): server_port = env.h3_port else: server_descr = f'httpd/{env.httpd_version()}' - server_port = env.https_port + server_port = env.http_port if args.http_plain else env.https_port card = ScoreRunner(env=env, protocol=protocol, server_descr=server_descr, @@ -849,7 +852,8 @@ def run_score(args, protocol): upload_parallel=args.upload_parallel, with_flame=args.flame, socks_args=socks_args, - limit_rate=args.limit_rate) + limit_rate=args.limit_rate, + http_plain=args.http_plain) card.setup_resources(server_docs, downloads) cards.append(card) @@ -961,6 +965,8 @@ def main(): default = False, help="produce a flame graph on curl") parser.add_argument("--limit-rate", action='store', type=str, default=None, help="use curl's --limit-rate") + parser.add_argument("--http-plain", action='store_true', + default=False, help="run http: test instead of https:") parser.add_argument("-H", "--handshakes", action='store_true', default=False, help="evaluate handshakes only") From 621696d98c5a00647471fc9b23962418fa145e1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 12:18:28 +0000 Subject: [PATCH 0257/1062] GHA: update dependency ngtcp2/ngtcp2 to v1.23.0 Closes #21815 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 99a3b8c8322c..7a79fab4baf3 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -58,7 +58,7 @@ env: # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com NGHTTP3_VERSION: 1.16.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - NGTCP2_VERSION: 1.22.1 + NGTCP2_VERSION: 1.23.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 # no tagged releases From f1a6f190a66215029b10fad630629959f69b50c1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 29 May 2026 13:39:48 +0200 Subject: [PATCH 0258/1062] badwords: prefer 'workaround' (without hyphen) Closes #21807 --- docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md | 4 ++-- docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md | 4 ++-- docs/libcurl/opts/CURLOPT_STDERR.md | 2 +- docs/tests/TEST-SUITE.md | 2 +- include/curl/curl.h | 2 +- lib/curl_addrinfo.c | 2 +- lib/gopher.c | 2 +- lib/if2ip.h | 2 +- lib/vtls/openssl.c | 12 ++++++------ scripts/badwords.txt | 2 ++ src/tool_getparam.c | 2 +- 11 files changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md index e7c596d15ac5..0b491eeff653 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md @@ -38,10 +38,10 @@ behaviors. Available bits: Tells libcurl to not attempt to use any workarounds for a security flaw in the SSL3 and TLS1.0 protocols. If this option is not used or this bit is set to 0, -the SSL layer libcurl uses may use a work-around for this flaw although it +the SSL layer libcurl uses may use a workaround for this flaw although it might cause interoperability problems with some (older) SSL implementations. -**WARNING:** avoiding this work-around lessens the security, and by setting +**WARNING:** avoiding this workaround lessens the security, and by setting this option to 1 you ask for exactly that. This option is only supported for Secure Transport and OpenSSL. diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 1314ae0e8d4c..13dd45077dcc 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -36,10 +36,10 @@ behaviors. Available bits: Tells libcurl to not attempt to use any workarounds for a security flaw in the SSL3 and TLS1.0 protocols. If this option is not used or this bit is set to 0, -the SSL layer libcurl uses may use a work-around for this flaw although it +the SSL layer libcurl uses may use a workaround for this flaw although it might cause interoperability problems with some (older) SSL implementations. -**WARNING:** avoiding this work-around lessens the security, and by setting +**WARNING:** avoiding this workaround lessens the security, and by setting this option to 1 you ask for exactly that. This option is only supported for Secure Transport and OpenSSL. diff --git a/docs/libcurl/opts/CURLOPT_STDERR.md b/docs/libcurl/opts/CURLOPT_STDERR.md index 213eb9ef0e8c..9294fafa60a0 100644 --- a/docs/libcurl/opts/CURLOPT_STDERR.md +++ b/docs/libcurl/opts/CURLOPT_STDERR.md @@ -33,7 +33,7 @@ data. If you are using libcurl as a Windows DLL, this option causes an exception and a crash in the library since it cannot access a FILE * passed on from the -application. A work-around is to instead use CURLOPT_DEBUGFUNCTION(3). +application. A workaround is to instead use CURLOPT_DEBUGFUNCTION(3). # DEFAULT diff --git a/docs/tests/TEST-SUITE.md b/docs/tests/TEST-SUITE.md index dc7671e2e651..51a3f20b2288 100644 --- a/docs/tests/TEST-SUITE.md +++ b/docs/tests/TEST-SUITE.md @@ -136,7 +136,7 @@ set to identify the IP address and port number of the DNS server to use. host information - curl built to use `getaddrinfo()` for resolving *and* is built with c-ares - 1.26.0 or later, gets a special work-around. In such builds, when the + 1.26.0 or later, gets a special workaround. In such builds, when the environment variable is set, curl instead invokes a getaddrinfo wrapper that emulates the function and acknowledges the DNS server environment variable. This way, the getaddrinfo-using code paths in curl are verified, diff --git a/include/curl/curl.h b/include/curl/curl.h index 31c1bfb988ec..36af33e92291 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -934,7 +934,7 @@ typedef enum { /* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the name of improving interoperability with older servers. Some SSL libraries - have introduced work-arounds for this flaw but those work-arounds sometimes + have introduced workarounds for this flaw but those workarounds sometimes make the SSL communication fail. To regain functionality with those broken servers, a user can this way allow the vulnerability back. */ #define CURLSSLOPT_ALLOW_BEAST (1L << 0) diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 52d1e96a5462..901727541f15 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -603,7 +603,7 @@ int curl_dbg_getaddrinfo(const char *hostname, #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /* - * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS + * Works around the sin6_port is always zero bug on iOS 9.3.2 and macOS * 10.11.5. */ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) diff --git a/lib/gopher.c b/lib/gopher.c index f087121d0784..039697466b73 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -133,7 +133,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - /* Do not busyloop. The entire loop thing is a work-around as it causes a + /* Do not busyloop. The entire loop thing is a workaround as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that are not possible to send now will be sent in the doing function repeatedly diff --git a/lib/if2ip.h b/lib/if2ip.h index 12fdaabd736a..dc79c383b793 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -54,7 +54,7 @@ if2ip_result_t Curl_if2ip(int af, #ifdef __INTERIX -/* Nedelcho Stanev's work-around for SFU 3.0 */ +/* Nedelcho Stanev's workaround for SFU 3.0 */ struct ifreq { #define IFNAMSIZ 16 #define IFHWADDRLEN 6 diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 64c904dbf684..93409eb20999 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3724,8 +3724,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, /* OpenSSL contains code to work around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those - work-arounds. The man page for this option states that SSL_OP_ALL enables - all the work-arounds and that "It is usually safe to use SSL_OP_ALL to + workarounds. The man page for this option states that SSL_OP_ALL enables + all the workarounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." @@ -3750,11 +3750,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, CVE-2010-4180 when using previous OpenSSL versions we no longer enable this option regardless of OpenSSL version and SSL_OP_ALL definition. - OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability: + OpenSSL added a workaround for an SSL 3.0/TLS 1.0 CBC vulnerability: https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt. - In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around + In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that workaround despite the fact that SSL_OP_ALL is documented to do "rather harmless" - workarounds. In order to keep the secure work-around, the + workarounds. In order to keep the secure workaround, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. */ @@ -3764,7 +3764,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ctx_options &= ~(ctx_option_t)SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; /* unless the user explicitly asks to allow the protocol vulnerability we - use the work-around */ + use the workaround */ if(!ssl_config->enable_beast) ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 4f806eb71821..81929c3d5293 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -31,6 +31,8 @@ zero terminated:null-terminated nul terminator:null-terminator null terminator:null-terminator zero terminator:null-terminator +work-around:workaround or work around +work-arounds:workarounds or works around it's:it is aren't:are not can't:cannot diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 7e776ea6b645..05642e7a83aa 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -1449,7 +1449,7 @@ static ParameterError parse_range(struct OperationConfig *config, curlx_str_single(&nextarg, '-')) { /* Specifying a range WITHOUT A DASH does create an illegal HTTP range (and does not actually be range by definition). The man page previously - claimed that to be a good way, why this code is added to work-around + claimed that to be a good way, why this code is added to work around it. */ char buffer[32]; warnf("A specified range MUST include at least one dash (-). " From 4ff212f8ed328fb95261e11add8f4d2f0616895a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 19 May 2026 10:57:53 +0200 Subject: [PATCH 0259/1062] url: connection reuse fixes for starttls Add test_31_13 to check connection reuse on mixed --ssl-reqd setting. For that add debug env var CURL_DBG_NO_USE_SSL_ON_FIRST to disable --ssl-reqd for the first url. Check that the connection without SSL from the first url is not reused on the second URL that requires it. Tweak special ftp: protocol check to fail a DEBUGASSERT on mismatched `use_ssl` settings as that should have been caught before in the connection reuse matching (imap/smtp etc. do not have this extra check and rely on the general part doing its job). Closes #21665 --- docs/libcurl/libcurl-env-dbg.md | 5 +++++ lib/ftp.c | 24 ++++++++++++++++-------- lib/url.c | 9 +++------ src/config2setopts.c | 7 +++++++ tests/http/test_31_vsftpds.py | 24 ++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index 9fa9c069d1f9..70d0f12c88bd 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -199,3 +199,8 @@ Make `curl` use the quick exit option, even when built in debug mode. When happy eyeballing for https: wait for the HTTPS-RR resolve answer to arrive before starting any connect attempt. + +## `CURL_DBG_NO_USE_SSL_ON_FIRST` + +When passing `--ssl-reqd`, clear it for the first URL in a curl command. +This allows testing of connection reuse in mixed `STARTTLS` needs. diff --git a/lib/ftp.c b/lib/ftp.c index 3723e7e96817..5a37856fd6fb 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -3115,7 +3115,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; if(ftpcode == 230) { /* 230 User logged in - already! Take as 220 if TLS required. */ - if(data->set.use_ssl <= CURLUSESSL_TRY || + if(ftpc->use_ssl <= CURLUSESSL_TRY || Curl_conn_is_ssl(conn, FIRSTSOCKET)) return ftp_state_user_resp(data, ftpc, ftpcode); } @@ -3125,7 +3125,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, return CURLE_WEIRD_SERVER_REPLY; } - if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* We do not have an SSL/TLS control connection yet, but FTPS is requested. Try an FTPS connection now */ @@ -3218,7 +3218,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* remain in this same state */ } else { - if(data->set.use_ssl > CURLUSESSL_TRY) + if(ftpc->use_ssl > CURLUSESSL_TRY) /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ result = CURLE_USE_SSL_FAILED; else @@ -3239,7 +3239,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PBSZ: result = Curl_pp_sendf(data, &ftpc->pp, "PROT %c", - data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); + ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); if(!result) ftp_state(data, ftpc, FTP_PROT); break; @@ -3247,10 +3247,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PROT: if(ftpcode / 100 == 2) /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL); + conn->bits.ftp_use_data_ssl = (ftpc->use_ssl != CURLUSESSL_CONTROL); /* FTP servers typically responds with 500 if they decide to reject our 'P' request */ - else if(data->set.use_ssl > CURLUSESSL_CONTROL) + else if(ftpc->use_ssl > CURLUSESSL_CONTROL) /* we failed and bails out */ return CURLE_USE_SSL_FAILED; @@ -4453,14 +4453,22 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) { struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN); struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + /* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */ if(!nftpc || !cftpc || Curl_timestrcmp(nftpc->account, cftpc->account) || Curl_timestrcmp(nftpc->alternative_to_user, cftpc->alternative_to_user) || - (nftpc->use_ssl != cftpc->use_ssl) || (nftpc->ccc != cftpc->ccc)) return FALSE; + /* A mismatch on `use_ssl` MUST have been found in connection matching + * before we come here. This is a check on MAYBE/MUST use of STARTTLS and + * it only works on ftp. But imap/smtp etc have the same `use_ssl` and + * no extra match like ftp. We lack tests in this area, so let ftp fail + * loudly here to help other cases. */ + if(nftpc->use_ssl > cftpc->use_ssl) { + DEBUGASSERT(0); + return FALSE; + } return TRUE; } diff --git a/lib/url.c b/lib/url.c index b7ba30fe2bb1..868767a77259 100644 --- a/lib/url.c +++ b/lib/url.c @@ -974,12 +974,6 @@ static bool url_match_destination(struct connectdata *conn, m->needle->scheme->protocol) { return FALSE; } - if(!url_match_ssl_use(conn, m)) { - DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T - " has compatible protocol family, but no SSL, no match", - conn->connection_id)); - return FALSE; - } } /* Scheme mismatch is acceptable, just compare hostname/port */ return Curl_peer_same_destination(m->needle->origin, conn->origin); @@ -1141,6 +1135,9 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_needs(conn, m)) return FALSE; + if(!url_match_ssl_use(conn, m)) + return FALSE; + if(!url_match_proxy_use(conn, m)) return FALSE; if(!url_match_ssl_config(conn, m)) diff --git a/src/config2setopts.c b/src/config2setopts.c index 9138b3b14742..06c5dc6bb960 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -951,6 +951,13 @@ CURLcode config2setopts(struct OperationConfig *config, result = ssl_setopts(config, curl); if(setopt_bad(result)) return result; +#ifdef DEBUGBUILD + if(!per->urlnum) { + char *env = getenv("CURL_DBG_NO_USE_SSL_ON_FIRST"); + if(env) + my_setopt_enum(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); + } +#endif } if(config->path_as_is) diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index f36bb1715a46..688d157f8f02 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -270,6 +270,30 @@ def test_31_12_upload_eprt(self, env: Env, vsftpds: VsFTPD): dstfile = os.path.join(vsftpds.docs_dir, docname) assert os.path.exists(dstfile), f'{r.dump_logs()}' + # connection reuse with STARTTLS required + # 1st download without STARTTLS, 2nd with --ssl-reqd + @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") + def test_31_13_starttls_reuse(self, env: Env, vsftpds: VsFTPD): + run_env = os.environ.copy() + run_env['CURL_DBG_NO_USE_SSL_ON_FIRST'] = '1' + curl = CurlClient(env=env, run_env=run_env) + url1 = f'ftp://{env.ftp_domain}:{vsftpds.port}/data-1k' + url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/data-10k' + r = curl.run_direct(with_stats=True, args=[ + '-svv', '--resolve', f'{env.ftp_domain}:{vsftpds.port}:127.0.0.1', + '--cacert', env.ca.cert_file, + url1, '--out-null', + url2, '--out-null', '--ssl-reqd' + ]) + r.check_exit_code(0) + r.check_stats(count=2, http_status=226) + # expect 4 connections to have been made: + # 1. 1st CONTROL without STARTTLS + # 2. 1st DATA for download + # 3. 2nd CONTROL with STARTTLS (not reuse of 1) + # 4. 2nd DATA for download + assert r.total_connects == 4, f'{r.dump_logs()}' + def check_downloads(self, client, srcfile: str, count: int, complete: bool = True): for i in range(count): From 780ccb256e0dcea71b4e5758da4df997a526c06f Mon Sep 17 00:00:00 2001 From: tiymat <138939221+tiymat@users.noreply.github.com> Date: Tue, 26 May 2026 23:32:32 -0230 Subject: [PATCH 0260/1062] urlapi: drop base fragment on empty redirect Extended test 1560 to verify Fixes #21745 Closes #21763 --- lib/urlapi.c | 7 +++++-- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index a5ec95032b46..d92887bf8857 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1721,8 +1721,11 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, and this is a redirect */ uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); if(!uc) { - /* success, meaning the "" is a fine relative URL, but nothing - changes */ + /* success, meaning the "" is a fine relative URL, and the new URL + inherits scheme/authority/path/query, but not fragment, from the + existing URL (RFC 3986 section 5.2.2) */ + curlx_safefree(u->fragment); + u->fragment_present = FALSE; curlx_free(oldurl); return CURLUE_OK; } diff --git a/tests/data/test1560 b/tests/data/test1560 index e27229739f8e..7f78aece2f13 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -37,7 +37,7 @@ lib%TESTNUMBER success -Allocations: 3200 +Allocations: 3250 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index bdf8b56cad5b..a62d9334b223 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1356,6 +1356,33 @@ static const struct redircase set_url_list[] = { "", /* blank redirect */ "https://example.com/", 0, 0, CURLUE_OK }, + {"file:///test?test#test", + "", "file:///test?test", + 0, 0, CURLUE_OK}, + {"https://example.com/path?query#frag", + "", "https://example.com/path?query", + 0, 0, CURLUE_OK}, + {"ftp://example.com/dir/file#anchor", + "", "ftp://example.com/dir/file", + 0, 0, CURLUE_OK}, + {"http://example.com/path#frag", + "", "http://example.com/path", + 0, 0, CURLUE_OK}, + {"http://example.com/#frag", + "", "http://example.com/", + 0, 0, CURLUE_OK}, + {"http://user:pass@example.com/path?query#frag", + "", "http://user:pass@example.com/path?query", + 0, 0, CURLUE_OK}, + {"http://example.com:8080/path?query#frag", + "", "http://example.com:8080/path?query", + 0, 0, CURLUE_OK}, + {"https://user:pass@example.com:8443/path?query#frag", + "", "https://user:pass@example.com:8443/path?query", + 0, 0, CURLUE_OK}, + {"http://[::1]/path#frag", + "", "http://[::1]/path", + 0, 0, CURLUE_OK}, {"http://firstplace.example.com/want/1314", "//somewhere.example.com/reply/1314", "http://somewhere.example.com/reply/1314", From 1b6724882c2a64e9e5aa9a30d534c7b808b403dc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 31 May 2026 23:23:45 +0200 Subject: [PATCH 0261/1062] urlapi: accept 0X prefix in IPv4 address as well Extend test 1560 accordingly Closes #21820 --- lib/urlapi.c | 2 +- tests/libtest/lib1560.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index d92887bf8857..9fd08890f380 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -520,7 +520,7 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) int rc; curl_off_t l; if(*c == '0') { - if(c[1] == 'x') { + if(Curl_raw_tolower(c[1]) == 'x') { c += 2; /* skip the prefix */ rc = curlx_str_hex(&c, &l, UINT_MAX); if(rc) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index a62d9334b223..a69511d492e7 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -706,6 +706,7 @@ static const struct urltestcase get_url_list[] = { {"https://0xffffffff", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_OK}, {"https://0x7f.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://0X7F.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://1.2.3.256.com", "https://1.2.3.256.com/", 0, 0, CURLUE_OK}, {"https://10.com", "https://10.com/", 0, 0, CURLUE_OK}, {"https://1.2.com", "https://1.2.com/", 0, 0, CURLUE_OK}, @@ -765,10 +766,12 @@ static const struct urltestcase get_url_list[] = { {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK}, {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, + {"https://0111.02.0X3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, {"https://0111.02.0x3.", "https://73.2.0.3/", 0, 0, CURLUE_OK}, {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK}, {"https://0111.02.030.", "https://73.2.0.24/", 0, 0, CURLUE_OK}, {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, + {"https://0XFF.0XFF.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK}, /* IPv4 numerical overflows or syntax errors will not normalize */ {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_OK}, From c5fb460e7c7a57bf3e3a985d10a8b175bbd61500 Mon Sep 17 00:00:00 2001 From: htasta Date: Fri, 17 Apr 2026 12:35:33 +0200 Subject: [PATCH 0262/1062] tool: add a retry delay for transfers to same origin on 429 Closes #21355 --- src/tool_operate.c | 185 ++++++++++++++++++++++++++++++++++++++++++++- src/tool_operate.h | 1 + tests/data/test142 | 2 +- tests/data/test440 | 2 +- tests/data/test445 | 2 +- tests/data/test767 | 2 +- 6 files changed, 188 insertions(+), 6 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index a8d928f496fd..3e6038d9a67e 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -185,6 +185,156 @@ static curl_off_t VmsSpecialSize(const char *name, } #endif /* __VMS */ +/* linked-list structure for the 429 delay */ +struct curl_429_list { + char *origin; + time_t startat; + struct curl_429_list *next; +}; + +/* a list of origins and their delay timer for 429 errors */ +static struct curl_429_list *http_429_list = NULL; + +/* + * Set the delay for new transfers for a given origin. + * If the origin is not found, a new item is appended to the list. + * If the list is NULL a new list will be created. + * On error the function returns NULL, otherwise the list is returned. + */ +static struct curl_429_list *curl_429_delay_set(struct curl_429_list *list, + char *origin, + uint32_t delayms) +{ + struct curl_429_list *item = NULL; + struct curl_429_list *last_item = NULL; + struct curl_429_list *new_item = NULL; + time_t startat = delayms ? time(NULL) + (delayms / 1000) : 0; + + /* try to find the origin in the existing list and update it's timer */ + if(list) { + item = list; + do { + if(curl_strequal(item->origin, origin)) { + item->startat = startat; + return list; + } + last_item = item; + item = item->next; + } while(item); + } + + /* origin not found, so we create a new item */ + new_item = curlx_malloc(sizeof(struct curl_429_list)); + if(!new_item) + return NULL; + new_item->origin = origin; + if(!new_item->origin) { + curlx_free(new_item); + return NULL; + } + new_item->startat = startat; + new_item->next = NULL; + if(!list) + return new_item; /* the new item is the newly created list */ + last_item->next = new_item; + return list; +} + +/* + * Check the list if it contains the given origin and return it. + * Returns NULL if the origin was not found in the list. + */ +static struct curl_429_list *curl_429_delay_get(struct curl_429_list *list, + const char *origin) +{ + struct curl_429_list *item = list; + + /* loop through the list to find this origin */ + while(item) { + if(curl_strequal(item->origin, origin)) { + return item; + } + item = item->next; + } + + return NULL; /* origin not found */ +} + +/* Free all the elements and their data. */ +static void curl_429_delay_free_all(struct curl_429_list *list) +{ + struct curl_429_list *next; + struct curl_429_list *item; + + if(!list) + return; + + item = list; + do { + next = item->next; + curlx_free(item); + item = next; + } while(next); +} + +/* + * extract the host, port and scheme from the URL and write it to porigin + * porigin needs to be freed by the user of this function + * if the URL is invalid origin is set to "-/-/-" + */ +static CURLcode set_per_transfer_origin(const char *url, char **porigin) +{ + char *host = NULL; + char *port = NULL; + char *scheme = NULL; + char *origin = NULL; + size_t len_origin = 0; + CURLcode err = CURLE_OK; + CURLUcode uerr = CURLUE_OK; + CURLU *uh = curl_url(); + + uerr = curl_url_set(uh, CURLUPART_URL, url, CURLU_GUESS_SCHEME); + if(uerr) + goto urlerr; + uerr = curl_url_get(uh, CURLUPART_HOST, &host, CURLU_URLDECODE); + if(uerr) + goto urlerr; + uerr = curl_url_get(uh, CURLUPART_PORT, &port, CURLU_DEFAULT_PORT); + if(uerr) + goto urlerr; + uerr = curl_url_get(uh, CURLUPART_SCHEME, &scheme, CURLU_DEFAULT_SCHEME); + if(uerr) + goto urlerr; + + len_origin = strlen(host) + strlen(port) + strlen(scheme) + 3; + origin = curlx_malloc(len_origin); + if(!origin) { + err = CURLE_OUT_OF_MEMORY; + goto clean; + } + curl_msnprintf(origin, len_origin, "%s/%s/%s", host, port, scheme); + *porigin = origin; + err = CURLE_OK; + goto clean; + +urlerr: + origin = curlx_strdup("-/-/-"); + if(!origin) { + err = CURLE_OUT_OF_MEMORY; + goto clean; + } + *porigin = origin; + err = CURLE_OK; + goto clean; + +clean: + curl_free(host); + curl_free(port); + curl_free(scheme); + curl_url_cleanup(uh); + return err; +} + struct per_transfer *transfers; /* first node */ static struct per_transfer *transfersl; /* last node */ @@ -238,6 +388,7 @@ static struct per_transfer *del_per_transfer(struct per_transfer *per) curlx_free(per->uploadfile); curlx_free(per->outfile); curlx_free(per->url); + curlx_free(per->origin); curl_easy_cleanup(per->curl); curlx_free(per); @@ -437,6 +588,7 @@ static CURLcode retrycheck(struct OperationConfig *config, bool *retryp, uint32_t *delayms) { + long http_error = 0; CURL *curl = per->curl; struct OutStruct *outs = &per->outs; enum retryreason reason = RETRY_NO; @@ -465,6 +617,7 @@ static CURLcode retrycheck(struct OperationConfig *config, /* This was HTTP(S) */ long response = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + http_error = response; switch(response) { case 408: /* Request Timeout */ @@ -523,6 +676,17 @@ static CURLcode retrycheck(struct OperationConfig *config, /* no retry */ return CURLE_OK; per->retry_remaining--; + per->num_retries++; + *delayms = sleeptime; + + /* The retry delay for 429 applies to all requests to the same host. + Update the global 429 delay list for this host. */ + if(http_error == 429) { + http_429_list = curl_429_delay_set(http_429_list, + per->origin, *delayms); + if(!http_429_list) + return CURLE_OUT_OF_MEMORY; + } /* Skip truncation of outfile if auto-resume is enabled for download and the partially received data is good. Only for HTTP GET requests in @@ -595,8 +759,6 @@ static CURLcode retrycheck(struct OperationConfig *config, outs->bytes = 0; /* clear for next round */ } } - per->num_retries++; - *delayms = sleeptime; result = CURLE_OK; } return result; @@ -694,6 +856,7 @@ static CURLcode post_close_output(struct per_transfer *per, /* Close the outs file */ if(outs->fopened && outs->stream) { rc = curlx_fclose(outs->stream); + outs->stream = NULL; if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; @@ -1430,6 +1593,11 @@ static CURLcode create_single(struct OperationConfig *config, if(!per->url) break; + /* store the origin in the per transfer struct for 429 delay checks */ + result = set_per_transfer_origin(per->url, &per->origin); + if(result) + return result; + result = setup_outfile(config, per, u, outs, skipped); if(result) return result; @@ -1570,6 +1738,16 @@ static CURLcode add_parallel_transfers(CURLM *multi, CURLSH *share, sleeping = TRUE; continue; } + if(http_429_list) { + struct curl_429_list *item; + item = curl_429_delay_get(http_429_list, per->origin); + if(item && (time(NULL) < item->startat)) { + per->startat = item->startat; + per->added = FALSE; + sleeping = TRUE; + continue; + } + } per->added = TRUE; result = pre_transfer(per); @@ -2354,6 +2532,9 @@ static CURLcode run_all_transfers(CURLSH *share, global->noprogress = orig_noprogress; global->isatty = orig_isatty; + curl_429_delay_free_all(http_429_list); + http_429_list = NULL; + return result; } diff --git a/src/tool_operate.h b/src/tool_operate.h index 69b304dfc030..bfebb8757283 100644 --- a/src/tool_operate.h +++ b/src/tool_operate.h @@ -44,6 +44,7 @@ struct per_transfer { struct curltime start; /* start of this transfer */ struct curltime retrystart; char *url; + char *origin; curl_off_t urlnum; /* the index of the given URL */ char *outfile; int infd; diff --git a/tests/data/test142 b/tests/data/test142 index a7b85b1b5fb0..790d7a944676 100644 --- a/tests/data/test142 +++ b/tests/data/test142 @@ -188,7 +188,7 @@ RETR %TESTNUMBER QUIT -Allocations: 180 +Allocations: 190 Maximum allocated: 150000 diff --git a/tests/data/test440 b/tests/data/test440 index 3ed08f4730dd..a1517fe699e0 100644 --- a/tests/data/test440 +++ b/tests/data/test440 @@ -75,7 +75,7 @@ https://this.hsts.example./%TESTNUMBER 7 -Allocations: 160 +Allocations: 170 diff --git a/tests/data/test445 b/tests/data/test445 index a652cde588b0..927d45c1416f 100644 --- a/tests/data/test445 +++ b/tests/data/test445 @@ -55,7 +55,7 @@ Refuse tunneling protocols through HTTP proxy 7 -Allocations: 1500 +Allocations: 1560 diff --git a/tests/data/test767 b/tests/data/test767 index a0d96f1b6df2..201bcec4e15a 100644 --- a/tests/data/test767 +++ b/tests/data/test767 @@ -49,7 +49,7 @@ Accept: */* -Allocations: 135 +Allocations: 140 Maximum allocated: 136000 From 8da87fcef1671e1a8985163ee3b596ec794e310f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2026 08:50:11 +0200 Subject: [PATCH 0263/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 74 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 14726d1213c5..6d2daeaf4e62 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1479 - Contributors: 3691 + Authors: 1481 + Contributors: 3696 This release includes the following changes: @@ -19,10 +19,12 @@ This release includes the following changes: This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] + o autotools: mbedtls detection fixes [163] o BUFQ.md: re-sync with source code [111] o build: omit zlib pkg-config reference for Android [130] o cf-h2-prox: fix peer leak [132] o cf-h2-proxy: drop interim responses [47] + o cf-socket: set scope_id for IPv6 link-local addresses [150] o cfilters: fix busy loop on blocked transfers [72] o CIPHERS.md: fix the example that uses only TLS 1.3 [137] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] @@ -32,21 +34,26 @@ This release includes the following bugfixes: o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] o connect: remove deref of freed pointer in trace call [128] + o content_encoding: fix limit failure message [171] + o content_encoding: timeout during slow decoding [170] o cookie: compare path case sensitively [52] o cookie: simplify strstore(), remove outdated comment [12] o cookie: trim trailing dots when checking PSL [39] o creds: add sasl service name [75] o creds: mask OAuth bearer token in trace logs [117] + o creds: remove two unused functions [158] o curl_easy_pause.md: rephrase the stream cache when pause clause [120] o curl_easy_setopt.md: change options when no transfer runs [122] o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] + o curl_sha512_256: fix result code on error [166] o CURLOPT_ECH.md: simplify the description language [18] o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] o CURLOPT_SHARE: warn about early remove [51] o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] o delta: harden external command invocations [98] + o dnscache: remove Curl_dns_entry_link [160] o docs/libcurl: fix the version for curl_multi_socket_action o docs: end "...can be used several times..." sentences with period [34] o docs: fix --follow doc typo [97] @@ -65,7 +72,10 @@ This release includes the following bugfixes: o gsasl: fix potential double free [56] o gtls: fix ignored return and uninitialized status in OCSP check [49] o gtls: fix some typos [15] + o gtls: use the correct return code in trace output [173] + o h3-proxy: fix callback return values, and a typo in tests [139] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] + o http: don't pass on set cookies to new origins [140] o idn: replace header guards with forward declaration [100] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o KNOWN_BUGS: remove stale Threads::Threads entry [135] @@ -77,6 +87,7 @@ This release includes the following bugfixes: o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] + o m4: drop redundant conditions in TLS library detections [155] o managen: apply minor fixes and improvements [115] o mbedtls: null-terminate the private key blob [36] o mk-unity.pl: `#include`, and not concatenate input headers [124] @@ -85,11 +96,13 @@ This release includes the following bugfixes: o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] o netrc: scanner refactor [121] o ngtcp2: fail handshake directly [138] + o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] o rustls: error on CURLOPT_CRLFILE with native CA store [59] + o schannel: check `schannel_sha256sum()` success, and more [165] o schannel: enforce Extended Key Usage for custom CA roots [29] o schannel: error on TLS 1.3-only with cipher list [136] o schannel: fix revoke_best_effort setting for proxy [70] @@ -101,8 +114,8 @@ This release includes the following bugfixes: o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] o show-headers.md: mention bold headers and --no-styled-output [17] - o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] + o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o src: fix comment typos [83] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] @@ -110,9 +123,13 @@ This release includes the following bugfixes: o test1981: explicitly set the locale [85] o tests: add an assert to avoid IPC blocking [69] o tests: fix unit1636 with --disable-progress-meter [37] + o tftp: avoid the timeout calc if the timeout is crazy [151] o tftp: stricter option name checks [90] + o tidy-up: add space around operators, where missing [147] + o tidy-up: apply clang-format fixes [153] o tidy-up: miscellaneous [106] o tls: fix incomplete mTLS config in conn reuse and session cache [108] + o tool: add a retry delay for transfers to same origin on 429 [61] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] o tool_formparse: tool2curlparts is no longer recursive [33] @@ -122,21 +139,28 @@ This release includes the following bugfixes: o transfer: clear referer when set to NULL [112] o unix-sockets: ignore proxy settings [6] o url: compare full origin when setting credentials [42] + o url: connection reuse fixes for starttls [68] o url: detect proxy changes read from environment [110] o url: fix connection reuse for starttls protocols [27] o url: keep the question mark for empty queries [73] o url: remove ssh_config_matches [31] o url: remove superfluous check [131] o url: url_match_destination fix [43] + o urlapi: accept 0X prefix in IPv4 address as well [63] o urlapi: change more lowercase percent-encoded to uppercase [71] o urlapi: compare zone-id in Curl_url_same_origin() [95] o urlapi: consume trailing dots after IPv4 numerical addresses [50] o urlapi: deny hostnames with more than one trailing dot [58] + o urlapi: drop base fragment on empty redirect [64] + o urlapi: fix an issue parsing file URLs [149] o urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set [46] + o urlapi: forbid '|' in host [172] o urlapi: handle redirect without set scheme with default-scheme [38] o user-agent.md: mention double quotes too [3] + o vtls: more large buffer support and error checks for SHA-256 [164] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] o vtls_scache: include signature_algorithms in the SSL peer cache key [123] + o vtls_spack: drop redundant macro fallbacks [167] o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] o websockets: auto-tunnel through http proxy [102] @@ -164,17 +188,19 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 0xN3R3K3, 11soda11, Alan De Smet, amitbidlan, Andrei Rybak, Andrew Nesbitt, - Aritra Basu, Bastian Jesuiter, Bill Mill, chrizilla on github, - co-authors in libssh2, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, - Dario Vinella, dependabot[bot], Earnestly on github, Elise Vance, - Emanuel Krollmann, Fabian Keil, Harry Sintonen, jeffhuang, Jeremy Nicoll, + 0xN3R3K3, 11soda11, Alan De Smet, ambikeesshh, amitbidlan, Andrei Rybak, + Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, Bartel Sielski, + Bastian Jesuiter, Bill Mill, chrizilla on github, co-authors in libssh2, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, + dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, + Fabian Keil, Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, Johannes Schlatow, Joshua Rogers, Kai Pastor, Mark Esler, Max Dymond, mik, - mulan_dh on hackerone, parasol-aser, penpal, Peter Krefting, Raymond Steen, - Ray Satiro, renovate[bot], Sergio Correia, sfan5 on github, Shintomon Mathew, - Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, Viktor Szakats, + Mike-menny on github, mulan_dh on hackerone, parasol-aser, penpal, + Peter Krefting, Raymond Steen, Ray Satiro, renovate[bot], Ross Burton, + Sergio Correia, sfan5 on github, Shintomon Mathew, Sollace on github, + Song X. Gao, Stefan Eissing, Tim Martin, tiymat, Viktor Szakats, Will Cosgrove, Xi Ruoyao, x-xiang on github - (47 contributors) + (54 contributors) References to bug reports and discussions on issues: @@ -238,10 +264,14 @@ References to bug reports and discussions on issues: [58] = https://curl.se/bug/?i=21622 [59] = https://curl.se/bug/?i=21614 [60] = https://curl.se/bug/?i=21621 + [61] = https://curl.se/bug/?i=21355 [62] = https://curl.se/bug/?i=21617 + [63] = https://curl.se/bug/?i=21820 + [64] = https://curl.se/bug/?i=21745 [65] = https://curl.se/bug/?i=21682 [66] = https://curl.se/bug/?i=21593 [67] = https://curl.se/bug/?i=21597 + [68] = https://curl.se/bug/?i=21665 [69] = https://curl.se/bug/?i=21688 [70] = https://curl.se/bug/?i=21683 [71] = https://curl.se/bug/?i=21592 @@ -307,3 +337,23 @@ References to bug reports and discussions on issues: [136] = https://curl.se/bug/?i=21702 [137] = https://curl.se/bug/?i=21719 [138] = https://curl.se/bug/?i=21712 + [139] = https://curl.se/bug/?i=21802 + [140] = https://curl.se/bug/?i=21794 + [147] = https://curl.se/bug/?i=21793 + [149] = https://curl.se/bug/?i=21743 + [150] = https://curl.se/bug/?i=21669 + [151] = https://curl.se/bug/?i=21782 + [153] = https://curl.se/bug/?i=21786 + [154] = https://curl.se/bug/?i=21784 + [155] = https://curl.se/bug/?i=21781 + [158] = https://curl.se/bug/?i=21776 + [160] = https://curl.se/bug/?i=21774 + [163] = https://curl.se/bug/?i=21727 + [164] = https://curl.se/bug/?i=21771 + [165] = https://curl.se/bug/?i=21739 + [166] = https://curl.se/bug/?i=21767 + [167] = https://curl.se/bug/?i=21768 + [170] = https://curl.se/bug/?i=21603 + [171] = https://curl.se/bug/?i=21756 + [172] = https://curl.se/bug/?i=21762 + [173] = https://curl.se/bug/?i=21766 From 4beffe7737d935e87cc188b8258ca5cc4f6b6b59 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2026 09:15:50 +0200 Subject: [PATCH 0264/1062] Revert "tool: add a retry delay for transfers to same origin on 429" This reverts commit c5fb460e7c7a57bf3e3a985d10a8b175bbd61500. This needs some further work before we can do this. Fixes #21822 Closes #21824 --- src/tool_operate.c | 185 +-------------------------------------------- src/tool_operate.h | 1 - tests/data/test142 | 2 +- tests/data/test440 | 2 +- tests/data/test445 | 2 +- tests/data/test767 | 2 +- 6 files changed, 6 insertions(+), 188 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index 3e6038d9a67e..a8d928f496fd 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -185,156 +185,6 @@ static curl_off_t VmsSpecialSize(const char *name, } #endif /* __VMS */ -/* linked-list structure for the 429 delay */ -struct curl_429_list { - char *origin; - time_t startat; - struct curl_429_list *next; -}; - -/* a list of origins and their delay timer for 429 errors */ -static struct curl_429_list *http_429_list = NULL; - -/* - * Set the delay for new transfers for a given origin. - * If the origin is not found, a new item is appended to the list. - * If the list is NULL a new list will be created. - * On error the function returns NULL, otherwise the list is returned. - */ -static struct curl_429_list *curl_429_delay_set(struct curl_429_list *list, - char *origin, - uint32_t delayms) -{ - struct curl_429_list *item = NULL; - struct curl_429_list *last_item = NULL; - struct curl_429_list *new_item = NULL; - time_t startat = delayms ? time(NULL) + (delayms / 1000) : 0; - - /* try to find the origin in the existing list and update it's timer */ - if(list) { - item = list; - do { - if(curl_strequal(item->origin, origin)) { - item->startat = startat; - return list; - } - last_item = item; - item = item->next; - } while(item); - } - - /* origin not found, so we create a new item */ - new_item = curlx_malloc(sizeof(struct curl_429_list)); - if(!new_item) - return NULL; - new_item->origin = origin; - if(!new_item->origin) { - curlx_free(new_item); - return NULL; - } - new_item->startat = startat; - new_item->next = NULL; - if(!list) - return new_item; /* the new item is the newly created list */ - last_item->next = new_item; - return list; -} - -/* - * Check the list if it contains the given origin and return it. - * Returns NULL if the origin was not found in the list. - */ -static struct curl_429_list *curl_429_delay_get(struct curl_429_list *list, - const char *origin) -{ - struct curl_429_list *item = list; - - /* loop through the list to find this origin */ - while(item) { - if(curl_strequal(item->origin, origin)) { - return item; - } - item = item->next; - } - - return NULL; /* origin not found */ -} - -/* Free all the elements and their data. */ -static void curl_429_delay_free_all(struct curl_429_list *list) -{ - struct curl_429_list *next; - struct curl_429_list *item; - - if(!list) - return; - - item = list; - do { - next = item->next; - curlx_free(item); - item = next; - } while(next); -} - -/* - * extract the host, port and scheme from the URL and write it to porigin - * porigin needs to be freed by the user of this function - * if the URL is invalid origin is set to "-/-/-" - */ -static CURLcode set_per_transfer_origin(const char *url, char **porigin) -{ - char *host = NULL; - char *port = NULL; - char *scheme = NULL; - char *origin = NULL; - size_t len_origin = 0; - CURLcode err = CURLE_OK; - CURLUcode uerr = CURLUE_OK; - CURLU *uh = curl_url(); - - uerr = curl_url_set(uh, CURLUPART_URL, url, CURLU_GUESS_SCHEME); - if(uerr) - goto urlerr; - uerr = curl_url_get(uh, CURLUPART_HOST, &host, CURLU_URLDECODE); - if(uerr) - goto urlerr; - uerr = curl_url_get(uh, CURLUPART_PORT, &port, CURLU_DEFAULT_PORT); - if(uerr) - goto urlerr; - uerr = curl_url_get(uh, CURLUPART_SCHEME, &scheme, CURLU_DEFAULT_SCHEME); - if(uerr) - goto urlerr; - - len_origin = strlen(host) + strlen(port) + strlen(scheme) + 3; - origin = curlx_malloc(len_origin); - if(!origin) { - err = CURLE_OUT_OF_MEMORY; - goto clean; - } - curl_msnprintf(origin, len_origin, "%s/%s/%s", host, port, scheme); - *porigin = origin; - err = CURLE_OK; - goto clean; - -urlerr: - origin = curlx_strdup("-/-/-"); - if(!origin) { - err = CURLE_OUT_OF_MEMORY; - goto clean; - } - *porigin = origin; - err = CURLE_OK; - goto clean; - -clean: - curl_free(host); - curl_free(port); - curl_free(scheme); - curl_url_cleanup(uh); - return err; -} - struct per_transfer *transfers; /* first node */ static struct per_transfer *transfersl; /* last node */ @@ -388,7 +238,6 @@ static struct per_transfer *del_per_transfer(struct per_transfer *per) curlx_free(per->uploadfile); curlx_free(per->outfile); curlx_free(per->url); - curlx_free(per->origin); curl_easy_cleanup(per->curl); curlx_free(per); @@ -588,7 +437,6 @@ static CURLcode retrycheck(struct OperationConfig *config, bool *retryp, uint32_t *delayms) { - long http_error = 0; CURL *curl = per->curl; struct OutStruct *outs = &per->outs; enum retryreason reason = RETRY_NO; @@ -617,7 +465,6 @@ static CURLcode retrycheck(struct OperationConfig *config, /* This was HTTP(S) */ long response = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - http_error = response; switch(response) { case 408: /* Request Timeout */ @@ -676,17 +523,6 @@ static CURLcode retrycheck(struct OperationConfig *config, /* no retry */ return CURLE_OK; per->retry_remaining--; - per->num_retries++; - *delayms = sleeptime; - - /* The retry delay for 429 applies to all requests to the same host. - Update the global 429 delay list for this host. */ - if(http_error == 429) { - http_429_list = curl_429_delay_set(http_429_list, - per->origin, *delayms); - if(!http_429_list) - return CURLE_OUT_OF_MEMORY; - } /* Skip truncation of outfile if auto-resume is enabled for download and the partially received data is good. Only for HTTP GET requests in @@ -759,6 +595,8 @@ static CURLcode retrycheck(struct OperationConfig *config, outs->bytes = 0; /* clear for next round */ } } + per->num_retries++; + *delayms = sleeptime; result = CURLE_OK; } return result; @@ -856,7 +694,6 @@ static CURLcode post_close_output(struct per_transfer *per, /* Close the outs file */ if(outs->fopened && outs->stream) { rc = curlx_fclose(outs->stream); - outs->stream = NULL; if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; @@ -1593,11 +1430,6 @@ static CURLcode create_single(struct OperationConfig *config, if(!per->url) break; - /* store the origin in the per transfer struct for 429 delay checks */ - result = set_per_transfer_origin(per->url, &per->origin); - if(result) - return result; - result = setup_outfile(config, per, u, outs, skipped); if(result) return result; @@ -1738,16 +1570,6 @@ static CURLcode add_parallel_transfers(CURLM *multi, CURLSH *share, sleeping = TRUE; continue; } - if(http_429_list) { - struct curl_429_list *item; - item = curl_429_delay_get(http_429_list, per->origin); - if(item && (time(NULL) < item->startat)) { - per->startat = item->startat; - per->added = FALSE; - sleeping = TRUE; - continue; - } - } per->added = TRUE; result = pre_transfer(per); @@ -2532,9 +2354,6 @@ static CURLcode run_all_transfers(CURLSH *share, global->noprogress = orig_noprogress; global->isatty = orig_isatty; - curl_429_delay_free_all(http_429_list); - http_429_list = NULL; - return result; } diff --git a/src/tool_operate.h b/src/tool_operate.h index bfebb8757283..69b304dfc030 100644 --- a/src/tool_operate.h +++ b/src/tool_operate.h @@ -44,7 +44,6 @@ struct per_transfer { struct curltime start; /* start of this transfer */ struct curltime retrystart; char *url; - char *origin; curl_off_t urlnum; /* the index of the given URL */ char *outfile; int infd; diff --git a/tests/data/test142 b/tests/data/test142 index 790d7a944676..a7b85b1b5fb0 100644 --- a/tests/data/test142 +++ b/tests/data/test142 @@ -188,7 +188,7 @@ RETR %TESTNUMBER QUIT -Allocations: 190 +Allocations: 180 Maximum allocated: 150000 diff --git a/tests/data/test440 b/tests/data/test440 index a1517fe699e0..3ed08f4730dd 100644 --- a/tests/data/test440 +++ b/tests/data/test440 @@ -75,7 +75,7 @@ https://this.hsts.example./%TESTNUMBER 7 -Allocations: 170 +Allocations: 160 diff --git a/tests/data/test445 b/tests/data/test445 index 927d45c1416f..a652cde588b0 100644 --- a/tests/data/test445 +++ b/tests/data/test445 @@ -55,7 +55,7 @@ Refuse tunneling protocols through HTTP proxy 7 -Allocations: 1560 +Allocations: 1500 diff --git a/tests/data/test767 b/tests/data/test767 index 201bcec4e15a..a0d96f1b6df2 100644 --- a/tests/data/test767 +++ b/tests/data/test767 @@ -49,7 +49,7 @@ Accept: */* -Allocations: 140 +Allocations: 135 Maximum allocated: 136000 From d3391229b0a0eb04f1ffe85f1483a890c5e00009 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 20 May 2026 13:25:49 +0200 Subject: [PATCH 0265/1062] vtls_config: adjust to origin When a transfer goes against another origin than the initial one, do not add the following to the ssl configuration: client cert, client key, srp user/pass, pinned key. Closes #21695 --- lib/Makefile.inc | 2 + lib/url.c | 9 +- lib/urldata.h | 57 +----- lib/vtls/vtls.c | 325 --------------------------------- lib/vtls/vtls.h | 37 ---- lib/vtls/vtls_config.c | 399 +++++++++++++++++++++++++++++++++++++++++ lib/vtls/vtls_config.h | 125 +++++++++++++ tests/unit/unit3303.c | 17 +- 8 files changed, 550 insertions(+), 421 deletions(-) create mode 100644 lib/vtls/vtls_config.c create mode 100644 lib/vtls/vtls_config.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 0a9e6ce31143..28647baff4a9 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -96,6 +96,7 @@ LIB_VTLS_CFILES = \ vtls/schannel.c \ vtls/schannel_verify.c \ vtls/vtls.c \ + vtls/vtls_config.c \ vtls/vtls_scache.c \ vtls/vtls_spack.c \ vtls/wolfssl.c \ @@ -113,6 +114,7 @@ LIB_VTLS_HFILES = \ vtls/schannel.h \ vtls/schannel_int.h \ vtls/vtls.h \ + vtls/vtls_config.h \ vtls/vtls_int.h \ vtls/vtls_scache.h \ vtls/vtls_spack.h \ diff --git a/lib/url.c b/lib/url.c index 868767a77259..76a8c2e3e7dc 100644 --- a/lib/url.c +++ b/lib/url.c @@ -300,6 +300,10 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_netrc_cleanup(&data->state.netrc); #ifndef CURL_DISABLE_DIGEST_AUTH curlx_free(data->state.envproxy); +#endif + Curl_ssl_config_cleanup(&data->set.ssl.primary); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&data->set.proxy_ssl.primary); #endif curlx_free(data); return CURLE_OK; @@ -355,7 +359,9 @@ void Curl_init_userdefined(struct Curl_easy *data) set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ + Curl_ssl_config_init(&data->set.ssl.primary); #ifndef CURL_DISABLE_PROXY + Curl_ssl_config_init(&data->set.proxy_ssl.primary); set->proxyport = 0; set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ @@ -363,7 +369,6 @@ void Curl_init_userdefined(struct Curl_easy *data) set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; #endif - Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; @@ -2782,7 +2787,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) #endif /* Complete the easy's SSL configuration for connection cache matching */ - result = Curl_ssl_easy_config_complete(data); + result = Curl_ssl_easy_config_complete(data, needle->origin); if(result) goto out; diff --git a/lib/urldata.h b/lib/urldata.h index 4ee5108b1750..e5363cf56964 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -69,6 +69,7 @@ #include "request.h" #include "ratelimit.h" #include "netrc.h" +#include "vtls/vtls_config.h" /* On error return, the value of `pnwritten` has no meaning */ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -140,62 +141,6 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -struct ssl_primary_config { - char *CApath; /* certificate directory (does not work on Windows) */ - char *CAfile; /* certificate to verify peer against */ - char *issuercert; /* optional issuer certificate filename */ - char *clientcert; - char *cipher_list; /* list of ciphers to use */ - char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ - char *signature_algorithms; /* list of signature algorithms to use */ - char *pinned_key; - char *CRLfile; /* CRL to check certificate revocation */ - char *cert_type; /* format for certificate (default: PEM) */ - char *key; /* private key filename */ - char *key_type; /* format for private key (default: PEM) */ - char *key_passwd; /* plain text private key password */ - struct curl_blob *cert_blob; - struct curl_blob *ca_info_blob; - struct curl_blob *issuercert_blob; - struct curl_blob *key_blob; -#ifdef USE_TLS_SRP - char *username; /* TLS username (for, e.g., SRP) */ - char *password; /* TLS password (for, e.g., SRP) */ -#endif - char *curves; /* list of curves to use */ - uint32_t version_max; /* max supported version the client wants to use */ - uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - uint8_t version; /* what version the client wants to use */ - BIT(verifypeer); /* set TRUE if this is desired */ - BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ - BIT(verifystatus); /* set TRUE if certificate status must be checked */ - BIT(cache_session); /* cache session or not */ -}; - -struct ssl_config_data { - struct ssl_primary_config primary; - long certverifyresult; /* result from the certificate verification */ - curl_ssl_ctx_callback fsslctx; /* function to initialize SSL ctx */ - void *fsslctxp; /* parameter for call back */ - BIT(certinfo); /* gather lots of certificate info */ - BIT(earlydata); /* use TLS 1.3 early data */ - BIT(enable_beast); /* allow this flaw for interoperability's sake */ - BIT(no_revoke); /* disable SSL certificate revocation checks */ - BIT(no_partialchain); /* do not accept partial certificate chains */ - BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation - list errors */ - BIT(native_ca_store); /* use the native CA store of operating system */ - BIT(auto_client_cert); /* automatically locate and use a client - certificate for authentication (Schannel) */ - BIT(custom_cafile); /* application has set custom CA file */ - BIT(custom_capath); /* application has set custom CA path */ - BIT(custom_cablob); /* application has set custom CA blob */ -}; - -struct ssl_general_config { - int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ -}; - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index eb0aa4277f9a..c147a2ef19df 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -78,57 +78,6 @@ #include #endif - -#define CLONE_STRING(var) \ - do { \ - if(source->var) { \ - dest->var = curlx_strdup(source->var); \ - if(!dest->var) \ - return FALSE; \ - } \ - else \ - dest->var = NULL; \ - } while(0) - -#define CLONE_BLOB(var) \ - do { \ - if(blobdup(&dest->var, source->var)) \ - return FALSE; \ - } while(0) - -static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src) -{ - DEBUGASSERT(dest); - DEBUGASSERT(!*dest); - if(src) { - /* only if there is data to dupe! */ - struct curl_blob *d; - d = curlx_malloc(sizeof(struct curl_blob) + src->len); - if(!d) - return CURLE_OUT_OF_MEMORY; - d->len = src->len; - /* Always duplicate because the connection may survive longer than the - handle that passed in the blob. */ - d->flags = CURL_BLOB_COPY; - d->data = (void *)((char *)d + sizeof(struct curl_blob)); - memcpy(d->data, src->data, src->len); - *dest = d; - } - return CURLE_OK; -} - -/* returns TRUE if the blobs are identical */ -static bool blobcmp(struct curl_blob *first, struct curl_blob *second) -{ - if(!first && !second) /* both are NULL */ - return TRUE; - if(!first || !second) /* one is NULL */ - return FALSE; - if(first->len != second->len) /* different sizes */ - return FALSE; - return !memcmp(first->data, second->data, first->len); /* same data */ -} - #ifdef USE_SSL #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static const struct alpn_spec ALPN_SPEC_H11 = { @@ -177,280 +126,6 @@ static const struct alpn_spec *alpn_get_spec(http_majors wanted, #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ #endif /* USE_SSL */ -void Curl_ssl_easy_config_init(struct Curl_easy *data) -{ - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ - data->set.ssl.primary.verifypeer = TRUE; - data->set.ssl.primary.verifyhost = TRUE; - data->set.ssl.primary.cache_session = TRUE; /* caching by default */ -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl = data->set.ssl; -#endif -} - -static bool match_ssl_primary_config(struct Curl_easy *data, - struct ssl_primary_config *c1, - struct ssl_primary_config *c2) -{ - (void)data; - if((c1->version == c2->version) && - (c1->version_max == c2->version_max) && - (c1->ssl_options == c2->ssl_options) && - (c1->verifypeer == c2->verifypeer) && - (c1->verifyhost == c2->verifyhost) && - (c1->verifystatus == c2->verifystatus) && - blobcmp(c1->cert_blob, c2->cert_blob) && - blobcmp(c1->ca_info_blob, c2->ca_info_blob) && - blobcmp(c1->issuercert_blob, c2->issuercert_blob) && - blobcmp(c1->key_blob, c2->key_blob) && - Curl_safecmp(c1->CApath, c2->CApath) && - Curl_safecmp(c1->CAfile, c2->CAfile) && - Curl_safecmp(c1->issuercert, c2->issuercert) && - Curl_safecmp(c1->clientcert, c2->clientcert) && -#ifdef USE_TLS_SRP - !Curl_timestrcmp(c1->username, c2->username) && - !Curl_timestrcmp(c1->password, c2->password) && -#endif - curl_strequal(c1->cipher_list, c2->cipher_list) && - curl_strequal(c1->cipher_list13, c2->cipher_list13) && - curl_strequal(c1->curves, c2->curves) && - curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && - Curl_safecmp(c1->CRLfile, c2->CRLfile) && - Curl_safecmp(c1->pinned_key, c2->pinned_key) && - curl_strequal(c1->cert_type, c2->cert_type) && - Curl_safecmp(c1->key, c2->key) && - curl_strequal(c1->key_type, c2->key_type) && - !Curl_timestrcmp(c1->key_passwd, c2->key_passwd)) - return TRUE; - - return FALSE; -} - -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy) -{ -#ifndef CURL_DISABLE_PROXY - if(proxy) - return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, - &candidate->proxy_ssl_config); -#else - (void)proxy; -#endif - return match_ssl_primary_config(data, &data->set.ssl.primary, - &candidate->ssl_config); -} - -static bool clone_ssl_primary_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) -{ - dest->version = source->version; - dest->version_max = source->version_max; - dest->verifypeer = source->verifypeer; - dest->verifyhost = source->verifyhost; - dest->verifystatus = source->verifystatus; - dest->cache_session = source->cache_session; - dest->ssl_options = source->ssl_options; - - CLONE_BLOB(cert_blob); - CLONE_BLOB(ca_info_blob); - CLONE_BLOB(issuercert_blob); - CLONE_BLOB(key_blob); - CLONE_STRING(CApath); - CLONE_STRING(CAfile); - CLONE_STRING(issuercert); - CLONE_STRING(clientcert); - CLONE_STRING(cipher_list); - CLONE_STRING(cipher_list13); - CLONE_STRING(pinned_key); - CLONE_STRING(curves); - CLONE_STRING(signature_algorithms); - CLONE_STRING(CRLfile); - CLONE_STRING(cert_type); - CLONE_STRING(key); - CLONE_STRING(key_type); - CLONE_STRING(key_passwd); -#ifdef USE_TLS_SRP - CLONE_STRING(username); - CLONE_STRING(password); -#endif - - return TRUE; -} - -static void free_primary_ssl_config(struct ssl_primary_config *sslc) -{ - curlx_safefree(sslc->CApath); - curlx_safefree(sslc->CAfile); - curlx_safefree(sslc->issuercert); - curlx_safefree(sslc->clientcert); - curlx_safefree(sslc->cipher_list); - curlx_safefree(sslc->cipher_list13); - curlx_safefree(sslc->pinned_key); - curlx_safefree(sslc->cert_blob); - curlx_safefree(sslc->ca_info_blob); - curlx_safefree(sslc->issuercert_blob); - curlx_safefree(sslc->key_blob); - curlx_safefree(sslc->curves); - curlx_safefree(sslc->signature_algorithms); - curlx_safefree(sslc->CRLfile); - curlx_safefree(sslc->cert_type); - curlx_safefree(sslc->key); - curlx_safefree(sslc->key_type); - curlx_safefree(sslc->key_passwd); -#ifdef USE_TLS_SRP - curlx_safefree(sslc->username); - curlx_safefree(sslc->password); -#endif -} - -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) -{ - struct ssl_config_data *sslc = &data->set.ssl; -#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) - struct UserDefined *set = &data->set; - CURLcode result; -#endif - - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) - if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; -#endif -#ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); - if(result) - return result; - } -#endif -#ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); - if(result) - return result; - } -#endif - } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; - sslc->primary.signature_algorithms = - data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; - sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#endif - sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; - sslc->primary.key = data->set.str[STRING_KEY]; - sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; - sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; - sslc->primary.clientcert = data->set.str[STRING_CERT]; - sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; - -#ifndef CURL_DISABLE_PROXY - sslc = &data->set.proxy_ssl; - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) - if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; -#endif -#ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], - CURL_CA_PATH); - if(result) - return result; - } -#endif -#ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], - CURL_CA_BUNDLE); - if(result) - return result; - } -#endif - } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; - sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - sslc->primary.key = data->set.str[STRING_KEY_PROXY]; - sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; - sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif -#endif /* CURL_DISABLE_PROXY */ - - return CURLE_OK; -} - -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Clone "primary" SSL configurations from the easy handle to - * the connection. They are used for connection cache matching and - * probably outlive the easy handle */ - if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) - return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_PROXY - if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) - return CURLE_OUT_OF_MEMORY; -#endif - return CURLE_OK; -} - -void Curl_ssl_conn_config_cleanup(struct connectdata *conn) -{ - free_primary_ssl_config(&conn->ssl_config); -#ifndef CURL_DISABLE_PROXY - free_primary_ssl_config(&conn->proxy_ssl_config); -#endif -} - -void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) -{ - /* May be called on an easy that has no connection yet */ - if(data->conn) { - struct ssl_primary_config *src, *dest; -#ifndef CURL_DISABLE_PROXY - src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; - dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; -#else - (void)for_proxy; - src = &data->set.ssl.primary; - dest = &data->conn->ssl_config; -#endif - dest->verifyhost = src->verifyhost; - dest->verifypeer = src->verifypeer; - dest->verifystatus = src->verifystatus; - } -} - #ifdef USE_SSL static int multissl_setup(const struct Curl_ssl *backend); #endif diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index dfda7d0f2ed1..f0825c37ed9a 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -103,43 +103,6 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, curl_sslbackend Curl_ssl_backend(void); -/** - * Init SSL config for a new easy handle. - */ -void Curl_ssl_easy_config_init(struct Curl_easy *data); - -/** - * Init the `data->set.ssl` and `data->set.proxy_ssl` for - * connection matching use. - */ -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); - -/** - * Init SSL configs (main + proxy) for a new connection from the easy handle. - */ -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn); - -/** - * Free allocated resources in SSL configs (main + proxy) for - * the given connection. - */ -void Curl_ssl_conn_config_cleanup(struct connectdata *conn); - -/** - * Return TRUE iff SSL configuration from `data` is functionally the - * same as the one on `candidate`. - * @param proxy match the proxy SSL config or the main one - */ -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy); - -/* Update certain connection SSL config flags after they have - * been changed on the easy handle. Works for `verifypeer`, - * `verifyhost` and `verifystatus`. */ -void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); - /** * Init SSL peer information for filter. Can be called repeatedly. */ diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c new file mode 100644 index 000000000000..771c6101ae20 --- /dev/null +++ b/lib/vtls/vtls_config.c @@ -0,0 +1,399 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This file is for implementing all "generic" SSL functions that all libcurl + internals should use. It is then responsible for calling the proper + "backend" function. + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + + Note that this source code uses the functions of the configured SSL + backend via the global Curl_ssl instance. + + "SSL/TLS Strong Encryption: An Introduction" + https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html +*/ + +#include "curl_setup.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "urldata.h" +#include "setopt.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "vtls/vtls_config.h" + + +#define CLONE_STRING(var) \ + do { \ + if(source->var) { \ + dest->var = curlx_strdup(source->var); \ + if(!dest->var) \ + return FALSE; \ + } \ + else \ + dest->var = NULL; \ + } while(0) + +#define CLONE_BLOB(var) \ + do { \ + if(blobdup(&dest->var, source->var)) \ + return FALSE; \ + } while(0) + +static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src) +{ + DEBUGASSERT(dest); + DEBUGASSERT(!*dest); + if(src) { + /* only if there is data to dupe! */ + struct curl_blob *d; + d = curlx_malloc(sizeof(struct curl_blob) + src->len); + if(!d) + return CURLE_OUT_OF_MEMORY; + d->len = src->len; + /* Always duplicate because the connection may survive longer than the + handle that passed in the blob. */ + d->flags = CURL_BLOB_COPY; + d->data = (void *)((char *)d + sizeof(struct curl_blob)); + memcpy(d->data, src->data, src->len); + *dest = d; + } + return CURLE_OK; +} + +/* returns TRUE if the blobs are identical */ +static bool blobcmp(struct curl_blob *first, struct curl_blob *second) +{ + if(!first && !second) /* both are NULL */ + return TRUE; + if(!first || !second) /* one is NULL */ + return FALSE; + if(first->len != second->len) /* different sizes */ + return FALSE; + return !memcmp(first->data, second->data, first->len); /* same data */ +} + +void Curl_ssl_config_init(struct ssl_primary_config *sslc) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + sslc->verifypeer = TRUE; + sslc->verifyhost = TRUE; + sslc->cache_session = TRUE; /* caching by default */ +} + +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc) +{ + if(sslc->deep_copy) { + curlx_safefree(sslc->CApath); + curlx_safefree(sslc->CAfile); + curlx_safefree(sslc->issuercert); + curlx_safefree(sslc->clientcert); + curlx_safefree(sslc->cipher_list); + curlx_safefree(sslc->cipher_list13); + curlx_safefree(sslc->pinned_key); + curlx_safefree(sslc->cert_blob); + curlx_safefree(sslc->ca_info_blob); + curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->key_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); + curlx_safefree(sslc->cert_type); + curlx_safefree(sslc->key); + curlx_safefree(sslc->key_type); + curlx_safefree(sslc->key_passwd); +#ifdef USE_TLS_SRP + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); +#endif + sslc->deep_copy = FALSE; + } +} + +static bool match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) +{ + (void)data; + if((c1->version == c2->version) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + blobcmp(c1->key_blob, c2->key_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && +#ifdef USE_TLS_SRP + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && +#endif + curl_strequal(c1->cipher_list, c2->cipher_list) && + curl_strequal(c1->cipher_list13, c2->cipher_list13) && + curl_strequal(c1->curves, c2->curves) && + curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && + Curl_safecmp(c1->CRLfile, c2->CRLfile) && + Curl_safecmp(c1->pinned_key, c2->pinned_key) && + curl_strequal(c1->cert_type, c2->cert_type) && + Curl_safecmp(c1->key, c2->key) && + curl_strequal(c1->key_type, c2->key_type) && + !Curl_timestrcmp(c1->key_passwd, c2->key_passwd)) + return TRUE; + + return FALSE; +} + +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) +{ + DEBUGASSERT(!dest->deep_copy); + dest->deep_copy = TRUE; + dest->version = source->version; + dest->version_max = source->version_max; + dest->verifypeer = source->verifypeer; + dest->verifyhost = source->verifyhost; + dest->verifystatus = source->verifystatus; + dest->cache_session = source->cache_session; + dest->ssl_options = source->ssl_options; + + CLONE_BLOB(cert_blob); + CLONE_BLOB(ca_info_blob); + CLONE_BLOB(issuercert_blob); + CLONE_STRING(CApath); + CLONE_STRING(CAfile); + CLONE_STRING(issuercert); + CLONE_STRING(cipher_list); + CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); + CLONE_STRING(curves); + CLONE_STRING(signature_algorithms); + CLONE_STRING(CRLfile); + /* SSL credentials: client certificate, SRP auth */ + CLONE_STRING(clientcert); + CLONE_STRING(cert_type); + CLONE_STRING(key); + CLONE_STRING(key_type); + CLONE_STRING(key_passwd); + CLONE_BLOB(key_blob); +#ifdef USE_TLS_SRP + CLONE_STRING(username); + CLONE_STRING(password); +#endif + + return TRUE; +} + +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin) +{ + struct ssl_config_data *sslc = &data->set.ssl; +#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) + struct UserDefined *set = &data->set; + CURLcode result; +#endif + + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) + if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) + sslc->native_ca_store = TRUE; +#endif +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; + sslc->primary.signature_algorithms = + data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; + /* Maybe these should not be used for another origin. But for + * backwards compatibility, keep them in. */ + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + + if(Curl_peer_equal(data->state.initial_origin, origin)) { + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; + sslc->primary.key = data->set.str[STRING_KEY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; + sslc->primary.clientcert = data->set.str[STRING_CERT]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + } + else { + sslc->primary.pinned_key = NULL; + sslc->primary.cert_blob = NULL; + sslc->primary.cert_type = NULL; + sslc->primary.key = NULL; + sslc->primary.key_type = NULL; + sslc->primary.key_passwd = NULL; + sslc->primary.clientcert = NULL; + sslc->primary.key_blob = NULL; +#ifdef USE_TLS_SRP + sslc->primary.username = NULL; + sslc->primary.password = NULL; +#endif + } + +#ifndef CURL_DISABLE_PROXY + sslc = &data->set.proxy_ssl; + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) + if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) + sslc->native_ca_store = TRUE; +#endif +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], + CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], + CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + sslc->primary.key = data->set.str[STRING_KEY_PROXY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; +#endif +#endif /* CURL_DISABLE_PROXY */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the easy handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_ssl_config_cleanup(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} diff --git a/lib/vtls/vtls_config.h b/lib/vtls/vtls_config.h new file mode 100644 index 000000000000..44e691dd267e --- /dev/null +++ b/lib/vtls/vtls_config.h @@ -0,0 +1,125 @@ +#ifndef HEADER_CURL_VTLS_CONFIG_H +#define HEADER_CURL_VTLS_CONFIG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct Curl_peer; + +struct ssl_primary_config { + char *CApath; /* certificate directory (does not work on Windows) */ + char *CAfile; /* certificate to verify peer against */ + char *issuercert; /* optional issuer certificate filename */ + char *clientcert; + char *cipher_list; /* list of ciphers to use */ + char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ + char *signature_algorithms; /* list of signature algorithms to use */ + char *pinned_key; + char *CRLfile; /* CRL to check certificate revocation */ + char *cert_type; /* format for certificate (default: PEM) */ + char *key; /* private key filename */ + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ + struct curl_blob *cert_blob; + struct curl_blob *ca_info_blob; + struct curl_blob *issuercert_blob; + struct curl_blob *key_blob; +#ifdef USE_TLS_SRP + char *username; /* TLS username (for, e.g., SRP) */ + char *password; /* TLS password (for, e.g., SRP) */ +#endif + char *curves; /* list of curves to use */ + uint32_t version_max; /* max supported version the client wants to use */ + uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + uint8_t version; /* what version the client wants to use */ + BIT(verifypeer); /* set TRUE if this is desired */ + BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ + BIT(verifystatus); /* set TRUE if certificate status must be checked */ + BIT(cache_session); /* cache session or not */ + BIT(deep_copy); /* members are deep copies, eg. owned here */ +}; + +struct ssl_config_data { + struct ssl_primary_config primary; + long certverifyresult; /* result from the certificate verification */ + curl_ssl_ctx_callback fsslctx; /* function to initialize SSL ctx */ + void *fsslctxp; /* parameter for call back */ + BIT(certinfo); /* gather lots of certificate info */ + BIT(earlydata); /* use TLS 1.3 early data */ + BIT(enable_beast); /* allow this flaw for interoperability's sake */ + BIT(no_revoke); /* disable SSL certificate revocation checks */ + BIT(no_partialchain); /* do not accept partial certificate chains */ + BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation + list errors */ + BIT(native_ca_store); /* use the native CA store of operating system */ + BIT(auto_client_cert); /* automatically locate and use a client + certificate for authentication (Schannel) */ + BIT(custom_cafile); /* application has set custom CA file */ + BIT(custom_capath); /* application has set custom CA path */ + BIT(custom_cablob); /* application has set custom CA blob */ +}; + +struct ssl_general_config { + int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ +}; + +void Curl_ssl_config_init(struct ssl_primary_config *sslc); +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `data` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * been changed on the easy handle. Works for `verifypeer`, + * `verifyhost` and `verifystatus`. */ +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); + +#endif /* HEADER_CURL_VTLS_CONFIG_H */ diff --git a/tests/unit/unit3303.c b/tests/unit/unit3303.c index e979cbec8d03..56f52ebf5730 100644 --- a/tests/unit/unit3303.c +++ b/tests/unit/unit3303.c @@ -41,6 +41,8 @@ static CURLcode test_unit3303(const char *arg) static char alt_key[] = "other.key"; static char alt_ktype[] = "DER"; static char alt_ctype[] = "P12"; + struct Curl_peer *origin = NULL; + CURLcode result; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); @@ -49,13 +51,24 @@ static CURLcode test_unit3303(const char *arg) goto unit_test_abort; } + result = Curl_peer_create((struct Curl_easy *)curl, + &Curl_scheme_https, + "test.curl.se", 1234, &origin); + if(result) { + curl_easy_cleanup(curl); + curl_global_cleanup(); + goto unit_test_abort; + } + Curl_peer_link(&((struct Curl_easy *)curl)->state.initial_origin, origin); + curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret"); curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); - if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) { + if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl, origin)) { + Curl_peer_unlink(&origin); curl_easy_cleanup(curl); curl_global_cleanup(); goto unit_test_abort; @@ -66,6 +79,7 @@ static CURLcode test_unit3303(const char *arg) if(conn) Curl_ssl_conn_config_cleanup(conn); curlx_free(conn); + Curl_peer_unlink(&origin); curl_easy_cleanup(curl); curl_global_cleanup(); goto unit_test_abort; @@ -118,6 +132,7 @@ static CURLcode test_unit3303(const char *arg) Curl_ssl_conn_config_cleanup(conn); curlx_free(conn); curl_easy_cleanup(curl); + Curl_peer_unlink(&origin); curl_global_cleanup(); #endif /* USE_SSL */ From 872c313d7647ac437c83128e6bea7a8ed16cd2ef Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2026 08:55:37 +0200 Subject: [PATCH 0266/1062] lib1560: drop unused variable 'url' Spotted by Copilot Closes #21821 --- tests/libtest/lib1560.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index a69511d492e7..35ea5c4195e9 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1611,7 +1611,6 @@ static int setget_parts(bool has_utf8) else rc = CURLUE_OK; if(!rc) { - char *url = NULL; CURLUcode uc = updateurl(urlp, setget_parts_list[i].set, setget_parts_list[i].setflags); @@ -1629,7 +1628,6 @@ static int setget_parts(bool has_utf8) setget_parts_list[i].getflags)) error++; /* add */ } - curl_free(url); } else if(rc != CURLUE_OK) { curl_mfprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n", From d806323ffd5142e4c6d7a5b88f2fd496742730a4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 30 May 2026 10:53:21 +0200 Subject: [PATCH 0267/1062] pytest: fixes and tidy-ups to h3-proxy tests - merge tests into a single class. For shorter names, to fix sort order by test number, and to align with other tests. - fix preconditions to make `test_60_04_guard_proxy_http3_unsupported` actually run. - replace local precondition with constant of the same effect. - drop redundant non-`ngtcp2` requirement for `test_60_04_guard_proxy_http3_unsupported`. (seemed relevant for no longer supported openssl-quic builds.) - drop unused `NGTCP2_ONLY_MSG` constant. Follow-up to e4139a73c82d2035142f5ae36196adb4e9831dae #21798 - avoid creating unnecessary test data blobs, and minimize their scopes. Follow-up to 91facd7bb3bb366525b7cb41221f6359c5e936db #21791 Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21811 --- tests/http/test_60_h3_proxy.py | 157 ++++++++++++--------------------- 1 file changed, 56 insertions(+), 101 deletions(-) diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 4d1998165049..7ebba8a2b19d 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -55,14 +55,6 @@ condition=not Env.have_nghttpx(), reason="no nghttpx available" ) -H3_PROXY_COMMON_MARKS = [ - MARK_NEEDS_HTTPS_PROXY, - MARK_NEEDS_HTTP3, - MARK_NEEDS_PROXY_HTTP3, - MARK_NEEDS_NGHTTP3, -] - -NGTCP2_ONLY_MSG = "only supported with the ngtcp2 quic stack" UNSUPPORTED_OPT_MSG = "does not support this" H2O_HELLO_MSG = '"message": "Hello from h2o HTTP/3 server"' @@ -145,11 +137,15 @@ def _h2o_proxy_args( return xargs -class TestH3ProxySuccess: - """Success matrix for HTTP/3 proxy CONNECT / CONNECT-UDP.""" +@MARK_NEEDS_HTTPS_PROXY +@MARK_NEEDS_HTTP3 +@MARK_NEEDS_NGHTTP3 +class TestH3Proxy: - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] + # Success matrix for HTTP/3 proxy CONNECT / CONNECT-UDP. + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O @pytest.mark.parametrize( ["alpn_proto", "proxy_proto"], [ @@ -192,12 +188,10 @@ def test_60_01_connect_tunnel( r.check_response(count=1, http_status=200) _check_download_message(curl, H2O_HELLO_MSG) + # Failure matrix when proxy side does not support requested mode. -class TestH3ProxyFailure: - """Failure matrix when proxy side does not support requested mode.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_NGHTTPX] - + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_NGHTTPX @pytest.mark.parametrize( ["alpn_proto", "proxy_proto", "exp_err"], [ @@ -260,12 +254,10 @@ def test_60_02_connect_tunnel_fail( f"Expected protocol/proxy error but got: {r.dump_logs()}" ) + # Behavior checks for tunnel vs non-tunnel proxy mode selection. -class TestH3ProxyModeSelection: - """Behavior checks for tunnel vs non-tunnel proxy mode selection.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_NGHTTPX] - + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_NGHTTPX @pytest.mark.parametrize( ["proxy_proto"], [ @@ -301,22 +293,8 @@ def test_60_03_h3_target_auto_connect_udp( f"expected CONNECT-UDP attempt in output, got: {r.dump_logs()}" ) - -class TestH3ProxyRuntimeGuards: - """Guard checks for unsupported HTTP/3 proxy options.""" - - pytestmark = [ - MARK_NEEDS_HTTPS_PROXY, - MARK_NEEDS_PROXY_HTTP3, - pytest.mark.skipif( - condition=Env.curl_uses_lib("ngtcp2"), - reason="guard only applies to non-ngtcp2 builds", - ), - ] + # Guard checks for unsupported HTTP/3 proxy options. - @pytest.mark.skipif( - condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" - ) @pytest.mark.skipif( condition=Env.curl_has_feature("proxy-HTTP3"), reason="curl has h3 proxy support" ) @@ -340,23 +318,11 @@ def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): f"Expected unsupported option failure but got: {r.stderr}" ) + # Robustness checks for shutdown and proxy loss during transfer. -class TestH3ProxyRobustness: - """Robustness checks for shutdown and proxy loss during transfer.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - - @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env, h2o_server): - if not env.have_h2o(): - pytest.skip("h2o not available") - env.make_data_file( - indir=h2o_server.docs_dir, fname="proxy-drop-20m", fsize=20 * 1024 * 1024 - ) - - def test_60_05_graceful_shutdown( - self, env: Env, h2o_server, h2o_proxy - ): + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_05_graceful_shutdown(self, env: Env, h2o_server, h2o_proxy): if not env.curl_is_debug(): pytest.skip("needs debug curl for shutdown trace lines") if not env.curl_is_verbose(): @@ -380,9 +346,12 @@ def test_60_05_graceful_shutdown( ] assert shutdown_lines, f"No shutdown trace lines found:\n{r.stderr}" + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname="proxy-drop-20m", fsize=20 * 1024 * 1024) proxy_port = h2o_proxy.port url = f"https://localhost:{h2o_server.port}/proxy-drop-20m" out_path = os.path.join(env.gen_dir, "proxy-drop.out") @@ -423,22 +392,13 @@ def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): proc.wait(timeout=5) assert h2o_proxy.start(), "failed to restart h2o proxy" + # Large file transfers and multiplexing through HTTP/3 proxy. -class TestH3ProxyDataTransfer: - """Large file transfers and multiplexing through HTTP/3 proxy.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - - @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env, h2o_server): - if not env.have_h2o(): - pytest.skip("h2o not available") - env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) - env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) - env.make_data_file(indir=env.gen_dir, fname="upload-2m", fsize=2 * 1024 * 1024) - + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/download-10m" proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) @@ -448,8 +408,11 @@ def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): r.check_response(count=1, http_status=200) _check_download_size(curl, 10 * 1024 * 1024) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_08_large_upload(self, env: Env, httpd, h2o_server, h2o_proxy): _require_available(h2o_proxy=h2o_proxy) + env.make_data_file(indir=env.gen_dir, fname="upload-2m", fsize=2 * 1024 * 1024) fdata = os.path.join(env.gen_dir, "upload-2m") curl = CurlClient(env=env) url = f"https://localhost:{httpd.ports['https']}/curltest/echo?id=[0-0]" @@ -463,8 +426,11 @@ def test_60_08_large_upload(self, env: Env, httpd, h2o_server, h2o_proxy): ) r.check_response(count=1, http_status=200) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_09_parallel_downloads(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) count = 5 curl = CurlClient(env=env) urln = f"https://localhost:{h2o_server.port}/download-1m?[0-{count - 1}]" @@ -475,12 +441,8 @@ def test_60_09_parallel_downloads(self, env: Env, h2o_server, h2o_proxy): ) r.check_response(count=count, http_status=200) - -class TestH3ProxyConnectionManagement: - """Proxy authentication, connection reuse, and session resumption.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_10_proxy_basic_auth(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) @@ -493,6 +455,8 @@ def test_60_10_proxy_basic_auth(self, env: Env, h2o_server, h2o_proxy): r.check_response(count=1, http_status=200) _check_download_message(curl, H2O_HELLO_MSG) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) curl = CurlClient(env=env) @@ -506,6 +470,8 @@ def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): f"expected proxy connection reuse, got {r.total_connects} connects" ) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O @pytest.mark.skipif(condition=not Env.curl_has_feature('SSLS-EXPORT'), reason='curl lacks SSL session export support') def test_60_12_quic_session_resumption(self, env: Env, h2o_server, h2o_proxy): @@ -530,20 +496,9 @@ def test_60_12_quic_session_resumption(self, env: Env, h2o_server, h2o_proxy): reuses = [line for line in r2.trace_lines if '[SSLS] took session for proxy.http.curl.se' in line] assert len(reuses), f'{r2.dump_logs()}' + # CONNECT-UDP tunnel payload size and capsule-protocol tests. -class TestH3ProxyUdpTunnel: - """CONNECT-UDP tunnel payload size and capsule-protocol tests.""" - - pytestmark = H3_PROXY_COMMON_MARKS - - @pytest.fixture(autouse=True, scope="class") - def _class_scope(self, env, h2o_server): - if not env.have_h2o(): - return - env.make_data_file(indir=h2o_server.docs_dir, fname="download-1400", fsize=1400) - env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) - env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) - + @MARK_NEEDS_PROXY_HTTP3 @MARK_NEEDS_H2O @pytest.mark.parametrize( "fname,fsize", @@ -557,6 +512,7 @@ def test_60_13_udp_tunnel_payload_sizes( self, env: Env, h2o_server, h2o_proxy, fname, fsize ): _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname=fname, fsize=fsize) curl = CurlClient(env=env) url = f"https://localhost:{h2o_server.port}/{fname}" proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) @@ -566,6 +522,7 @@ def test_60_13_udp_tunnel_payload_sizes( r.check_response(count=1, http_status=200) _check_download_size(curl, fsize) + @MARK_NEEDS_PROXY_HTTP3 @MARK_NEEDS_NGHTTPX def test_60_14_udp_tunnel_capsule_absent( self, env: Env, httpd, nghttpx, nghttpx_fwd @@ -585,12 +542,10 @@ def test_60_14_udp_tunnel_capsule_absent( "expected failure: nghttpx does not support CONNECT-UDP / Capsule-Protocol" ) + # Timeout and protocol-mismatch edge cases. -class TestH3ProxyEdgeCases: - """Timeout and protocol-mismatch edge cases.""" - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - + #@MARK_NEEDS_PROXY_HTTP3 + #@MARK_NEEDS_H2O #def test_60_15_connect_timeout(self, env: Env, h2o_proxy): # _require_available(h2o_proxy=h2o_proxy) # curl = CurlClient(env=env, timeout=15) @@ -610,6 +565,8 @@ class TestH3ProxyEdgeCases: # f"timeout not respected: took {r.duration.total_seconds():.1f}s" # ) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O @MARK_NEEDS_NGHTTP2 def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): _require_available(httpd=httpd, h2o_proxy=h2o_proxy) @@ -624,18 +581,14 @@ def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): ) r.check_response(count=1, http_status=200) + # Verify that happy eyeballs is active for HTTP/3 proxy connections. + # + # With the H3-PROXY filter sitting above HAPPY-EYEBALLS -> UDP, address + # family selection to the proxy is done by happy eyeballs. -class TestH3ProxyHappyEyeballs: - """ - Verify that happy eyeballs is active for HTTP/3 proxy connections. - - With the H3-PROXY filter sitting above HAPPY-EYEBALLS -> UDP, address - family selection to the proxy is done by happy eyeballs. - """ - - pytestmark = H3_PROXY_COMMON_MARKS + [MARK_NEEDS_H2O] - - def test_60_17_h3_proxy_happy_eyeballs_filter_present(self, env: Env, h2o_server, h2o_proxy): + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_17_happy_eyeballs_filter_present(self, env: Env, h2o_server, h2o_proxy): """Verbose trace confirms HAPPY-EYEBALLS filter is in the H3 proxy chain.""" if not env.curl_is_debug(): pytest.skip("needs debug curl for filter trace") @@ -651,8 +604,10 @@ def test_60_17_h3_proxy_happy_eyeballs_filter_present(self, env: Env, h2o_server f"expected HAPPY-EYEBALLS trace for H3 proxy, got: {r.stderr}" ) + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O @MARK_NEEDS_NGHTTP2 - def test_60_18_h3_proxy_ipv4_all_proto(self, env: Env, h2o_server, h2o_proxy): + def test_60_18_happy_eyeballs_ipv4_all_proto(self, env: Env, h2o_server, h2o_proxy): """IPv4-forced H3 proxy works for h1/h2/h3 inner protocols.""" _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) for alpn_proto in ["http/1.1", "h2", "h3"]: From 2802e65f6dcf1c59776fc191d158f3376a22da5c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 31 May 2026 18:31:17 +0200 Subject: [PATCH 0268/1062] pytest: pass `--disable` to curl To avoid a local `.curlrc` interfering with tests. Closes #21816 --- tests/http/testenv/curl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 5149a8578d7d..f5be5ac4495c 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -1127,7 +1127,7 @@ def _complete_args(self, urls, timeout=None, options=None, else: force_resolve = self._force_resolv - args = [self._curl, "-s", "--path-as-is"] + args = [self._curl, "--disable", "-s", "--path-as-is"] if 'CURL_TEST_EVENT' in os.environ: args.append('--test-event') From 5364e6e60ee1cd42a7bf0bbf014b01ffe1cf803a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 31 May 2026 20:44:57 +0200 Subject: [PATCH 0269/1062] cmake: add basic way to select pytests to run Not documented and experimental, example: `-D_CURL_PYTEST=/test_60_h3_proxy.py` Ideally, this should be an env like `TFLAGS` and it should allow selecting any test ID or a group of them, but so far could not figure out how even a basic env could work. Closes #21818 --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e75ca4e14ec9..6de7f96d0a26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -110,7 +110,7 @@ function(curl_add_pytests _targetname _test_flags) endif() string(REPLACE " " ";" _test_flags_list "${_test_flags}") add_custom_target(${_targetname} - COMMAND pytest ${_test_flags_list} "${CMAKE_CURRENT_SOURCE_DIR}/http" + COMMAND pytest ${_test_flags_list} "${CMAKE_CURRENT_SOURCE_DIR}/http${_CURL_PYTEST}" DEPENDS "${_depends}" VERBATIM USES_TERMINAL ) From 6ff5c8ac4a7e195fada7297906c72bc45c3cac4f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 31 May 2026 22:47:54 +0200 Subject: [PATCH 0270/1062] badwords: exclude wordlist input file from search To avoid hitting all lines in it. It doesn't happen in curl at the moment, but may happen in the future or in other projects using this script. Closes #21819 --- scripts/badwords | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/badwords b/scripts/badwords index 09676bacd906..f833573af7c8 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -318,7 +318,7 @@ sub file { } my @filemasks = @ARGV; -open(my $git_ls_files, '-|', 'git', 'ls-files', '--', @filemasks) or die "Failed running git ls-files: $!"; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--', ":!:$file", @filemasks) or die "Failed running git ls-files: $!"; my @files; while(my $each = <$git_ls_files>) { chomp $each; From ff300ac4aa546748b27415e7fd2527315435ef8e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2026 13:31:11 +0200 Subject: [PATCH 0271/1062] setopt: defref the old referer when setting a new Closes #21826 --- lib/setopt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/setopt.c b/lib/setopt.c index e67a3c8beb64..07fa60be7e26 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2024,6 +2024,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * String to set in the HTTP Referer: field. */ + Curl_bufref_free(&data->state.referer); result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); break; From 12869080a1ded242e512bc764031949539241619 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 15:01:09 +0200 Subject: [PATCH 0272/1062] data creds: detect change Reshuffle code a little to detect when the transfer's credentials actually change. Otherwise, leave the existing creds in place. This gives the precise location where we may want to reset other states that become invalid with change credentials. Also, by keeping a creds instance as long as it is valid, we can associate meta data with it. Closes #21755 --- lib/creds.c | 6 + lib/creds.h | 5 +- lib/url.c | 314 ++++++++++++++++++++++++++-------------------------- 3 files changed, 166 insertions(+), 159 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index 779ae2f13167..a11816ca548d 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -160,6 +160,12 @@ bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) !Curl_timestrcmp(c1->sasl_service, c2->sasl_service)); } +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return Curl_creds_same(c1, c2) && + ((c1 == c2) || (c1 && c2 && (c1->source == c2->source))); +} + #ifdef CURLVERBOSE void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, const char *msg) diff --git a/lib/creds.h b/lib/creds.h index fc978285dbb6..36deff323e96 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -64,9 +64,12 @@ void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src); /* Drop a reference, creds may be passed as NULL */ void Curl_creds_unlink(struct Curl_creds **pcreds); -/* TRUE if both creds are NULL or have same username and password. */ +/* TRUE if both creds are NULL or have same values, except source. */ bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); +/* TRUE if both creds are NULL or have all values equal. */ +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2); + /* Provides properties for creds or, if creds is NULL, the empty string */ #define Curl_creds_has_user(c) ((c) && (c)->user[0]) #define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) diff --git a/lib/url.c b/lib/url.c index 76a8c2e3e7dc..aa151eda7e15 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1422,15 +1422,131 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, #define hsts_upgrade(x, y, z, a, b) CURLE_OK #endif +#ifndef CURL_DISABLE_NETRC +static bool str_has_ctrl(const char *input) +{ + if(input) { + const unsigned char *str = (const unsigned char *)input; + while(*str) { + if(*str < 0x20) + return TRUE; + str++; + } + } + return FALSE; +} + +/* + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. + */ +static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, + struct connectdata *conn, + struct Curl_creds **pcreds) +{ + struct Curl_creds *ncreds_out = NULL; + CURLcode result = CURLE_OK; + + if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ + struct Curl_creds *ncreds_in = NULL; + bool scan_netrc = TRUE; + NETRCcode ret; + CURLUcode uc; + + if(*pcreds) { + switch((*pcreds)->source) { + case CREDS_OPTION: + /* we never override credentials set via CURLOPT_*, leave. */ + scan_netrc = FALSE; + break; + case CREDS_URL: /* only apply when netrc is not required */ + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + /* We ignore password from URL */ + ncreds_in = *pcreds; + } + else if(!Curl_creds_has_user(*pcreds) || + !Curl_creds_has_passwd(*pcreds)) { + /* We use netrc to complete what is missing */ + ncreds_in = *pcreds; + } + else + scan_netrc = FALSE; + break; + default: /* ignore credentials from other sources */ + break; + } + } + + if(!scan_netrc) + goto out; + + ret = Curl_netrc_scan(data, &data->state.netrc, + conn->origin->hostname, + Curl_creds_user(ncreds_in), + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else if(ret && ((ret == NETRC_NO_MATCH) || + (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { + infof(data, "Could not find host %s in the %s file; using defaults", + conn->origin->hostname, + (data->set.str[STRING_NETRC_FILE] ? + data->set.str[STRING_NETRC_FILE] : ".netrc")); + } + else if(ret) { + const char *m = Curl_netrc_strerror(ret); + failf(data, ".netrc error: %s", m); + result = CURLE_READ_ERROR; + goto out; + } + else if(ncreds_out) { + if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { + /* if the protocol cannot handle control codes in credentials, make + sure there are none */ + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { + failf(data, "control code detected in .netrc credentials"); + result = CURLE_READ_ERROR; + goto out; + } + } + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + conn->origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + *pcreds, CREDS_NETRC, pcreds); + if(result) + goto out; + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(*pcreds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(*pcreds), + CURLU_URLENCODE); + if(uc) + result = Curl_uc_to_curlcode(uc); + } + else + DEBUGASSERT(0); + } + +out: + Curl_creds_unlink(&ncreds_out); + return result; +} +#endif /* CURL_DISABLE_NETRC */ + static CURLcode url_set_data_creds(struct Curl_easy *data, struct connectdata *conn, CURLU *uh) { + struct Curl_creds *newcreds = NULL; CURLcode result = CURLE_OK; - /* We reset any existing credentials on the transfer. Then - * set the CURLOPT_* credentials ONLY IF the origin is the initial one. */ - Curl_creds_unlink(&data->state.creds); if((data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD] || data->set.str[STRING_BEARER] || @@ -1442,29 +1558,27 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, data->set.str[STRING_BEARER], data->set.str[STRING_SASL_AUTHZID], data->set.str[STRING_SERVICE_NAME], - CREDS_OPTION, &data->state.creds); + CREDS_OPTION, &newcreds); if(result) - return result; + goto out; } /* Extract credentials from the URL only if there are none OR * if no CURLOPT_USER was set. */ - if(!data->state.creds || !Curl_creds_has_user(data->state.creds)) { + if(!newcreds || !Curl_creds_has_user(newcreds)) { char *udecoded = NULL; char *pdecoded = NULL; CURLUcode uc; uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(uc && (uc != CURLUE_NO_USER)) { - result = Curl_uc_to_curlcode(uc); - goto out; - } - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(uc && (uc != CURLUE_NO_PASSWORD)) { + if(uc && (uc != CURLUE_NO_USER)) result = Curl_uc_to_curlcode(uc); - goto out; + if(!result) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(uc && (uc != CURLUE_NO_PASSWORD)) + result = Curl_uc_to_curlcode(uc); } - if(data->state.up.user) { + if(!result && data->state.up.user) { result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, conn->scheme->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); @@ -1475,14 +1589,29 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, REJECT_ZERO : REJECT_CTRL); } if(!result) - result = Curl_creds_merge(udecoded, pdecoded, data->state.creds, - CREDS_URL, &data->state.creds); -out: + result = Curl_creds_merge(udecoded, pdecoded, newcreds, + CREDS_URL, &newcreds); + curlx_free(udecoded); curlx_free(pdecoded); - if(result) + if(result) { failf(data, "error extracting credentials from URL"); + goto out; + } + } + +#ifndef CURL_DISABLE_NETRC + /* Check for overridden login details and set them accordingly so that + they are known when protocol->setup_connection is called! */ + result = url_set_data_creds_netrc(data, conn, &newcreds); +#endif /* CURL_DISABLE_NETRC */ + +out: + if(!result && !Curl_creds_equal(data->state.creds, newcreds)) { + /* Do we have more things to trigger on credentials change? */ + Curl_creds_link(&data->state.creds, newcreds); } + Curl_creds_unlink(&newcreds); return result; } @@ -1608,6 +1737,14 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, /* Fill in the conn parts that do not use authority, yet. */ conn->scope_id = conn->origin->scopeid; #endif + if(data->set.str[STRING_OPTIONS]) { + curlx_free(conn->options); + conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]); + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } #ifdef CURLVERBOSE Curl_creds_trace(data, data->state.creds, "transfer credentials"); @@ -2163,140 +2300,6 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, return CURLE_OUT_OF_MEMORY; } -#ifndef CURL_DISABLE_NETRC -static bool str_has_ctrl(const char *input) -{ - if(input) { - const unsigned char *str = (const unsigned char *)input; - while(*str) { - if(*str < 0x20) - return TRUE; - str++; - } - } - return FALSE; -} -#endif - -/* - * Override the login details from the URL with that in the CURLOPT_USERPWD - * option or a .netrc file, if applicable. - */ -static CURLcode override_login(struct Curl_easy *data, - struct connectdata *conn) -{ - char **optionsp = &conn->options; -#ifndef CURL_DISABLE_NETRC - struct Curl_creds *ncreds_out = NULL; -#endif - CURLcode result = CURLE_OK; - - if(data->set.str[STRING_OPTIONS]) { - curlx_free(*optionsp); - *optionsp = curlx_strdup(data->set.str[STRING_OPTIONS]); - if(!*optionsp) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - -#ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ - struct Curl_creds *ncreds_in = NULL; - bool scan_netrc = TRUE; - NETRCcode ret; - CURLUcode uc; - - if(data->state.creds) { - switch(data->state.creds->source) { - case CREDS_OPTION: - /* we never override credentials set via CURLOPT_*, leave. */ - scan_netrc = FALSE; - break; - case CREDS_URL: /* only apply when netrc is not required */ - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - /* We ignore password from URL */ - ncreds_in = data->state.creds; - } - else if(!Curl_creds_has_user(data->state.creds) || - !Curl_creds_has_passwd(data->state.creds)) { - /* We use netrc to complete what is missing */ - ncreds_in = data->state.creds; - } - else - scan_netrc = FALSE; - break; - default: /* ignore credentials from other sources */ - break; - } - } - - if(!scan_netrc) - goto out; - - ret = Curl_netrc_scan(data, &data->state.netrc, - conn->origin->hostname, - Curl_creds_user(ncreds_in), - data->set.str[STRING_NETRC_FILE], - &ncreds_out); - DEBUGASSERT(!ret || !ncreds_out); - if(ret == NETRC_OUT_OF_MEMORY) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - else if(ret && ((ret == NETRC_NO_MATCH) || - (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { - infof(data, "Could not find host %s in the %s file; using defaults", - conn->origin->hostname, - (data->set.str[STRING_NETRC_FILE] ? - data->set.str[STRING_NETRC_FILE] : ".netrc")); - } - else if(ret) { - const char *m = Curl_netrc_strerror(ret); - failf(data, ".netrc error: %s", m); - result = CURLE_READ_ERROR; - goto out; - } - else if(ncreds_out) { - if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { - /* if the protocol cannot handle control codes in credentials, make - sure there are none */ - if(str_has_ctrl(ncreds_out->user) || - str_has_ctrl(ncreds_out->passwd)) { - failf(data, "control code detected in .netrc credentials"); - result = CURLE_READ_ERROR; - goto out; - } - } - CURL_TRC_M(data, "netrc: using credentials for %s as %s", - conn->origin->hostname, ncreds_out->user); - result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, - data->state.creds, CREDS_NETRC, - &data->state.creds); - if(result) - goto out; - /* for updated strings, we update them in the URL */ - uc = curl_url_set(data->state.uh, CURLUPART_USER, - Curl_creds_user(data->state.creds), CURLU_URLENCODE); - if(!uc) - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - Curl_creds_passwd(data->state.creds), - CURLU_URLENCODE); - if(uc) - result = Curl_uc_to_curlcode(uc); - } - else - DEBUGASSERT(0); - } -#endif - -out: -#ifndef CURL_DISABLE_NETRC - Curl_creds_unlink(&ncreds_out); -#endif - return result; -} - /* * Set the login details so they are available in the connection */ @@ -2608,6 +2611,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, result = parseurlandfillconn(data, needle); if(result) goto out; + DEBUGASSERT(needle->origin); network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK); @@ -2657,12 +2661,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, } #endif /* CURL_DISABLE_PROXY */ - /* Check for overridden login details and set them accordingly so that - they are known when protocol->setup_connection is called! */ - result = override_login(data, needle); - if(result) - goto out; - result = url_set_conn_login(data, needle); /* default credentials */ if(result) goto out; From c53426231daf6acf5fcc4292fbff024082fd9934 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2026 16:01:17 +0200 Subject: [PATCH 0273/1062] setopt: CURLOPT_MAXCONNECTS set to 0 restores default value Closes #21829 --- lib/setopt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 07fa60be7e26..d4c1ca69a42b 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -847,9 +847,9 @@ static CURLcode setopt_long_net(struct Curl_easy *data, CURLoption option, s->dns_cache_timeout_ms = -1; break; case CURLOPT_MAXCONNECTS: - result = value_range(&arg, 1, 1, INT_MAX); + result = value_range(&arg, 0, 0, INT_MAX); if(!result) - s->maxconnects = (uint32_t)arg; + s->maxconnects = arg ? (uint32_t)arg : DEFAULT_CONNCACHE_SIZE; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: return setopt_set_timeout_sec(&s->server_response_timeout, arg); From 032b15c4342e8eddb51fbe089a9d8ba3ee2070fb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 1 Jun 2026 14:23:30 +0200 Subject: [PATCH 0274/1062] cf-setup: improve readability Restructure the code in cf-setup connect to make it better readable what is happening for establishing the connection's filter chain. Closes #21827 --- lib/cf-h3-proxy.c | 11 -- lib/connect.c | 307 +++++++++++++++++++++------------ lib/http_proxy.c | 16 +- tests/http/test_60_h3_proxy.py | 28 +-- 4 files changed, 211 insertions(+), 151 deletions(-) diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index a78561538846..4fdaef47d0cf 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -3449,17 +3449,6 @@ CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, cf->next->conn = cf->conn; cf->next->sockindex = cf->sockindex; - if(ctx->udp_tunnel) { - struct Curl_cfilter *cf_caps = NULL; - result = Curl_cf_capsule_create(&cf_caps, data, conn); - if(result) - goto out; - cf_caps->conn = conn; - cf_caps->sockindex = cf->sockindex; - cf_caps->next = cf; - cf = cf_caps; - } - out: *pcf = (!result) ? cf : NULL; if(result) { diff --git a/lib/connect.c b/lib/connect.c index 0ed7b22a5b48..9d74e35bc76e 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -55,6 +55,7 @@ #include "cfilters.h" #include "connect.h" #include "cf-dns.h" +#include "cf-capsule.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -342,174 +343,256 @@ struct cf_setup_ctx { uint8_t transport; }; -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +#ifndef CURL_DISABLE_PROXY + +static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct Curl_peer *first_peer = - Curl_conn_get_first_peer(cf->conn, cf->sockindex); - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - /* connect current sub-chain */ -connect_sub_chain: - VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); - - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - /* What type of thing we do connect to first? - * - without a proxy, `ctx->transport` defines it - * - with non-tunneling proxy, `ctx->transport` also applies, but - * for QUIC we need the cf-h3-proxy, not the standard vquic one - * - with tunneling proxy, transport is defined by the proxytype - * chosen and `ctx->transport` is tunneled through it. - */ - uint8_t transport_out = ctx->transport; - bool tunnel_proxy = FALSE; -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - CURL_TRC_CF(data, cf, "happy eyeballing, httpproxy=%d, type=%d, " - "transport=%d", - cf->conn->bits.httpproxy, cf->conn->http_proxy.proxytype, - ctx->transport); - if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { - transport_out = - Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); - tunnel_proxy = TRUE; - if((transport_out == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { - failf(data, "HTTP/3 proxy not possible via SOCKS"); + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { + if(data->set.haproxyprotocol) { + if(ctx->transport == TRNSPRT_QUIC) { + failf(data, "haproxy protocol does not support QUIC"); return CURLE_UNSUPPORTED_PROTOCOL; } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", result); + return result; + } + CURL_TRC_CF(data, cf, "added HAPROXY filter"); } -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ - - result = cf_ip_happy_insert_after(cf, data, first_peer, - ctx->transport, transport_out, - tunnel_proxy); - if(result) - return result; - ctx->state = (tunnel_proxy && (transport_out == TRNSPRT_QUIC)) ? - CF_SETUP_CNNCT_HTTP_PROXY : CF_SETUP_CNNCT_EYEBALLS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; + ctx->state = CF_SETUP_CNNCT_HAPROXY; } + return result; +} - /* sub-chain connected, do we need to add more? */ -#ifndef CURL_DISABLE_PROXY +static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - struct Curl_peer *dest; /* where SOCKS should tunnel to */ + /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ + struct Curl_peer *second_peer; if(cf->conn->bits.httpproxy) - dest = cf->conn->http_proxy.peer; + second_peer = cf->conn->http_proxy.peer; else - dest = Curl_conn_get_destination(cf->conn, cf->sockindex); - if(!dest) + second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if(!second_peer) return CURLE_FAILED_INIT; result = Curl_cf_socks_proxy_insert_after( - cf, data, dest, cf->conn->ip_version, + cf, data, second_peer, cf->conn->ip_version, cf->conn->socks_proxy.proxytype, cf->conn->socks_proxy.creds); - if(result) { - /* 'dest' might be freed now so it can't be dereferenced */ - CURL_TRC_CF(data, cf, "added SOCKS filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", result); return result; } - CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", - dest->hostname, dest->port, result); + + CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", + second_peer->hostname, second_peer->port); ctx->state = CF_SETUP_CNNCT_SOCKS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } + return result; +} + +#ifndef CURL_DISABLE_HTTP +static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { #ifdef USE_SSL if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", + result); return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); } #endif /* USE_SSL */ -#ifndef CURL_DISABLE_HTTP if(cf->conn->bits.tunnel_proxy) { struct Curl_peer *dest; /* where HTTP should tunnel to */ dest = Curl_conn_get_destination(cf->conn, cf->sockindex); result = Curl_cf_http_proxy_insert_after( cf, data, dest, ctx->transport, cf->conn->http_proxy.proxytype); - if(result) + if(result) { + CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", + result); return result; + } + CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); } -#endif /* !CURL_DISABLE_HTTP */ ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } -#endif /* !CURL_DISABLE_PROXY */ + return result; +} +#endif /* !CURL_DISABLE_HTTP */ +#endif /* CURL_DISABLE_PROXY */ - if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { -#ifndef CURL_DISABLE_PROXY - if(data->set.haproxyprotocol) { - if(ctx->transport == TRNSPRT_QUIC) { - failf(data, "haproxy protocol not support QUIC"); +static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { + /* What is the fist hop we directly connect to and what transport + * do we use for it? Only on the first hop we can do Happy Eyeballs. */ + struct Curl_peer *first_peer = + Curl_conn_get_first_peer(cf->conn, cf->sockindex); + uint8_t first_transport = ctx->transport; + bool tunnel_proxy = FALSE; + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { + first_transport = + Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); + if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { + failf(data, "HTTP/3 proxy not possible via SOCKS"); return CURLE_UNSUPPORTED_PROTOCOL; } - result = Curl_cf_haproxy_insert_after(cf, data); - if(result) - return result; + tunnel_proxy = TRUE; + } +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + + result = cf_ip_happy_insert_after(cf, data, first_peer, + ctx->transport, first_transport, + tunnel_proxy); + if(result) { + CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", result); + return result; + } + + if(tunnel_proxy && (first_transport == TRNSPRT_QUIC)) { + CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + } + else { + CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u", + tunnel_proxy ? "proxy" : "origin", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_EYEBALLS; } -#endif /* !CURL_DISABLE_PROXY */ - ctx->state = CF_SETUP_CNNCT_HAPROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } + return result; +} - /* Adding Curl_cf_quic_insert_after() because now we - need the next filter to be QUIC/HTTP/3 (which has SSL) */ +static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)data; /* not used in all builds */ + if(ctx->state < CF_SETUP_CNNCT_SSL) { #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) - if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && - cf->conn->bits.tunnel_proxy && - (data->state.http_neg.wanted == CURL_HTTP_V3x)) { - if(ctx->state < CF_SETUP_CNNCT_SSL) { + !defined(CURL_DISABLE_PROXY) + /* Wanting QUIC with a HTTP tunneling filter, we now need to add + * the QUIC filter on top. Without tunneling, this has already + * happened in the Happy Eyeball filter. */ + if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && + cf->conn->bits.tunnel_proxy) { + result = Curl_cf_capsule_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", result); + return result; + } result = Curl_cf_quic_insert_after(cf); - if(result) + if(result) { + CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", result); return result; - ctx->state = CF_SETUP_CNNCT_SSL; + } + CURL_TRC_CF(data, cf, "added QUIC filter for origin"); } - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - else -#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ - { - if(ctx->state < CF_SETUP_CNNCT_SSL) { + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */ #ifdef USE_SSL - if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || - (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ - !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ - result = Curl_cf_ssl_insert_after(cf, data); - if(result) - return result; + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || + (ctx->ssl_mode != CURL_CF_SSL_DISABLE && + cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + result = Curl_cf_ssl_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", + result); + return result; } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; + CURL_TRC_CF(data, cf, "added SSL filter for origin"); } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; } + return result; +} + +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* connect current sub-chain */ +connect_sub_chain: + VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); + + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + result = cf_setup_add_ip_happy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_PROXY + result = cf_setup_add_socks(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_HTTP + result = cf_setup_add_http_proxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_HTTP */ + + result = cf_setup_add_haproxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_PROXY */ + + result = cf_setup_add_origin_filters(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; ctx->state = CF_SETUP_DONE; cf->connected = TRUE; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 39cfb1244681..9f1ed7963c5c 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -654,22 +654,8 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, } else { /* subchain connected and we had already installed the protocol filter. - * This means the protocol tunnel is established, we are done. - */ + * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->sub_filter_installed); - if(ctx->udp_tunnel) { -#ifdef USE_PROXY_HTTP3 - /* Insert capsule filter between us and the protocol sub-filter. - * This handles encap/decap of UDP datagrams in capsule format. */ - result = Curl_cf_capsule_insert_after(cf, data); - if(result) - goto out; - CURL_TRC_CF(data, cf, "installed capsule filter for UDP tunnel"); -#else - result = CURLE_NOT_BUILT_IN; - goto out; -#endif /* USE_PROXY_HTTP3 */ - } result = CURLE_OK; } diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 7ebba8a2b19d..39c6d265f6f1 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -195,12 +195,12 @@ def test_60_01_connect_tunnel( @pytest.mark.parametrize( ["alpn_proto", "proxy_proto", "exp_err"], [ - #pytest.param( - # "http/1.1", - # "h3", - # "could not connect to server", - # id="fail_h1_over_h3_proxytunnel", - #), + pytest.param( + "http/1.1", + "h3", + "could not connect to server", + id="fail_h1_over_h3_proxytunnel", + ), pytest.param( "h2", "h3", @@ -208,12 +208,12 @@ def test_60_01_connect_tunnel( marks=MARK_NEEDS_NGHTTP2, id="fail_h2_over_h3_proxytunnel", ), - #pytest.param( - # "h3", - # "h3", - # "could not connect to server", - # id="fail_h3_over_h3_proxytunnel", - #), + pytest.param( + "h3", + "h3", + "could not connect to server", + id="fail_h3_over_h3_proxytunnel", + ), #pytest.param( # "h3", # "h2", @@ -235,11 +235,13 @@ def test_60_02_connect_tunnel_fail( httpd, nghttpx, nghttpx_fwd, + h2o_proxy, alpn_proto, proxy_proto, exp_err, ): - _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd) + _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd, + h2o_proxy=h2o_proxy) curl = CurlClient(env=env) url = f"https://localhost:{env.https_port}/data.json" From d2290555498e85a87a4e48225c59942befdd4814 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 28 May 2026 23:50:52 +0200 Subject: [PATCH 0275/1062] tidy-up: miscellaneous - drop more uses of the word "just". (not enforced here) - drop some uses of the "will" word. - "then" -> "than". - tests/http/testenv/curl.py: fix copy-paste typo in error message. - pytest: replace `shutdownh` with `shutdown` in test names. Spotted by GitHub Code Quality. - comment typos. - whitespace and newlines fixes. Closes #21830 --- .github/stale.yml | 2 +- .github/workflows/macos.yml | 2 +- CMake/FindGSS.cmake | 4 ++-- CMake/PickyWarnings.cmake | 4 ++-- CMakeLists.txt | 2 +- REUSE.toml | 4 ++-- acinclude.m4 | 2 +- configure.ac | 12 ++++++------ lib/cf-h3-proxy.c | 2 +- lib/config-os400.h | 2 +- lib/request.c | 2 +- lib/sendf.h | 2 +- lib/setopt.c | 2 +- lib/url.c | 2 +- lib/urlapi.c | 2 +- lib/vquic/curl_ngtcp2.c | 2 +- m4/curl-compilers.m4 | 4 ++-- m4/curl-gnutls.m4 | 2 +- projects/OS400/.checksrc | 2 +- projects/vms/build_gnv_curl_pcsi_desc.com | 2 +- projects/vms/build_vms.com | 2 +- projects/vms/config_h.com | 2 +- projects/vms/curl_gnv_build_steps.txt | 2 +- projects/vms/curl_release_note_start.txt | 2 +- projects/vms/make_gnv_curl_install.sh | 2 +- projects/vms/make_pcsi_curl_kit_name.com | 2 +- projects/vms/stage_curl_install.com | 2 +- scripts/cd2nroff | 2 +- scripts/checksrc.pl | 2 +- scripts/managen | 4 ++-- scripts/mdlinkcheck | 2 +- scripts/mk-ca-bundle.pl | 4 ++-- scripts/nroff2cd | 6 +++--- scripts/wcurl | 4 ++-- src/tool_getparam.c | 14 +++++++------- src/tool_operate.c | 1 + tests/certs/Makefile.am | 2 +- tests/data/DISABLED | 4 ++-- tests/data/test1075 | 2 +- tests/data/test1144 | 2 +- tests/data/test1152 | 2 +- tests/data/test1400 | 2 +- tests/data/test1401 | 2 +- tests/data/test1402 | 2 +- tests/data/test1403 | 2 +- tests/data/test1404 | 2 +- tests/data/test1405 | 4 ++-- tests/data/test1406 | 2 +- tests/data/test1407 | 2 +- tests/data/test1420 | 2 +- tests/data/test1465 | 2 +- tests/data/test1481 | 2 +- tests/data/test157 | 2 +- tests/data/test2027 | 2 +- tests/data/test2030 | 2 +- tests/data/test218 | 4 ++-- tests/data/test2304 | 2 +- tests/data/test306 | 2 +- tests/data/test341 | 2 +- tests/data/test583 | 2 +- tests/data/test750 | 2 +- tests/data/test950 | 2 +- tests/ech_tests.sh | 4 ++-- tests/ftpserver.pl | 8 ++++---- tests/getpart.pm | 2 +- tests/globalconfig.pm | 2 +- tests/http-server.pl | 2 +- tests/http/test_02_download.py | 4 ++-- tests/http/test_05_errors.py | 2 +- tests/http/test_14_auth.py | 2 +- tests/http/test_30_vsftpd.py | 4 ++-- tests/http/test_31_vsftpds.py | 4 ++-- tests/http/test_32_ftps_vsftpd.py | 4 ++-- tests/http/test_60_h3_proxy.py | 2 +- tests/http/testenv/curl.py | 2 +- tests/http/testenv/mod_curltest/mod_curltest.c | 8 ++++---- tests/libtest/first.c | 2 +- tests/libtest/first.h | 4 ++-- tests/libtest/lib1518.c | 2 +- tests/libtest/lib1537.c | 2 +- tests/libtest/lib1592.c | 6 +++--- tests/libtest/lib1908.c | 2 +- tests/libtest/lib1911.c | 2 +- tests/libtest/lib1939.c | 2 +- tests/libtest/lib1977.c | 2 +- tests/libtest/lib3027.c | 2 +- tests/libtest/lib501.c | 2 +- tests/libtest/lib505.c | 2 +- tests/libtest/lib518.c | 4 ++-- tests/libtest/lib537.c | 4 ++-- tests/libtest/test613.pl | 2 +- tests/negtelnetserver.py | 2 +- tests/rtspserver.pl | 2 +- tests/runtests.pl | 9 ++++----- tests/secureserver.pl | 6 +++--- tests/server/getpart.c | 4 ++-- tests/server/rtspd.c | 4 ++-- tests/server/sockfilt.c | 17 ++++++++--------- tests/server/sws.c | 6 +++--- tests/server/tftpd.c | 12 ++++++------ tests/serverhelp.pm | 2 +- tests/servers.pm | 12 ++++++------ tests/smbserver.py | 2 +- tests/sshserver.pl | 2 +- tests/test1119.pl | 4 ++-- tests/test1173.pl | 2 +- tests/tftpserver.pl | 2 +- tests/unit/unit1303.c | 2 +- tests/unit/unit1609.c | 2 +- tests/unit/unit1615.c | 2 +- tests/unit/unit1625.c | 2 +- tests/unit/unit1652.c | 6 +++--- tests/unit/unit1655.c | 2 +- tests/unit/unit1660.c | 2 +- tests/unit/unit1666.c | 2 +- tests/unit/unit2600.c | 2 +- 116 files changed, 184 insertions(+), 185 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 69a822e78be8..1c2b57c2cf90 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -15,7 +15,7 @@ staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you + recent activity. It is closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b47af0a6aaa0..e1471fcc68a0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -658,7 +658,7 @@ jobs: # Reduce build combinations, by dropping less interesting ones - { image: macos-26, compiler: 'gcc-13' } - { compiler: 'gcc-14' , build: cmake } - # Reduce autotools to just one job that is also build with cmake + # Reduce autotools to only one job that is also build with cmake - { compiler: 'gcc-13' , build: autotools } - { compiler: 'gcc-14' , build: autotools } - { compiler: 'gcc-15' , build: autotools } diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 9237fb30b192..283aeefba97f 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -121,7 +121,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr RESULT_VARIABLE _gss_configure_failed OUTPUT_STRIP_TRAILING_WHITESPACE) - # Older versions may not have the "--version" parameter. In this case we just do not care. + # Older versions may not have the "--version" parameter. In this case we do not care. if(_gss_configure_failed) set(_gss_version 0) else() @@ -134,7 +134,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr RESULT_VARIABLE _gss_configure_failed OUTPUT_STRIP_TRAILING_WHITESPACE) - # Older versions may not have the "--vendor" parameter. In this case we just do not care. + # Older versions may not have the "--vendor" parameter. In this case we do not care. if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal") set(_gss_flavour "MIT") # assume a default, should not really matter endif() diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index 2de72a6d849e..b326345c1a40 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -407,7 +407,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-wd4746") list(APPEND _picky "-wd4820") # 'A': 'N' bytes padding added after data member 'B' if(MSVC_VERSION GREATER_EQUAL 1900) - list(APPEND _picky "-wd5045") # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified + list(APPEND _picky "-wd5045") # Compiler inserts Spectre mitigation for memory load if /Qspectre switch specified endif() endif() endif() @@ -437,7 +437,7 @@ if(CMAKE_C_STANDARD STREQUAL 90) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)) - list(APPEND _picky "-Wno-comma") # Just silly + list(APPEND _picky "-Wno-comma") # Silly endif() endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 362cc8ab90c8..198f18944a1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2180,7 +2180,7 @@ if(NOT CURL_DISABLE_INSTALL) set(_explicit_libs "") get_target_property(_imported "${_lib}" IMPORTED) if(NOT _imported) - # Reading the LOCATION property on non-imported target does error out. + # Reading the LOCATION property on non-imported target errors out. # Assume the user does not need this information in the .pc file. continue() endif() diff --git a/REUSE.toml b/REUSE.toml index c2e8b7928889..87be5825cc04 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -2,8 +2,8 @@ # SPDX-FileCopyrightText: Daniel Stenberg, , et al. # This file describes the licensing and copyright situation for files that -# cannot be annotated directly, for example because of being -# uncommentable. Unless this is the case, a file should be annotated directly. +# cannot be annotated directly, for example because of being uncommentable. +# Unless this is the case, a file should be annotated directly. # # This follows the REUSE specification: https://reuse.software/spec-3.2/#reusetoml diff --git a/acinclude.m4 b/acinclude.m4 index 73afac6e0836..11fe68c73729 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1040,7 +1040,7 @@ AC_DEFUN([CURL_VERIFY_RUNTIMELIBS], [ dnl this test is of course not sensible if we are cross-compiling! if test "$cross_compiling" != "yes"; then - dnl just run a program to verify that the libs checked for previous to this + dnl run a program to verify that the libs checked for previous to this dnl point also is available runtime! AC_MSG_CHECKING([runtime libs availability]) CURL_RUN_IFELSE([ diff --git a/configure.ac b/configure.ac index 20b4a9d91b34..b2a996aa0b75 100644 --- a/configure.ac +++ b/configure.ac @@ -227,7 +227,7 @@ exec $CC "$@" EOF dnl ********************************************************************** -dnl See which TLS backend(s) that are requested. Just do all the +dnl See which TLS backend(s) that are requested. Do all the dnl TLS AC_ARG_WITH() invokes here and do the checks later dnl ********************************************************************** OPT_SCHANNEL=no @@ -1610,7 +1610,7 @@ if test "x$OPT_BROTLI" != "xno"; then DIR_BROTLI=`echo $LD_BROTLI | $SED -e 's/^-L//'` ;; off) - dnl no --with-brotli option given, just check default places + dnl no --with-brotli option given, check default places ;; *) dnl use the given --with-brotli spot @@ -1700,7 +1700,7 @@ if test "x$OPT_ZSTD" != "xno"; then ;; off) - dnl no --with-zstd option given, just check default places + dnl no --with-zstd option given, check default places ;; *) dnl use the given --with-zstd spot @@ -1846,7 +1846,7 @@ int main(int argc, char **argv) { #ifdef _WIN32 /* on Windows, writing to the argv does not hide the argument in - process lists so it can just be skipped */ + process lists so it can be skipped */ (void)argc; (void)argv; return 1; @@ -2437,7 +2437,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then ;; off) - dnl no --with-libssh2 option given, just check default places + dnl no --with-libssh2 option given, check default places ;; *) dnl use the given --with-libssh2 spot @@ -2514,7 +2514,7 @@ elif test "x$OPT_LIBSSH" != "xno"; then ;; off) - dnl no --with-libssh option given, just check default places + dnl no --with-libssh option given, check default places ;; *) dnl use the given --with-libssh spot diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index 4fdaef47d0cf..d5fb45e73dff 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -1404,7 +1404,7 @@ static void cb_ngtcp2_rand(uint8_t *dest, size_t destlen, result = Curl_rand(NULL, dest, destlen); if(result) { /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ + failed, fill 0 and call it *random*. */ memset(dest, 0, destlen); } } diff --git a/lib/config-os400.h b/lib/config-os400.h index 1bb11c48ac35..eacd6e8942c8 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -238,7 +238,7 @@ /* Size of time_t in number of bytes */ #define SIZEOF_TIME_T 4 -/* Define to 1 if all of the C89 standard headers exist (not just the ones +/* Define to 1 if all of the C89 standard headers exist (not only the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #define STDC_HEADERS 1 diff --git a/lib/request.c b/lib/request.c index c231a63eaa38..4ecb462951e1 100644 --- a/lib/request.c +++ b/lib/request.c @@ -392,7 +392,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, blen = curlx_dyn_len(req); /* if the sendbuf is empty and the request without body and * the length to send fits info a sendbuf chunk, we send it directly. - * If `blen` is larger then `chunk_size`, we can not. Because we + * If `blen` is larger than `chunk_size`, we can not. Because we * might have to retry a blocked send later from sendbuf and that * would result in retry sends with a shrunken length. That is trouble. */ if(Curl_bufq_is_empty(&data->req.sendbuf) && diff --git a/lib/sendf.h b/lib/sendf.h index 75c6e248ea47..787fe7ff69ba 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -377,7 +377,7 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data); * values will be ignored. * @return CURLE_OK if offset could be set * CURLE_READ_ERROR if not supported by reader or seek/read failed - * of offset larger then total length + * of offset larger than total length * CURLE_PARTIAL_FILE if offset led to 0 total length */ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); diff --git a/lib/setopt.c b/lib/setopt.c index d4c1ca69a42b..368548f704f5 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2774,7 +2774,7 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, break; case CURLOPT_MAX_SEND_SPEED_LARGE: /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * When transfer uploads are faster than CURLOPT_MAX_SEND_SPEED_LARGE * bytes per second the transfer is throttled.. */ if(offt < 0) diff --git a/lib/url.c b/lib/url.c index aa151eda7e15..d7af1560f9ea 100644 --- a/lib/url.c +++ b/lib/url.c @@ -980,7 +980,7 @@ static bool url_match_destination(struct connectdata *conn, return FALSE; } } - /* Scheme mismatch is acceptable, just compare hostname/port */ + /* Scheme mismatch is acceptable, compare hostname/port */ return Curl_peer_same_destination(m->needle->origin, conn->origin); } diff --git a/lib/urlapi.c b/lib/urlapi.c index 9fd08890f380..08e29aa5134a 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -480,7 +480,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, /* more than one trailing dot is not allowed */ return CURLUE_BAD_HOSTNAME; else if((hlen == 1) && (hostname[0] == '.')) - /* just a single dot is not allowed */ + /* a single dot alone is not allowed */ return CURLUE_BAD_HOSTNAME; } return CURLUE_OK; diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index a5e0ea2cf944..deb21e882dca 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1697,7 +1697,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, stream->upload_left = -1; /* unknown */ break; default: - /* there is not request body */ + /* there is no request body */ stream->upload_left = 0; /* no request body */ break; } diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index 27500c879b69..3c96093abbc3 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -174,7 +174,7 @@ dnl ------------------------------------------------- dnl Verify if compiler being used is GNU C dnl dnl $compiler_num is set to MAJOR * 100 + MINOR for gcc less than version -dnl 7 and just $MAJOR * 100 for gcc version 7 and later. +dnl 7 and $MAJOR * 100 for gcc version 7 and later. dnl dnl Examples: dnl Version 1.2.3 => 102 @@ -966,7 +966,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -Wno-c99-extensions" # Avoid: warning: '_Bool' is a C99 extension fi if test "$compiler_num" -ge "309"; then - tmp_CFLAGS="$tmp_CFLAGS -Wno-comma" # Just silly + tmp_CFLAGS="$tmp_CFLAGS -Wno-comma" # Silly fi ;; esac diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index 93e1e1b9c0da..3eab1c5d3192 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -72,7 +72,7 @@ if test "x$OPT_GNUTLS" != "xno"; then addlib=-lgnutls addld=-L$OPT_GNUTLS/lib$libsuff addcflags=-I$OPT_GNUTLS/include - dnl we just do not know + dnl we do not know version="" gtlslib=$OPT_GNUTLS/lib$libsuff fi diff --git a/projects/OS400/.checksrc b/projects/OS400/.checksrc index 3bd88ff1e7db..e27d5729de0b 100644 --- a/projects/OS400/.checksrc +++ b/projects/OS400/.checksrc @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: curl -# Possibly not what we want, but cannot test, just silence the warnings +# Possibly not what we want, but cannot test, thus silence the warnings allowfunc calloc allowfunc free allowfunc malloc diff --git a/projects/vms/build_gnv_curl_pcsi_desc.com b/projects/vms/build_gnv_curl_pcsi_desc.com index cbdd6379693b..7726b4aa9f43 100644 --- a/projects/vms/build_gnv_curl_pcsi_desc.com +++ b/projects/vms/build_gnv_curl_pcsi_desc.com @@ -425,7 +425,7 @@ $ destname = "[gnv.beta" + destname - "[gnv.usr" $ endif $ endif $! -$! It should be just a directory then. +$! It should be a directory then. $!------------------------------------- $ filedir = f$edit(f$parse(filename,,,"DIRECTORY"), "lowercase") $! If this is not a directory then start processing files. diff --git a/projects/vms/build_vms.com b/projects/vms/build_vms.com index 283e39ecf493..d79fdb594079 100644 --- a/projects/vms/build_vms.com +++ b/projects/vms/build_vms.com @@ -723,7 +723,7 @@ $ endif $ 'vo_c' " SSLLIB = ''ssllib'" $! $! TODO: Why are we translating the logical name? -$! The logical aname used to find the shared image should just be used +$! The logical aname used to find the shared image should be used $! as translating it could result in the wrong location at run time. $ if (openssl .eq. 1) $ then diff --git a/projects/vms/config_h.com b/projects/vms/config_h.com index e42ecd9d78db..0b23cc6633e5 100644 --- a/projects/vms/config_h.com +++ b/projects/vms/config_h.com @@ -10,7 +10,7 @@ $! The CONFIGURE shell script will be examined for hints and a few symbols $! but most of the tests will not produce valid results on OpenVMS. Some $! will produce false positives and some will produce false negatives. $! -$! It is easier to just read the config.h_in file and make up tests based +$! It is easier to read the config.h_in file and make up tests based $! on what is in it! $! $! This file will create an empty config_vms.h file if one does not exist. diff --git a/projects/vms/curl_gnv_build_steps.txt b/projects/vms/curl_gnv_build_steps.txt index 687b7b63f498..88af34f5802e 100644 --- a/projects/vms/curl_gnv_build_steps.txt +++ b/projects/vms/curl_gnv_build_steps.txt @@ -268,7 +268,7 @@ branding the PCSI kit based on who is making the kit. This compares the VMS specific source with the backup staging directory for it and updates with any changes. - Leave off "UPDATE" to just check without doing any changes. + Leave off "UPDATE" to check without doing any changes. If you are not using NFS mounted disks and do not want to have a separate directory for staging the sources for backup make sure diff --git a/projects/vms/curl_release_note_start.txt b/projects/vms/curl_release_note_start.txt index 1a67b36020b0..184b458dd669 100644 --- a/projects/vms/curl_release_note_start.txt +++ b/projects/vms/curl_release_note_start.txt @@ -36,7 +36,7 @@ the GNV$LIBCURL shared image and create logical names GNV$LIBCURL to reference it. It will create the GNV$CURL_INCLUDE logical name for build procedures to access the header files. -Normally to use curl from DCL, just create a foreign command as: +Normally to use curl from DCL, create a foreign command as: curl :== $gnv$gnu:[usr.bin]gnv$curl.exe If you need to work around having the older HP SSL kit installed, then diff --git a/projects/vms/make_gnv_curl_install.sh b/projects/vms/make_gnv_curl_install.sh index 4723070387d0..623fcc3a6be0 100755 --- a/projects/vms/make_gnv_curl_install.sh +++ b/projects/vms/make_gnv_curl_install.sh @@ -37,7 +37,7 @@ export GNV_CC_MAIN_POSIX_EXIT=1 make cd ../.. # adjust the libcurl.pc file, GNV currently ignores the Lib: line. -# but is noisy about it, so we just remove it. +# but is noisy about it, so we remove it. sed -e 's/^Libs:/#Libs:/g' libcurl.pc > libcurl.pc_new rm libcurl.pc mv libcurl.pc_new libcurl.pc diff --git a/projects/vms/make_pcsi_curl_kit_name.com b/projects/vms/make_pcsi_curl_kit_name.com index 956f7c167798..c7c2b26ada7e 100644 --- a/projects/vms/make_pcsi_curl_kit_name.com +++ b/projects/vms/make_pcsi_curl_kit_name.com @@ -67,7 +67,7 @@ $ write sys$output "*****" $! $! $! Base is one of 'VMS', 'AXPVMS', 'I64VMS', 'VAXVMS' and indicates what -$! binaries are in the kit. A kit with just 'VMS' can be installed on all +$! binaries are in the kit. A kit with only 'VMS' can be installed on all $! architectures. $! $ base = "VMS" diff --git a/projects/vms/stage_curl_install.com b/projects/vms/stage_curl_install.com index 10ae17adcb3e..48f6514e5f4e 100644 --- a/projects/vms/stage_curl_install.com +++ b/projects/vms/stage_curl_install.com @@ -96,7 +96,7 @@ $ this_dir = f$element(i, ",", dest_dirs) $ i = i + 1 $ if this_dir .eqs. "" then goto curl_dir_loop $ if this_dir .eqs. "," then goto curl_dir_loop_end -$! Just create the directories, do not delete them. +$! Create the directories, do not delete them. $! -------------------------------------------------- $ if remove_files .eq. 0 $ then diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 62c9df025a08..d051433a72c6 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -426,7 +426,7 @@ sub single { print STDERR "$f:$line:1:ERROR: un-escaped < or > used\n"; $errors++; } - # convert backslash-'<' or '> to just the second character + # convert backslash-'<' or '> to the second character $d =~ s/\\([<>])/$1/g; # mentions of curl symbols with man pages use italics by default diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 64b1da9a8553..41bf846bcd42 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -879,7 +879,7 @@ sub scanfile { } } - # check for "return" with parentheses around just a value/name + # check for "return" with parentheses around a value/name if($l =~ /^(.*\W)return \(\w*\);/) { checkwarn("RETURNPAREN", $line, length($1)+7, $file, $l, "return with paren"); diff --git a/scripts/managen b/scripts/managen index e9b5d6157e67..2b82e74a8bbf 100755 --- a/scripts/managen +++ b/scripts/managen @@ -494,7 +494,7 @@ sub render { } } - # convert backslash-'<' or '> to just the second character + # convert backslash-'<' or '> to the second character $d =~ s/\\([><])/$1/g; # convert single backslash to double-backslash $d =~ s/\\/\\\\/g if($manpage); @@ -916,7 +916,7 @@ sub single { push @ex, "[0q]Example$s:\n"; # # long ASCII examples are wrapped. Preferably at the last space - # before the margin. Or at a colon. Otherwise it just cuts at the + # before the margin. Or at a colon. Otherwise it cuts at the # exact boundary. # foreach my $e (@examples) { diff --git a/scripts/mdlinkcheck b/scripts/mdlinkcheck index 4569b764d789..3c86ddb75ada 100755 --- a/scripts/mdlinkcheck +++ b/scripts/mdlinkcheck @@ -108,7 +108,7 @@ sub storelink { #print "-- whitelisted: $link\n"; $whitelist{$link}++; } - # example.com is just example + # example.com is used as example elsif($link =~ /^https:\/\/(.*)example.(com|org|net)/) { #print "-- example: $link\n"; } diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index f36c16892df6..6ef5d55e83c8 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -421,7 +421,7 @@ (%) ## It contains the certificates in ${format}PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. -## Just configure this file as the SSLCACertificateFile. +## Configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version $version. ## SHA256: $newhash @@ -493,7 +493,7 @@ (%) # # The latter is for certificates that have already been removed and are not # included. Not all explicitly distrusted certificates are ignored at this - # point, just those without an actual certificate. + # point, only those without an actual certificate. elsif(!$main_block && !$trust_block) { next; } diff --git a/scripts/nroff2cd b/scripts/nroff2cd index 51b6974b8dfb..6b2e3226a922 100755 --- a/scripts/nroff2cd +++ b/scripts/nroff2cd @@ -30,7 +30,7 @@ This script converts an nroff file to curldown Example: cd2nroff [options] > Note: when converting .nf sections, this tool does not know if the -section is code or just regular quotes. It then assumes and uses ~~~c +section is code or regular quotes. It then assumes and uses ~~~c for code. =end comment @@ -116,7 +116,7 @@ HEAD # if there are enclosing quotes, remove them first $word =~ s/[\"\'](.*)[\"\']\z/$1/; if($word eq "SEE ALSO") { - # we just slurp up this section + # we slurp up this section next; } push @desc, "\n# $word\n\n"; @@ -131,7 +131,7 @@ HEAD push @desc, "\n## $word\n\n"; } elsif($d =~ /^\.IP/) { - # .IP with no text we just skip + # .IP with no text we skip } elsif($d =~ /^\.BR (.*)/) { # only used for SEE ALSO diff --git a/scripts/wcurl b/scripts/wcurl index c39806cf946d..a5c1f95e1cc4 100755 --- a/scripts/wcurl +++ b/scripts/wcurl @@ -67,7 +67,7 @@ Options: --no-decode-filename: Do not percent-decode the output filename, even if the percent-encoding in the URL was done by wcurl, e.g.: The URL contained whitespace. - --dry-run: Do not actually execute curl, just print what would be invoked. + --dry-run: Do not actually execute curl, only print what would be invoked. -V, --version: Print version information. @@ -197,7 +197,7 @@ get_url_filename() # sed to also replace ':' with the percent_encoded %3A */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,' -e 's,:,%3A,g')" ;; esac - # No slash means there was just a hostname and no path; return empty string. + # No slash means there was only a hostname and no path; return empty string. } # Execute curl with the list of URLs provided by the user. diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 05642e7a83aa..00fd8515a583 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -539,11 +539,11 @@ struct sizeunit { static const struct sizeunit *getunit(char unit) { static const struct sizeunit list[] = { - {'p', (curl_off_t)1125899906842624, 16 }, /* Peta */ - {'t', (curl_off_t)1099511627776, 13 }, /* Tera */ - {'g', 1073741824, 10 }, /* Giga */ - {'m', 1048576, 7 }, /* Mega */ - {'k', 1024, 4 }, /* Kilo */ + { 'p', (curl_off_t)1125899906842624, 16 }, /* Peta */ + { 't', (curl_off_t)1099511627776, 13 }, /* Tera */ + { 'g', 1073741824, 10 }, /* Giga */ + { 'm', 1048576, 7 }, /* Mega */ + { 'k', 1024, 4 }, /* Kilo */ }; size_t i; @@ -1627,12 +1627,12 @@ static ParameterError parse_time_cond(struct OperationConfig *config, config->timecond = CURL_TIMECOND_IFMODSINCE; break; case '-': - /* If-Unmodified-Since: (section 14.24 in RFC2068) */ + /* If-Unmodified-Since: (section 14.24 in RFC2068) */ config->timecond = CURL_TIMECOND_IFUNMODSINCE; nextarg++; break; case '=': - /* Last-Modified: (section 14.29 in RFC2068) */ + /* Last-Modified: (section 14.29 in RFC2068) */ config->timecond = CURL_TIMECOND_LASTMOD; nextarg++; break; diff --git a/src/tool_operate.c b/src/tool_operate.c index a8d928f496fd..dbf4ceea73d8 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -722,6 +722,7 @@ static CURLcode post_close_output(struct per_transfer *per, } return result; } + /* * Call this after a transfer has completed. */ diff --git a/tests/certs/Makefile.am b/tests/certs/Makefile.am index d28b1674da3d..078b4490b6e2 100644 --- a/tests/certs/Makefile.am +++ b/tests/certs/Makefile.am @@ -35,7 +35,7 @@ all-am: test-ca.cacert # Rebuild the certificates -# Generate all certs in a single shot, but declare just a single target file +# Generate all certs in a single shot, but declare only a single target file # to support GNU Make <4.3 without the "grouped explicit targets" feature. test-ca.cacert: $(CERTCONFIG_CA) $(CERTCONFIGS) genserv.pl @PERL@ $(srcdir)/genserv.pl test $(CERTCONFIGS) diff --git a/tests/data/DISABLED b/tests/data/DISABLED index b6fe06d7955e..60dca3ca6a64 100644 --- a/tests/data/DISABLED +++ b/tests/data/DISABLED @@ -23,7 +23,7 @@ ########################################################################### # # This file can be used to specify test cases that should not run when all -# test cases are run by runtests.pl. Just add the plain test case numbers, one +# test cases are run by runtests.pl. Add the plain test case numbers, one # per line. # Lines starting with '#' letters are treated as comments. # @@ -43,7 +43,7 @@ 1184 1209 1211 -# fnmatch differences are just too common to make testing them sensible +# fnmatch differences are too common to make testing them sensible 1307 1316 1512 diff --git a/tests/data/test1075 b/tests/data/test1075 index f5431927039b..6f2a21bba191 100644 --- a/tests/data/test1075 +++ b/tests/data/test1075 @@ -13,7 +13,7 @@ HTTP Basic auth # The test server provides no way to respond differently to a subsequent # Basic authenticated request (we really want to respond with 200 for -# the second), so just respond with 401 for both and let curl deal with it. +# the second), so respond with 401 for both and let curl deal with it. HTTP/1.1 401 Authorization Required Server: testcurl diff --git a/tests/data/test1144 b/tests/data/test1144 index a7baf6ef0e58..4446e63212d8 100644 --- a/tests/data/test1144 +++ b/tests/data/test1144 @@ -11,7 +11,7 @@ HTTP/0.9 # Server-side -No headers at all, just data swsclose +No headers at all, only data swsclose Let's get diff --git a/tests/data/test1152 b/tests/data/test1152 index ad548e64f194..692f51faa06a 100644 --- a/tests/data/test1152 +++ b/tests/data/test1152 @@ -9,7 +9,7 @@ FAILURE # Server-side -REPLY PWD 257 "just one +REPLY PWD 257 "Only one diff --git a/tests/data/test1400 b/tests/data/test1400 index cf4aabbb8710..d3279a9e7cd4 100644 --- a/tests/data/test1400 +++ b/tests/data/test1400 @@ -51,7 +51,7 @@ Accept: */* # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with # CURLOPT_INTERLEAVEDATA requires RTSP protocol -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1401 b/tests/data/test1401 index 3f84805e00a0..74d174833922 100644 --- a/tests/data/test1401 +++ b/tests/data/test1401 @@ -62,7 +62,7 @@ X-Men: cyclops, iceman # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1402 b/tests/data/test1402 index 82061a385ca7..6dd608458abc 100644 --- a/tests/data/test1402 +++ b/tests/data/test1402 @@ -54,7 +54,7 @@ foo=bar%AMPbaz=quux # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1403 b/tests/data/test1403 index c3416a330586..14383314d48e 100644 --- a/tests/data/test1403 +++ b/tests/data/test1403 @@ -51,7 +51,7 @@ Accept: */* # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1404 b/tests/data/test1404 index 9fbd55526f16..d1a1d5217c7b 100644 --- a/tests/data/test1404 +++ b/tests/data/test1404 @@ -96,7 +96,7 @@ dummy data # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1405 b/tests/data/test1405 index 853c967ba77e..611dbf720fbf 100644 --- a/tests/data/test1405 +++ b/tests/data/test1405 @@ -65,11 +65,11 @@ QUIT # CURLOPT_USERAGENT and CURLOPT_MAXREDIRS requires HTTP protocol # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol -# support, IOW depends on configuration - just ignore these. +# support, IOW depends on configuration - ignore these. $_ = '' if /CURLOPT_USERAGENT/ $_ = '' if /CURLOPT_MAXREDIRS/ # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1406 b/tests/data/test1406 index 26ad1c7998fa..d7c4adcfa993 100644 --- a/tests/data/test1406 +++ b/tests/data/test1406 @@ -61,7 +61,7 @@ body . -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_MAXREDIRS/ $_ = '' if /CURLOPT_SSL_VERIFYPEER/ diff --git a/tests/data/test1407 b/tests/data/test1407 index 1436786115db..599eb1d3e4ef 100644 --- a/tests/data/test1407 +++ b/tests/data/test1407 @@ -48,7 +48,7 @@ LIST %TESTNUMBER QUIT -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_USERAGENT and CURLOPT_MAXREDIRS requires HTTP protocol # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_USERAGENT/ diff --git a/tests/data/test1420 b/tests/data/test1420 index 57fdbe18d89c..cdf51cd0cf35 100644 --- a/tests/data/test1420 +++ b/tests/data/test1420 @@ -54,7 +54,7 @@ A004 FETCH 1 BODY[] A005 LOGOUT -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_MAXREDIRS/ $_ = '' if /CURLOPT_SSL_VERIFYPEER/ diff --git a/tests/data/test1465 b/tests/data/test1465 index 2c4804d5195a..d8d6115b1fde 100644 --- a/tests/data/test1465 +++ b/tests/data/test1465 @@ -58,7 +58,7 @@ Content-Type: application/x-www-form-urlencoded # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1481 b/tests/data/test1481 index 98c018bc2699..8a0d31078533 100644 --- a/tests/data/test1481 +++ b/tests/data/test1481 @@ -54,7 +54,7 @@ Proxy-Connection: Keep-Alive # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with # CURLOPT_INTERLEAVEDATA requires RTSP protocol -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test157 b/tests/data/test157 index 422d88f69aee..fe9c9bdd920b 100644 --- a/tests/data/test157 +++ b/tests/data/test157 @@ -16,7 +16,7 @@ Server: Apache/1.3.27 (Darwin) PHP/4.1.2 Content-Type: text/html; charset=iso-8859-1 Connection: close -GET received and served just fine. Thank you very much +GET received and served fine. Thank you very much diff --git a/tests/data/test2027 b/tests/data/test2027 index 2a5fa6e3db15..dfcefa302a16 100644 --- a/tests/data/test2027 +++ b/tests/data/test2027 @@ -16,7 +16,7 @@ Explanation for the duplicate 400 requests: libcurl does not detect that a given Digest password is wrong already on the first 401 response (as the data400 gives). libcurl will instead consider the -new response just as a duplicate and it sends another and detects the auth +new response as a duplicate and it sends another and detects the auth problem on the second 401 response! --> diff --git a/tests/data/test2030 b/tests/data/test2030 index e0d7ba551374..5027fd45da4b 100644 --- a/tests/data/test2030 +++ b/tests/data/test2030 @@ -21,7 +21,7 @@ Explanation for the duplicate 400 requests: libcurl does not detect that a given Digest password is wrong already on the first 401 response (as the data400 gives). libcurl will instead consider the -new response just as a duplicate and it sends another and detects the auth +new response as a duplicate and it sends another and detects the auth problem on the second 401 response! --> diff --git a/tests/data/test218 b/tests/data/test218 index 41bd83818ef4..14fc604830e5 100644 --- a/tests/data/test218 +++ b/tests/data/test218 @@ -31,7 +31,7 @@ HTTP PUT from a file but enforce chunked transfer-encoding -T %LOGDIR/file%TESTNUMBER -H "Transfer-Encoding: chunked" http://%HOSTIP:%HTTPPORT/%TESTNUMBER -just some tiny teeny contents +some tiny teeny test contents @@ -45,7 +45,7 @@ Accept: */*%CR Transfer-Encoding: chunked%CR %CR 1e%CR -just some tiny teeny contents +some tiny teeny test contents %CR 0%CR %CR diff --git a/tests/data/test2304 b/tests/data/test2304 index 26bbda187e6a..aab6dc822b48 100644 --- a/tests/data/test2304 +++ b/tests/data/test2304 @@ -62,7 +62,7 @@ Connection: Upgrade # This test used to check that "connection closed" was output, but -# that is flaky since the outgoing PING just before might fail already +# that is flaky since the outgoing PING before might fail already # and then the test exists before the output gets to be written diff --git a/tests/data/test306 b/tests/data/test306 index 5ef7a59010bc..b0974a77a36f 100644 --- a/tests/data/test306 +++ b/tests/data/test306 @@ -10,7 +10,7 @@ HTTP GET # Server-side -No headers at all, just data swsclose +No headers at all, data swsclose Let's get diff --git a/tests/data/test341 b/tests/data/test341 index 8aea4fbb7ce4..0d204a4f1310 100644 --- a/tests/data/test341 +++ b/tests/data/test341 @@ -35,7 +35,7 @@ chunky-trailer: header data http -A non existing file with --etag-compare is just a blank +A non existing file with --etag-compare is a blank http://%HOSTIP:%HTTPPORT/%TESTNUMBER --etag-compare %LOGDIR/etag%TESTNUMBER diff --git a/tests/data/test583 b/tests/data/test583 index 9463cd887fc5..8428d31395c9 100644 --- a/tests/data/test583 +++ b/tests/data/test583 @@ -25,7 +25,7 @@ lib%TESTNUMBER SFTP with multi interface, remove handle early -# The command here uses 'localhost' just to make sure that curl_multi_perform +# The command here uses 'localhost' to make sure that curl_multi_perform # does not reach too far in the first invoke. When using c-ares at least, the # name resolve causes it to return rather quickly and thus we could trigger # the problem we are looking to verify. diff --git a/tests/data/test750 b/tests/data/test750 index 8e4d220c7480..03352ece66b9 100644 --- a/tests/data/test750 +++ b/tests/data/test750 @@ -32,7 +32,7 @@ http proxy -HTTP CONNECT with proxy returning just HTML and closing +HTTP CONNECT with proxy returning HTML and closing http://test.example --proxy http://%HOSTIP:%HTTPPORT --proxytunnel -sS diff --git a/tests/data/test950 b/tests/data/test950 index 3ea2cb7b51a5..6d6fcc303389 100644 --- a/tests/data/test950 +++ b/tests/data/test950 @@ -24,7 +24,7 @@ smtp SMTP VRFY with custom request -# the custom request just does it lowercase to remain the same command +# the custom request does it lowercase to remain the same command smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient --request "vrfy" diff --git a/tests/ech_tests.sh b/tests/ech_tests.sh index e1246dae187b..e5b413fa82ca 100755 --- a/tests/ech_tests.sh +++ b/tests/ech_tests.sh @@ -31,7 +31,7 @@ # TODO: Translate this into something that approximates a valid curl test:-) # Should be useful though even before such translation and a pile less work # to do this than that. The pile of work required would include making an -# ECH-enabled server and a DoH server. For now, this is just run manually. +# ECH-enabled server and a DoH server. For now, this is run manually. # # set -x @@ -1083,7 +1083,7 @@ else echo "NOT all good, log in $logfile" fi -# send a mail to root (will be forwarded) but just once every 24 hours +# send a mail to root (will be forwarded) but only once every 24 hours # 'cause we only really need "new" news itsnews="yes" age_of_news=0 diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 88533019c8b1..c1a677e4000b 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -562,8 +562,8 @@ sub protocolsetup { 'LIST' => '150 here comes a directory', 'NLST' => '150 here comes a directory', 'CWD' => '250 CWD command successful.', - 'SYST' => '215 UNIX Type: L8', # just fake something - 'QUIT' => '221 bye bye baby', # just reply something + 'SYST' => '215 UNIX Type: L8', # fake something + 'QUIT' => '221 bye bye baby', # reply something 'MKD' => '257 Created your requested directory', 'REST' => '350 Yeah yeah we set it there for you', 'DELE' => '200 OK OK OK whatever you say', @@ -1143,7 +1143,7 @@ sub QUIT_smtp { my $selected; # Any IMAP parameter can come in escaped and in double quotes. -# This function is dumb (so far) and just removes the quotes if present. +# This function is dumb (so far) and removes the quotes if present. sub fix_imap_params { foreach (@_) { $_ = $1 if /^"(.*)"$/; @@ -3289,7 +3289,7 @@ sub customize { my $delay = $delayreply{$FTPCMD}; if($delay) { - # just go sleep this many seconds! + # go sleep this many seconds! logmsg("Sleep for $delay seconds\n"); my $twentieths = $delay * 20; while($twentieths--) { diff --git a/tests/getpart.pm b/tests/getpart.pm index ad52782de0a8..fd3a99915e15 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -195,7 +195,7 @@ sub partexists { } # The code currently never calls this more than once per part per file, so -# caching a result that is never used again just slows things down. +# caching a result that is never used again only slows things down. # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result sub loadtest { diff --git a/tests/globalconfig.pm b/tests/globalconfig.pm index ac636f2748b1..c9a95427b25d 100644 --- a/tests/globalconfig.pm +++ b/tests/globalconfig.pm @@ -124,7 +124,7 @@ our $CURLINFO=dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . our $VCURL=$CURL; # what curl binary to use to verify the servers with # VCURL is handy to set to the system one when the one you - # just built hangs or crashes and thus prevent verification + # built hangs or crashes and thus prevent verification # the path to the script that analyzes the memory debug output file our $memanalyze="$perl " . shell_quote("$srcdir/memanalyze.pl"); our $valgrind; # path to valgrind, or empty if disabled diff --git a/tests/http-server.pl b/tests/http-server.pl index 006b6f381781..14c66a8b42a6 100755 --- a/tests/http-server.pl +++ b/tests/http-server.pl @@ -40,7 +40,7 @@ BEGIN ); my $verbose = 0; # set to 1 for debugging -my $port = 8990; # just a default +my $port = 8990; # a default my $unix_socket; # location to place a listening Unix socket my $ipvnum = 4; # default IP version of http server my $idnum = 1; # default http server instance number diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index 6e1352ec3a08..69b31183d546 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -145,7 +145,7 @@ def test_02_07_download_reuse(self, env: Env, httpd, nghttpx, proto): ]) r.check_response(http_status=200, count=count) # should have used at most 2 connections only (test servers allow 100 req/conn) - # it may be just 1 on slow systems where request are answered faster than + # it may be 1 on slow systems where request are answered faster than # curl can exhaust the capacity or if curl runs with address-sanitizer speed assert r.total_connects <= 2, "h2 should use fewer connections here" @@ -417,7 +417,7 @@ def test_02_25_h2_upgrade_x(self, env: Env, httpd): assert r.exit_code == 0, f'{client.dump_logs()}' # Special client that tests TLS session reuse in parallel transfers - # TODO: just uses a single connection for h2/h3. Not sure how to prevent that + # TODO: uses a single connection for h2/h3. Not sure how to prevent that @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx): url = f'https://{env.authority_for(env.domain1, proto)}/data-100k' diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index 3483a28e8a26..19cada0c9e57 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -100,7 +100,7 @@ def test_05_03_required(self, env: Env, httpd, nghttpx): assert r.stats[0]['http_version'] == '1.1', r.dump_logs() # On the URL used here, Apache is doing an "unclean" TLS shutdown, - # meaning it sends no shutdown notice and just closes TCP. + # meaning it sends no shutdown notice and closes TCP. # The HTTP response delivers a body without Content-Length. We expect: # - http/1.0 to fail since it relies on a clean connection close to # detect the end of the body diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 7a1bc25c0fdf..69866baedf98 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -95,7 +95,7 @@ def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and not env.curl_uses_lib('ngtcp2'): # See pytest.skip("quiche has problems with large requests") - # just large enough that nghttp2 will submit + # large enough that nghttp2 will submit password = 'x' * (47 * 1024) fdata = os.path.join(env.gen_dir, 'data-10m') curl = CurlClient(env=env) diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index adbd6ecc597a..a57882f5202f 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -148,7 +148,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_30_06_shutdownh_download(self, env: Env, vsftpd: VsFTPD): + def test_30_06_shutdown_download(self, env: Env, vsftpd: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -166,7 +166,7 @@ def test_30_06_shutdownh_download(self, env: Env, vsftpd: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_30_07_shutdownh_upload(self, env: Env, vsftpd: VsFTPD): + def test_30_07_shutdown_upload(self, env: Env, vsftpd: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 688d157f8f02..5858d9e461fa 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -153,7 +153,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_31_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): + def test_31_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -170,7 +170,7 @@ def test_31_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_31_07_shutdownh_upload(self, env: Env, vsftpds: VsFTPD): + def test_31_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index 19eec643c66b..b16766466139 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -166,7 +166,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_32_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): + def test_32_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -183,7 +183,7 @@ def test_32_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_32_07_shutdownh_upload(self, env: Env, vsftpds: VsFTPD): + def test_32_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 39c6d265f6f1..9382ef70d9d2 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -285,7 +285,7 @@ def test_60_03_h3_target_auto_connect_udp( ) # An HTTP/3 target auto-triggers CONNECT-UDP even without --proxytunnel, - # just as HTTPS targets auto-trigger CONNECT. nghttpx does not support + # as HTTPS targets auto-trigger CONNECT. nghttpx does not support # CONNECT-UDP so this fails, which confirms auto-CONNECT-UDP is active. assert r.exit_code != 0, ( "expected failure: h3 target auto-triggers CONNECT-UDP " diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index f5be5ac4495c..8fb17349ab98 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -1239,7 +1239,7 @@ def fin_response(resp): def _perf_collapse(self, perf: PerfProfile, file_err): if not os.path.exists(perf.file): - raise Exception(f'dtrace output file does not exist: {perf.file}') + raise Exception(f'perf output file does not exist: {perf.file}') fg_collapse = os.path.join(self._fg_dir, 'stackcollapse-perf.pl') if not os.path.exists(fg_collapse): raise Exception(f'FlameGraph script not found: {fg_collapse}') diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 5e0f6400fb57..bd09c8b377d8 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -339,7 +339,7 @@ static int curltest_tweak_handler(request_rec *r) } } else if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -551,7 +551,7 @@ static int curltest_put_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -748,7 +748,7 @@ static int curltest_sslinfo_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -862,7 +862,7 @@ static int curltest_limit_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } diff --git a/tests/libtest/first.c b/tests/libtest/first.c index a57277b205ee..c244e5dc7e69 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -201,7 +201,7 @@ CURLcode ws_recv_pong(CURL *curl, const char *expected_payload) return CURLE_RECV_ERROR; } -/* just close the connection */ +/* close the connection */ void ws_close(CURL *curl) { size_t sent; diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 7c4bb2df46aa..33191edc5966 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -54,7 +54,7 @@ extern int unitfail; /* for unittests */ #include "curlx/wait.h" /* for curlx_wait_ms() */ #ifdef HAVE_SYS_SELECT_H -/* since so many tests use select(), we can just as well include it here */ +/* since so many tests use select(), we can as well include it here */ #include #endif @@ -99,7 +99,7 @@ extern char *hexdump(const unsigned char *buf, size_t len); #ifndef CURL_DISABLE_WEBSOCKETS CURLcode ws_send_ping(CURL *curl, const char *send_payload); CURLcode ws_recv_pong(CURL *curl, const char *expected_payload); -void ws_close(CURL *curl); /* just close the connection */ +void ws_close(CURL *curl); /* close the connection */ #endif /* diff --git a/tests/libtest/lib1518.c b/tests/libtest/lib1518.c index f6fe44c5e94c..f4552f21e600 100644 --- a/tests/libtest/lib1518.c +++ b/tests/libtest/lib1518.c @@ -64,7 +64,7 @@ static CURLcode test_lib1518(const char *URL) } else { test_setopt(curl, CURLOPT_URL, URL); - /* just to make it explicit and visible in this test: */ + /* to make it explicit and visible in this test: */ test_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); } diff --git a/tests/libtest/lib1537.c b/tests/libtest/lib1537.c index c9668c0ddf10..1ef0be9c7522 100644 --- a/tests/libtest/lib1537.c +++ b/tests/libtest/lib1537.c @@ -77,7 +77,7 @@ static CURLcode test_lib1537(const char *URL) curl_mprintf("escape -1 length: %s\n", ptr); /* weird input length */ - outlen = 2017; /* just a value */ + outlen = 2017; /* an arbitrary value */ ptr = curl_easy_unescape(NULL, "moahahaha", -1, &outlen); curl_mprintf("unescape -1 length: %s %d\n", ptr, outlen); diff --git a/tests/libtest/lib1592.c b/tests/libtest/lib1592.c index 29dbe0b70033..46c2610a3dbf 100644 --- a/tests/libtest/lib1592.c +++ b/tests/libtest/lib1592.c @@ -61,20 +61,20 @@ static CURLcode test_lib1592(const char *URL) /* Since we could set the DNS server, presume we are working with a resolver that can be cancelled (i.e. c-ares). Thus, curl_multi_remove_handle() should not block even when the resolver - request is outstanding. So, set a request timeout _longer_ than the + request is outstanding. Thus, set a request timeout _longer_ than the test hang timeout so we will fail if the handle removal call incorrectly blocks. */ timeout = TEST_HANG_TIMEOUT * 2; else { /* If we cannot set the DNS server, presume that we are configured to use a resolver that cannot be cancelled (i.e. the threaded resolver or the - non-threaded blocking resolver). So, we just test that the + non-threaded blocking resolver). Thus, we test that the curl_multi_remove_handle() call does finish well within our test timeout. But, it is unlikely that the resolver request will take any time at all because we have not been able to configure the resolver to use an - non-responsive DNS server. At least we exercise the flow. + non-responsive DNS server. At least we exercise the flow. */ curl_mfprintf(stderr, "CURLOPT_DNS_SERVERS not supported; " diff --git a/tests/libtest/lib1908.c b/tests/libtest/lib1908.c index f7ead3406eb7..9d6a7a12199a 100644 --- a/tests/libtest/lib1908.c +++ b/tests/libtest/lib1908.c @@ -52,7 +52,7 @@ static CURLcode test_lib1908(const char *URL) curl_easy_reset(curl); /* using the same filename for the alt-svc cache, this clobbers the - content just written from the 'curldupe' handle */ + content written from the 'curldupe' handle */ curl_easy_cleanup(curl); } curl_global_cleanup(); diff --git a/tests/libtest/lib1911.c b/tests/libtest/lib1911.c index f265174891d7..a463b0fa1668 100644 --- a/tests/libtest/lib1911.c +++ b/tests/libtest/lib1911.c @@ -43,7 +43,7 @@ static CURLcode test_lib1911(const char *URL) return TEST_ERR_EASY_INIT; } - /* make it a null-terminated C string with just As */ + /* make it a null-terminated C string with only As */ memset(testbuf, 'A', MAX_INPUT_LENGTH + 1); testbuf[MAX_INPUT_LENGTH + 1] = 0; diff --git a/tests/libtest/lib1939.c b/tests/libtest/lib1939.c index 502b60051717..be1e303beb5e 100644 --- a/tests/libtest/lib1939.c +++ b/tests/libtest/lib1939.c @@ -51,7 +51,7 @@ static CURLcode test_lib1939(const char *URL) mresult = curl_multi_add_handle(multi, curl); if(!mresult) - /* Run the multi handle once, just enough to start establishing an + /* Run the multi handle once, enough to start establishing an HTTPS connection. */ mresult = curl_multi_perform(multi, &running_handles); diff --git a/tests/libtest/lib1977.c b/tests/libtest/lib1977.c index 608ba4e8b6d1..a5db34226c15 100644 --- a/tests/libtest/lib1977.c +++ b/tests/libtest/lib1977.c @@ -34,7 +34,7 @@ static CURLcode test_lib1977(const char *URL) global_init(CURL_GLOBAL_ALL); easy_init(curl); - /* first transfer: set just the URL in the first CURLU handle */ + /* first transfer: set the URL in the first CURLU handle */ curl_url_set(curlu, CURLUPART_URL, URL, CURLU_DEFAULT_SCHEME); easy_setopt(curl, CURLOPT_CURLU, curlu); diff --git a/tests/libtest/lib3027.c b/tests/libtest/lib3027.c index 845eea239a73..013c13f43ec4 100644 --- a/tests/libtest/lib3027.c +++ b/tests/libtest/lib3027.c @@ -41,7 +41,7 @@ static CURLcode test_lib3027(const char *URL) result = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); /* MTDM fails with 550, so filetime should be -1 */ if((result == CURLE_OK) && (filetime != -1)) { - /* we just need to return something which is not CURLE_OK */ + /* we need to return something which is not CURLE_OK */ result = CURLE_UNSUPPORTED_PROTOCOL; } } diff --git a/tests/libtest/lib501.c b/tests/libtest/lib501.c index 358594c0377e..355d99e9991c 100644 --- a/tests/libtest/lib501.c +++ b/tests/libtest/lib501.c @@ -44,7 +44,7 @@ static CURLcode test_lib501(const char *URL) test_setopt(curl, CURLOPT_HEADER, 1L); - /* just verify that setting this to -1 is fine */ + /* verify that setting this to -1 is fine */ test_setopt(curl, CURLOPT_MAXREDIRS, -1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib505.c b/tests/libtest/lib505.c index 7ad6894ae88b..8eaa12b154a4 100644 --- a/tests/libtest/lib505.c +++ b/tests/libtest/lib505.c @@ -24,7 +24,7 @@ #include "first.h" /* - * This example shows an FTP upload, with a rename of the file just after + * This example shows an FTP upload, with a rename of the file right after * a successful upload. * * Example based on source code provided by Erick Nuwendam. Thanks! diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index c961ca3347d8..b4b4c63cedeb 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -136,7 +136,7 @@ static int t518_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); t518_msgbuff[0] = '\0'; @@ -147,7 +147,7 @@ static int t518_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); t518_msgbuff[0] = '\0'; diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 16e2f1b333e3..9b99b7489948 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -137,7 +137,7 @@ static int t537_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); t537_msgbuff[0] = '\0'; @@ -148,7 +148,7 @@ static int t537_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); t537_msgbuff[0] = '\0'; diff --git a/tests/libtest/test613.pl b/tests/libtest/test613.pl index f653b36c1328..bec12b9c5a13 100755 --- a/tests/libtest/test613.pl +++ b/tests/libtest/test613.pl @@ -140,7 +140,7 @@ sub errout { my $line = sprintf("%s%s???????%5d U U %15d %s %s\n", $1,$2,$5,$6,$7,$8); push @canondir, $line; } else { - # Unexpected format; just pass it through and let the test fail + # Unexpected format; pass it through and let the test fail push @canondir, $_; } } diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index ae0e3ae9415f..a9a4880018f5 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -167,7 +167,7 @@ def no_neg(self, byte_int, buffer): log.debug("Starting negotiation (IAC)") self.state = self.START_NEG else: - # Just append the incoming byte to the buffer + # Append the incoming byte to the buffer buffer.append(byte_int) def start_neg(self, byte_int): diff --git a/tests/rtspserver.pl b/tests/rtspserver.pl index d23ed7e6373e..cbe914eee087 100755 --- a/tests/rtspserver.pl +++ b/tests/rtspserver.pl @@ -38,7 +38,7 @@ BEGIN ); my $verbose = 0; # set to 1 for debugging -my $port = 8990; # just a default +my $port = 8990; # a default my $ipvnum = 4; # default IP version of rtsp server my $idnum = 1; # default rtsp server instance number my $proto = 'rtsp'; # protocol the rtsp server speaks diff --git a/tests/runtests.pl b/tests/runtests.pl index cc8fa51b29e2..cd0572a59e17 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -1724,8 +1724,7 @@ sub singletest_check { my @sout = sort @out; if($hostname) { - # when a hostname is set, we filter out requests to just this - # pattern + # when a hostname is set, we filter out requests to this pattern @sout = grep {/$hostname/} @sout; } @@ -2675,7 +2674,7 @@ sub pickrunner { # since valgrind 2.1.x, '--tool' option is mandatory # use it, if it is supported by the version installed on the system # (this happened in 2003, so we could probably do not need to care about - # that old version any longer and just delete this check) + # that old version any longer and delete this check) runclient("valgrind --help 2>&1 | grep -- --tool >$dev_null 2>&1"); if(($? >> 8)) { $valgrind_tool=""; @@ -2690,7 +2689,7 @@ sub pickrunner { # valgrind 3 renamed the --logfile option to --log-file!!! # (this happened in 2005, so we could probably do not need to care about - # that old version any longer and just delete this check) + # that old version any longer and delete this check) my $ver=join(' ', runclientoutput("valgrind --version")); # cut off all but digits and dots $ver =~ s/[^0-9.]//g; @@ -3212,7 +3211,7 @@ sub displaylogs { $endwaitcnt += $runnerwait; if($endwaitcnt >= 10) { # Once all tests have been scheduled on a runner at the end of a test - # run, we just wait for their results to come in. If we are still + # run, we wait for their results to come in. If we are still # waiting after a couple of minutes ($endwaitcnt multiplied by # $runnerwait, plus $jobs because that number will not time out), display # the same test runner status as we give with a SIGUSR1. This will diff --git a/tests/secureserver.pl b/tests/secureserver.pl index ae7a2c464996..7e64e1d894c8 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -24,7 +24,7 @@ #*************************************************************************** # This is the HTTPS, FTPS, POP3S, IMAPS, SMTPS, server used for curl test -# harness. Actually just a layer that runs stunnel properly using the +# harness. Actually a layer that runs stunnel properly using the # non-secure test harness servers. use strict; @@ -50,7 +50,7 @@ BEGIN my $verbose=0; # set to 1 for debugging -my $accept_port = 8991; # just our default, weird enough +my $accept_port = 8991; # our default, weird enough my $target_port = 8999; # default test http-server port my $stuncert; @@ -370,7 +370,7 @@ sub exit_signal_handler { # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } diff --git a/tests/server/getpart.c b/tests/server/getpart.c index fe3212a15ce7..226bbc441243 100644 --- a/tests/server/getpart.c +++ b/tests/server/getpart.c @@ -202,7 +202,7 @@ static int decodedata(char **buf, /* dest buffer */ /* * currently there is no way to tell apart an OOM condition in * curlx_base64_decode() from zero length decoded data. For now, - * let's just assume it is an OOM condition, currently we have + * let's assume it is an OOM condition, currently we have * no input for this function that decodes to zero length data. */ free(buf64); @@ -231,7 +231,7 @@ static int decodedata(char **buf, /* dest buffer */ * and the size of the data is stored at the addresses that caller specifies. * * If the returned data is a string the returned size will be the length of - * the string excluding null-termination. Otherwise it will just be the size + * the string excluding null-termination. Otherwise it will be the size * of the returned binary data. * * Calling function is responsible to free returned buffer. diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 97b2ba40d9d0..3684cf0f9291 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -97,7 +97,7 @@ struct rtspd_httprequest { #define CMD_AUTH_REQUIRED "auth_required" /* 'idle' means that it will accept the request fine but never respond - any data. Just keep the connection alive. */ + any data. Keep the connection alive. */ #define CMD_IDLE "idle" /* 'stream' means to send a never-ending stream of data */ @@ -890,7 +890,7 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) responsesize = count; do { - /* Ok, we send no more than 200 bytes at a time, just to make sure that + /* Ok, we send no more than 200 bytes at a time, to make sure that larger chunks are split up so that the client will need to do multiple recv() calls to get it and thus we exercise that code better */ size_t num = count; diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 94d632425166..fabf6864e04e 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -68,14 +68,13 @@ * 'exit_signal_handler' for both signals. * * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal - * will just set to one the global var 'got_exit_signal' storing in global var + * sets to one the global var 'got_exit_signal' storing in global var * 'exit_signal' the signal that triggered this change. * * Nothing fancy that could introduce problems is used, the program at certain * points in its normal flow checks if var 'got_exit_signal' is set and in - * case this is true it just makes its way out of loops and functions in - * structured and well behaved manner to achieve proper program cleanup and - * termination. + * case this is true it makes its way out of loops and functions in structured + * and well behaved manner to achieve proper program cleanup and termination. * * Even with the above mechanism implemented it is worthwhile to note that * other signals might still be received, or that there might be systems on @@ -172,7 +171,7 @@ static ssize_t write_wincon(int fd, const void *buf, size_t count) #endif /* On Windows, we sometimes get this for a broken pipe, seemingly - * when the client just closed stdin? */ + * when the client closed stdin? */ #define CURL_WIN32_EPIPE 109 /* @@ -897,7 +896,7 @@ static bool disc_handshake(void) return FALSE; } else if(!memcmp("QUIT", buffer, 4)) { - /* just die */ + /* die */ logmsg("quits"); return FALSE; } @@ -945,7 +944,7 @@ static bool juggle(curl_socket_t *sockfdp, } #ifdef HAVE_GETPPID - /* As a last resort, quit if sockfilt process becomes orphan. Just in case + /* As a last resort, quit if sockfilt process becomes orphan. In case parent ftpserver process has died without killing its sockfilt children */ if(getppid() <= 1) { logmsg("process becomes orphan, exiting"); @@ -1055,7 +1054,7 @@ static bool juggle(curl_socket_t *sockfdp, buffer[0], buffer[1], buffer[2], buffer[3]); if(!memcmp("PING", buffer, 4)) { - /* send reply on stdout, just proving we are alive */ + /* send reply on stdout, proving we are alive */ if(!write_stdout("PONG\n", 5)) return FALSE; } @@ -1073,7 +1072,7 @@ static bool juggle(curl_socket_t *sockfdp, return FALSE; } else if(!memcmp("QUIT", buffer, 4)) { - /* just die */ + /* die */ logmsg("quits"); return FALSE; } diff --git a/tests/server/sws.c b/tests/server/sws.c index c2f62384b8df..7dfa8b79e2fd 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -113,7 +113,7 @@ static const char *cmdfile = "log/server.cmd"; #define CMD_AUTH_REQUIRED "auth_required" /* 'idle' means that it will accept the request fine but never respond - any data. Just keep the connection alive. */ + any data. Keep the connection alive. */ #define CMD_IDLE "idle" /* 'stream' means to send a never-ending stream of data */ @@ -304,7 +304,7 @@ static int sws_parse_servercmd(struct sws_httprequest *req) else { logmsg("Unknown instruction found: %s", cmd); } - /* try to deal with CRLF or just LF */ + /* try to deal with CRLF or LF */ check = strchr(cmd, '\r'); if(!check) check = strchr(cmd, '\n'); @@ -966,7 +966,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) responsesize = count; do { - /* Ok, we send no more than N bytes at a time, just to make sure that + /* Ok, we send no more than N bytes at a time, to make sure that larger chunks are split up so that the client will need to do multiple recv() calls to get it and thus we exercise that code better */ size_t num = count; diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 44e7976f1cd4..d81d3d39d256 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -413,7 +413,7 @@ static ssize_t write_behind(struct testcase *test, int convert) b = &bfs[nextone]; if(b->counter < -1) /* anything to flush? */ - return 0; /* just nop if nothing to do */ + return 0; /* nop if nothing to do */ if(!test->ofile) { char outfile[256]; @@ -453,11 +453,11 @@ static ssize_t write_behind(struct testcase *test, int convert) while(ct--) { /* loop over the buffer */ c = (unsigned char)*p++; /* pick up a character */ if(prevchar == '\r') { /* if prev char was cr */ - if(c == '\n') /* if have cr,lf then just */ + if(c == '\n') /* if have cr,lf then */ curl_lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */ else if(c == '\0') /* if have cr,nul then */ - goto skipit; /* just skip over the putc */ - /* else just fall through and allow it */ + goto skipit; /* skip over the putc */ + /* else fall through and allow it */ } /* formerly putc(c, file); */ @@ -570,7 +570,7 @@ static int tftpd_parse_servercmd(struct testcase *req) else { logmsg("Unknown instruction found: %s", cmd); } - /* try to deal with CRLF or just LF */ + /* try to deal with CRLF or LF */ check = strchr(cmd, '\r'); if(!check) check = strchr(cmd, '\n'); @@ -858,7 +858,7 @@ static void recvtftp(struct testcase *test, const struct formats *pf) rap->th_block = htons(recvblock); (void)swrite(peer, &ackbuf.storage[0], 4); #if defined(HAVE_ALARM) && defined(SIGALRM) - mysignal(SIGALRM, justtimeout); /* just abort read on timeout */ + mysignal(SIGALRM, justtimeout); /* abort read on timeout */ alarm(rexmtval); #endif /* normally times out and quits */ diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 3f345ad74a44..7544c8b62fd1 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -67,7 +67,7 @@ use testutil qw( our $logfile; # server log filename, for logmsg #*************************************************************************** -# Just for convenience, test harness uses 'https' and 'httptls' literals as +# For convenience, test harness uses 'https' and 'httptls' literals as # values for 'proto' variable in order to differentiate different servers. # 'https' literal is used for stunnel based https test servers, and 'httptls' # is used for non-stunnel https test servers. diff --git a/tests/servers.pm b/tests/servers.pm index db01c1f501df..33fcaaa5507f 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -265,7 +265,7 @@ sub init_serverpidfile_hash { } ####################################################################### -# Check if a given child process has just died. Reaps it if so. +# Check if a given child process has died. Reaps it if so. # sub checkdied { my $pid = $_[0]; @@ -354,7 +354,7 @@ sub startnew { exec("exec $cmd") || die "Cannot exec() $cmd: $!"; # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } @@ -382,17 +382,17 @@ sub startnew { $pid2 = pidfromfile($pidfile, 0); if(($pid2 > 0) && pidexists($pid2)) { # if $pid2 is valid, then make sure this pid is alive, as - # otherwise it is just likely to be the _previous_ pidfile or + # otherwise it is likely to be the _previous_ pidfile or # similar! last; } if(checkdied($child)) { logmsg "startnew: child process has died, server might start up\n" if($verbose); - # We cannot just abort waiting for the server with a + # We cannot abort waiting for the server with a # return (-1,-1); # because the server might have forked and could still start - # up normally. Instead, just reduce the amount of time we remain + # up normally. Instead, reduce the amount of time we remain # waiting. $count >>= 2; } @@ -1012,7 +1012,7 @@ sub verifytelnet { # particular can take a long time to start if it needs to generate # keys on a slow or loaded host. # -# Just for convenience, test harness uses 'https' and 'httptls' literals +# For convenience, test harness uses 'https' and 'httptls' literals # as values for 'proto' variable in order to differentiate different # servers. 'https' literal is used for stunnel based https test servers, # and 'httptls' is used for non-stunnel https test servers. diff --git a/tests/smbserver.py b/tests/smbserver.py index 000dda76e76a..9b38c4695a73 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -76,7 +76,7 @@ def __enter__(self): signal.signal(signal.SIGTERM, self._sighandler) def __exit__(self, *_): - # Call for shutdown just in case it was not done already + # Call for shutdown in case it was not done already self.shutdown_event.set() # Wait for thread, and therefore also the server, to finish self.join() diff --git a/tests/sshserver.pl b/tests/sshserver.pl index 58e64d2f4ed1..4133e86db61f 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -1190,7 +1190,7 @@ sub sshd_supports_opt { # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } diff --git a/tests/test1119.pl b/tests/test1119.pl index 0c9ae70ed5f4..845453d8a10b 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -165,7 +165,7 @@ sub scanman_md_dir { my $ignored=0; for my $e (sort @syms) { - # OBSOLETE - names that are just placeholders for a position where we + # OBSOLETE - names that are placeholders for a position where we # previously had a name, that is now removed. The OBSOLETE names should # never be used for anything. # @@ -176,7 +176,7 @@ sub scanman_md_dir { # # CURL_TEMP_ - are defined and *undefined* again within the file # - # *_LAST and *_LASTENTRY are just suffix for the placeholders used for the + # *_LAST and *_LASTENTRY are suffix for the placeholders used for the # last entry in many enum series. # diff --git a/tests/test1173.pl b/tests/test1173.pl index 6e2c9141afca..baeff857626b 100755 --- a/tests/test1173.pl +++ b/tests/test1173.pl @@ -158,7 +158,7 @@ sub scanmanpage { while(<$m>) { chomp; if($_ =~ /^.so /) { - # this man page is just a referral + # this man page is a referral close($m); return; } diff --git a/tests/tftpserver.pl b/tests/tftpserver.pl index bb96bb743ead..2ea46dccf94b 100755 --- a/tests/tftpserver.pl +++ b/tests/tftpserver.pl @@ -38,7 +38,7 @@ BEGIN ); my $verbose = 0; # set to 1 for debugging -my $port = 8997; # just a default +my $port = 8997; # a default my $ipvnum = 4; # default IP version of tftp server my $idnum = 1; # default tftp server instance number my $proto = 'tftp'; # protocol the tftp server speaks diff --git a/tests/unit/unit1303.c b/tests/unit/unit1303.c index 8a10af69d606..49c48e3b2c21 100644 --- a/tests/unit/unit1303.c +++ b/tests/unit/unit1303.c @@ -44,7 +44,7 @@ static void t1303_stop(struct Curl_easy *easy) curl_global_cleanup(); } -/* BASE is just a define to make us fool around with decently large number so +/* BASE is a define to make us fool around with decently large number so that we are not zero-based */ #define BASE 1000000 diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index 90a75b7e5584..356fa2a6c985 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -46,7 +46,7 @@ static CURLcode t1609_setup(void) we set address using CURLOPT_RESOLVE, it usually marks as permanent (by setting timestamp to zero). However, if address already exists - in the cache, then it does not mark it, but just leaves it as it is. + in the cache, then it does not mark it, but leaves it as it is. So we fixing this by timestamp to zero if address already exists too. Test: diff --git a/tests/unit/unit1615.c b/tests/unit/unit1615.c index 7e26a158a13e..8ea9c3e7113f 100644 --- a/tests/unit/unit1615.c +++ b/tests/unit/unit1615.c @@ -108,7 +108,7 @@ static CURLcode test_unit1615(const char *arg) }; unsigned char output_buf[CURL_SHA512_256_DIGEST_LENGTH]; - const unsigned char *computed_hash; /* Just to mute compiler warning */ + const unsigned char *computed_hash; /* to mute compiler warning */ /* Mute compiler warnings in 'verify_memory' macros below */ computed_hash = output_buf; diff --git a/tests/unit/unit1625.c b/tests/unit/unit1625.c index be52ba2d83a8..370b65a4fa73 100644 --- a/tests/unit/unit1625.c +++ b/tests/unit/unit1625.c @@ -52,7 +52,7 @@ static CURLcode test_unit1625(const char *arg) { "Encoding: a, chunked, ninja", "Encoding:", "chunked", TRUE }, /* empty incoming header */ { "Encoding:", "Encoding:", "chunked", FALSE }, - /* just spaces in header */ + /* spaces in header */ { "Encoding: ", "Encoding:", "chunked", FALSE }, /* last among several with no spaces */ { "Encoding: ab,cd,ef,gh,ig,kl", "Encoding:", "kl", TRUE }, diff --git a/tests/unit/unit1652.c b/tests/unit/unit1652.c index 7d940df05593..3b1a5a99aefe 100644 --- a/tests/unit/unit1652.c +++ b/tests/unit/unit1652.c @@ -119,7 +119,7 @@ static CURLcode test_unit1652(const char *arg) * Any input that long or longer will truncated, ending in '...\n'. */ - /* A string just long enough to not be truncated */ + /* A string long enough to not be truncated */ memset(input, '\0', sizeof(input)); memset(input, 'A', 2045); Curl_infof(easy, "%s", input); @@ -130,7 +130,7 @@ static CURLcode test_unit1652(const char *arg) fail_unless(output[sizeof(output) - 1] == '\0', "No truncation of infof input"); - /* Just over the limit without newline for truncation via '...' */ + /* Over the limit without newline for truncation via '...' */ memset(input + 2045, 'A', 4); Curl_infof(easy, "%s", input); curl_mfprintf(stderr, "output len %zu: %s", strlen(output), output); @@ -138,7 +138,7 @@ static CURLcode test_unit1652(const char *arg) fail_unless(output[sizeof(output) - 1] == '\0', "Truncation of infof input 1"); - /* Just over the limit with newline for truncation via '...' */ + /* Over the limit with newline for truncation via '...' */ memset(input + 2045, 'A', 4); memset(input + 2045 + 4, '\n', 1); Curl_infof(easy, "%s", input); diff --git a/tests/unit/unit1655.c b/tests/unit/unit1655.c index 54f930b8b474..78a08a2d8b53 100644 --- a/tests/unit/unit1655.c +++ b/tests/unit/unit1655.c @@ -51,7 +51,7 @@ static CURLcode test_unit1655(const char *arg) static const char toolong[] = /* ..|....1.........2.........3.........4.........5.........6... */ /* 3456789012345678901234567890123456789012345678901234567890123 */ - "here.is.a.hostname.which.is.just.barely.too.long." /* 49: 49 */ + "here.is.a.hostname.which.is.only.barely.too.long." /* 49: 49 */ "to.be.encoded.as.a.QNAME.of.the.maximum.allowed.length." /* 55: 104 */ "which.is.256.including.a.final.zero-length.label." /* 49: 153 */ diff --git a/tests/unit/unit1660.c b/tests/unit/unit1660.c index 62b4ea009598..f9259f1c764b 100644 --- a/tests/unit/unit1660.c +++ b/tests/unit/unit1660.c @@ -45,7 +45,7 @@ static CURLcode test_unit1660(const char *arg) struct testit { const char *host; const char *chost; /* if non-NULL, use to lookup with */ - const char *hdr; /* if NULL, just do the lookup */ + const char *hdr; /* if NULL, do the lookup */ const CURLcode result; /* parse result */ }; diff --git a/tests/unit/unit1666.c b/tests/unit/unit1666.c index a86dc146a1e7..362b16b4d548 100644 --- a/tests/unit/unit1666.c +++ b/tests/unit/unit1666.c @@ -146,7 +146,7 @@ static CURLcode test_unit1666(const char *arg) "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01"), "", CURLE_TOO_LARGE }, - /* one byte shorter than the previous is just below the limit: */ + /* one byte shorter than the previous is below the limit: */ { OID("\x2b\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 47d91ce5530b..a28028c52b87 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -160,7 +160,7 @@ static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { struct cf_test_ctx *ctx = cf->ctx; - /* just for testing, give one socket with events back */ + /* for testing, give one socket with events back */ return Curl_pollset_set(data, ps, ctx->idx, TRUE, TRUE); } From c5000b786b1045d1c34f27f94aa009b126657669 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 1 Jun 2026 18:53:20 +0200 Subject: [PATCH 0276/1062] build: say Quiche support is experimental, where missing Follow-up to f2183f51b6651dae759164d064c62fa075d8f695 #21795 Closes #21832 --- CMakeLists.txt | 2 +- configure.ac | 6 +++--- docs/EXPERIMENTAL.md | 7 +++++++ docs/INSTALL-CMAKE.md | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 198f18944a1f..b3d0e7211652 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1167,7 +1167,7 @@ if(USE_NGTCP2) list(APPEND CURL_LIBS CURL::nghttp3) endif() -option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) +option(USE_QUICHE "Use quiche library for HTTP/3 support (experimental)" OFF) if(USE_QUICHE) if(USE_NGTCP2) message(FATAL_ERROR "Only one HTTP/3 backend can be selected") diff --git a/configure.ac b/configure.ac index b2a996aa0b75..38cf72111deb 100644 --- a/configure.ac +++ b/configure.ac @@ -3713,8 +3713,8 @@ if test "$disable_http" = "yes" || test "$USE_NGTCP" = "1"; then fi AC_ARG_WITH(quiche, -AS_HELP_STRING([--with-quiche=PATH],[Enable quiche usage]) -AS_HELP_STRING([--without-quiche],[Disable quiche usage]), +AS_HELP_STRING([--with-quiche=PATH],[Enable quiche usage (experimental)]) +AS_HELP_STRING([--without-quiche],[Disable quiche usage (experimental)]), [OPT_QUICHE=$withval]) case "$OPT_QUICHE" in no) @@ -3775,7 +3775,7 @@ if test "$want_quiche" != "no"; then AC_CHECK_LIB(quiche, quiche_conn_send_ack_eliciting, [ AC_CHECK_HEADERS(quiche.h, - experimental="$experimental HTTP3" + experimental="$experimental Quiche" AC_MSG_NOTICE([HTTP3 support is experimental]) curl_h3_msg="enabled (quiche)" AC_DEFINE(USE_QUICHE, 1, [if quiche is in use]) diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index ca8277fa14ca..9e39a9b36cb5 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -53,6 +53,13 @@ Graduation requirements: - implementation stability over time with no known severe regressions +### The Quiche backend + +Graduation requirements: + +- the library needs to consider itself non-beta. +- a reasonable expectation of a stable API going forward. + ### The Rustls backend Graduation requirements: diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 1ea6760ec177..1d8cd15a2936 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -357,7 +357,7 @@ Details via CMake - `USE_LIBIDN2`: Use libidn2 for IDN support. Default: `ON` - `USE_NGHTTP2`: Use nghttp2 library. Default: `ON` - `USE_NGTCP2`: Use ngtcp2 and nghttp3 libraries for HTTP/3 support. Default: `OFF` -- `USE_QUICHE`: Use quiche library for HTTP/3 support. Default: `OFF` +- `USE_QUICHE`: Use quiche library for HTTP/3 support (experimental). Default: `OFF` ## Dependency options (via CMake) From 4e98f6d22518def2a9bae972080daddadc96a7c2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 1 Jun 2026 01:29:14 +0200 Subject: [PATCH 0277/1062] units: drop redundant pointer check and workaround All users of the `verify_memory()` macro used a fixed-length buffer for the test output, which then needed a workaround to silence GCC `-Waddress` warnings. ``` tests/unit/unit1615.c: In function 'test_unit1615': tests/libtest/unitcheck.h:51:8: error: the address of 'output_buf' will always evaluate as 'true' [-Werror=address] 51 | if((dynamic) && memcmp(dynamic, check, len)) { \ | ^ tests/unit/unit1615.c:114:3: note: in expansion of macro 'verify_memory' 114 | verify_memory(output_buf, precomp_hash1, CURL_SHA512_256_DIGEST_LENGTH); | ^~~~~~~~~~~~~ ``` Drop redundant address check and the workarounds with it. Closes #21833 --- tests/libtest/unitcheck.h | 2 +- tests/unit/unit1600.c | 7 +++---- tests/unit/unit1601.c | 5 ++--- tests/unit/unit1610.c | 5 ++--- tests/unit/unit1611.c | 5 ++--- tests/unit/unit1612.c | 5 ++--- tests/unit/unit1615.c | 20 ++++++++------------ 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/tests/libtest/unitcheck.h b/tests/libtest/unitcheck.h index 462f8b45f32c..bc616d135b48 100644 --- a/tests/libtest/unitcheck.h +++ b/tests/libtest/unitcheck.h @@ -48,7 +48,7 @@ #define verify_memory(dynamic, check, len) \ do { \ - if((dynamic) && memcmp(dynamic, check, len)) { \ + if(memcmp(dynamic, check, len)) { \ curl_mfprintf(stderr, "%s:%d Memory buffer FAILED match size %d. " \ "'%s' is not\n", __FILE__, __LINE__, len, \ hexdump((const unsigned char *)(check), len)); \ diff --git a/tests/unit/unit1600.c b/tests/unit/unit1600.c index 649e92a4cab4..011faccccddf 100644 --- a/tests/unit/unit1600.c +++ b/tests/unit/unit1600.c @@ -53,18 +53,17 @@ static CURLcode test_unit1600(const char *arg) #if defined(USE_NTLM) && \ (!defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)) unsigned char output[21]; - const unsigned char *testp = output; Curl_ntlm_core_mk_nt_hash("1", output); - verify_memory(testp, + verify_memory(output, "\x69\x94\x3c\x5e\x63\xb4\xd2\xc1\x04\xdb" "\xbc\xc1\x51\x38\xb7\x2b\x00\x00\x00\x00\x00", 21); Curl_ntlm_core_mk_nt_hash("hello-you-fool", output); - verify_memory(testp, + verify_memory(output, "\x39\xaf\x87\xa6\x75\x0a\x7a\x00\xba\xa0" "\xd3\x4f\x04\x9e\xc1\xd0\x00\x00\x00\x00\x00", 21); @@ -77,7 +76,7 @@ static CURLcode test_unit1600(const char *arg) "AAAAAAAA", output); - verify_memory(testp, + verify_memory(output, "\x36\x9d\xae\x06\x84\x7e\xe1\xc1\x4a\x94\x39\xea\x6f\x44\x8c" "\x65\x00\x00\x00\x00\x00", 21); diff --git a/tests/unit/unit1601.c b/tests/unit/unit1601.c index 8d41be782245..8da6f4c2fcbc 100644 --- a/tests/unit/unit1601.c +++ b/tests/unit/unit1601.c @@ -34,16 +34,15 @@ static CURLcode test_unit1601(const char *arg) static const char string1[] = "1"; static const char string2[] = "hello-you-fool"; unsigned char output[MD5_DIGEST_LEN]; - const unsigned char *testp = output; Curl_md5it(output, (const unsigned char *)string1, strlen(string1)); - verify_memory(testp, "\xc4\xca\x42\x38\xa0\xb9\x23\x82\x0d\xcc\x50\x9a\x6f" + verify_memory(output, "\xc4\xca\x42\x38\xa0\xb9\x23\x82\x0d\xcc\x50\x9a\x6f" "\x75\x84\x9b", MD5_DIGEST_LEN); Curl_md5it(output, (const unsigned char *)string2, strlen(string2)); - verify_memory(testp, "\x88\x67\x0b\x6d\x5d\x74\x2f\xad\xa5\xcd\xf9\xb6\x82" + verify_memory(output, "\x88\x67\x0b\x6d\x5d\x74\x2f\xad\xa5\xcd\xf9\xb6\x82" "\x87\x5f\x22", MD5_DIGEST_LEN); #endif diff --git a/tests/unit/unit1610.c b/tests/unit/unit1610.c index eeb3ccceb74d..450b5cf3c2d2 100644 --- a/tests/unit/unit1610.c +++ b/tests/unit/unit1610.c @@ -41,11 +41,10 @@ static CURLcode test_unit1610(const char *arg) static const char string1[] = "1"; static const char string2[] = "hello-you-fool"; unsigned char output[CURL_SHA256_DIGEST_LENGTH]; - const unsigned char *testp = output; Curl_sha256it(output, (const unsigned char *)string1, strlen(string1)); - verify_memory(testp, + verify_memory(output, "\x6b\x86\xb2\x73\xff\x34\xfc\xe1\x9d\x6b\x80\x4e\xff\x5a\x3f" "\x57\x47\xad\xa4\xea\xa2\x2f\x1d\x49\xc0\x1e\x52\xdd\xb7\x87" "\x5b\x4b", @@ -53,7 +52,7 @@ static CURLcode test_unit1610(const char *arg) Curl_sha256it(output, (const unsigned char *)string2, strlen(string2)); - verify_memory(testp, + verify_memory(output, "\xcb\xb1\x6a\x8a\xb9\xcb\xb9\x35\xa8\xcb\xa0\x2e\x28\xc0\x26" "\x30\xd1\x19\x9c\x1f\x02\x17\xf4\x7c\x96\x20\xf3\xef\xe8\x27" "\x15\xae", diff --git a/tests/unit/unit1611.c b/tests/unit/unit1611.c index c22c5f1689c5..15e5e5b6d19e 100644 --- a/tests/unit/unit1611.c +++ b/tests/unit/unit1611.c @@ -32,18 +32,17 @@ static CURLcode test_unit1611(const char *arg) static const char string1[] = "1"; static const char string2[] = "hello-you-fool"; unsigned char output[MD4_DIGEST_LENGTH]; - const unsigned char *testp = output; Curl_md4it(output, (const unsigned char *)string1, strlen(string1)); - verify_memory(testp, + verify_memory(output, "\x8b\xe1\xec\x69\x7b\x14\xad\x3a\x53\xb3\x71\x43\x61\x20\x64" "\x1d", MD4_DIGEST_LENGTH); Curl_md4it(output, (const unsigned char *)string2, strlen(string2)); - verify_memory(testp, + verify_memory(output, "\xa7\x16\x1c\xad\x7e\xbe\xdb\xbc\xf8\xc7\x23\x10\x2d\x2c\xe2" "\x0b", MD4_DIGEST_LENGTH); diff --git a/tests/unit/unit1612.c b/tests/unit/unit1612.c index cc7c36c4c869..8518d659743a 100644 --- a/tests/unit/unit1612.c +++ b/tests/unit/unit1612.c @@ -36,14 +36,13 @@ static CURLcode test_unit1612(const char *arg) static const char string1[] = "1"; static const char string2[] = "hello-you-fool"; unsigned char output[HMAC_MD5_LENGTH]; - const unsigned char *testp = output; Curl_hmacit(&Curl_HMAC_MD5, (const unsigned char *)password, strlen(password), (const unsigned char *)string1, strlen(string1), output); - verify_memory(testp, + verify_memory(output, "\xd1\x29\x75\x43\x58\xdc\xab\x78\xdf\xcd\x7f\x2b\x29\x31\x13" "\x37", HMAC_MD5_LENGTH); @@ -53,7 +52,7 @@ static CURLcode test_unit1612(const char *arg) (const unsigned char *)string2, strlen(string2), output); - verify_memory(testp, + verify_memory(output, "\x75\xf1\xa7\xb9\xf5\x40\xe5\xa4\x98\x83\x9f\x64\x5a\x27\x6d" "\xd0", HMAC_MD5_LENGTH); diff --git a/tests/unit/unit1615.c b/tests/unit/unit1615.c index 8ea9c3e7113f..705ef392f1bb 100644 --- a/tests/unit/unit1615.c +++ b/tests/unit/unit1615.c @@ -108,42 +108,38 @@ static CURLcode test_unit1615(const char *arg) }; unsigned char output_buf[CURL_SHA512_256_DIGEST_LENGTH]; - const unsigned char *computed_hash; /* to mute compiler warning */ - - /* Mute compiler warnings in 'verify_memory' macros below */ - computed_hash = output_buf; Curl_sha512_256it(output_buf, (const unsigned char *)test_str1, CURL_ARRAYSIZE(test_str1) - 1); - verify_memory(computed_hash, precomp_hash1, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash1, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str2, CURL_ARRAYSIZE(test_str2) - 1); - verify_memory(computed_hash, precomp_hash2, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash2, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str3, CURL_ARRAYSIZE(test_str3) - 1); - verify_memory(computed_hash, precomp_hash3, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash3, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str4, CURL_ARRAYSIZE(test_str4) - 1); - verify_memory(computed_hash, precomp_hash4, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash4, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str5, CURL_ARRAYSIZE(test_str5) - 1); - verify_memory(computed_hash, precomp_hash5, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash5, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str6, CURL_ARRAYSIZE(test_str6) - 1); - verify_memory(computed_hash, precomp_hash6, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash6, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, (const unsigned char *)test_str7, CURL_ARRAYSIZE(test_str7) - 1); - verify_memory(computed_hash, precomp_hash7, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash7, CURL_SHA512_256_DIGEST_LENGTH); Curl_sha512_256it(output_buf, test_seq8, CURL_ARRAYSIZE(test_seq8)); - verify_memory(computed_hash, precomp_hash8, CURL_SHA512_256_DIGEST_LENGTH); + verify_memory(output_buf, precomp_hash8, CURL_SHA512_256_DIGEST_LENGTH); #endif /* CURL_HAVE_SHA512_256 */ From 2a639572043dc39d057e2a80214ba47a76dcc3d2 Mon Sep 17 00:00:00 2001 From: "Randall S. Becker" Date: Mon, 1 Jun 2026 21:18:00 +0100 Subject: [PATCH 0278/1062] capsule: include `arpa/inet.h` for `ntohs()` declaration Some platforms require inclusion of arpa/inet.h in order to use ntohs(). Follow-up to e78b1b3eccfa6a2e367a1225ea1b66dafcdac3c4 #21153 Closes #21834 --- lib/capsule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/capsule.c b/lib/capsule.c index 4f5d0e7e2497..1ba0ccfb849c 100644 --- a/lib/capsule.c +++ b/lib/capsule.c @@ -26,6 +26,10 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +#ifdef HAVE_ARPA_INET_H +#include /* for htons() */ +#endif + #include #include "urldata.h" #include "curlx/dynbuf.h" From 28341c303d6fdaabb5a9c97abc3ae04614e70693 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 1 Jun 2026 23:12:56 +0200 Subject: [PATCH 0279/1062] lib505: tidy up slist pointer use Bring code closer to `curl_slist_append()` man page and clarify variable names. Closes #21835 --- tests/libtest/lib505.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/libtest/lib505.c b/tests/libtest/lib505.c index 8eaa12b154a4..b618598507be 100644 --- a/tests/libtest/lib505.c +++ b/tests/libtest/lib505.c @@ -38,9 +38,8 @@ static CURLcode test_lib505(const char *URL) FILE *hd_src; int hd; curlx_struct_stat file_info; - struct curl_slist *hl; - - struct curl_slist *headerlist = NULL; + struct curl_slist *headerlist; + struct curl_slist *temp; static const char *buf_1 = "RNFR 505"; static const char *buf_2 = "RNTO 505-forreal"; @@ -92,24 +91,24 @@ static CURLcode test_lib505(const char *URL) /* build a list of commands to pass to libcurl */ - hl = curl_slist_append(headerlist, buf_1); - if(!hl) { + headerlist = curl_slist_append(NULL, buf_1); + if(!headerlist) { curl_mfprintf(stderr, "curl_slist_append() failed\n"); curl_easy_cleanup(curl); curl_global_cleanup(); curlx_fclose(hd_src); return TEST_ERR_MAJOR_BAD; } - headerlist = curl_slist_append(hl, buf_2); - if(!headerlist) { + temp = curl_slist_append(headerlist, buf_2); + if(!temp) { curl_mfprintf(stderr, "curl_slist_append() failed\n"); - curl_slist_free_all(hl); + curl_slist_free_all(headerlist); curl_easy_cleanup(curl); curl_global_cleanup(); curlx_fclose(hd_src); return TEST_ERR_MAJOR_BAD; } - headerlist = hl; + headerlist = temp; /* enable uploading */ test_setopt(curl, CURLOPT_UPLOAD, 1L); From ba600296d2a1d8a4e53a36eb4bd30c1f0d0152cc Mon Sep 17 00:00:00 2001 From: Josef Cejka Date: Wed, 20 May 2026 18:07:16 +0200 Subject: [PATCH 0280/1062] http: prefer chunked encoding over Content-Length: 0 Call http_size() before checking the request for empty body to prefer Transfer-Encoding: chunked even if Content-Length is 0. Closes #21706 --- lib/http.c | 19 ++++++----- tests/data/Makefile.am | 2 +- tests/data/test1677 | 77 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 tests/data/test1677 diff --git a/lib/http.c b/lib/http.c index 26f5280502f1..e16d15a446ff 100644 --- a/lib/http.c +++ b/lib/http.c @@ -4172,6 +4172,17 @@ static CURLcode http_on_response(struct Curl_easy *data, goto out; } + /* final response without error, prepare to receive the body */ + result = http_firstwrite(data); + if(result) + goto out; + + /* This is the last response that we get for the current request. Check on + * the body size and determine if the response is complete. */ + result = http_size(data); + if(result) + goto out; + /* If we requested a "no body", this is a good time to get * out and return home. */ @@ -4185,14 +4196,6 @@ static CURLcode http_on_response(struct Curl_easy *data, if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; - /* final response without error, prepare to receive the body */ - result = http_firstwrite(data); - - if(!result) - /* This is the last response that we get for the current request. Check on - * the body size and determine if the response is complete. */ - result = http_size(data); - out: if(last_hd) /* if not written yet, write it now */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 058be3d73488..defe69c9175c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -222,7 +222,7 @@ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ test1666 test1667 test1668 test1669 \ \ -test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ +test1670 test1671 test1672 test1673 test1674 test1675 test1676 test1677 \ \ test1680 test1681 test1682 test1683 test1684 test1685 \ \ diff --git a/tests/data/test1677 b/tests/data/test1677 new file mode 100644 index 000000000000..dbf5ca17a744 --- /dev/null +++ b/tests/data/test1677 @@ -0,0 +1,77 @@ + + + + +HTTP +HTTP POST +chunked Transfer-Encoding +Content-Length + + + +# Regression test for bug where curl stops reading chunked response +# when both Content-Length: 0 and Transfer-Encoding: chunked headers +# are present. Per RFC 7230 Section 3.3.3, Transfer-Encoding should +# take precedence and Content-Length should be ignored. +# The writedelay simulates the timing issue where chunks arrive in +# separate packets. + +# Server-side + + +writedelay: 500 + + +HTTP/1.1 200 OK +content-type: text/json +connection: keep-alive +content-length: 0 +transfer-encoding: chunked + +1a +random data in first chunk +1d + another data in second chunk +15 + third and last chunk +0 + + + + +# Client-side + + +http + + +HTTP POST response with both Transfer-Encoding chunked and Content-Length 0 + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -d "testdata" + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 8 +Content-Type: application/x-www-form-urlencoded + +testdata + + +HTTP/1.1 200 OK +content-type: text/json +connection: keep-alive +content-length: 0 +transfer-encoding: chunked + +random data in first chunk another data in second chunk third and last chunk + + + From 31cb54e1fa52cd954dba5ee1a902c6d4ae3746d1 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 1 Jun 2026 15:31:40 +0200 Subject: [PATCH 0281/1062] cfilters: remove close method closing a filter chain and reconnecting it again is a complication that only the HTTP/1.x proxy filter used. Remove it from all filters. Instead, a filter can return CURLE_AGAIN during the connect phase and the cf-setup filter will tear down all "sub filters" and restart over. With this, a filter never resets to the initial phase but progresses through connect -> connected -> shutdown -> destroy once. Closes #21831 --- lib/cf-capsule.c | 19 ------------------- lib/cf-dns.c | 8 -------- lib/cf-h1-proxy.c | 17 +---------------- lib/cf-h2-proxy.c | 16 ---------------- lib/cf-h3-proxy.c | 19 ------------------- lib/cf-haproxy.c | 18 ------------------ lib/cf-https-connect.c | 14 -------------- lib/cf-ip-happy.c | 17 ----------------- lib/cf-socket.c | 34 +++++++++------------------------- lib/cfilters.c | 32 ++------------------------------ lib/cfilters.h | 12 ------------ lib/connect.c | 35 ++++++++++++++++++++++++----------- lib/cshutdn.c | 4 ++-- lib/ftp.c | 1 - lib/http2.c | 27 ++------------------------- lib/http_proxy.c | 10 ---------- lib/socks.c | 9 --------- lib/vquic/curl_ngtcp2.c | 26 +++++++++----------------- lib/vquic/curl_quiche.c | 11 ----------- lib/vtls/vtls.c | 14 -------------- tests/unit/unit2600.c | 1 - 21 files changed, 49 insertions(+), 295 deletions(-) diff --git a/lib/cf-capsule.c b/lib/cf-capsule.c index 333a8d7efe0d..faf837dd6cfc 100644 --- a/lib/cf-capsule.c +++ b/lib/cf-capsule.c @@ -59,24 +59,6 @@ static void capsule_cf_destroy(struct Curl_cfilter *cf, } } -static void capsule_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_capsule_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(ctx) { - Curl_bufq_reset(&ctx->recvbuf); - curlx_safefree(ctx->pending); - ctx->pending_len = 0; - ctx->pending_offset = 0; - ctx->pending_payload = 0; - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode capsule_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -212,7 +194,6 @@ struct Curl_cftype Curl_cft_capsule = { 0, capsule_cf_destroy, capsule_cf_connect, - capsule_cf_close, Curl_cf_def_shutdown, Curl_cf_def_adjust_pollset, capsule_cf_data_pending, diff --git a/lib/cf-dns.c b/lib/cf-dns.c index b75b5620ebc7..3453202eb11e 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -310,13 +310,6 @@ static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 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) @@ -362,7 +355,6 @@ struct Curl_cftype Curl_cft_dns = { 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, diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 5dd02b2b0a06..f7974a99319d 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -757,9 +757,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, */ CURL_TRC_CF(data, cf, "CONNECT need to close+open"); infof(data, "Connect me again please"); - Curl_conn_cf_close(cf, data); - result = Curl_conn_cf_connect(cf->next, data, &done); - return result; + return CURLE_AGAIN; } else { /* staying on this connection, reset state */ @@ -905,18 +903,6 @@ static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, curlx_safefree(cf->ctx); } -static void cf_h1_proxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h1_proxy_ctx *pctx = cf->ctx; - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(pctx && pctx->ts) - h1_tunnel_go_state(cf, pctx->ts, H1_TUNNEL_INIT, data); - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) @@ -950,7 +936,6 @@ struct Curl_cftype Curl_cft_h1_proxy = { 0, cf_h1_proxy_destroy, cf_h1_proxy_connect, - cf_h1_proxy_close, Curl_cf_def_shutdown, cf_h1_proxy_adjust_pollset, cf_h1_proxy_data_pending, diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 316ed6c75a8a..f8e5acb0552f 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -1034,21 +1034,6 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, return result; } -static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_proxy_ctx_clear(ctx); - CF_DATA_RESTORE(cf, save); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1484,7 +1469,6 @@ struct Curl_cftype Curl_cft_h2_proxy = { CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, - cf_h2_proxy_close, cf_h2_proxy_shutdown, cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index d5fb45e73dff..eee70e5e817f 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -3374,24 +3374,6 @@ static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, } } -static void cf_h3_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *ctx = cf->ctx; - - if(ctx) { - if(ctx->ngtcp2_ctx) { - cf_ngtcp2_proxy_close(cf, data); - cf_ngtcp2_proxy_ctx_free(ctx->ngtcp2_ctx); - ctx->ngtcp2_ctx = NULL; - } - cf_h3_proxy_ctx_clear(ctx); - cf->connected = FALSE; - } - - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { @@ -3404,7 +3386,6 @@ struct Curl_cftype Curl_cft_h3_proxy = { CURL_LOG_LVL_NONE, cf_h3_proxy_destroy, cf_h3_proxy_connect, - cf_h3_proxy_close, cf_h3_proxy_shutdown, cf_h3_proxy_adjust_pollset, cf_h3_proxy_data_pending, diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index afa7b55b7813..1b9e0791f15a 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -45,13 +45,6 @@ struct cf_haproxy_ctx { struct dynbuf data_out; }; -static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) -{ - DEBUGASSERT(ctx); - ctx->state = HAPROXY_INIT; - curlx_dyn_reset(&ctx->data_out); -} - static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) { if(ctx) { @@ -173,16 +166,6 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, cf_haproxy_ctx_free(cf->ctx); } -static void cf_haproxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - cf_haproxy_ctx_reset(cf->ctx); - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -202,7 +185,6 @@ struct Curl_cftype Curl_cft_haproxy = { 0, cf_haproxy_destroy, cf_haproxy_connect, - cf_haproxy_close, Curl_cf_def_shutdown, cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 8e8f73138c05..c42701a15e49 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -62,7 +62,6 @@ static void cf_hc_baller_discard(struct cf_hc_baller *b, struct Curl_easy *data) { if(b->cf) { - Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } @@ -731,18 +730,6 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, return result; } -static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf_hc_ctx_close(data, cf->ctx); - cf->connected = FALSE; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; @@ -757,7 +744,6 @@ struct Curl_cftype Curl_cft_http_connect = { CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, - cf_hc_close, cf_hc_shutdown, cf_hc_adjust_pollset, cf_hc_data_pending, diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index cfada937c86b..e2a49b82aaba 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -891,22 +891,6 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, return result; } -static void cf_ip_happy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ip_happy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf_ip_happy_ctx_clear(cf, data); - cf->connected = FALSE; - ctx->state = SCFST_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -971,7 +955,6 @@ struct Curl_cftype Curl_cft_ip_happy = { CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, - cf_ip_happy_close, cf_ip_happy_shutdown, cf_ip_happy_adjust_pollset, cf_ip_happy_data_pending, diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 729f8748bfc9..8673562de280 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -972,24 +972,6 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, return CURLE_OK; } -static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - if(ctx && ctx->sock != CURL_SOCKET_BAD) { - CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); - if(ctx->sock == cf->conn->sock[cf->sockindex]) - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - ctx->active = FALSE; - memset(&ctx->started_at, 0, sizeof(ctx->started_at)); - memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); - } - - cf->connected = FALSE; -} - static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1016,10 +998,16 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; - cf_socket_close(cf, data); CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); - cf->ctx = NULL; + if(ctx) { + if(ctx->sock != CURL_SOCKET_BAD) { + CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); + if(ctx->sock == cf->conn->sock[cf->sockindex]) + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + } + curlx_free(ctx); + } } static void set_local_ip(struct Curl_cfilter *cf, @@ -1752,7 +1740,6 @@ struct Curl_cftype Curl_cft_tcp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1920,7 +1907,6 @@ struct Curl_cftype Curl_cft_udp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_udp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1976,7 +1962,6 @@ struct Curl_cftype Curl_cft_unix = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -2208,7 +2193,6 @@ struct Curl_cftype Curl_cft_tcp_accept = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_accept_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/cfilters.c b/lib/cfilters.c index 46c17c199d99..b2ad4a03cb94 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -33,17 +33,6 @@ #include "select.h" #include "curlx/strparse.h" -#ifdef UNITTESTS -/* @unittest 2600 */ -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} -#endif - CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { @@ -169,22 +158,11 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, void Curl_conn_cf_discard_all(struct Curl_easy *data, struct connectdata *conn, int sockindex) { + struct curltime *pt = &conn->shutdown.start[sockindex]; + memset(pt, 0, sizeof(*pt)); Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -void Curl_conn_close(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data->conn); - /* it is valid to call that without filters being present */ - cf = data->conn->cfilter[sockindex]; - if(cf) { - cf->cft->do_close(cf, data); - } - Curl_shutdown_clear(data, sockindex); -} - CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) { struct Curl_cfilter *cf; @@ -426,12 +404,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf) - cf->cft->do_close(cf, data); -} - CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) diff --git a/lib/cfilters.h b/lib/cfilters.h index f4a03b8a2b81..17cc634b37f6 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -39,10 +39,6 @@ struct curl_tlssessioninfo; typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data); -/* Callback to close the connection immediately. */ -typedef void Curl_cft_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - /* Callback to close the connection filter gracefully, non-blocking. * Implementations MUST NOT chain calls to cf->next. */ @@ -218,7 +214,6 @@ struct Curl_cftype { int log_level; /* log level for such filters */ Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ Curl_cft_connect *do_connect; /* establish connection */ - Curl_cft_close *do_close; /* close conn */ Curl_cft_shutdown *do_shutdown; /* shutdown conn */ Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ Curl_cft_data_pending *has_data_pending; /* conn has data pending */ @@ -322,7 +317,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); @@ -433,12 +427,6 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data, const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn); -/** - * Close the filter chain at `sockindex` for connection `data->conn`. - * Filters remain in place and may be connected again afterwards. - */ -void Curl_conn_close(struct Curl_easy *data, int sockindex); - /** * Shutdown the connection at `sockindex` non-blocking, using timeout * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. diff --git a/lib/connect.c b/lib/connect.c index 9d74e35bc76e..119231a96a83 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -341,6 +341,7 @@ struct cf_setup_ctx { cf_setup_state state; int ssl_mode; uint8_t transport; + uint8_t retry_count; }; #ifndef CURL_DISABLE_PROXY @@ -538,9 +539,9 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, return result; } -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; @@ -600,19 +601,32 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static void cf_setup_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result; - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - ctx->state = CF_SETUP_INIT; + /* In some situations, a server/proxy may close the connection and + * we need to connect again (HTTP/1.x proxy auth, for example). + * We used to close the filters and reuse them for another attempt, + * however that complicates filter code and it is simpler to tear them + * all down and start over. */ +retry: + result = cf_setup_connect_steps(cf, data, done); - if(cf->next) { - cf->next->cft->do_close(cf->next, data); + if(result == CURLE_AGAIN) { + ++ctx->retry_count; + if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */ + return CURLE_COULDNT_CONNECT; + + CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count); Curl_conn_cf_discard_chain(&cf->next, data); + ctx->state = CF_SETUP_INIT; + goto retry; } + return result; } static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -629,7 +643,6 @@ struct Curl_cftype Curl_cft_setup = { CURL_LOG_LVL_NONE, cf_setup_destroy, cf_setup_connect, - cf_setup_close, Curl_cf_def_shutdown, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/cshutdn.c b/lib/cshutdn.c index f284c0ea7c89..cc77e401fd32 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -150,8 +150,8 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T, conn->bits.shutdown_filters ? "" : "force ", conn->connection_id); - Curl_conn_close(admin, SECONDARYSOCKET); - Curl_conn_close(admin, FIRSTSOCKET); + Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET); + Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); Curl_detach_connection(admin); if(data->multi) diff --git a/lib/ftp.c b/lib/ftp.c index 5a37856fd6fb..864fb1509c3b 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -355,7 +355,6 @@ static void close_secondarysocket(struct Curl_easy *data, { (void)ftpc; CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc)); - Curl_conn_close(data, SECONDARYSOCKET); Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } diff --git a/lib/http2.c b/lib/http2.c index ab70455faee9..736c04e10095 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -189,6 +189,8 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) { if(ctx && ctx->initialized) { + if(ctx->h2) + nghttp2_session_del(ctx->h2); Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); Curl_bufcp_free(&ctx->stream_bufcp); @@ -199,14 +201,6 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) curlx_free(ctx); } -static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) -{ - if(ctx->h2) { - nghttp2_session_del(ctx->h2); - ctx->h2 = NULL; - } -} - static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) { #if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE @@ -2550,22 +2544,6 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, return result; } -static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_ctx_close(ctx); - CF_DATA_RESTORE(cf, save); - cf->connected = FALSE; - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; @@ -2788,7 +2766,6 @@ struct Curl_cftype Curl_cft_nghttp2 = { CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, - cf_h2_close, cf_h2_shutdown, cf_h2_adjust_pollset, cf_h2_data_pending, diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 9f1ed7963c5c..17e834aaf262 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -709,22 +709,12 @@ static void http_proxy_cf_destroy(struct Curl_cfilter *cf, } } -static void http_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP, 0, http_proxy_cf_destroy, http_proxy_cf_connect, - http_proxy_cf_close, Curl_cf_def_shutdown, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/socks.c b/lib/socks.c index 27c3c714f53a..8c675e7bbe60 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1289,14 +1289,6 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, return result; } -static void socks_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1339,7 +1331,6 @@ struct Curl_cftype Curl_cft_socks_proxy = { 0, socks_proxy_cf_destroy, socks_proxy_cf_connect, - socks_proxy_cf_close, Curl_cf_def_shutdown, socks_cf_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index deb21e882dca..796a5b782ae0 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -2389,26 +2389,19 @@ static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, cf_ngtcp2_shutdown(cf, data, &done); } -static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->qconn) { - cf_ngtcp2_conn_close(cf, data); - cf_ngtcp2_ctx_close(ctx); - CURL_TRC_CF(data, cf, "close"); - } - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} -static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ CURL_TRC_CF(data, cf, "destroy"); - if(cf->ctx) { - cf_ngtcp2_close(cf, data); + if(ctx) { + if(ctx->qconn) { + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); + cf_ngtcp2_conn_close(cf, data); + cf_ngtcp2_ctx_close(ctx); + CF_DATA_RESTORE(cf, save); + } cf_ngtcp2_ctx_free(cf->ctx); cf->ctx = NULL; } @@ -3087,7 +3080,6 @@ struct Curl_cftype Curl_cft_http3 = { 0, cf_ngtcp2_destroy, cf_ngtcp2_connect, - cf_ngtcp2_close, cf_ngtcp2_shutdown, cf_ngtcp2_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 08b02fec78f6..04b4f7d6db2d 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1499,16 +1499,6 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, return result; } -static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf->ctx) { - bool done; - (void)cf_quiche_shutdown(cf, data, &done); - cf_quiche_ctx_close(cf->ctx); - cf->connected = FALSE; - } -} - static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; @@ -1626,7 +1616,6 @@ struct Curl_cftype Curl_cft_http3 = { 0, cf_quiche_destroy, cf_quiche_connect, - cf_quiche_close, cf_quiche_shutdown, cf_quiche_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index c147a2ef19df..4c456a7fcb54 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -978,18 +978,6 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf->ctx = NULL; } -static void ssl_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - if(cf->next) - cf->next->cft->do_close(cf->next, data); - CF_DATA_RESTORE(cf, save); -} - static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1351,7 +1339,6 @@ struct Curl_cftype Curl_cft_ssl = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1371,7 +1358,6 @@ struct Curl_cftype Curl_cft_ssl_proxy = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index a28028c52b87..96683052daba 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -177,7 +177,6 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, CURL_LOG_LVL_NONE, cf_test_destroy, cf_test_connect, - cf_def_close, Curl_cf_def_shutdown, cf_test_adjust_pollset, Curl_cf_def_data_pending, From 669e795e9427cbda973a0ba9f8f4028716f0cdd4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 2 Jun 2026 08:31:40 +0200 Subject: [PATCH 0282/1062] Makefile.am: drop test1190 listed twice Spotted by GitHub Code Quality Closes #21839 --- tests/data/Makefile.am | 136 ++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index defe69c9175c..da4bdbfbce7a 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -159,72 +159,69 @@ test1157 test1158 test1159 test1160 test1161 test1162 test1163 test1164 \ test1165 test1166 test1167 test1168 test1169 test1170 test1171 test1172 \ test1173 test1174 test1175 test1176 test1177 test1178 test1179 test1180 \ test1181 test1182 test1183 test1184 test1185 test1186 test1187 test1188 \ -test1189 test1190 test1190 test1191 test1192 test1193 test1194 test1195 \ -test1196 test1197 test1198 test1199 test1200 test1201 test1202 test1203 \ -test1204 test1205 test1206 test1207 test1208 test1209 test1210 test1211 \ -test1212 test1213 test1214 test1215 test1216 test1217 test1218 test1219 \ -test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 \ -test1228 test1229 test1230 test1231 test1232 test1233 test1234 test1235 \ -test1236 test1237 test1238 test1239 test1240 test1241 test1242 test1243 \ -test1244 test1245 test1246 test1247 test1248 test1249 test1250 test1251 \ -test1252 test1253 test1254 test1255 test1256 test1257 test1258 test1259 \ -test1260 test1261 test1262 test1263 test1264 test1265 test1266 test1267 \ -test1268 test1269 test1270 test1271 test1272 test1273 test1274 test1275 \ -test1276 test1277 test1278 test1279 test1280 test1281 test1282 test1283 \ -test1284 test1285 test1286 test1287 test1288 test1289 test1290 test1291 \ -test1292 test1293 test1294 test1295 test1296 test1297 test1298 test1299 \ -test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \ -test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \ -test1316 test1317 test1318 test1319 test1320 test1321 test1322 test1323 \ -test1324 test1325 test1326 test1327 test1328 test1329 test1330 test1331 \ -test1332 test1333 test1334 test1335 test1336 test1337 test1338 test1339 \ -test1340 test1341 test1342 test1343 test1344 test1345 test1346 test1347 \ -test1348 test1349 test1350 test1351 test1352 test1353 test1354 test1355 \ -test1356 test1357 test1358 test1359 test1360 test1361 test1362 test1363 \ -test1364 test1365 test1366 test1367 test1368 test1369 test1370 test1371 \ -test1372 test1373 test1374 test1375 test1376 test1377 test1378 test1379 \ -test1380 test1381 test1382 test1383 test1384 test1385 test1386 test1387 \ -test1388 test1389 test1390 test1391 test1392 test1393 test1394 test1395 \ -test1396 test1397 test1398 test1399 test1400 test1401 test1402 test1403 \ -test1404 test1405 test1406 test1407 test1408 test1409 test1410 test1411 \ -test1412 test1413 test1414 test1415 test1416 test1417 test1418 test1419 \ -test1420 test1421 test1422 test1423 test1424 test1425 test1426 test1427 \ -test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \ -test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \ -test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \ -test1452 test1453 test1454 test1455 test1456 test1457 test1458 test1459 \ -test1460 test1461 test1462 test1463 test1464 test1465 test1466 test1467 \ -test1468 test1469 test1470 test1471 test1472 test1473 test1474 test1475 \ -test1476 test1477 test1478 test1479 test1480 test1481 test1482 test1483 \ -test1484 test1485 test1486 test1487 test1488 test1489 test1490 test1491 \ -test1492 test1493 test1494 test1495 test1496 test1497 test1498 test1499 \ -test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ -test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ -test1516 test1517 test1518 test1519 test1520 test1521 test1522 test1523 \ -test1524 test1525 test1526 test1527 test1528 test1529 test1530 test1531 \ -test1532 test1533 test1534 test1535 test1536 test1537 test1538 test1539 \ -test1540 test1541 test1542 test1543 test1544 test1545 test1546 test1547 \ -test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 \ -test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 \ -test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 \ -test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 \ -test1580 test1581 test1582 test1583 test1584 test1585 test1586 test1587 \ -test1588 test1589 test1590 test1591 test1592 test1593 test1594 test1595 \ -test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ -test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ -test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ -test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ -test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 \ -test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 \ -test1644 test1645 test1646 test1647 test1648 test1649 \ -\ -test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ -test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ -test1666 test1667 test1668 test1669 \ -\ -test1670 test1671 test1672 test1673 test1674 test1675 test1676 test1677 \ -\ -test1680 test1681 test1682 test1683 test1684 test1685 \ +test1189 test1190 test1191 test1192 test1193 test1194 test1195 test1196 \ +test1197 test1198 test1199 test1200 test1201 test1202 test1203 test1204 \ +test1205 test1206 test1207 test1208 test1209 test1210 test1211 test1212 \ +test1213 test1214 test1215 test1216 test1217 test1218 test1219 test1220 \ +test1221 test1222 test1223 test1224 test1225 test1226 test1227 test1228 \ +test1229 test1230 test1231 test1232 test1233 test1234 test1235 test1236 \ +test1237 test1238 test1239 test1240 test1241 test1242 test1243 test1244 \ +test1245 test1246 test1247 test1248 test1249 test1250 test1251 test1252 \ +test1253 test1254 test1255 test1256 test1257 test1258 test1259 test1260 \ +test1261 test1262 test1263 test1264 test1265 test1266 test1267 test1268 \ +test1269 test1270 test1271 test1272 test1273 test1274 test1275 test1276 \ +test1277 test1278 test1279 test1280 test1281 test1282 test1283 test1284 \ +test1285 test1286 test1287 test1288 test1289 test1290 test1291 test1292 \ +test1293 test1294 test1295 test1296 test1297 test1298 test1299 test1300 \ +test1301 test1302 test1303 test1304 test1305 test1306 test1307 test1308 \ +test1309 test1310 test1311 test1312 test1313 test1314 test1315 test1316 \ +test1317 test1318 test1319 test1320 test1321 test1322 test1323 test1324 \ +test1325 test1326 test1327 test1328 test1329 test1330 test1331 test1332 \ +test1333 test1334 test1335 test1336 test1337 test1338 test1339 test1340 \ +test1341 test1342 test1343 test1344 test1345 test1346 test1347 test1348 \ +test1349 test1350 test1351 test1352 test1353 test1354 test1355 test1356 \ +test1357 test1358 test1359 test1360 test1361 test1362 test1363 test1364 \ +test1365 test1366 test1367 test1368 test1369 test1370 test1371 test1372 \ +test1373 test1374 test1375 test1376 test1377 test1378 test1379 test1380 \ +test1381 test1382 test1383 test1384 test1385 test1386 test1387 test1388 \ +test1389 test1390 test1391 test1392 test1393 test1394 test1395 test1396 \ +test1397 test1398 test1399 test1400 test1401 test1402 test1403 test1404 \ +test1405 test1406 test1407 test1408 test1409 test1410 test1411 test1412 \ +test1413 test1414 test1415 test1416 test1417 test1418 test1419 test1420 \ +test1421 test1422 test1423 test1424 test1425 test1426 test1427 test1428 \ +test1429 test1430 test1431 test1432 test1433 test1434 test1435 test1436 \ +test1437 test1438 test1439 test1440 test1441 test1442 test1443 test1444 \ +test1445 test1446 test1447 test1448 test1449 test1450 test1451 test1452 \ +test1453 test1454 test1455 test1456 test1457 test1458 test1459 test1460 \ +test1461 test1462 test1463 test1464 test1465 test1466 test1467 test1468 \ +test1469 test1470 test1471 test1472 test1473 test1474 test1475 test1476 \ +test1477 test1478 test1479 test1480 test1481 test1482 test1483 test1484 \ +test1485 test1486 test1487 test1488 test1489 test1490 test1491 test1492 \ +test1493 test1494 test1495 test1496 test1497 test1498 test1499 test1500 \ +test1501 test1502 test1503 test1504 test1505 test1506 test1507 test1508 \ +test1509 test1510 test1511 test1512 test1513 test1514 test1515 test1516 \ +test1517 test1518 test1519 test1520 test1521 test1522 test1523 test1524 \ +test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \ +test1533 test1534 test1535 test1536 test1537 test1538 test1539 test1540 \ +test1541 test1542 test1543 test1544 test1545 test1546 test1547 test1548 \ +test1549 test1550 test1551 test1552 test1553 test1554 test1555 test1556 \ +test1557 test1558 test1559 test1560 test1561 test1562 test1563 test1564 \ +test1565 test1566 test1567 test1568 test1569 test1570 test1571 test1572 \ +test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 \ +test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 \ +test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ +test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 \ +test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 \ +test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 \ +test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 \ +test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ +test1637 test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ +test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 \ +test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ +test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ +test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ +test1677 test1680 test1681 test1682 test1683 test1684 \ +test1685 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ @@ -239,10 +236,9 @@ test1916 test1917 test1918 test1919 test1920 test1921 \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ test1955 test1956 test1957 test1958 test1959 test1960 test1964 test1965 \ -test1966 test1967 \ -\ -test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ -test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ +test1966 test1967 test1970 test1971 test1972 test1973 \ +test1974 test1975 test1976 test1977 test1978 test1979 test1980 test1981 \ +test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ From 7d2382ebfacc7a3c7bdfb47994be9341beee645a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 08:18:53 +0200 Subject: [PATCH 0283/1062] tool_help: rectify a bad assert The condition was wrong, and now it also verifies 'tlen'. Reported-by: Gao Liyou Ref: #21825 Closes #21837 --- src/tool_help.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tool_help.c b/src/tool_help.c index 212972f38248..c9d76d5d2072 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -166,8 +166,9 @@ void inithelpscan(struct scan_ctx *ctx, ctx->flen = strlen(arg); ctx->endarg = endarg; ctx->elen = strlen(endarg); - DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) || - (ctx->flen < sizeof(ctx->rbuf))); + DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) && + (ctx->flen < sizeof(ctx->rbuf)) && + (ctx->tlen < sizeof(ctx->rbuf))); ctx->show = 0; ctx->olen = 0; memset(ctx->rbuf, 0, sizeof(ctx->rbuf)); From b825417043faa016a52ee400ce3a1c94f719a6e4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 08:10:47 +0200 Subject: [PATCH 0284/1062] tool_operhlp: avoid NULL to %s If the filename allocation fails. Reported-by: Gao Liyou Ref: #21825 Closes #21836 --- src/tool_operhlp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tool_operhlp.c b/src/tool_operhlp.c index 0e360cbcbde5..316393abb8af 100644 --- a/src/tool_operhlp.c +++ b/src/tool_operhlp.c @@ -175,6 +175,8 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) return result; } +#define DEFAULT_FILENAME "curl_response" + /* Extracts the name portion of the URL. * Returns a pointer to a heap-allocated string or NULL if * no name part, at location indicated by first argument. @@ -218,8 +220,9 @@ CURLcode get_url_file_name(char **filename, const char *url, SANITIZEcode *sc) } else { /* no slash => empty string, use default */ - *filename = curlx_strdup("curl_response"); - warnf("No remote filename, uses \"%s\"", *filename); + *filename = curlx_strdup(DEFAULT_FILENAME); + if(*filename) + warnf("No remote filename, uses \"" DEFAULT_FILENAME "\""); } curl_free(path); From 4c49ed1b7b817d5a4e991a88aa1b5a68e6c7975e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 2 Jun 2026 09:10:44 +0200 Subject: [PATCH 0285/1062] os400sys: fix theoretical length overflows When converting a `size_t` to `unsigned int`. Another instance spotted by Copilot. Reported-by: Gao Liyou Ref: #21825 Closes #21840 --- projects/OS400/os400sys.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index 36ed2480ead6..dbceaf62593a 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -370,6 +370,10 @@ static int Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf) return 0; } +/* Max string input length is a precaution against abuse and to detect junk + input easier and better. */ +#define CURL_MAX_INPUT_LENGTH 8000000 + OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, gss_OID in_name_type, gss_name_t *out_name) { @@ -381,7 +385,14 @@ OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, return gss_import_name(minor_status, in_name, in_name_type, out_name); memcpy((char *)&in, (char *)in_name, sizeof(in)); - i = in.length; + if(in.length > CURL_MAX_INPUT_LENGTH) { + if(minor_status) + /* !checksrc! disable ERRNOVAR 1 */ + *minor_status = ENOMEM; + + return GSS_S_FAILURE; + } + i = (unsigned int)in.length; in.value = malloc(i + 1); if(!in.value) { @@ -445,8 +456,15 @@ Curl_gss_init_sec_context_a(OM_uint32 *minor_status, if(inp) { if(inp->length && inp->value) { - unsigned int i = inp->length; + unsigned int i; + if(inp->length > CURL_MAX_INPUT_LENGTH) { + if(minor_status) + /* !checksrc! disable ERRNOVAR 1 */ + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + i = (unsigned int)inp->length; in.value = malloc(i + 1); if(!in.value) { if(minor_status) From 277db5490c3b0be58020cfdec17cd11e5cfb8c08 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 10:56:37 +0200 Subject: [PATCH 0286/1062] URL-SYNTAX: document more URL parsing details - IPv4 numerical address - IPv6 numerical address + zone id mention - No IPvFuture support - Some path parsing details Closes #21841 --- docs/URL-SYNTAX.md | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/docs/URL-SYNTAX.md b/docs/URL-SYNTAX.md index 2cd74d341bec..219e84ee932c 100644 --- a/docs/URL-SYNTAX.md +++ b/docs/URL-SYNTAX.md @@ -11,8 +11,8 @@ SPDX-License-Identifier: curl The official "URL syntax" is primarily defined in these two different specifications: -- [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) (although URL is called - "URI" in there) +- [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) (although URL is + called "URI" in there) - [The WHATWG URL Specification](https://url.spec.whatwg.org/) RFC 3986 is the earlier one, and curl has always tried to adhere to that one @@ -151,10 +151,9 @@ schemes: ## Userinfo -The userinfo field can be used to set username and password for -authentication purposes in this transfer. The use of this field is discouraged -since it often means passing around the password in plain text and is thus a -security risk. +The userinfo field can be used to set username and password for authentication +purposes in this transfer. The use of this field is discouraged since it often +means passing around the password in plain text and is thus a security risk. URLs for IMAP, POP3 and SMTP also support *login options* as part of the userinfo field. They are provided as a semicolon after the password and then @@ -176,6 +175,40 @@ brackets). For example: https://[2001:1890:1112:1::20]/ +libcurl rejects hostnames with more than one trailing dot. + +### Numerical IPv4 addresses + +libcurl parses and normalizes everything that appears to be a numerical IPv4 +address. Including octal and hexadecimal formats and using one, two, three or +four number groups. + +This normalizing is done so that curl can properly get documents from HTTP +servers (with the correctly formatted address in the `Host:` header), so that +IP based filtering for things like the `NO_PROXY` environment variable has a +higher chance of working correctly, to increase the chances that two URLs can +be compared and to allow users to extract and visualize the address in a readable +way and to make sure libcurl works identically across different name resolver +libraries and function calls. + +For a hostname that is only an IPv4 address with a trailing dot, the trailing +dot is removed in the normalizing process. + +### Numerical IPv6 addresses + +libcurl allows a zone id to be provided with a numerical IPv6 address, +separated with a percent character (`%`). The percent character may also be +percent-encoded as `%25`. Like this: + + http://[fe80::1%25eth0]/ + + http://[fe80::1%eth0]/ + +### `IPvFuture` + +RFC 3986 documents a numerical IP address format called `IPvFuture`. libcurl +does not recognize this format. Using it causes parse errors. + ### "localhost" Starting in curl 7.77.0, curl uses loopback IP addresses for the name @@ -212,6 +245,14 @@ DICT 2628, FTP 21, FTPS 990, GOPHER 70, GOPHERS 70, HTTP 80, HTTPS 443, IMAP 143, IMAPS 993, LDAP 389, LDAPS 636, MQTT 1883, POP3 110, POP3S 995, RTSP 554, SCP 22, SFTP 22, SMB 445, SMBS 445, SMTP 25, SMTPS 465, TELNET 23, TFTP 69 +## Path + +By default, libcurl removes sequences of `/./` and `/../` from the path as per +RFC 3986. + +libcurl might also normalize percent-encoded sequences to use uppercase +hexadecimal letters. + # Scheme specific behaviors ## FTP From d2f8e231a7548ef77db5438f21d484936775b72f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 08:31:00 +0200 Subject: [PATCH 0287/1062] KNOWN_BUGS: Digest does not care for 'domain' Room for improvement. Closes #21838 --- docs/KNOWN_BUGS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index b0702bc7a45e..32a76242efd0 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -215,6 +215,12 @@ https://curl.se/mail/lib-2012-07/0073.html We do not support auth-int for Digest using PUT or POST +## Digest does not care for `domain` + +libcurl ignores the `domain` directive in Digest authentication challenges +(`WWW-Authenticate:`). RFC 7616 defines it as a quoted, space-separated list +of URIs that define the protection space. + ## MIT Kerberos for Windows build libcurl fails to build with MIT Kerberos for Windows (`KfW`) due to its From 9d19b4730277e94c330d15c4ae2e1cb271514ff7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 10:58:15 +0200 Subject: [PATCH 0288/1062] lib1560: verify a few more URL variations Closes #21842 --- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/data/test1560 b/tests/data/test1560 index 7f78aece2f13..bd72d35d29f3 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -37,7 +37,7 @@ lib%TESTNUMBER success -Allocations: 3250 +Allocations: 3350 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 35ea5c4195e9..de218dded8ae 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -626,6 +626,51 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + /* IPvFuture format */ + {"http://[v1.fe80::abcd]/", "", 0, 0, CURLUE_BAD_IPV6}, + + /* trailing dot on valid host */ + {"http://example.com./", "http://example.com./", 0, 0, CURLUE_OK}, + + /* the exact upper valid port boundary */ + {"http://host:65535/", "http://host:65535/", 0, 0, CURLUE_OK}, + + /* Internationalized path (not host). */ + {"https://example.com/r\xc3\xa4ksm\xc3\xb6rg\xc3\xa5s", + "https://example.com/r%C3%A4ksm%C3%B6rg%C3%A5s", + CURLU_URLENCODE, 0, CURLUE_OK}, + + /* weird fragments */ + {"http://host/#a#b", "http://host/#a#b", 0, 0, CURLUE_OK}, + + /* Empty query parameter values */ + {"http://host/?a=", "http://host/?a=", 0, 0, CURLUE_OK}, + {"http://host/?a=&b=", "http://host/?a=&b=", 0, 0, CURLUE_OK}, + + /* Percent-encoded userinfo */ + {"https://user%20name@example.com/", "https://user%20name@example.com/", + 0, 0, CURLUE_OK}, + {"https://user:pa%3Ass@example.com/", "https://user:pa%3Ass@example.com/", + 0, 0, CURLUE_OK}, + + /* malformed unbracketed IPv6 */ + {"https://fe80:8080::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + {"https://::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + + /* Empty host with standard schemes */ + {"http:///", "", 0, 0, CURLUE_NO_HOST}, + {"https://?q=1", "", 0, 0, CURLUE_NO_HOST}, + + /* Empty path segment normalization */ + {"http://example.com//", "http://example.com//", 0, 0, CURLUE_OK}, + {"http://example.com///foo", "http://example.com///foo", 0, 0, CURLUE_OK}, + + /* Empty user and password combinations */ + {"http://@example.com/", "http://@example.com/", 0, 0, CURLUE_OK}, + {"http://user:@example.com/", "http://user:@example.com/", 0, 0, CURLUE_OK}, + {"http://:password@example.com/", "http://:password@example.com/", + 0, 0, CURLUE_OK}, + {"https://127.1.0x", "https://127.1.0x/", 0, 0, CURLUE_OK}, {"https://127.0x", "https://127.0x/", 0, 0, CURLUE_OK}, {"https://127.0x.1", "https://127.0x.1/", 0, 0, CURLUE_OK}, From d191de891a4d2be48908c6964e5cb157d002dae6 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Wed, 20 May 2026 00:37:27 +0200 Subject: [PATCH 0289/1062] telnet: honor CURLOPT_TIMEOUT in send_telnet_data() The poll-before-write loop used -1 (infinite) as the Curl_poll timeout, so a peer that stops reading could stall the transfer indefinitely, bypassing CURLOPT_TIMEOUT. Use Curl_timeleft_ms() instead and return CURLE_OPERATION_TIMEDOUT when the deadline is reached or exceeded. Closes #21685 --- lib/telnet.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/telnet.c b/lib/telnet.c index d1faec87a87d..83114ef2aa64 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -53,6 +53,7 @@ #include "curl_trc.h" #include "progress.h" #include "arpa_telnet.h" +#include "connect.h" #include "select.h" #include "curlx/strparse.h" @@ -645,13 +646,19 @@ static CURLcode send_telnet_data(struct Curl_easy *data, while(!result && total_written < outlen) { /* Make sure socket is writable to avoid EWOULDBLOCK condition */ struct pollfd pfd[1]; + timediff_t timeout_ms = Curl_timeleft_ms(data); pfd[0].fd = conn->sock[FIRSTSOCKET]; pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { + if(timeout_ms < 0) + return CURLE_OPERATION_TIMEDOUT; + /* 0 means no timeout configured; pass -1 to poll for infinite wait */ + switch(Curl_poll(pfd, 1, timeout_ms ? timeout_ms : -1)) { case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ result = CURLE_SEND_ERROR; break; + case 0: /* timeout */ + result = CURLE_OPERATION_TIMEDOUT; + break; default: /* write! */ bytes_written = 0; result = Curl_xfer_send(data, outbuf + total_written, From be6c4ee7faaa55c62567a8c3fb0f4e98a482292e Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Tue, 19 May 2026 15:54:30 +0200 Subject: [PATCH 0290/1062] gtls: verify OCSP response signature in gtls_verify_ocsp_status Since aeb1a281ca ("gtls: fix OCSP stapling management"), the function parses the stapled OCSP response and reads the certificate status via gnutls_ocsp_resp_get_single(), but never calls gnutls_ocsp_resp_verify() or gnutls_ocsp_resp_verify_direct(). A response with a forged or corrupted signature is accepted without question. Fix by calling gnutls_ocsp_resp_verify() against the trust list obtained from the session credentials immediately after gnutls_ocsp_resp_import(). This handles both directly-signed responses and delegated OCSP responders without requiring the issuer certificate to be present in the peer chain. The missing check only affects the CURLOPT_SSL_VERIFYSTATUS code path when CURLOPT_SSL_VERIFYPEER is disabled. With peer verification enabled, gnutls_certificate_verify_peers2() independently catches the invalid response via GNUTLS_CERT_INVALID_OCSP_STATUS before gtls_verify_ocsp_status() is reached. As a result, no attack is possible that is not already trivially achievable without OCSP stapling when peer verification is off. This is a correctness and consistency fix, not a security vulnerability. Reported-by: Joshua Rogers Closes #21677 --- lib/vtls/gtls.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 0b0744517d58..3019143ad610 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1429,6 +1429,9 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, { gnutls_ocsp_resp_t ocsp_resp = NULL; gnutls_datum_t status_request; + gnutls_certificate_credentials_t creds = NULL; + gnutls_x509_trust_list_t tlist = NULL; + unsigned int verify_status = 0; gnutls_ocsp_cert_status_t status = GNUTLS_OCSP_CERT_UNKNOWN; gnutls_x509_crl_reason_t reason; CURLcode result = CURLE_OK; @@ -1461,13 +1464,23 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } - rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); - if(rc < 0) { - failf(data, "Invalid OCSP response received"); + if(!gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, + (void **)&creds)) + gnutls_certificate_get_trust_list(creds, &tlist); + if(!tlist) { + failf(data, "OCSP response signature verification failed"); result = CURLE_SSL_INVALIDCERTSTATUS; goto out; } + rc = gnutls_ocsp_resp_verify(ocsp_resp, tlist, &verify_status, 0); + if(rc < 0 || verify_status) { + failf(data, "OCSP response signature verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); switch(status) { case GNUTLS_OCSP_CERT_GOOD: From 89683e05b9e0884ccb860a3e01822c20145a12ef Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 2 Jun 2026 13:51:35 +0200 Subject: [PATCH 0291/1062] tidy-up: use test/example domains more Closes #21849 --- docs/examples/ephiperfifo.c | 2 +- docs/examples/evhiperfifo.c | 2 +- docs/examples/ghiper.c | 2 +- docs/examples/hiperfifo.c | 2 +- tests/data/test536 | 8 ++++---- tests/libtest/lib536.c | 4 ++-- tests/unit/unit2600.c | 22 +++++++++++----------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c index 62c3b2c674c8..12afb5621e8d 100644 --- a/docs/examples/ephiperfifo.c +++ b/docs/examples/ephiperfifo.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index e4fdf7d08929..37e41b726e9f 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -45,7 +45,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo diff --git a/docs/examples/ghiper.c b/docs/examples/ghiper.c index 31db2bc9f9d1..b0b5e022d648 100644 --- a/docs/examples/ghiper.c +++ b/docs/examples/ghiper.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c index 2b0ae0fdb964..d2a5f11724e0 100644 --- a/docs/examples/hiperfifo.c +++ b/docs/examples/hiperfifo.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo diff --git a/tests/data/test536 b/tests/data/test536 index b03bc52318f1..e0bcc906f7de 100644 --- a/tests/data/test536 +++ b/tests/data/test536 @@ -43,7 +43,7 @@ CURLINFO_USED_PROXY # 1 - the non-proxy using URL # 2 - the CURLOPT_RESOLVE string to change IP for the name -http://%HOSTIP:%HTTPPORT goingdirect.com:%HTTPPORT goingdirect.com:%HTTPPORT:%HOSTIP +http://%HOSTIP:%HTTPPORT goingdirect.test:%HTTPPORT goingdirect.test:%HTTPPORT:%HOSTIP proxy @@ -53,13 +53,13 @@ proxy # Verify data after the test has been "shot" -GET http://usingproxy.com/ HTTP/1.1 -Host: usingproxy.com +GET http://usingproxy.test/ HTTP/1.1 +Host: usingproxy.test Accept: */* Proxy-Connection: Keep-Alive GET / HTTP/1.1 -Host: goingdirect.com:%HTTPPORT +Host: goingdirect.test:%HTTPPORT Accept: */* diff --git a/tests/libtest/lib536.c b/tests/libtest/lib536.c index 69208d1dcfde..e63757ac91d8 100644 --- a/tests/libtest/lib536.c +++ b/tests/libtest/lib536.c @@ -37,7 +37,7 @@ static CURLcode test_lib536(const char *URL) CURL *curl; struct curl_slist *host = NULL; - static const char *url_with_proxy = "http://usingproxy.com/"; + static const char *url_with_proxy = "http://usingproxy.test/"; const char *url_without_proxy = libtest_arg2; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { @@ -59,7 +59,7 @@ static CURLcode test_lib536(const char *URL) test_setopt(curl, CURLOPT_RESOLVE, host); test_setopt(curl, CURLOPT_PROXY, URL); test_setopt(curl, CURLOPT_URL, url_with_proxy); - test_setopt(curl, CURLOPT_NOPROXY, "goingdirect.com"); + test_setopt(curl, CURLOPT_NOPROXY, "goingdirect.test"); test_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 96683052daba..213fd0b178db 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -359,7 +359,7 @@ static void test_connect(CURL *easy, const struct test_case *tc) * Max Duration checks needs to be conservative since CI jobs are not * as sharp. */ -#define TURL "http://test.com:123" +#define TURL "http://abc.test:123" #define R_FAIL CURLE_COULDNT_CONNECT /* timeout values accounting for low cpu resources in CI */ @@ -375,46 +375,46 @@ static CURLcode test_unit2600(const char *arg) static const struct test_case TEST_CASES[] = { /* TIMEOUT_MS, FAIL_MS CREATED DURATION Result, HE_PREF */ /* CNCT HE v4 v6 v4 v6 MIN MAX MAX_CONCURRENT */ - { 1, TURL, "test.com:123:192.0.2.1", CURL_IPRESOLVE_WHATEVER, + { 1, TURL, "abc.test:123:192.0.2.1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 1, 0, 200, TC_TMOT, R_FAIL, NULL, 1 }, /* 1 ipv4, fails after ~200ms, reports COULDNT_CONNECT */ - { 2, TURL, "test.com:123:192.0.2.1,192.0.2.2", CURL_IPRESOLVE_WHATEVER, + { 2, TURL, "abc.test:123:192.0.2.1,192.0.2.2", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 2, 0, 400, TC_TMOT, R_FAIL, NULL, 2 }, /* 2 ipv4, fails after ~400ms, reports COULDNT_CONNECT */ #ifdef USE_IPV6 - { 3, TURL, "test.com:123:::1", CURL_IPRESOLVE_WHATEVER, + { 3, TURL, "abc.test:123:::1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 0, 1, 200, TC_TMOT, R_FAIL, NULL, 1 }, /* 1 ipv6, fails after ~200ms, reports COULDNT_CONNECT */ - { 4, TURL, "test.com:123:::1,::2", CURL_IPRESOLVE_WHATEVER, + { 4, TURL, "abc.test:123:::1,::2", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 0, 2, 400, TC_TMOT, R_FAIL, NULL, 2 }, /* 2 ipv6, fails after ~400ms, reports COULDNT_CONNECT */ - { 5, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_WHATEVER, + { 5, TURL, "abc.test:123:192.0.2.1,::1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 1, 1, 350, TC_TMOT, R_FAIL, "v6", 2 }, /* mixed ip4+6, v6 always first, v4 kicks in on HE, fails after ~350ms */ - { 6, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_WHATEVER, + { 6, TURL, "abc.test:123:::1,192.0.2.1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 1, 1, 350, TC_TMOT, R_FAIL, "v6", 2 }, /* mixed ip6+4, v6 starts, v4 never starts due to high HE, TIMEOUT */ - { 7, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_V4, + { 7, TURL, "abc.test:123:192.0.2.1,::1", CURL_IPRESOLVE_V4, CNCT_TMOT, 150, 500, 500, 1, 0, 400, TC_TMOT, R_FAIL, NULL, 1 }, /* mixed ip4+6, but only use v4, check it uses full connect timeout, although another address of the 'wrong' family is available */ - { 8, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_V6, + { 8, TURL, "abc.test:123:::1,192.0.2.1", CURL_IPRESOLVE_V6, CNCT_TMOT, 150, 500, 500, 0, 1, 400, TC_TMOT, R_FAIL, NULL, 1 }, /* mixed ip4+6, but only use v6, check it uses full connect timeout, although another address of the 'wrong' family is available */ - { 9, TURL, "test.com:123:::1,192.0.2.1,::2,::3", CURL_IPRESOLVE_WHATEVER, + { 9, TURL, "abc.test:123:::1,192.0.2.1,::2,::3", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 50, 400, 400, 1, 3, 550, TC_TMOT, R_FAIL, NULL, 4 }, /* 1 v4, 3 v6, fails after (3*HE)+400ms, ~550ms, COULDNT_CONNECT */ - { 10, TURL, "test.com:123:::1,192.0.2.1,::2,::3,::4,::5,::6,::7,::8", + { 10, TURL, "abc.test:123:::1,192.0.2.1,::2,::3,::4,::5,::6,::7,::8", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 20, 500, 500, 1, 8, 550, TC_TMOT, R_FAIL, NULL, 6 }, From a4313f1a98218dd577ee3da5e91ee432727012b6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 17:01:32 +0200 Subject: [PATCH 0292/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 57 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 6d2daeaf4e62..b6dd267fb953 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1481 - Contributors: 3696 + Authors: 1482 + Contributors: 3706 This release includes the following changes: @@ -73,9 +73,11 @@ This release includes the following bugfixes: o gtls: fix ignored return and uninitialized status in OCSP check [49] o gtls: fix some typos [15] o gtls: use the correct return code in trace output [173] + o gtls: verify OCSP response signature in gtls_verify_ocsp_status [86] o h3-proxy: fix callback return values, and a typo in tests [139] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] o http: don't pass on set cookies to new origins [140] + o http: prefer chunked encoding over Content-Length: 0 [146] o idn: replace header guards with forward declaration [100] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o KNOWN_BUGS: remove stale Threads::Threads entry [135] @@ -88,6 +90,7 @@ This release includes the following bugfixes: o libcurl-easy.md: minor clarifications [19] o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] o m4: drop redundant conditions in TLS library detections [155] + o Makefile.am: drop test1190 listed twice [144] o managen: apply minor fixes and improvements [115] o mbedtls: null-terminate the private key blob [36] o mk-unity.pl: `#include`, and not concatenate input headers [124] @@ -96,6 +99,8 @@ This release includes the following bugfixes: o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] o netrc: scanner refactor [121] o ngtcp2: fail handshake directly [138] + o os400sys: fix theoretical length overflows [141] + o pytest: pass `--disable` to curl [175] o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o rtsp: bump buf after rtsp_filter_rtp() [88] @@ -110,6 +115,8 @@ This release includes the following bugfixes: o scripts: catch Credits-to contributors [127] o setopt: changing the proxy port is also a proxy change [23] o setopt: clear proxy auth properly on NULL [81] + o setopt: CURLOPT_MAXCONNECTS set to 0 restores default value [161] + o setopt: defref the old referer when setting a new [168] o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] @@ -119,6 +126,7 @@ This release includes the following bugfixes: o src: fix comment typos [83] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] + o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] o test1588: use %TESTNUMBER, not hard-coded number [118] o test1981: explicitly set the locale [85] o tests: add an assert to avoid IPC blocking [69] @@ -129,15 +137,17 @@ This release includes the following bugfixes: o tidy-up: apply clang-format fixes [153] o tidy-up: miscellaneous [106] o tls: fix incomplete mTLS config in conn reuse and session cache [108] - o tool: add a retry delay for transfers to same origin on 429 [61] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] o tool_formparse: tool2curlparts is no longer recursive [33] + o tool_help: rectify a bad assert [143] + o tool_operhlp: avoid NULL to %s [142] o tool_urlglob: avoid overflow at end of range [22] o tool_urlglob: better 'Duplicate glob name' position [82] o tool_urlglob: make globbing error reported for correct position [91] o transfer: clear referer when set to NULL [112] o unix-sockets: ignore proxy settings [6] + o URL-SYNTAX: document more URL parsing details [134] o url: compare full origin when setting credentials [42] o url: connection reuse fixes for starttls [68] o url: detect proxy changes read from environment [110] @@ -188,19 +198,22 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 0xN3R3K3, 11soda11, Alan De Smet, ambikeesshh, amitbidlan, Andrei Rybak, - Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, Bartel Sielski, - Bastian Jesuiter, Bill Mill, chrizilla on github, co-authors in libssh2, - Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, - dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, - Fabian Keil, Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, - Johannes Schlatow, Joshua Rogers, Kai Pastor, Mark Esler, Max Dymond, mik, - Mike-menny on github, mulan_dh on hackerone, parasol-aser, penpal, - Peter Krefting, Raymond Steen, Ray Satiro, renovate[bot], Ross Burton, - Sergio Correia, sfan5 on github, Shintomon Mathew, Sollace on github, - Song X. Gao, Stefan Eissing, Tim Martin, tiymat, Viktor Szakats, - Will Cosgrove, Xi Ruoyao, x-xiang on github - (54 contributors) + 0xN3R3K3, 11soda11, Ady Elouej, Alan De Smet, ambikeesshh, amitbidlan, + Andrei Rybak, Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, + Bartel Sielski, Bastian Jesuiter, Bill Mill, chrizilla on github, + co-authors in libssh2, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, + Dario Vinella, dependabot[bot], Earnestly on github, Elise Vance, + Emanuel Krollmann, Eunsoo Kim, Fabian Keil, Gao Liyou, Guancheng Li, + Guannan Wang, Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, + Jiashuo Liang, Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, + Mark Esler, Max Dymond, mik, Mike-menny on github, Muhamad Arga Reksapati, + mulan_dh on hackerone, parasol-aser, penpal, Peter Krefting, + Randall S. Becker, Raymond Steen, Ray Satiro, renjian on hackerone, + renovate[bot], Ross Burton, Sergio Correia, sfan5 on github, + Shintomon Mathew, Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, + tiymat, vegagent on hackerone, Viktor Szakats, Will Cosgrove, Xi Ruoyao, + x-xiang on github, Zhanpeng Liu + (66 contributors) References to bug reports and discussions on issues: @@ -264,7 +277,6 @@ References to bug reports and discussions on issues: [58] = https://curl.se/bug/?i=21622 [59] = https://curl.se/bug/?i=21614 [60] = https://curl.se/bug/?i=21621 - [61] = https://curl.se/bug/?i=21355 [62] = https://curl.se/bug/?i=21617 [63] = https://curl.se/bug/?i=21820 [64] = https://curl.se/bug/?i=21745 @@ -289,6 +301,7 @@ References to bug reports and discussions on issues: [83] = https://curl.se/bug/?i=21570 [84] = https://curl.se/bug/?i=21569 [85] = https://curl.se/bug/?i=21749 + [86] = https://curl.se/bug/?i=21677 [87] = https://curl.se/bug/?i=21562 [88] = https://curl.se/bug/?i=21563 [89] = https://curl.se/bug/?i=21528 @@ -306,6 +319,7 @@ References to bug reports and discussions on issues: [101] = https://curl.se/bug/?i=21550 [102] = https://curl.se/bug/?i=21663 [103] = https://curl.se/bug/?i=21750 + [104] = https://curl.se/bug/?i=21685 [105] = https://curl.se/bug/?i=21672 [106] = https://curl.se/bug/?i=21646 [107] = https://curl.se/bug/?i=21705 @@ -333,12 +347,18 @@ References to bug reports and discussions on issues: [130] = https://curl.se/bug/?i=21647 [131] = https://curl.se/bug/?i=21650 [132] = https://curl.se/bug/?i=21602 + [134] = https://curl.se/bug/?i=21841 [135] = https://curl.se/bug/?i=21734 [136] = https://curl.se/bug/?i=21702 [137] = https://curl.se/bug/?i=21719 [138] = https://curl.se/bug/?i=21712 [139] = https://curl.se/bug/?i=21802 [140] = https://curl.se/bug/?i=21794 + [141] = https://curl.se/bug/?i=21840 + [142] = https://curl.se/bug/?i=21836 + [143] = https://curl.se/bug/?i=21837 + [144] = https://curl.se/bug/?i=21839 + [146] = https://curl.se/bug/?i=21706 [147] = https://curl.se/bug/?i=21793 [149] = https://curl.se/bug/?i=21743 [150] = https://curl.se/bug/?i=21669 @@ -348,12 +368,15 @@ References to bug reports and discussions on issues: [155] = https://curl.se/bug/?i=21781 [158] = https://curl.se/bug/?i=21776 [160] = https://curl.se/bug/?i=21774 + [161] = https://curl.se/bug/?i=21829 [163] = https://curl.se/bug/?i=21727 [164] = https://curl.se/bug/?i=21771 [165] = https://curl.se/bug/?i=21739 [166] = https://curl.se/bug/?i=21767 [167] = https://curl.se/bug/?i=21768 + [168] = https://curl.se/bug/?i=21826 [170] = https://curl.se/bug/?i=21603 [171] = https://curl.se/bug/?i=21756 [172] = https://curl.se/bug/?i=21762 [173] = https://curl.se/bug/?i=21766 + [175] = https://curl.se/bug/?i=21816 From 74f18f27a21c08d924dea525dad37fc388b489e0 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 28 May 2026 14:42:03 -0400 Subject: [PATCH 0293/1062] github: Add AI usage warning to issue, doc and PR templates - Explain to contributors that though AI use is acceptable they must not file unless they can understand and explain their work without AI. Assisted-by: Viktor Szakats Ref: https://github.com/curl/curl/discussions/21792 Closes https://github.com/curl/curl/pull/21801 --- .github/ISSUE_TEMPLATE/bug_report.yml | 11 +++++++++++ .github/ISSUE_TEMPLATE/docs.yml | 11 +++++++++++ .github/pull_request_template.md | 8 ++++++++ REUSE.toml | 1 + 4 files changed, 31 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c2b79901afbd..52011a6f191a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -15,6 +15,17 @@ body: **SECURITY RELATED?** Submit here: https://hackerone.com/curl + - type: markdown + attributes: + value: " + > [!IMPORTANT] + + > If you cannot understand or explain your work without using + Artificial Intelligence (AI) then do not file here. Do not paste + massive AI generated explanations. We accept the use of AI as long as + it is digestible. Please explain your issues or improvements briefly + and clearly in your own human voice." + - type: textarea id: reproducer attributes: diff --git a/.github/ISSUE_TEMPLATE/docs.yml b/.github/ISSUE_TEMPLATE/docs.yml index 1b60a597adf0..d0c3852183ab 100644 --- a/.github/ISSUE_TEMPLATE/docs.yml +++ b/.github/ISSUE_TEMPLATE/docs.yml @@ -14,6 +14,17 @@ body: Only file documentation bugs here! Ask questions on the mailing lists https://curl.se/mail/ + - type: markdown + attributes: + value: " + > [!IMPORTANT] + + > If you cannot understand or explain your work without using + Artificial Intelligence (AI) then do not file here. Do not paste + massive AI generated explanations. We accept the use of AI as long as + it is digestible. Please explain your issues or improvements briefly + and clearly in your own human voice." + - type: textarea id: source attributes: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..34e39a217218 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ + diff --git a/REUSE.toml b/REUSE.toml index 87be5825cc04..9e6d2d166b44 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -13,6 +13,7 @@ SPDX-PackageDownloadLocation = "https://curl.se/" [[annotations]] path = [ + ".github/pull_request_template.md", "docs/INSTALL", "docs/libcurl/symbols-in-versions", "docs/options-in-versions", From a79467343fa709e6065c16293b79504d234d7311 Mon Sep 17 00:00:00 2001 From: Vasiliy-Kkk <61242428+Vasiliy-Kkk@users.noreply.github.com> Date: Wed, 27 May 2026 15:40:13 +0300 Subject: [PATCH 0294/1062] schannel: use fopen instead CreateFile - Refactor CA file reading to use the typical fopen/fread instead of CreateFile/ReadFile. Closes https://github.com/curl/curl/pull/21773 --- lib/vtls/schannel_verify.c | 66 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index d8edec9b2eab..f2f4218e3884 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -250,49 +250,51 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, struct Curl_easy *data) { CURLcode result; - HANDLE ca_file_handle; - LARGE_INTEGER file_size; + FILE *ca_file_handle; char *ca_file_buffer = NULL; - size_t ca_file_bufsize = 0; - DWORD total_bytes_read = 0; + long ca_file_bufsize = 0; + long total_bytes_read = 0; /* * Read the CA file completely into memory before parsing it. This * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ - ca_file_handle = curlx_CreateFile(ca_file, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(ca_file_handle == INVALID_HANDLE_VALUE) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to open CA file '%s': %s", ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file_handle = curlx_fopen(ca_file, "rb"); + if(!ca_file_handle) { + failf(data, "schannel: failed to open CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(!GetFileSizeEx(ca_file_handle, &file_size)) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to determine size of CA file '%s': %s", - ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(curlx_fseek(ca_file_handle, 0, SEEK_END)) { + failf(data, "schannel: failed seeking to end of CA file '%s'", ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + ca_file_bufsize = ftell(ca_file_handle); + + if(curlx_fseek(ca_file_handle, 0, SEEK_SET)) { + failf(data, "schannel: failed seeking to beginning of CA file '%s'", + ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(file_size.QuadPart > MAX_CAFILE_SIZE) { + if(ca_file_bufsize < 0) { + failf(data, "schannel: failed to get length of CA file '%s'", ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(ca_file_bufsize > MAX_CAFILE_SIZE) { failf(data, "schannel: CA file exceeds max size of %d bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - ca_file_bufsize = (size_t)file_size.QuadPart; ca_file_buffer = (char *)curlx_malloc(ca_file_bufsize + 1); if(!ca_file_buffer) { result = CURLE_OUT_OF_MEMORY; @@ -300,23 +302,21 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, } while(total_bytes_read < ca_file_bufsize) { - DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); - DWORD bytes_read = 0; + size_t nread = fread(ca_file_buffer + total_bytes_read, 1, + ca_file_bufsize - total_bytes_read, ca_file_handle); - if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, - bytes_to_read, &bytes_read, NULL)) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to read from CA file '%s': %s", ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(ferror(ca_file_handle)) { + failf(data, "schannel: failed to read from CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(bytes_read == 0) { + + if(nread == 0) { /* Premature EOF -- adjust the bufsize to the new value */ ca_file_bufsize = total_bytes_read; } else { - total_bytes_read += bytes_read; + total_bytes_read += (long)nread; } } @@ -329,8 +329,8 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, data); cleanup: - if(ca_file_handle != INVALID_HANDLE_VALUE) { - CloseHandle(ca_file_handle); + if(ca_file_handle) { + curlx_fclose(ca_file_handle); } curlx_safefree(ca_file_buffer); From 2932b7f56f4658d7371719fe68648d7504a3af28 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 2 Jun 2026 15:33:14 +0200 Subject: [PATCH 0295/1062] gtls: minor fixes and improvements - fix GnuTLS function name reference in `Curl_gtls_shared_creds_create()` error message. Spotted by GitHub Code Quality. - unfold a line. - in `Curl_gtls_verifyserver()`: - report the failure of `gnutls_x509_crt_import()`. Spotted by GitHub Code Quality. - fix a minor inconsistency in error strings. - drop redundant NULL checks for `config->issuercert`. Closes #21850 --- lib/vtls/gtls.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 3019143ad610..6fda7590ce98 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -409,7 +409,8 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, rc = gnutls_certificate_allocate_credentials(&shared->creds); if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); + failf(data, "gnutls_certificate_allocate_credentials() failed: %s", + gnutls_strerror(rc)); curlx_free(shared); return CURLE_SSL_CONNECT_ERROR; } @@ -1464,8 +1465,7 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } - if(!gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, - (void **)&creds)) + if(!gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, (void **)&creds)) gnutls_certificate_get_trust_list(creds, &tlist); if(!tlist) { failf(data, "OCSP response signature verification failed"); @@ -1819,17 +1819,22 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } issuerp = load_file(config->issuercert); rc = gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - if(!rc) - rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); unload_file(issuerp); + if(rc) { + failf(data, "failed to import issuer certificate (%s) (Issuer Cert: %s)", + gnutls_strerror(rc), config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - config->issuercert ? config->issuercert : "none"); + failf(data, "server certificate issuer check failed (Issuer Cert: %s)", + config->issuercert); result = CURLE_SSL_ISSUER_ERROR; goto out; } infof(data, " SSL certificate issuer check OK (Issuer Cert: %s)", - config->issuercert ? config->issuercert : "none"); + config->issuercert); } /* This function checks if the given certificate's subject matches the From 5d178de986ee124a8ec88f252769488d6644decb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Jun 2026 17:40:48 +0200 Subject: [PATCH 0296/1062] hsts.md: mention multiple curl invokes effect Reported-by: zhanhb on github Ref: #21847 Closes #21851 --- docs/cmdline-opts/hsts.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/cmdline-opts/hsts.md b/docs/cmdline-opts/hsts.md index f99b91c28144..7653c65cd5d9 100644 --- a/docs/cmdline-opts/hsts.md +++ b/docs/cmdline-opts/hsts.md @@ -18,7 +18,9 @@ Example: Enable HSTS for the transfer. If the filename points to an existing HSTS cache file, that is used. After a completed transfer, the cache is saved to the -filename again if it has been modified. +filename again if it has been modified. If you run multiple curl invokes at +the same time using the same HSTS cache file, they might interfere with each +other in possibly undesired ways. If curl is told to use `http://` for a transfer involving a hostname that exists in the HSTS cache, it upgrades the transfer to use HTTPS. Each HSTS From 22d979400acee773da1ad9c462e5c75630650f21 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 2 Jun 2026 13:27:22 +0200 Subject: [PATCH 0297/1062] vquic: moving related things into subdir Move QUIC related implementations into the vquic directory. Rename files that implement a connection filter accordingly. Closes #21848 --- lib/Makefile.inc | 30 ++++++------- lib/cf-h2-proxy.c | 1 - lib/cf-ip-happy.c | 1 - lib/connect.c | 2 +- lib/curl_trc.c | 1 - lib/http_proxy.c | 3 +- lib/{ => vquic}/capsule.c | 2 +- lib/{ => vquic}/capsule.h | 0 lib/{ => vquic}/cf-capsule.c | 4 +- lib/{ => vquic}/cf-capsule.h | 0 .../cf-ngtcp2-proxy.c} | 22 +++++----- .../cf-ngtcp2-proxy.h} | 22 +++++----- lib/vquic/{curl_ngtcp2.c => cf-ngtcp2.c} | 2 +- lib/vquic/{curl_ngtcp2.h => cf-ngtcp2.h} | 0 lib/vquic/{curl_quiche.c => cf-quiche.c} | 2 +- lib/vquic/{curl_quiche.h => cf-quiche.h} | 0 lib/vquic/vquic.c | 44 ++++++++++++++++++- lib/vquic/vquic.h | 18 ++++++++ tests/unit/unit3400.c | 2 +- 19 files changed, 104 insertions(+), 52 deletions(-) rename lib/{ => vquic}/capsule.c (99%) rename lib/{ => vquic}/capsule.h (100%) rename lib/{ => vquic}/cf-capsule.c (99%) rename lib/{ => vquic}/cf-capsule.h (100%) rename lib/{cf-h3-proxy.c => vquic/cf-ngtcp2-proxy.c} (99%) rename lib/{cf-h3-proxy.h => vquic/cf-ngtcp2-proxy.h} (67%) rename lib/vquic/{curl_ngtcp2.c => cf-ngtcp2.c} (99%) rename lib/vquic/{curl_ngtcp2.h => cf-ngtcp2.h} (100%) rename lib/vquic/{curl_quiche.c => cf-quiche.c} (99%) rename lib/vquic/{curl_quiche.h => cf-quiche.h} (100%) diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 28647baff4a9..88ca0a1ef20a 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -121,17 +121,23 @@ LIB_VTLS_HFILES = \ vtls/wolfssl.h \ vtls/x509asn1.h -LIB_VQUIC_CFILES = \ - vquic/curl_ngtcp2.c \ - vquic/curl_quiche.c \ - vquic/vquic.c \ +LIB_VQUIC_CFILES = \ + vquic/capsule.c \ + vquic/cf-capsule.c \ + vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c \ + vquic/vquic.c \ vquic/vquic-tls.c -LIB_VQUIC_HFILES = \ - vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ +LIB_VQUIC_HFILES = \ + vquic/capsule.h \ + vquic/cf-capsule.h \ + vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h LIB_VSSH_CFILES = \ @@ -152,11 +158,8 @@ LIB_CFILES = \ bufq.c \ bufref.c \ cf-dns.c \ - capsule.c \ - cf-capsule.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ - cf-h3-proxy.c \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ @@ -287,11 +290,8 @@ LIB_HFILES = \ bufq.h \ bufref.h \ cf-dns.h \ - capsule.h \ - cf-capsule.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ - cf-h3-proxy.h \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index f8e5acb0552f..f12c094df43b 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -42,7 +42,6 @@ #include "sendf.h" #include "select.h" #include "cf-h2-proxy.h" -#include "capsule.h" #define PROXY_H2_CHUNK_SIZE (16 * 1024) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index e2a49b82aaba..4f1787e78458 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -51,7 +51,6 @@ #include "cfilters.h" #include "cf-dns.h" #include "cf-ip-happy.h" -#include "cf-h3-proxy.h" #include "curl_addrinfo.h" #include "curl_trc.h" #include "multiif.h" diff --git a/lib/connect.c b/lib/connect.c index 119231a96a83..8578aed73db2 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -55,7 +55,6 @@ #include "cfilters.h" #include "connect.h" #include "cf-dns.h" -#include "cf-capsule.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -65,6 +64,7 @@ #include "curlx/strparse.h" #include "vtls/vtls.h" /* for vtls cfilters */ #include "vquic/vquic.h" /* for QUIC cfilters */ +#include "vquic/cf-capsule.h" #include "progress.h" #include "conncache.h" #include "multihandle.h" diff --git a/lib/curl_trc.c b/lib/curl_trc.c index d54c171a549d..a30c87ad023f 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -35,7 +35,6 @@ #include "http_proxy.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" -#include "cf-h3-proxy.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 17e834aaf262..8c3be63b12aa 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -33,10 +33,9 @@ #include "cfilters.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" -#include "cf-h3-proxy.h" -#include "cf-capsule.h" #include "connect.h" #include "vauth/vauth.h" +#include "vquic/vquic.h" #include "curlx/strparse.h" static CURLcode dynhds_add_custom(struct Curl_easy *data, diff --git a/lib/capsule.c b/lib/vquic/capsule.c similarity index 99% rename from lib/capsule.c rename to lib/vquic/capsule.c index 1ba0ccfb849c..2123526fff53 100644 --- a/lib/capsule.c +++ b/lib/vquic/capsule.c @@ -36,7 +36,7 @@ #include "cfilters.h" #include "curl_trc.h" #include "bufq.h" -#include "capsule.h" +#include "vquic/capsule.h" /** diff --git a/lib/capsule.h b/lib/vquic/capsule.h similarity index 100% rename from lib/capsule.h rename to lib/vquic/capsule.h diff --git a/lib/cf-capsule.c b/lib/vquic/cf-capsule.c similarity index 99% rename from lib/cf-capsule.c rename to lib/vquic/cf-capsule.c index faf837dd6cfc..6b5d8f107e4a 100644 --- a/lib/cf-capsule.c +++ b/lib/vquic/cf-capsule.c @@ -31,8 +31,8 @@ #include "curl_trc.h" #include "curlx/dynbuf.h" #include "bufq.h" -#include "capsule.h" -#include "cf-capsule.h" +#include "vquic/capsule.h" +#include "vquic/cf-capsule.h" /* recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */ #define CAPSULE_RECV_CHUNKS 4 diff --git a/lib/cf-capsule.h b/lib/vquic/cf-capsule.h similarity index 100% rename from lib/cf-capsule.h rename to lib/vquic/cf-capsule.h diff --git a/lib/cf-h3-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c similarity index 99% rename from lib/cf-h3-proxy.c rename to lib/vquic/cf-ngtcp2-proxy.c index eee70e5e817f..f449484292a9 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -67,7 +67,7 @@ #include "vquic/vquic-tls.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" -#include "cf-h3-proxy.h" +#include "vquic/cf-ngtcp2-proxy.h" #include "capsule.h" /* A stream window is the maximum amount we need to buffer for @@ -3397,12 +3397,12 @@ struct Curl_cftype Curl_cft_h3_proxy = { cf_h3_proxy_query, }; -CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out) { struct Curl_cfilter *cf = NULL; struct cf_h3_proxy_ctx *ctx; @@ -3443,10 +3443,10 @@ CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, return result; } -CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel) +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel) { struct Curl_cfilter *cf = NULL; struct cf_h3_proxy_ctx *ctx; diff --git a/lib/cf-h3-proxy.h b/lib/vquic/cf-ngtcp2-proxy.h similarity index 67% rename from lib/cf-h3-proxy.h rename to lib/vquic/cf-ngtcp2-proxy.h index 40f0fccf0698..fc176fbab409 100644 --- a/lib/cf-h3-proxy.h +++ b/lib/vquic/cf-ngtcp2-proxy.h @@ -30,19 +30,17 @@ defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ defined(USE_NGTCP2) && defined(USE_OPENSSL) -CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel); +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel); -CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); - -extern struct Curl_cftype Curl_cft_h3_proxy; +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out); #endif diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/cf-ngtcp2.c similarity index 99% rename from lib/vquic/curl_ngtcp2.c rename to lib/vquic/cf-ngtcp2.c index 796a5b782ae0..d27ffeaca036 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -69,7 +69,7 @@ #include "vquic/vquic-tls.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" -#include "vquic/curl_ngtcp2.h" +#include "vquic/cf-ngtcp2.h" #define QUIC_MAX_STREAMS (256 * 1024) diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/cf-ngtcp2.h similarity index 100% rename from lib/vquic/curl_ngtcp2.h rename to lib/vquic/cf-ngtcp2.h diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/cf-quiche.c similarity index 99% rename from lib/vquic/curl_quiche.c rename to lib/vquic/cf-quiche.c index 04b4f7d6db2d..5736341e1a6a 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/cf-quiche.c @@ -44,7 +44,7 @@ #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-quiche.h" #include "transfer.h" #include "url.h" #include "bufref.h" diff --git a/lib/vquic/curl_quiche.h b/lib/vquic/cf-quiche.h similarity index 100% rename from lib/vquic/curl_quiche.h rename to lib/vquic/cf-quiche.h diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index dba907bbf090..2f0e0729516d 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -41,8 +41,9 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" -#include "vquic/curl_ngtcp2.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-ngtcp2.h" +#include "vquic/cf-ngtcp2-proxy.h" +#include "vquic/cf-quiche.h" #include "multiif.h" #include "progress.h" #include "rand.h" @@ -788,6 +789,45 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, #endif } +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, dest, udp_tunnel); +#else + (void)cf_at; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out) +{ + (void)transport_in; + (void)transport_out; + DEBUGASSERT(transport_out == TRNSPRT_QUIC); +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_create(pcf, data, conn, addr, + transport_in, transport_out); +#else + *pcf = NULL; + (void)data; + (void)conn; + (void)addr; + return CURLE_NOT_BUILT_IN; +#endif +} + +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn, unsigned char transport) diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index e3d894f8c089..fe53803be3d1 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -50,6 +50,24 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, extern struct Curl_cftype Curl_cft_http3; +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel); + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + uint8_t transport_in, + uint8_t transport_out); + +extern struct Curl_cftype Curl_cft_h3_proxy; + +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + #else #define Curl_vquic_init() 1 #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */ diff --git a/tests/unit/unit3400.c b/tests/unit/unit3400.c index 8df86c19f400..03b70b0dcd86 100644 --- a/tests/unit/unit3400.c +++ b/tests/unit/unit3400.c @@ -25,7 +25,7 @@ #include "unitcheck.h" #include "bufq.h" -#include "capsule.h" +#include "vquic/capsule.h" #if defined(USE_PROXY_HTTP3) && defined(USE_NGTCP2) && \ !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) From fb6be547e826f266e96bbbc3df4be18f5b8d37c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 20:20:31 +0000 Subject: [PATCH 0298/1062] GHA: bump 2 GitHub Actions - updates `actions/labeler` from 6.0.1 to 6.1.0 - updates `github/codeql-action` from 4.35.2 to 4.36.0 Closes #21852 --- .github/workflows/codeql.yml | 8 ++++---- .github/workflows/label.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 335fa10abf1c..dcf4640ca63b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -49,13 +49,13 @@ jobs: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -93,7 +93,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: cpp build-mode: manual @@ -140,4 +140,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 013ef874fed2..3922b8b7dfc0 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -30,6 +30,6 @@ jobs: pull-requests: write # To edit labels on PRs steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' From 67300814294ab0aa311b8568b460619ecc522949 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Jun 2026 08:10:14 +0200 Subject: [PATCH 0299/1062] cookie: refactor parse_cookie_header - introduce a few static helper functions - simplify the bad octet checks - simplify the too long cookie/value check Closes #21854 --- lib/cookie.c | 380 +++++++++++++++++++++++++++------------------------ 1 file changed, 204 insertions(+), 176 deletions(-) diff --git a/lib/cookie.c b/lib/cookie.c index 63615a496c91..b288a2c1d06a 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -346,9 +346,9 @@ static bool bad_domain(const char *domain, size_t len) static bool invalid_octets(const char *ptr, size_t len) { const unsigned char *p = (const unsigned char *)ptr; - /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ + /* Reject all bytes \x01 - \x1f + \x7f */ while(len && *p) { - if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f)) + if((*p < 0x20) || (*p == 0x7f)) return TRUE; p++; len--; @@ -413,19 +413,189 @@ static CURLcode storecookie(struct Cookie *co, const struct Curl_str *cp, return result; } -/* this function return errors on OOM etc, not on plain cookie format - problems */ -static CURLcode parse_cookie_header( - struct Curl_easy *data, - struct Cookie *co, - const struct CookieInfo *ci, - bool *okay, /* if the cookie was fine */ - const char *ptr, - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is - set, used to get default path for - the cookie unless set */ - bool secure) /* TRUE if connection is over secure origin */ +/* + * Parse the first name/value pair of the cookie header, which is the actual + * cookie name and value. + */ +static bool parse_first_pair(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie, + struct Curl_str *name, + struct Curl_str *val, + bool sep) +{ + /* The first name/value pair is the actual cookie name */ + if(!sep || !curlx_strlen(name)) { + infof(data, "invalid cookie, dropped"); + return FALSE; + } + + /* + * Check for too long individual name or contents. Chrome and Firefox + * support 4095 or 4096 bytes combo + */ + if((curlx_strlen(name) + curlx_strlen(val)) > MAX_NAME) { + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + curlx_strlen(name), curlx_strlen(val)); + return FALSE; + } + + /* Check if we have a reserved prefix set. */ + if(!strncmp("__Secure-", curlx_str(name), 9)) + co->prefix_secure = TRUE; + else if(!strncmp("__Host-", curlx_str(name), 7)) + co->prefix_host = TRUE; + + cookie[COOKIE_NAME] = *name; + cookie[COOKIE_VALUE] = *val; + return TRUE; +} + +static bool parse_flag(struct Curl_easy *data, struct Cookie *co, + const struct CookieInfo *ci, + struct Curl_str *name, bool secure) +{ + /* + * secure cookies are only allowed to be set when the connection is + * using a secure protocol, or when the cookie is being set by + * reading from file + */ + if(curlx_str_casecompare(name, "secure")) { + if(secure || !ci->running) + co->secure = TRUE; + else { + infof(data, "skipped cookie because not 'secure'"); + return FALSE; + } + } + else if(curlx_str_casecompare(name, "httponly")) + co->httponly = TRUE; + + return TRUE; +} + +static bool parse_domain(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie_domain, + struct Curl_str *val, + const char **domainp) +{ + bool is_ip; + const char *domain = *domainp; + const char *v = curlx_str(val); + /* + * Now, we make sure that our host is within the given domain, or + * the given domain is not valid and thus cannot be set. + */ + + if('.' == *v) + curlx_str_nudge(val, 1); + +#ifndef USE_LIBPSL + /* + * Without PSL we do not know when the incoming cookie is set on a + * TLD or otherwise "protected" suffix. To reduce risk, we require a + * dot OR the exact hostname being "localhost". + */ + if(bad_domain(curlx_str(val), curlx_strlen(val))) { + *domainp = ":"; + domain = ":"; + } +#endif + + is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(val)); + + if(!domain || + (is_ip && + !strncmp(curlx_str(val), domain, curlx_strlen(val)) && + (curlx_strlen(val) == strlen(domain))) || + (!is_ip && cookie_tailmatch(curlx_str(val), + curlx_strlen(val), domain))) { + *cookie_domain = *val; + if(!is_ip) + co->tailmatch = TRUE; /* we always do that if the domain name was + given */ + } + else { + /* + * We did not get a tailmatch and then the attempted set domain is + * not a domain to which the current host belongs. Mark as bad. + */ + infof(data, "skipped cookie with bad tailmatch domain: %s", + curlx_str(val)); + return FALSE; + } + return TRUE; +} + +static void parse_maxage(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + int rc; + const char *maxage = curlx_str(val); + if(*maxage == '\"') + maxage++; + rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); + if(!*nowp) + *nowp = time(NULL); + switch(rc) { + case STRE_OVERFLOW: + /* overflow, used max value */ + co->expires = CURL_OFF_T_MAX; + break; + default: + /* negative or otherwise bad, expire */ + co->expires = 1; + break; + case STRE_OK: + if(!co->expires) + co->expires = 1; /* expire now */ + else if(CURL_OFF_T_MAX - *nowp < co->expires) + /* would overflow */ + co->expires = CURL_OFF_T_MAX; + else + co->expires += *nowp; + break; + } + cap_expires(*nowp, co); +} + +static void parse_expires(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + /* + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie + */ + if(!co->expires && (curlx_strlen(val) < MAX_DATE_LENGTH)) { + char dbuf[MAX_DATE_LENGTH + 1]; + time_t date = 0; + memcpy(dbuf, curlx_str(val), curlx_strlen(val)); + dbuf[curlx_strlen(val)] = 0; + if(!Curl_getdate_capped(dbuf, &date)) { + if(!date) + date++; + co->expires = (curl_off_t)date; + } + else + co->expires = 0; + if(!*nowp) + *nowp = time(NULL); + cap_expires(*nowp, co); + } +} + +/* this function returns errors on OOM etc, not for cookie format problems */ +static CURLcode +parse_cookie_header(struct Curl_easy *data, + struct Cookie *co, + const struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, /* the header */ + const char *domain, /* default domain */ + /* full path used when this cookie is set */ + const char *path, + bool secure_origin) { /* This line was read off an HTTP-header */ time_t now = 0; @@ -441,22 +611,25 @@ static CURLcode parse_cookie_header( memset(cookie, 0, sizeof(cookie)); do { struct Curl_str name; - struct Curl_str val; /* we have a = pair or a stand-alone word here */ if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) { + struct Curl_str val; bool sep = FALSE; curlx_str_trimblanks(&name); + if(invalid_octets(curlx_str(&name), curlx_strlen(&name))) { + infof(data, "invalid octets in name, cookie dropped"); + return CURLE_OK; + } + if(!curlx_str_single(&ptr, '=')) { sep = TRUE; /* a '=' was used */ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); - /* Reject cookies with a TAB inside the value */ - if(curlx_strlen(&val) && - memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { - infof(data, "cookie contains TAB, dropping"); + if(invalid_octets(curlx_str(&val), curlx_strlen(&val))) { + infof(data, "invalid octets in value, cookie dropped"); return CURLE_OK; } } @@ -464,167 +637,23 @@ static CURLcode parse_cookie_header( curlx_str_init(&val); if(!curlx_strlen(&cookie[COOKIE_NAME])) { - /* The first name/value pair is the actual cookie name */ - if(!sep || - /* Bad name/value pair. */ - invalid_octets(curlx_str(&name), curlx_strlen(&name)) || - invalid_octets(curlx_str(&val), curlx_strlen(&val)) || - !curlx_strlen(&name)) { - infof(data, "invalid octets in name/value, cookie dropped"); - return CURLE_OK; - } - - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(curlx_strlen(&name) >= (MAX_NAME - 1) || - curlx_strlen(&val) >= (MAX_NAME - 1) || - ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) { - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - curlx_strlen(&name), curlx_strlen(&val)); + if(!parse_first_pair(data, co, cookie, &name, &val, sep)) return CURLE_OK; - } - - /* Check if we have a reserved prefix set. */ - if(!strncmp("__Secure-", curlx_str(&name), 9)) - co->prefix_secure = TRUE; - else if(!strncmp("__Host-", curlx_str(&name), 7)) - co->prefix_host = TRUE; - - cookie[COOKIE_NAME] = name; - cookie[COOKIE_VALUE] = val; } else if(!sep) { - /* - * this is a "" with no content - */ - - /* - * secure cookies are only allowed to be set when the connection is - * using a secure protocol, or when the cookie is being set by - * reading from file - */ - if(curlx_str_casecompare(&name, "secure")) { - if(secure || !ci->running) - co->secure = TRUE; - else { - infof(data, "skipped cookie because not 'secure'"); - return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "httponly")) - co->httponly = TRUE; + if(!parse_flag(data, co, ci, &name, secure_origin)) + return CURLE_OK; } - else if(curlx_str_casecompare(&name, "path")) { + else if(curlx_str_casecompare(&name, "path")) cookie[COOKIE_PATH] = val; - } else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) { - bool is_ip; - const char *v = curlx_str(&val); - /* - * Now, we make sure that our host is within the given domain, or - * the given domain is not valid and thus cannot be set. - */ - - if('.' == *v) - curlx_str_nudge(&val, 1); - -#ifndef USE_LIBPSL - /* - * Without PSL we do not know when the incoming cookie is set on a - * TLD or otherwise "protected" suffix. To reduce risk, we require a - * dot OR the exact hostname being "localhost". - */ - if(bad_domain(curlx_str(&val), curlx_strlen(&val))) - domain = ":"; -#endif - - is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val)); - - if(!domain || - (is_ip && - !strncmp(curlx_str(&val), domain, curlx_strlen(&val)) && - (curlx_strlen(&val) == strlen(domain))) || - (!is_ip && cookie_tailmatch(curlx_str(&val), - curlx_strlen(&val), domain))) { - cookie[COOKIE_DOMAIN] = val; - if(!is_ip) - co->tailmatch = TRUE; /* we always do that if the domain name was - given */ - } - else { - /* - * We did not get a tailmatch and then the attempted set domain is - * not a domain to which the current host belongs. Mark as bad. - */ - infof(data, "skipped cookie with bad tailmatch domain: %s", - curlx_str(&val)); + if(!parse_domain(data, co, &cookie[COOKIE_DOMAIN], &val, &domain)) return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) { - /* - * Defined in RFC2109: - * - * Optional. The Max-Age attribute defines the lifetime of the - * cookie, in seconds. The delta-seconds value is a decimal non- - * negative integer. After delta-seconds seconds elapse, the - * client should discard the cookie. A value of zero means the - * cookie should be discarded immediately. - */ - int rc; - const char *maxage = curlx_str(&val); - if(*maxage == '\"') - maxage++; - rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); - if(!now) - now = time(NULL); - switch(rc) { - case STRE_OVERFLOW: - /* overflow, used max value */ - co->expires = CURL_OFF_T_MAX; - break; - default: - /* negative or otherwise bad, expire */ - co->expires = 1; - break; - case STRE_OK: - if(!co->expires) - co->expires = 1; /* expire now */ - else if(CURL_OFF_T_MAX - now < co->expires) - /* would overflow */ - co->expires = CURL_OFF_T_MAX; - else - co->expires += now; - break; - } - cap_expires(now, co); - } - else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val) && - !co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) { - /* - * Let max-age have priority. - * - * If the date cannot get parsed for whatever reason, the cookie - * will be treated as a session cookie - */ - char dbuf[MAX_DATE_LENGTH + 1]; - time_t date = 0; - memcpy(dbuf, curlx_str(&val), curlx_strlen(&val)); - dbuf[curlx_strlen(&val)] = 0; - if(!Curl_getdate_capped(dbuf, &date)) { - if(!date) - date++; - co->expires = (curl_off_t)date; - } - else - co->expires = 0; - if(!now) - now = time(NULL); - cap_expires(now, co); } + else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) + parse_maxage(co, &val, &now); + else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) + parse_expires(co, &val, &now); } } while(!curlx_str_single(&ptr, ';')); @@ -641,8 +670,7 @@ static CURLcode parse_netscape(struct Cookie *co, const struct CookieInfo *ci, bool *okay, const char *lineptr, - bool secure) /* TRUE if connection is over - secure origin */ + bool secure_origin) { /* * This line is NOT an HTTP header style line, we do offer support for @@ -715,7 +743,7 @@ static CURLcode parse_netscape(struct Cookie *co, case 3: co->secure = FALSE; if(curl_strnequal(ptr, "TRUE", len)) { - if(secure || ci->running) + if(secure_origin || ci->running) co->secure = TRUE; else return CURLE_OK; From cf7919262dbdd52db0a028acb8bf1c77a173d788 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Jun 2026 09:43:41 +0200 Subject: [PATCH 0300/1062] tool_parsecfg: refactor parseconfig() - introduce helper functions - remove #ifdef'ed debug code Closes #21855 --- src/tool_parsecfg.c | 271 ++++++++++++++++++++++++++------------------ 1 file changed, 162 insertions(+), 109 deletions(-) diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c index f5910f404e77..173378d3acd0 100644 --- a/src/tool_parsecfg.c +++ b/src/tool_parsecfg.c @@ -77,15 +77,18 @@ static int unslashquote(const char *line, struct dynbuf *param) return 0; /* ok */ } -/* return 0 on everything-is-fine, and non-zero otherwise */ -ParameterError parseconfig(const char *filename, int max_recursive, - char **resolved) +/* + * Open the config file. When filename is NULL, tries to find .curlrc in the + * home directory (and on Windows, in the executable directory). Updates + * *namep to the effective filename and *pathalloc to any allocated path + * that must be freed by the caller. Returns the opened FILE or NULL. + */ +static FILE *open_config_file(const char *filename, + const char **namep, + char **pathalloc) { FILE *file = NULL; - bool usedarg = FALSE; - ParameterError err = PARAM_OK; - struct OperationConfig *config = global->last; - char *pathalloc = NULL; + *pathalloc = NULL; if(!filename) { /* NULL means load .curlrc from homedir! */ @@ -94,9 +97,9 @@ ParameterError parseconfig(const char *filename, int max_recursive, file = curlx_fopen(curlrc, FOPEN_READTEXT); if(!file) { curlx_free(curlrc); - return PARAM_READ_ERROR; + return NULL; } - filename = pathalloc = curlrc; + *namep = *pathalloc = curlrc; } #ifdef _WIN32 else { @@ -107,16 +110,155 @@ ParameterError parseconfig(const char *filename, int max_recursive, file = tool_execpath("_curlrc", &fullp); if(file) /* this is the filename we read from */ - filename = fullp; + *namep = fullp; } #endif } else { if(strcmp(filename, "-")) file = curlx_fopen(filename, FOPEN_READTEXT); - else + else { file = stdin; + *namep = ""; + } } + return file; +} + +/* + * Extract the parameter value from a config line. The line pointer should + * be positioned after the option keyword has been null-terminated. + * Skips separators and whitespace, then handles quoted and unquoted + * parameter values. Sets *param_out to the parameter string; unquoted empty + * values set it to NULL, while quoted empty values become an empty string. + */ +static ParameterError extract_param(char *line, + bool dashed_option, + struct dynbuf *pbuf, + const char *filename, + int lineno, + const char *option, + char **param_out) +{ + /* pass spaces and separator(s) */ + while(ISBLANK(*line) || ISSEP(*line, dashed_option)) + line++; + + /* the parameter starts here (unless quoted) */ + if(*line == '\"') { + /* quoted parameter, do the quote dance */ + int rc = unslashquote(++line, pbuf); + if(rc) + return PARAM_BAD_USE; + *param_out = curlx_dyn_len(pbuf) ? curlx_dyn_ptr(pbuf) : CURL_UNCONST(""); + } + else { + if(*line == '\'') { + warnf("%s:%d Option '%s' uses argument with leading single quote. " + "It is probably a mistake. Consider double quotes.", + filename, lineno, option); + } + *param_out = line; /* parameter starts here */ + while(*line && !ISSPACE(*line)) /* stop also on CRLF */ + line++; + + if(*line) { + *line = '\0'; /* null-terminate */ + + /* to detect mistakes better, see if there is data following */ + line++; + /* pass all spaces */ + while(ISBLANK(*line)) + line++; + + switch(*line) { + case '\0': + case '\r': + case '\n': + case '#': /* comment */ + break; + default: + warnf("%s:%d Option '%s' uses argument with unquoted whitespace. " + "This may cause side-effects. Consider double quotes.", + filename, lineno, option); + } + } + if(!**param_out) + /* do this so getparameter can check for required parameters. + Otherwise it always thinks there is a parameter. */ + *param_out = NULL; + } + return PARAM_OK; +} + +/* + * Process the result from getparameter. Handles PARAM_NEXT_OPERATION + * by allocating a new config, and reports errors for other non-OK results. + * Updates *configp if a new operation config is allocated. + * Returns PARAM_OK if processing should continue, or an error code. + */ +static ParameterError +process_config_result(ParameterError res, + struct OperationConfig **configp, + const char *param, + bool usedarg, + const char *filename, + int lineno, + const char *option) +{ + if(!res && param && *param && !usedarg) + /* we passed in a parameter that was not used! */ + res = PARAM_GOT_EXTRA_PARAMETER; + + if(res == PARAM_NEXT_OPERATION) { + struct OperationConfig *config = *configp; + if(config->url_list && config->url_list->url) { + /* Allocate the next config */ + config->next = config_alloc(); + if(config->next) { + /* Update the last operation pointer */ + global->last = config->next; + + /* Move onto the new config */ + config->next->prev = config; + *configp = config->next; + } + else + res = PARAM_NO_MEM; + } + } + + if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) { + const char *display = filename; + /* the help request is not really an error */ + if(!strcmp(filename, "-")) + display = ""; + if(res != PARAM_HELP_REQUESTED && + res != PARAM_MANUAL_REQUESTED && + res != PARAM_VERSION_INFO_REQUESTED && + res != PARAM_ENGINES_REQUESTED && + res != PARAM_CA_EMBED_REQUESTED) { + const char *reason = param2text(res); + errorf("%s:%d config file option '%s' %s", + display, lineno, option, reason); + if(res == PARAM_OPTION_UNKNOWN) + res = PARAM_CONFIG_OPTION_UNKNOWN; + return res; + } + } + return PARAM_OK; +} + +ParameterError parseconfig(const char *filename, int max_recursive, + char **resolved) +{ + FILE *file = NULL; + bool usedarg = FALSE; + ParameterError err = PARAM_OK; + struct OperationConfig *config = global->last; + char *pathalloc = NULL; + + file = open_config_file(filename, &filename, &pathalloc); if(file) { char *line; @@ -151,109 +293,20 @@ ParameterError parseconfig(const char *filename, int max_recursive, /* ... and has ended here */ if(*line) - *line++ = '\0'; /* null-terminate, we have a local copy of the data */ - -#ifdef DEBUG_CONFIG - curl_mfprintf(tool_stderr, "GOT: %s\n", option); -#endif - - /* pass spaces and separator(s) */ - while(ISBLANK(*line) || ISSEP(*line, dashed_option)) - line++; - - /* the parameter starts here (unless quoted) */ - if(*line == '\"') { - /* quoted parameter, do the quote dance */ - int rc = unslashquote(++line, &pbuf); - if(rc) { - err = PARAM_BAD_USE; - break; - } - param = curlx_dyn_len(&pbuf) ? curlx_dyn_ptr(&pbuf) : CURL_UNCONST(""); - } - else { - if(*line == '\'') { - warnf("%s:%d Option '%s' uses argument with leading single quote. " - "It is probably a mistake. Consider double quotes.", - filename, lineno, option); - } - param = line; /* parameter starts here */ - while(*line && !ISSPACE(*line)) /* stop also on CRLF */ - line++; - - if(*line) { - *line = '\0'; /* null-terminate */ + *line++ = '\0'; /* null-terminate, we have a local copy */ - /* to detect mistakes better, see if there is data following */ - line++; - /* pass all spaces */ - while(ISBLANK(*line)) - line++; - - switch(*line) { - case '\0': - case '\r': - case '\n': - case '#': /* comment */ - break; - default: - warnf("%s:%d Option '%s' uses argument with unquoted whitespace. " - "This may cause side-effects. Consider double quotes.", - filename, lineno, option); - } - } - if(!*param) - /* do this so getparameter can check for required parameters. - Otherwise it always thinks there is a parameter. */ - param = NULL; - } + /* if there is a parameter for this option, extract it */ + err = extract_param(line, dashed_option, &pbuf, filename, lineno, + option, ¶m); + if(err) + break; -#ifdef DEBUG_CONFIG - curl_mfprintf(tool_stderr, "PARAM: \"%s\"\n", - (param ? param : "(null)")); -#endif res = getparameter(option, param, &usedarg, config, max_recursive); - config = global->last; - if(!res && param && *param && !usedarg) - /* we passed in a parameter that was not used! */ - res = PARAM_GOT_EXTRA_PARAMETER; - - if(res == PARAM_NEXT_OPERATION) { - if(config->url_list && config->url_list->url) { - /* Allocate the next config */ - config->next = config_alloc(); - if(config->next) { - /* Update the last operation pointer */ - global->last = config->next; - - /* Move onto the new config */ - config->next->prev = config; - config = config->next; - } - else - res = PARAM_NO_MEM; - } - } + config = global->last; - if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) { - /* the help request is not really an error */ - if(!strcmp(filename, "-")) { - filename = ""; - } - if(res != PARAM_HELP_REQUESTED && - res != PARAM_MANUAL_REQUESTED && - res != PARAM_VERSION_INFO_REQUESTED && - res != PARAM_ENGINES_REQUESTED && - res != PARAM_CA_EMBED_REQUESTED) { - const char *reason = param2text(res); - errorf("%s:%d config file option '%s' %s", - filename, lineno, option, reason); - if(res == PARAM_OPTION_UNKNOWN) - res = PARAM_CONFIG_OPTION_UNKNOWN; - err = res; - } - } + err = process_config_result(res, &config, param, usedarg, filename, + lineno, option); } curlx_dyn_free(&buf); curlx_dyn_free(&pbuf); From ef8f68568f85627dcefee614e81f76f94e62043a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Jun 2026 10:20:02 +0200 Subject: [PATCH 0301/1062] urlapi: simplify urlget_url somewhat - make file_url() a separate function that returns a file:// URL - group the checks that need scheme info Closes #21856 --- lib/urlapi.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 08e29aa5134a..8151da95916b 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1447,6 +1447,20 @@ static CURLUcode urlget_format(const CURLU *u, CURLUPart what, return CURLUE_OK; } +static CURLUcode file_url(const CURLU *u, char **part, + const char *fragmentsep, + const char *querysep) +{ + char *url = curl_maprintf("file://%s%s%s%s%s", + u->path, querysep, u->query ? u->query : "", + fragmentsep, u->fragment ? u->fragment : ""); + if(!url) + return CURLUE_OUT_OF_MEMORY; + + *part = url; + return CURLUE_OK; +} + static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) { char *url; @@ -1458,11 +1472,8 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) (u->query_present && flags & CURLU_GET_EMPTY)) ? "?" : ""; char portbuf[7]; - if(u->scheme && curl_strequal("file", u->scheme)) { - url = curl_maprintf("file://%s%s%s%s%s", - u->path, querysep, u->query ? u->query : "", - fragmentsep, u->fragment ? u->fragment : ""); - } + if(curl_strequal("file", u->scheme)) + return file_url(u, part, fragmentsep, querysep); else if(!u->host) return CURLUE_NO_HOST; else { @@ -1479,25 +1490,22 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) return CURLUE_NO_SCHEME; h = Curl_get_scheme(scheme); - if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there is no stored port number, but asked to deliver - a default one for the scheme */ - if(h) { + if(h) { + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there is no stored port number, but asked to deliver a default one + for the scheme */ curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) + else if(port && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + /* there is a stored port number, but asked to inhibit if it matches + the default port for the scheme */ port = NULL; + if(!(h->flags & PROTOPT_URLOPTIONS)) + options = NULL; } - if(h && !(h->flags & PROTOPT_URLOPTIONS)) - options = NULL; - if(u->host[0] == '[') { if(u->zoneid) { /* make it '[ host %25 zoneid ]' */ From fda48a5a9cf8cfab8356bae71005342cf48ab3d7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Jun 2026 10:42:26 +0200 Subject: [PATCH 0302/1062] top-complexity: drop threshold to 50 Closes #21857 --- scripts/top-complexity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/top-complexity b/scripts/top-complexity index f2d869f607a2..38b68a3300e8 100755 --- a/scripts/top-complexity +++ b/scripts/top-complexity @@ -82,7 +82,7 @@ my %whitelist = ( # complexity above this level is treated as an error and contributes to the # script's exit code -my $cutoff = 60; +my $cutoff = 50; # show this many from the top my $top = $ARGV[0] ? $ARGV[0] : 25; From 75a94f3cd80d2dec5d6532137436bb553d27d3f5 Mon Sep 17 00:00:00 2001 From: Marcel Raad Date: Wed, 3 Jun 2026 08:58:08 +0200 Subject: [PATCH 0303/1062] tests: add `cookies` feature to some tests These fail without cookie support. Closes https://github.com/curl/curl/pull/21858 --- tests/data/test2015 | 1 + tests/data/test2504 | 3 +++ tests/data/test7 | 3 +++ 3 files changed, 7 insertions(+) diff --git a/tests/data/test2015 b/tests/data/test2015 index 6cd758471e10..d0479c5b7bd6 100644 --- a/tests/data/test2015 +++ b/tests/data/test2015 @@ -66,6 +66,7 @@ HTTP with cookie with with -b and redirect to new host http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -b "test=yes" --location +cookies proxy diff --git a/tests/data/test2504 b/tests/data/test2504 index 8cec1c8210f2..7bb4a81ed7e6 100644 --- a/tests/data/test2504 +++ b/tests/data/test2504 @@ -34,6 +34,9 @@ custom Host with cookie, handle reuse, no custom Host: http://%HOSTIP:%HTTPPORT + +cookies + # Verify data after the test has been "shot" diff --git a/tests/data/test7 b/tests/data/test7 index 0f00d8009762..ccdac1927380 100644 --- a/tests/data/test7 +++ b/tests/data/test7 @@ -35,6 +35,9 @@ HTTP with cookie parser and header recording http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER -b none -D %LOGDIR/heads%TESTNUMBER.txt + +cookies + # Verify data after the test has been "shot" From 3d721a1d41a7091525d6bbacae8d6580ca3199cd Mon Sep 17 00:00:00 2001 From: Andreas Falkenhahn Date: Wed, 3 Jun 2026 23:03:51 +0200 Subject: [PATCH 0304/1062] BINDINGS: Update Hollywood link - Change link from archive.org back to hollywood-mal.com since the site is up and running. Closes https://github.com/curl/curl/pull/21862 --- docs/BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/BINDINGS.md b/docs/BINDINGS.md index 9a53f81f29f8..5736bc75a966 100644 --- a/docs/BINDINGS.md +++ b/docs/BINDINGS.md @@ -61,7 +61,7 @@ Go: [go-curl](https://github.com/andelf/go-curl) by ShuYu Wang [Haskell](https://hackage.haskell.org/package/curl) Written by Galois, Inc -[Hollywood](https://web.archive.org/web/20250116185836/www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn +[Hollywood](https://www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn [Java](https://github.com/covers1624/curl4j) From c32427d0c1ac2c2a923243bf83db1e0fd703e788 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 5 Jun 2026 08:58:14 +0200 Subject: [PATCH 0305/1062] VULN-DISCLOSURE-POLICY.md: emphasize comm as a human Closes #21870 --- docs/VULN-DISCLOSURE-POLICY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index 379a6d0da56b..f999d4896836 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -36,6 +36,13 @@ announcement. [HackerOne](https://hackerone.com/curl). Issues filed there reach a handful of selected and trusted people. +- When communicating in the curl project, please explain your issues or + improvements briefly and clearly in your own human voice. Do not lazily + paste massive, AI-generated explanations; as a contributor doing this + infrequently, it is your responsibility to invest a few extra minutes into + making your message digestible. The maintainers review submissions + constantly, and clear writing reduces their daily burden and friction. + - The curl project cannot handle vulnerability reports sent to us over email. We lose track of the reports. We cannot easily disclose them. Please do not send us reports over email. From 56eca2afb4806f1032872fa97d1834b3c1385276 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 5 Jun 2026 08:34:46 +0200 Subject: [PATCH 0306/1062] quic: count zero length packets against max With a flood of zero lenght UDP packets to curl, the receive loop might run longer than intended to. Count such packets against the max to terminate the loop as intended. URL: https://hackerone.com/reports/3783438 Reported-by: vectorqueue on hackerone Closes #21869 --- lib/vquic/vquic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 2f0e0729516d..475f18e04a0f 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -508,8 +508,10 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, VERBOSE(++calls); for(i = 0; i < mcount; ++i) { /* A zero-length UDP packet is no QUIC packet. Ignore. */ - if(!mmsg[i].msg_len) + if(!mmsg[i].msg_len) { + ++pkts; continue; + } total_nread += mmsg[i].msg_len; gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); @@ -593,8 +595,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ++calls; /* A 0-length UDP packet is no QUIC packet */ - if(!nread) + if(!nread) { + ++pkts; continue; + } gso_size = vquic_msghdr_get_udp_gro(&msg); if(gso_size == 0) From 3c7d136225eb8e3095bf0d2c9aa61d6d18df7480 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 02:35:58 +0200 Subject: [PATCH 0307/1062] libssh2: use non-deprecated `libssh2_knownhost_addc()` Supported since libssh2 v1.2.5. Replacing `libssh2_knownhost_add()`, which was deprecated in that same version. The new API supports a comment field. Ref: https://github.com/libssh2/libssh2/pull/1977 Closes #21866 --- lib/vssh/libssh2.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 31c3024449f1..4a401edd2dd8 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -427,12 +427,13 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { /* the found host+key did not match but has been told to be fine anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->origin->hostname, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); + int addrc = libssh2_knownhost_addc(sshc->kh, + conn->origin->hostname, NULL, + remotekey, keylen, + NULL, 0, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, NULL); if(addrc) infof(data, "WARNING: adding the known host %s failed", conn->origin->hostname); From 5c9ac36e581601cffdb28adf982a87bd861bbb0c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 02:45:53 +0200 Subject: [PATCH 0308/1062] libssh2: do not use deprecated macros when unavailable To support building with `LIBSSH2_NO_DEPRECATED` macro defined, a future libssh2 that may have dropped these macros. Ref: https://github.com/libssh2/libssh2/pull/1977 Closes #21867 --- lib/vssh/libssh2.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 4a401edd2dd8..817e652e88e7 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -276,9 +276,11 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) case LIBSSH2_HOSTKEY_TYPE_RSA: keytype = CURLKHTYPE_RSA; break; - case LIBSSH2_HOSTKEY_TYPE_DSS: +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ keytype = CURLKHTYPE_DSS; break; +#endif #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keytype = CURLKHTYPE_ECDSA; @@ -337,9 +339,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, case LIBSSH2_HOSTKEY_TYPE_RSA: keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; break; - case LIBSSH2_HOSTKEY_TYPE_DSS: +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; break; +#endif case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; break; @@ -618,7 +622,9 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, static const char hostkey_method_ssh_ecdsa_256[] = "ecdsa-sha2-nistp256"; static const char hostkey_method_ssh_rsa_all[] = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS static const char hostkey_method_ssh_dss[] = "ssh-dss"; +#endif bool found = FALSE; if(sshc->kh && @@ -687,9 +693,11 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, case LIBSSH2_KNOWNHOST_KEY_SSHRSA: hostkey_method = hostkey_method_ssh_rsa_all; break; - case LIBSSH2_KNOWNHOST_KEY_SSHDSS: +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: /* deprecated upstream */ hostkey_method = hostkey_method_ssh_dss; break; +#endif case LIBSSH2_KNOWNHOST_KEY_RSA1: failf(data, "Found host key type RSA1 which is not supported"); return CURLE_SSH; From cb307544ad56fd5825c20efbbf40e75bdda2230d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 05:09:45 +0200 Subject: [PATCH 0309/1062] libssh2: sync version check with INTERNALS.md Follow-up to cf3b9657bcb7acd3525ca081b4ed16e860604d6d Closes #21868 --- lib/vssh/ssh.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index 24309f5207cf..de76c8a253d0 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -229,9 +229,9 @@ struct ssh_conn { /* Feature detection based on version numbers to better work with non-configure platforms */ -#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208) -#error "SCP/SFTP protocols require libssh2 1.2.8 or later" -/* 1.2.8 was released on April 5 2011 */ +#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010900) +#error "SCP/SFTP protocols require libssh2 1.9.0 or greater" +/* 1.9.0 was released on June 20 2019 */ #endif #endif /* USE_LIBSSH2 */ From 1b8f4dba2847e3a0c761341cae3ea0e9cc425aa6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 01:23:06 +0200 Subject: [PATCH 0310/1062] tidy-up: drop stray casts for allocated pointers Closes #21865 --- docs/FAQ.md | 2 +- docs/examples/multi-event.c | 4 +--- docs/examples/multi-uv.c | 4 +--- lib/content_encoding.c | 2 +- lib/mime.c | 6 ++---- lib/vquic/cf-ngtcp2.c | 4 ++-- lib/vtls/schannel.c | 4 ++-- lib/vtls/schannel_verify.c | 4 ++-- projects/OS400/ccsidcurl.c | 7 +++---- projects/OS400/curlcl.c | 2 +- projects/OS400/curlmain.c | 2 +- src/mkhelp.pl | 2 +- src/tool_cb_wrt.c | 3 +-- src/tool_formparse.c | 2 +- src/tool_operate.c | 4 +--- tests/libtest/lib2302.c | 2 +- 16 files changed, 22 insertions(+), 32 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 96f6d7a0541b..e3aee06f277a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -1017,7 +1017,7 @@ WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)data; - mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); + mem->memory = realloc(mem->memory, mem->size + realsize + 1); if(mem->memory) { memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; diff --git a/docs/examples/multi-event.c b/docs/examples/multi-event.c index 4c52cbe3b3ad..58744b6e2e6f 100644 --- a/docs/examples/multi-event.c +++ b/docs/examples/multi-event.c @@ -99,9 +99,7 @@ static void curl_perform(int fd, short event, void *arg) static struct curl_context *create_curl_context(curl_socket_t sockfd) { - struct curl_context *context; - - context = (struct curl_context *)malloc(sizeof(*context)); + struct curl_context *context = malloc(sizeof(*context)); context->sockfd = sockfd; diff --git a/docs/examples/multi-uv.c b/docs/examples/multi-uv.c index 8d6227fc7e78..094df35105ca 100644 --- a/docs/examples/multi-uv.c +++ b/docs/examples/multi-uv.c @@ -58,9 +58,7 @@ struct curl_context { static struct curl_context *create_curl_context(curl_socket_t sockfd, struct datauv *uv) { - struct curl_context *context; - - context = (struct curl_context *)malloc(sizeof(*context)); + struct curl_context *context = malloc(sizeof(*context)); context->sockfd = sockfd; context->uv = uv; diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 889271bdffce..a3a5877bfae6 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -89,7 +89,7 @@ static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void)opaque; /* not a typo, keep it curlx_calloc() */ - return (voidpf)curlx_calloc(items, size); + return curlx_calloc(items, size); } static void zfree_cb(voidpf opaque, voidpf ptr) diff --git a/lib/mime.c b/lib/mime.c index c15807a2e94e..c41b852e486b 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1202,9 +1202,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, /* Create a mime handle. */ curl_mime *curl_mime_init(void *easy) { - curl_mime *mime; - - mime = (curl_mime *)curlx_malloc(sizeof(*mime)); + curl_mime *mime = curlx_malloc(sizeof(*mime)); if(mime) { mime->parent = NULL; @@ -1241,7 +1239,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime) if(!mime) return NULL; - part = (curl_mimepart *)curlx_malloc(sizeof(*part)); + part = curlx_malloc(sizeof(*part)); if(part) { Curl_mime_initpart(part); diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index d27ffeaca036..ad0c9e582f68 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -1938,8 +1938,8 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, } if(ctx->tunnel_inbuf_len < max_udp_payload) { - unsigned char *newbuf = - (unsigned char *)curlx_realloc(ctx->tunnel_inbuf, max_udp_payload); + unsigned char *newbuf = curlx_realloc(ctx->tunnel_inbuf, + max_udp_payload); if(!newbuf) return CURLE_OUT_OF_MEMORY; ctx->tunnel_inbuf = newbuf; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 0298b2b65fc6..3782593c8ba7 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -468,7 +468,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, if(data->set.ssl.primary.key_passwd) pwd_len = strlen(data->set.ssl.primary.key_passwd); - pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); + pszPassword = curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); if(pszPassword) { int str_w_len = 0; if(pwd_len > 0) @@ -2001,7 +2001,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* calculate the complete message length and allocate a buffer for it */ data_len = backend->stream_sizes.cbHeader + len + backend->stream_sizes.cbTrailer; - ptr = (unsigned char *)curlx_malloc(data_len); + ptr = curlx_malloc(data_len); if(!ptr) { return CURLE_OUT_OF_MEMORY; } diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index f2f4218e3884..127fcf2b3f60 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -295,7 +295,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, goto cleanup; } - ca_file_buffer = (char *)curlx_malloc(ca_file_bufsize + 1); + ca_file_buffer = curlx_malloc(ca_file_bufsize + 1); if(!ca_file_buffer) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -568,7 +568,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ - cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); + cert_hostname_buff = curlx_malloc(len * sizeof(TCHAR)); if(!cert_hostname_buff) { result = CURLE_OUT_OF_MEMORY; goto cleanup; diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index a25197c1f2d0..cba89f8642ea 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -576,13 +576,12 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) case CURLINFO_CERTINFO: cipf = *(struct curl_certinfo **)paramp; if(cipf) { - cipt = (struct curl_certinfo *)malloc(sizeof(*cipt)); + cipt = malloc(sizeof(*cipt)); if(!cipt) result = CURLE_OUT_OF_MEMORY; else { - cipt->certinfo = - (struct curl_slist **)calloc(cipf->num_of_certs + 1, - sizeof(struct curl_slist *)); + cipt->certinfo = calloc(cipf->num_of_certs + 1, + sizeof(struct curl_slist *)); if(!cipt->certinfo) result = CURLE_OUT_OF_MEMORY; else { diff --git a/projects/OS400/curlcl.c b/projects/OS400/curlcl.c index 7a7f3c6459d6..8085307f31ca 100644 --- a/projects/OS400/curlcl.c +++ b/projects/OS400/curlcl.c @@ -149,7 +149,7 @@ int main(int argsc, struct arguments *args) if(!exitcode) { /* Allocate space for parsed arguments. */ - argv = (char **)malloc((argc + 1) * sizeof(*argv) + argsize); + argv = malloc((argc + 1) * sizeof(*argv) + argsize); if(!argv) { fputs("Memory allocation error\n", stderr); exitcode = -2; diff --git a/projects/OS400/curlmain.c b/projects/OS400/curlmain.c index 54ca865264dd..649d98a17daa 100644 --- a/projects/OS400/curlmain.c +++ b/projects/OS400/curlmain.c @@ -86,7 +86,7 @@ int main(int argc, char **argv) } /* Allocate memory for the ASCII arguments and vector. */ - argv = (char **)malloc((argc + 1) * sizeof(*argv) + bytecount); + argv = malloc((argc + 1) * sizeof(*argv) + bytecount); /* Build the vector and convert argument encoding. */ outbuf = (char *)(argv + argc + 1); diff --git a/src/mkhelp.pl b/src/mkhelp.pl index 053f74177619..89a4a9a1f205 100755 --- a/src/mkhelp.pl +++ b/src/mkhelp.pl @@ -105,7 +105,7 @@ { (void)opaque; /* not a typo, keep it curlx_calloc() */ - return (voidpf)curlx_calloc(items, size); + return curlx_calloc(items, size); } static void zfree_func(voidpf opaque, voidpf ptr) { diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index d412a1ea88d7..d514e34966ef 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -213,8 +213,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, /* grow the buffer if needed */ if(len > global->term.len) { - wchar_t *buf = (wchar_t *)curlx_realloc(global->term.buf, - len * sizeof(wchar_t)); + wchar_t *buf = curlx_realloc(global->term.buf, len * sizeof(wchar_t)); if(!buf) return CURL_WRITEFUNC_ERROR; global->term.len = len; diff --git a/src/tool_formparse.c b/src/tool_formparse.c index d93e8a68d0af..7e6d625cb251 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -33,7 +33,7 @@ static struct tool_mime *tool_mime_new(struct tool_mime *parent, toolmimekind kind) { - struct tool_mime *m = (struct tool_mime *)curlx_calloc(1, sizeof(*m)); + struct tool_mime *m = curlx_calloc(1, sizeof(*m)); if(m) { m->kind = kind; diff --git a/src/tool_operate.c b/src/tool_operate.c index dbf4ceea73d8..c4272cf567b1 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1709,9 +1709,7 @@ static int cb_timeout(CURLM *multi, long timeout_ms, void *userp) static struct contextuv *create_context(curl_socket_t sockfd, struct datauv *uv) { - struct contextuv *c; - - c = (struct contextuv *)curlx_malloc(sizeof(*c)); + struct contextuv *c = curlx_malloc(sizeof(*c)); c->sockfd = sockfd; c->uv = uv; diff --git a/tests/libtest/lib2302.c b/tests/libtest/lib2302.c index 7eb4931c5c27..01185bf0c329 100644 --- a/tests/libtest/lib2302.c +++ b/tests/libtest/lib2302.c @@ -102,7 +102,7 @@ static CURLcode test_lib2302(const char *URL) global_init(CURL_GLOBAL_ALL); memset(&ws_data, 0, sizeof(ws_data)); - ws_data.buf = (char *)curlx_calloc(LIB2302_BUFSIZE, 1); + ws_data.buf = curlx_calloc(LIB2302_BUFSIZE, 1); if(ws_data.buf) { curl = curl_easy_init(); if(curl) { From 0c8c6f4fc07435b7c088dc7349cd64cc9ccea44f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 16:22:43 +0200 Subject: [PATCH 0311/1062] libssh2: replace macro names with non-misspelled alternatives They are available in libssh2 0.15+. Closes #21876 --- lib/vssh/libssh2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 817e652e88e7..39faccf6663c 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -104,10 +104,10 @@ static const char *sftp_libssh2_strerror(unsigned long err) case LIBSSH2_FX_QUOTA_EXCEEDED: return "User quota exceeded"; - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; + case LIBSSH2_FX_UNKNOWN_PRINCIPAL: + return "Unknown principal"; - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return "File lock conflict"; case LIBSSH2_FX_DIR_NOT_EMPTY: @@ -170,7 +170,7 @@ static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) case LIBSSH2_FX_PERMISSION_DENIED: case LIBSSH2_FX_WRITE_PROTECT: - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return CURLE_REMOTE_ACCESS_DENIED; case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: From 982e19f231802b354ee724ecceeeb4033891eed1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 16:31:09 +0200 Subject: [PATCH 0312/1062] vquic: drop stray casts for `iovec.iov_len` Spotted by GitHub Code Quality Closes #21877 --- lib/vquic/vquic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 475f18e04a0f..c0a0cbe3b7e1 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -473,7 +473,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, memset(&mmsg, 0, sizeof(mmsg)); for(i = 0; i < n; ++i) { msg_iov[i].iov_base = bufs[i]; - msg_iov[i].iov_len = (int)sizeof(bufs[i]); + msg_iov[i].iov_len = sizeof(bufs[i]); mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; @@ -561,7 +561,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, * operating systems out there that mess with `msg_iov.iov_len`. */ memset(&msg, 0, sizeof(msg)); msg_iov.iov_base = buf; - msg_iov.iov_len = (int)sizeof(buf); + msg_iov.iov_len = sizeof(buf); msg.msg_iov = &msg_iov; msg.msg_iovlen = 1; msg.msg_control = msg_ctrl; From d3e9a815c4a68099b774742073c50b996d69fcd6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 2 Jun 2026 00:44:17 +0200 Subject: [PATCH 0313/1062] tidy-up: miscellaneous - fix typos. - badword: add two new words. - cpp: drop parentheses from standalone `#if` expressions. - libssh: vertical-align comment block with others. - clang-format. Closes #21880 --- docs/BINDINGS.md | 6 +- docs/FAQ.md | 2 +- docs/VULN-DISCLOSURE-POLICY.md | 4 +- docs/libcurl/curl_global_sslset.md | 4 +- include/curl/curl.h | 4 +- include/curl/system.h | 2 +- lib/curl_setup.h | 4 +- lib/mprintf.c | 6 +- lib/vquic/cf-capsule.c | 2 +- lib/vssh/libssh.c | 34 ++--- lib/vssh/libssh2.c | 2 +- projects/OS400/os400sys.c | 22 +-- scripts/badwords.txt | 2 + src/curlinfo.c | 4 +- src/tool_parsecfg.c | 15 +- tests/libtest/lib1538.c | 3 +- tests/libtest/lib1560.c | 6 +- tests/libtest/lib1901.c | 8 +- tests/libtest/lib1902.c | 2 +- tests/libtest/lib1915.c | 2 +- tests/libtest/lib1920.c | 8 +- tests/libtest/lib2405.c | 8 +- tests/libtest/lib2700.c | 2 +- tests/libtest/lib557.c | 12 +- tests/libtest/lib583.c | 2 +- tests/libtest/lib599.c | 2 +- tests/libtest/lib670.c | 2 +- tests/unit/unit1303.c | 110 +++++++------- tests/unit/unit1627.c | 2 +- tests/unit/unit1675.c | 230 ++++++++++++++--------------- tests/unit/unit2600.c | 2 +- 31 files changed, 254 insertions(+), 260 deletions(-) diff --git a/docs/BINDINGS.md b/docs/BINDINGS.md index 5736bc75a966..6c9aba61054b 100644 --- a/docs/BINDINGS.md +++ b/docs/BINDINGS.md @@ -67,11 +67,11 @@ Go: [go-curl](https://github.com/andelf/go-curl) by ShuYu Wang [Julia](https://github.com/JuliaWeb/LibCURL.jl) Written by Amit Murthy -[Kapito](https://github.com/puzza007/katipo) is an Erlang HTTP library around libcurl. +[Katipo](https://github.com/puzza007/katipo) is an Erlang HTTP library around libcurl. [Lisp](https://common-lisp.net/project/cl-curl/) Written by Liam Healy -[LibQurl](https://github.com/Qriist/LibQurl) a feature rich AutoHotKey v2 (AHKv2) wrapper around libcurl. +[LibQurl](https://github.com/Qriist/LibQurl) a feature-rich AutoHotKey v2 (AHKv2) wrapper around libcurl. Lua: [luacurl](https://web.archive.org/web/20201205052437/luacurl.luaforge.net/) by Alexander Marinov, [Lua-curl](https://github.com/Lua-cURL) by Jürgen Hötzel @@ -110,7 +110,7 @@ Bailiff and Bálint Szilakszi, [R](https://cran.r-project.org/package=curl) -[Rexx](https://rexxcurl.sourceforge.net/) Written Mark Hessling +[Rexx](https://rexxcurl.sourceforge.net/) Written by Mark Hessling [Ring](https://ring-lang.github.io/doc1.24/libcurl.html) RingLibCurl by Mahmoud Fayed diff --git a/docs/FAQ.md b/docs/FAQ.md index e3aee06f277a..e6d7c43cdc1e 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -33,7 +33,7 @@ platforms. The [internals document](https://curl.se/docs/install.html#Ports) lists more than 110 operating systems and 28 CPU architectures on which curl has been reported to run. -libcurl is free, thread-safe, IPv6 compatible, feature rich, well supported +libcurl is free, thread-safe, IPv6 compatible, feature-rich, well supported and fast. ### curl diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index f999d4896836..4523a1b2b6b3 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -157,7 +157,7 @@ made public. # Severity levels The curl project's security team rates security problems using four severity -levels depending how serious we consider the problem to be. We use **Low**, +levels depending on how serious we consider the problem to be. We use **Low**, **Medium**, **High** and **Critical**. We refrain from using numerical scoring of vulnerabilities. @@ -453,7 +453,7 @@ for all internal communication. Existing vulnerability disclosure process are followed for any embargoes and fixes. -Where possible, public communication are provided: +Where possible, public communications are provided: * regular communication from communication lead (for example daily update) * asynchronous communication from incident lead diff --git a/docs/libcurl/curl_global_sslset.md b/docs/libcurl/curl_global_sslset.md index 8218d355e27a..ccc7b7aa616c 100644 --- a/docs/libcurl/curl_global_sslset.md +++ b/docs/libcurl/curl_global_sslset.md @@ -40,7 +40,7 @@ specified, the *name* is ignored. If neither *id* nor *name* are specified, the function fails with **CURLSSLSET_UNKNOWN_BACKEND** and set the *avail* pointer to the -NULL-terminated list of available backends. The available backends are those +null-terminated list of available backends. The available backends are those that this particular build of libcurl supports. Since libcurl 7.60.0, the *avail* pointer is always set to the list of @@ -50,7 +50,7 @@ Upon success, the function returns **CURLSSLSET_OK**. If the specified SSL backend is not available, the function returns **CURLSSLSET_UNKNOWN_BACKEND** and sets the *avail* pointer to a -NULL-terminated list of available SSL backends. In this case, you may call the +null-terminated list of available SSL backends. In this case, you may call the function again to try to select a different backend. The SSL backend can be set only once. If it has already been set, a subsequent diff --git a/include/curl/curl.h b/include/curl/curl.h index 36af33e92291..9b2d0c855e26 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2812,14 +2812,14 @@ struct curl_slist { * backend can also be specified via the name parameter (passing -1 as id). If * both id and name are specified, the name is ignored. If neither id nor * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND - * and set the "avail" pointer to the NULL-terminated list of available + * and set the "avail" pointer to the null-terminated list of available * backends. * * Upon success, the function returns CURLSSLSET_OK. * * If the specified SSL backend is not available, the function returns * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a - * NULL-terminated list of available SSL backends. + * null-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. diff --git a/include/curl/system.h b/include/curl/system.h index c2dbab56e089..30216ea34c10 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -297,7 +297,7 @@ /* ===================================== */ #elif defined(_MSC_VER) -# if (_MSC_VER >= 1800) +# if _MSC_VER >= 1800 # include # define CURL_FORMAT_CURL_OFF_T PRId64 # define CURL_FORMAT_CURL_OFF_TU PRIu64 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index d4b805f9e20d..0e49429819c8 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -587,7 +587,7 @@ # endif #endif -#if (SIZEOF_CURL_OFF_T < 8) +#if SIZEOF_CURL_OFF_T < 8 #error "too small curl_off_t" #else /* assume SIZEOF_CURL_OFF_T == 8 */ @@ -598,7 +598,7 @@ #define FMT_OFF_T CURL_FORMAT_CURL_OFF_T #define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU -#if (SIZEOF_TIME_T == 4) +#if SIZEOF_TIME_T == 4 # ifdef HAVE_TIME_T_UNSIGNED # define TIME_T_MAX UINT_MAX # define TIME_T_MIN 0 diff --git a/lib/mprintf.c b/lib/mprintf.c index 06f6129c4b3e..230b554191e4 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -244,7 +244,7 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, fmt += 2; } else { -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; @@ -267,14 +267,14 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, case 'z': /* the code below generates a warning if -Wunreachable-code is used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) +#if SIZEOF_SIZE_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; #endif break; case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; diff --git a/lib/vquic/cf-capsule.c b/lib/vquic/cf-capsule.c index 6b5d8f107e4a..862b96cf93f5 100644 --- a/lib/vquic/cf-capsule.c +++ b/lib/vquic/cf-capsule.c @@ -105,7 +105,7 @@ static CURLcode capsule_cf_send(struct Curl_cfilter *cf, ctx->pending_offset += nwritten; if(ctx->pending_offset < ctx->pending_len) return CURLE_AGAIN; - /* pending capsule has been fully flusehd */ + /* pending capsule has been fully flushed */ *pnwritten = ctx->pending_payload; curlx_safefree(ctx->pending); return CURLE_OK; diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 817a463a2cce..4842ff3a916a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -3161,23 +3161,23 @@ void Curl_ssh_version(char *buffer, size_t buflen) * SCP. */ const struct Curl_protocol Curl_protocol_scp = { - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_is_dead */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ }; /* diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 39faccf6663c..32c60f110592 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3586,7 +3586,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) #ifdef CURL_LIBSSH2_DEBUG libssh2_trace(sshc->ssh_session, ~0); infof(data, "SSH socket: %d", (int)sock); -#endif /* CURL_LIBSSH2_DEBUG */ +#endif myssh_to(data, sshc, SSH_INIT); diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index dbceaf62593a..fc7ae3c6122f 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -435,17 +435,17 @@ OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, return rc; } -OM_uint32 -Curl_gss_init_sec_context_a(OM_uint32 *minor_status, - gss_cred_id_t cred_handle, - gss_ctx_id_t *context_handle, - gss_name_t target_name, gss_OID mech_type, - gss_flags_t req_flags, OM_uint32 time_req, - gss_channel_bindings_t input_chan_bindings, - gss_buffer_t input_token, - gss_OID *actual_mech_type, - gss_buffer_t output_token, gss_flags_t *ret_flags, - OM_uint32 *time_rec) +OM_uint32 Curl_gss_init_sec_context_a( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, gss_OID mech_type, + gss_flags_t req_flags, OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, gss_flags_t *ret_flags, + OM_uint32 *time_rec) { int rc; gss_buffer_desc in; diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 81929c3d5293..38efb03935cd 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -27,6 +27,7 @@ null terminate:null-terminate zero terminate:null-terminate nul terminated:null-terminated null terminated:null-terminated +NULL-terminated=null-terminated zero terminated:null-terminated nul terminator:null-terminator null terminator:null-terminator @@ -104,6 +105,7 @@ will:rewrite to present tense 63 bit:63-bit 64 bit:64-bit 128 bit:128-bit +256 bit:256-bit 8-bits:8 bits 16-bits:16 bits 32-bits:32 bits diff --git a/src/curlinfo.c b/src/curlinfo.c index 13b9d62ce068..94a02e965f21 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -195,14 +195,14 @@ static const char *disabled[] = { #endif , "large-time: " -#if (SIZEOF_TIME_T < 5) +#if SIZEOF_TIME_T < 5 "OFF" #else "ON" #endif , "large-size: " -#if (SIZEOF_SIZE_T < 5) +#if SIZEOF_SIZE_T < 5 "OFF" #else "ON" diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c index 173378d3acd0..e7d3a3f87827 100644 --- a/src/tool_parsecfg.c +++ b/src/tool_parsecfg.c @@ -197,14 +197,13 @@ static ParameterError extract_param(char *line, * Updates *configp if a new operation config is allocated. * Returns PARAM_OK if processing should continue, or an error code. */ -static ParameterError -process_config_result(ParameterError res, - struct OperationConfig **configp, - const char *param, - bool usedarg, - const char *filename, - int lineno, - const char *option) +static ParameterError process_config_result(ParameterError res, + struct OperationConfig **configp, + const char *param, + bool usedarg, + const char *filename, + int lineno, + const char *option) { if(!res && param && *param && !usedarg) /* we passed in a parameter that was not used! */ diff --git a/tests/libtest/lib1538.c b/tests/libtest/lib1538.c index 6a1db4e93e46..43c77ae5c3d6 100644 --- a/tests/libtest/lib1538.c +++ b/tests/libtest/lib1538.c @@ -45,8 +45,7 @@ static CURLcode test_lib1538(const char *URL) for(easyret = CURLE_OK; easyret <= CURL_LAST; easyret++) { curl_mprintf("e%d: %s\n", easyret, curl_easy_strerror(easyret)); } - for(mresult = CURLM_CALL_MULTI_PERFORM; mresult <= CURLM_LAST; - mresult++) { + for(mresult = CURLM_CALL_MULTI_PERFORM; mresult <= CURLM_LAST; mresult++) { curl_mprintf("m%d: %s\n", mresult, curl_multi_strerror(mresult)); } for(shareret = CURLSHE_OK; shareret <= CURLSHE_LAST; shareret++) { diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index de218dded8ae..e66ae84b5629 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1943,8 +1943,7 @@ static int scopeid(void) curl_free(url); } - rc = curl_url_set(u, CURLUPART_HOST, - "[fe80::20c:29ff:fe9c:409b%25eth0]", 0); + rc = curl_url_set(u, CURLUPART_HOST, "[fe80::20c:29ff:fe9c:409b%25eth0]", 0); if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", @@ -2228,8 +2227,7 @@ static int urldup(void) goto err; for(i = 0; url[i]; i++) { - CURLUcode rc = curl_url_set(h, CURLUPART_URL, url[i], - CURLU_GUESS_SCHEME); + CURLUcode rc = curl_url_set(h, CURLUPART_URL, url[i], CURLU_GUESS_SCHEME); if(rc) goto err; copy = curl_url_dup(h); diff --git a/tests/libtest/lib1901.c b/tests/libtest/lib1901.c index 80d06080de0f..a6ccbdb0cb00 100644 --- a/tests/libtest/lib1901.c +++ b/tests/libtest/lib1901.c @@ -25,13 +25,7 @@ static size_t t1901_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { - static const char *chunks[] = { - "one", - "two", - "three", - "four", - NULL - }; + static const char *chunks[] = { "one", "two", "three", "four", NULL }; static int ix = 0; (void)stream; if(chunks[ix]) { diff --git a/tests/libtest/lib1902.c b/tests/libtest/lib1902.c index da3b23a349d4..b2ceb3dc39b5 100644 --- a/tests/libtest/lib1902.c +++ b/tests/libtest/lib1902.c @@ -33,7 +33,7 @@ static CURLcode test_lib1902(const char *URL) curl = curl_easy_init(); if(curl) { easy_setopt(curl, CURLOPT_COOKIEFILE, URL); - easy_setopt(curl, CURLOPT_COOKIEJAR, URL); + easy_setopt(curl, CURLOPT_COOKIEJAR, URL); /* Do not perform any actual network operation, the issue occur when not calling curl.*perform */ diff --git a/tests/libtest/lib1915.c b/tests/libtest/lib1915.c index d5dd4dc2fcdf..f838e7bdb381 100644 --- a/tests/libtest/lib1915.c +++ b/tests/libtest/lib1915.c @@ -38,7 +38,7 @@ static CURLSTScode hstsread(CURL *curl, struct curl_hstsentry *e, void *userp) }; static const struct entry preload_hosts[] = { -#if (SIZEOF_TIME_T < 5) +#if SIZEOF_TIME_T < 5 { "1.example.com", "20370320 01:02:03" }, { "2.example.com.", "20370320 03:02:01" }, { "3.example.com", "20370319 01:02:03" }, diff --git a/tests/libtest/lib1920.c b/tests/libtest/lib1920.c index fe7e8b3206e3..84a747ee6f2c 100644 --- a/tests/libtest/lib1920.c +++ b/tests/libtest/lib1920.c @@ -33,9 +33,9 @@ static CURLcode test_lib1920(const char *URL) curl = curl_easy_init(); if(curl) { easy_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); - easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); - easy_setopt(curl, CURLOPT_URL, URL); - easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); @@ -44,7 +44,7 @@ static CURLcode test_lib1920(const char *URL) curl_easy_reset(curl); /* set the cookie jar name so that curl knows where to store the cookies after reset */ - easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); + easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); } } diff --git a/tests/libtest/lib2405.c b/tests/libtest/lib2405.c index eee6e9123708..a0c9a91755b9 100644 --- a/tests/libtest/lib2405.c +++ b/tests/libtest/lib2405.c @@ -53,10 +53,10 @@ goto test_cleanup; \ } -#define test_run_check(option, expected_fds) \ - do { \ - result = test_run(URL, option, &fd_count); \ - test_check(expected_fds); \ +#define test_run_check(option, expected_fds) \ + do { \ + result = test_run(URL, option, &fd_count); \ + test_check(expected_fds); \ } while(0) /* ---------------------------------------------------------------- */ diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index e093fa72661e..5eac6011dc06 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -50,7 +50,7 @@ static CURLcode send_header(CURL *curl, int flags, size_t size) retry: result = curl_ws_send(curl, NULL, 0, &nsent, (curl_off_t)size, - flags | CURLWS_OFFSET); + flags | CURLWS_OFFSET); if(result == CURLE_AGAIN) { assert(nsent == 0); goto retry; diff --git a/tests/libtest/lib557.c b/tests/libtest/lib557.c index 514e71cd3c75..e7fb499fb829 100644 --- a/tests/libtest/lib557.c +++ b/tests/libtest/lib557.c @@ -415,7 +415,7 @@ static int test_unsigned_long_formatting(void) int num_ulong_tests = 0; int failed = 0; -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 i = 1; ul_test[i].num = 0xFFFFFFFFUL; ul_test[i].expected = "4294967295"; i++; ul_test[i].num = 0xFFFF0000UL; ul_test[i].expected = "4294901760"; @@ -449,7 +449,7 @@ static int test_unsigned_long_formatting(void) num_ulong_tests = i; -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 /* !checksrc! disable LONGLINE all */ i = 1; ul_test[i].num = 0xFFFFFFFFFFFFFFFFUL; ul_test[i].expected = "18446744073709551615"; i++; ul_test[i].num = 0xFFFFFFFF00000000UL; ul_test[i].expected = "18446744069414584320"; @@ -541,7 +541,7 @@ static int test_signed_long_formatting(void) int num_slong_tests = 0; int failed = 0; -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 i = 1; sl_test[i].num = 0x7FFFFFFFL; sl_test[i].expected = "2147483647"; i++; sl_test[i].num = 0x7FFFFFFEL; sl_test[i].expected = "2147483646"; @@ -608,7 +608,7 @@ static int test_signed_long_formatting(void) num_slong_tests = i; -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 i = 1; sl_test[i].num = 0x7FFFFFFFFFFFFFFFL; sl_test[i].expected = "9223372036854775807"; i++; sl_test[i].num = 0x7FFFFFFFFFFFFFFEL; sl_test[i].expected = "9223372036854775806"; @@ -1186,11 +1186,11 @@ static int test_oct_hex_formatting(void) 0xFABC1230U, 0xFABC1230U, 0xFABC1230U, 1234U); errors += string_check(buf, "37257011060 fabc1230 FABC1230 +2322"); -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 curl_msnprintf(buf, sizeof(buf), "%lo %lx %lX %+lo", 0xFABC1230UL, 0xFABC1230UL, 0xFABC1230UL, 1234UL); errors += string_check(buf, "37257011060 fabc1230 FABC1230 +2322"); -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 curl_msnprintf(buf, sizeof(buf), "%lo %lx %lX %+lo", 0xFABCDEF123456780UL, 0xFABCDEF123456780UL, 0xFABCDEF123456780UL, 1234UL); diff --git a/tests/libtest/lib583.c b/tests/libtest/lib583.c index 984764eb12db..8f366003f483 100644 --- a/tests/libtest/lib583.c +++ b/tests/libtest/lib583.c @@ -45,7 +45,7 @@ static CURLcode test_lib583(const char *URL) easy_init(curl); easy_setopt(curl, CURLOPT_USERPWD, libtest_arg2); - easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[3]); + easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[3]); easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, test_argv[4]); easy_setopt(curl, CURLOPT_UPLOAD, 1L); diff --git a/tests/libtest/lib599.c b/tests/libtest/lib599.c index 079fc578dfda..4b26c46737fc 100644 --- a/tests/libtest/lib599.c +++ b/tests/libtest/lib599.c @@ -79,7 +79,7 @@ static CURLcode test_lib599(const char *URL) if(!result) { FILE *moo; result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, - &content_length); + &content_length); moo = curlx_fopen(libtest_arg2, "wb"); if(moo) { curl_mfprintf(moo, "CL %.0f\n", content_length); diff --git a/tests/libtest/lib670.c b/tests/libtest/lib670.c index 60e4f7ef25c4..037ca9c30a35 100644 --- a/tests/libtest/lib670.c +++ b/tests/libtest/lib670.c @@ -140,7 +140,7 @@ static CURLcode test_lib670(const char *URL) formrc = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, testname, CURLFORM_STREAM, &pooh, - CURLFORM_CONTENTLEN, (curl_off_t) 2, + CURLFORM_CONTENTLEN, (curl_off_t)2, CURLFORM_END); if(formrc) { curl_mfprintf(stderr, "curl_formadd() = %d\n", formrc); diff --git a/tests/unit/unit1303.c b/tests/unit/unit1303.c index 49c48e3b2c21..50cdaa12f126 100644 --- a/tests/unit/unit1303.c +++ b/tests/unit/unit1303.c @@ -49,10 +49,13 @@ static void t1303_stop(struct Curl_easy *easy) #define BASE 1000000 /* macro to set the pretended current time */ -#define NOW(x, y) now.tv_sec = x; now.tv_usec = y +#define NOW(x, y) \ + now.tv_sec = x; \ + now.tv_usec = y /* macro to set the millisecond based timeouts to use */ -#define TIMEOUTS(x, y) easy->set.timeout = x; \ - easy->set.connecttimeout = y +#define TIMEOUTS(x, y) \ + easy->set.timeout = x; \ + easy->set.connecttimeout = y /* * To test: @@ -83,57 +86,56 @@ static CURLcode test_unit1303(const char *arg) }; const struct timetest run[] = { - /* both timeouts set, not connecting */ - {BASE + 4, 0, 10000, 8000, FALSE, 6000, "6 seconds should be left"}, - {BASE + 4, 990000, 10000, 8000, FALSE, 5010, "5010 ms should be left"}, - {BASE + 10, 0, 10000, 8000, FALSE, -1, "timeout is -1, expired"}, - {BASE + 12, 0, 10000, 8000, FALSE, -2000, "-2000, overdue 2 seconds"}, - - /* both timeouts set, connecting */ - {BASE + 4, 0, 10000, 8000, TRUE, 4000, "4 seconds should be left"}, - {BASE + 4, 990000, 10000, 8000, TRUE, 3010, "3010 ms should be left"}, - {BASE + 8, 0, 10000, 8000, TRUE, -1, "timeout is -1, expired"}, - {BASE + 10, 0, 10000, 8000, TRUE, -2000, "-2000, overdue 2 seconds"}, - - /* no connect timeout set, not connecting */ - {BASE + 4, 0, 10000, 0, FALSE, 6000, "6 seconds should be left"}, - {BASE + 4, 990000, 10000, 0, FALSE, 5010, "5010 ms should be left"}, - {BASE + 10, 0, 10000, 0, FALSE, -1, "timeout is -1, expired"}, - {BASE + 12, 0, 10000, 0, FALSE, -2000, "-2000, overdue 2 seconds"}, - - /* no connect timeout set, connecting */ - {BASE + 4, 0, 10000, 0, TRUE, 6000, "6 seconds should be left"}, - {BASE + 4, 990000, 10000, 0, TRUE, 5010, "5010 ms should be left"}, - {BASE + 10, 0, 10000, 0, TRUE, -1, "timeout is -1, expired"}, - {BASE + 12, 0, 10000, 0, TRUE, -2000, "-2000, overdue 2 seconds"}, - - /* only connect timeout set, not connecting */ - {BASE + 4, 0, 0, 10000, FALSE, 0, "no timeout active"}, - {BASE + 4, 990000, 0, 10000, FALSE, 0, "no timeout active"}, - {BASE + 10, 0, 0, 10000, FALSE, 0, "no timeout active"}, - {BASE + 12, 0, 0, 10000, FALSE, 0, "no timeout active"}, - - /* only connect timeout set, connecting */ - {BASE + 4, 0, 0, 10000, TRUE, 6000, "6 seconds should be left"}, - {BASE + 4, 990000, 0, 10000, TRUE, 5010, "5010 ms should be left"}, - {BASE + 10, 0, 0, 10000, TRUE, -1, "timeout is -1, expired"}, - {BASE + 12, 0, 0, 10000, TRUE, -2000, "-2000, overdue 2 seconds"}, - - /* no timeout set, not connecting */ - {BASE + 4, 0, 0, 0, FALSE, 0, "no timeout active"}, - {BASE + 4, 990000, 0, 0, FALSE, 0, "no timeout active"}, - {BASE + 10, 0, 0, 0, FALSE, 0, "no timeout active"}, - {BASE + 12, 0, 0, 0, FALSE, 0, "no timeout active"}, - - /* no timeout set, connecting */ - {BASE + 4, 0, 0, 0, TRUE, 296000, "no timeout active"}, - {BASE + 4, 990000, 0, 0, TRUE, 295010, "no timeout active"}, - {BASE + 10, 0, 0, 0, TRUE, 290000, "no timeout active"}, - {BASE + 12, 0, 0, 0, TRUE, 288000, "no timeout active"}, - - /* both timeouts set, connecting, connect timeout the longer one */ - {BASE + 4, 0, 10000, 12000, TRUE, 6000, "6 seconds should be left"}, - + /* both timeouts set, not connecting */ + {BASE + 4, 0, 10000, 8000, FALSE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 8000, FALSE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 8000, FALSE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 8000, FALSE, -2000, "-2000, overdue 2 seconds"}, + + /* both timeouts set, connecting */ + {BASE + 4, 0, 10000, 8000, TRUE, 4000, "4 seconds should be left"}, + {BASE + 4, 990000, 10000, 8000, TRUE, 3010, "3010 ms should be left"}, + {BASE + 8, 0, 10000, 8000, TRUE, -1, "timeout is -1, expired"}, + {BASE + 10, 0, 10000, 8000, TRUE, -2000, "-2000, overdue 2 seconds"}, + + /* no connect timeout set, not connecting */ + {BASE + 4, 0, 10000, 0, FALSE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 0, FALSE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 0, FALSE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 0, FALSE, -2000, "-2000, overdue 2 seconds"}, + + /* no connect timeout set, connecting */ + {BASE + 4, 0, 10000, 0, TRUE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 0, TRUE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 0, TRUE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 0, TRUE, -2000, "-2000, overdue 2 seconds"}, + + /* only connect timeout set, not connecting */ + {BASE + 4, 0, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 4, 990000, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 10, 0, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 12, 0, 0, 10000, FALSE, 0, "no timeout active"}, + + /* only connect timeout set, connecting */ + {BASE + 4, 0, 0, 10000, TRUE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 0, 10000, TRUE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 0, 10000, TRUE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 0, 10000, TRUE, -2000, "-2000, overdue 2 seconds"}, + + /* no timeout set, not connecting */ + {BASE + 4, 0, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 4, 990000, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 10, 0, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 12, 0, 0, 0, FALSE, 0, "no timeout active"}, + + /* no timeout set, connecting */ + {BASE + 4, 0, 0, 0, TRUE, 296000, "no timeout active"}, + {BASE + 4, 990000, 0, 0, TRUE, 295010, "no timeout active"}, + {BASE + 10, 0, 0, 0, TRUE, 290000, "no timeout active"}, + {BASE + 12, 0, 0, 0, TRUE, 288000, "no timeout active"}, + + /* both timeouts set, connecting, connect timeout the longer one */ + {BASE + 4, 0, 10000, 12000, TRUE, 6000, "6 seconds should be left"}, }; /* this is the pretended start time of the transfer */ diff --git a/tests/unit/unit1627.c b/tests/unit/unit1627.c index f0764709b25c..1d1f598358fc 100644 --- a/tests/unit/unit1627.c +++ b/tests/unit/unit1627.c @@ -80,7 +80,7 @@ static CURLcode test_unit1627(const char *arg) break; } Curl_strntolower(buffer, okay[i], strlen(okay[i])); - buffer[ strlen(okay[i]) ] = 0; + buffer[strlen(okay[i])] = 0; if(strcmp(buffer, get->name)) { curl_mprintf("Input: %s is not lowercase: %s\n", buffer, get->name); break; diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index b5b372336af3..ad5e92a25ead 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -39,71 +39,71 @@ static CURLcode test_unit1675(const char *arg) const char *out; }; const struct ipv4_test tests[] = { - {"0x.0x.0x.0x", NULL}, /* invalid hex */ - {"0x.0x.0x", NULL}, /* invalid hex */ - {"0x.0x", NULL}, /* invalid hex */ - {"0x", NULL}, /* invalid hex */ - {"0", "0.0.0.0"}, - {"00", "0.0.0.0"}, - {"00000000000", "0.0.0.0"}, - {"127.0.0.1", "127.0.0.1"}, - {"0177.0.0.1", "127.0.0.1"}, - {"00177.0.0.1", "127.0.0.1"}, - {"0x7f.0.0.1", "127.0.0.1"}, - {"0x07f.0.0.1", "127.0.0.1"}, - {"1", "0.0.0.1"}, - {"010", "0.0.0.8"}, - {"001", "0.0.0.1"}, - {"127", "0.0.0.127"}, - {"127.1", "127.0.0.1"}, - {"127.0.1", "127.0.0.1"}, - {"1.16777215", "1.255.255.255"}, - {"1.16777216", NULL}, /* overflow */ - {"1.1.65535", "1.1.255.255"}, - {"1.1.65536", NULL}, /* overflow */ - {"0x7f000001", "127.0.0.1"}, - {"0x7F000001", "127.0.0.1"}, - {"0x7g000001", NULL}, /* bad hex */ - {"2130706433", "127.0.0.1"}, - {"017700000001", "127.0.0.1"}, - {"000000000017700000001", "127.0.0.1"}, - {"192.168.0.1", "192.168.0.1"}, - {"0300.0250.0000.0001", "192.168.0.1"}, - {"0xc0.0xa8.0.1", "192.168.0.1"}, - {"0xc0a80001", "192.168.0.1"}, - {"3232235521", "192.168.0.1"}, - {"4294967294", "255.255.255.254"}, - {"4294967295", "255.255.255.255"}, - {"037777777777", "255.255.255.255"}, - {"0xFFFFFFFF", "255.255.255.255"}, - {"0xFFFFFfff", "255.255.255.255"}, - {"1.2.3.4.5", NULL}, /* too many parts */ - {"256.0.0.1", NULL}, /* overflow */ - {"1.256.0.1", NULL}, /* overflow */ - {"1.1.256.1", NULL}, /* overflow */ - {"1.0.0.256", NULL}, /* overflow */ - {"0x100.0.0.1", NULL}, /* overflow */ - {"1.0x100.0.1", NULL}, /* overflow */ - {"1.1.0x100.1", NULL}, /* overflow */ - {"1.1.1.0x100", NULL}, /* overflow */ - {"0400.0.0.1", NULL}, /* overflow */ - {"4.0400.0.1", NULL}, /* overflow */ - {"4.4.0400.1", NULL}, /* overflow */ - {"4.4.4.0400", NULL}, /* overflow */ - {"4294967296", NULL}, /* overflow */ - {"040000000000", NULL}, /* overflow */ - {"0x100000000", NULL}, /* overflow */ - {"1.2.3.-4", NULL}, /* negative */ - {"1.2.-3.4", NULL}, /* negative */ - {"1.-2.3.4", NULL}, /* negative */ - {"-1.2.3.4", NULL}, /* negative */ - {"-12", NULL}, /* negative */ - {"-12.1", NULL}, /* negative */ - {"-12.2.3", NULL}, /* negative */ - {" 1.2.3.4", NULL}, /* space */ - {"1. 2.3.4", NULL}, /* space */ - {"1.2. 3.4", NULL}, /* space */ - {"1.2.3. 4", NULL}, /* space */ + { "0x.0x.0x.0x", NULL }, /* invalid hex */ + { "0x.0x.0x", NULL }, /* invalid hex */ + { "0x.0x", NULL }, /* invalid hex */ + { "0x", NULL }, /* invalid hex */ + { "0", "0.0.0.0" }, + { "00", "0.0.0.0" }, + { "00000000000", "0.0.0.0" }, + { "127.0.0.1", "127.0.0.1" }, + { "0177.0.0.1", "127.0.0.1" }, + { "00177.0.0.1", "127.0.0.1" }, + { "0x7f.0.0.1", "127.0.0.1" }, + { "0x07f.0.0.1", "127.0.0.1" }, + { "1", "0.0.0.1" }, + { "010", "0.0.0.8" }, + { "001", "0.0.0.1" }, + { "127", "0.0.0.127" }, + { "127.1", "127.0.0.1" }, + { "127.0.1", "127.0.0.1" }, + { "1.16777215", "1.255.255.255" }, + { "1.16777216", NULL }, /* overflow */ + { "1.1.65535", "1.1.255.255" }, + { "1.1.65536", NULL }, /* overflow */ + { "0x7f000001", "127.0.0.1" }, + { "0x7F000001", "127.0.0.1" }, + { "0x7g000001", NULL }, /* bad hex */ + { "2130706433", "127.0.0.1" }, + { "017700000001", "127.0.0.1" }, + { "000000000017700000001", "127.0.0.1" }, + { "192.168.0.1", "192.168.0.1" }, + { "0300.0250.0000.0001", "192.168.0.1" }, + { "0xc0.0xa8.0.1", "192.168.0.1" }, + { "0xc0a80001", "192.168.0.1" }, + { "3232235521", "192.168.0.1" }, + { "4294967294", "255.255.255.254" }, + { "4294967295", "255.255.255.255" }, + { "037777777777", "255.255.255.255" }, + { "0xFFFFFFFF", "255.255.255.255" }, + { "0xFFFFFfff", "255.255.255.255" }, + { "1.2.3.4.5", NULL }, /* too many parts */ + { "256.0.0.1", NULL }, /* overflow */ + { "1.256.0.1", NULL }, /* overflow */ + { "1.1.256.1", NULL }, /* overflow */ + { "1.0.0.256", NULL }, /* overflow */ + { "0x100.0.0.1", NULL }, /* overflow */ + { "1.0x100.0.1", NULL }, /* overflow */ + { "1.1.0x100.1", NULL }, /* overflow */ + { "1.1.1.0x100", NULL }, /* overflow */ + { "0400.0.0.1", NULL }, /* overflow */ + { "4.0400.0.1", NULL }, /* overflow */ + { "4.4.0400.1", NULL }, /* overflow */ + { "4.4.4.0400", NULL }, /* overflow */ + { "4294967296", NULL }, /* overflow */ + { "040000000000", NULL }, /* overflow */ + { "0x100000000", NULL }, /* overflow */ + { "1.2.3.-4", NULL }, /* negative */ + { "1.2.-3.4", NULL }, /* negative */ + { "1.-2.3.4", NULL }, /* negative */ + { "-1.2.3.4", NULL }, /* negative */ + { "-12", NULL }, /* negative */ + { "-12.1", NULL }, /* negative */ + { "-12.2.3", NULL }, /* negative */ + { " 1.2.3.4", NULL }, /* space */ + { "1. 2.3.4", NULL }, /* space */ + { "1.2. 3.4", NULL }, /* space */ + { "1.2.3. 4", NULL }, /* space */ }; curlx_dyn_init(&host, 256); @@ -148,33 +148,33 @@ static CURLcode test_unit1675(const char *arg) const char *out; }; const struct urlencode_test tests[] = { - {"http://leave\x01/hello\x01world", FALSE, QUERY_NO, - "http://leave\x01/hello%01world"}, - {"http://leave/hello\x01world", FALSE, QUERY_NO, - "http://leave/hello%01world"}, - {"http://le ave/hello\x01world", FALSE, QUERY_NO, - "http://le ave/hello%01world"}, - {"hello\x01world", TRUE, QUERY_NO, "hello%01world"}, - {"hello\xf0world", TRUE, QUERY_NO, "hello%F0world"}, - {"hello world", TRUE, QUERY_NO, "hello%20world"}, - {"hello%20world", TRUE, QUERY_NO, "hello%20world"}, - {"hello world", TRUE, QUERY_YES, "hello+world"}, - {"a+b c", TRUE, QUERY_NO, "a+b%20c"}, - {"a%20b%20c", TRUE, QUERY_NO, "a%20b%20c"}, - {"a%aab%aac", TRUE, QUERY_NO, "a%AAb%AAc"}, - {"a%aab%AAc", TRUE, QUERY_NO, "a%AAb%AAc"}, - {"w%w%x", TRUE, QUERY_NO, "w%w%x"}, - {"w%wf%xf", TRUE, QUERY_NO, "w%wf%xf"}, - {"w%fw%fw", TRUE, QUERY_NO, "w%fw%fw"}, - {"a+b c", TRUE, QUERY_YES, "a+b+c"}, - {"/foo/bar", TRUE, QUERY_NO, "/foo/bar"}, - {"/foo/bar", TRUE, QUERY_YES, "/foo/bar"}, - {"/foo/ bar", TRUE, QUERY_NO, "/foo/%20bar"}, - {"/foo/ bar", TRUE, QUERY_YES, "/foo/+bar"}, - {"~-._", TRUE, QUERY_NO, "~-._"}, - {"~-._", TRUE, QUERY_YES, "~-._"}, - {"foo bar?foo bar", TRUE, QUERY_NO, "foo%20bar?foo%20bar"}, - {"foo bar?foo bar", TRUE, QUERY_NOT_YET, "foo%20bar?foo+bar"}, + { "http://leave\x01/hello\x01world", FALSE, QUERY_NO, + "http://leave\x01/hello%01world" }, + { "http://leave/hello\x01world", FALSE, QUERY_NO, + "http://leave/hello%01world" }, + { "http://le ave/hello\x01world", FALSE, QUERY_NO, + "http://le ave/hello%01world" }, + { "hello\x01world", TRUE, QUERY_NO, "hello%01world" }, + { "hello\xf0world", TRUE, QUERY_NO, "hello%F0world" }, + { "hello world", TRUE, QUERY_NO, "hello%20world" }, + { "hello%20world", TRUE, QUERY_NO, "hello%20world" }, + { "hello world", TRUE, QUERY_YES, "hello+world" }, + { "a+b c", TRUE, QUERY_NO, "a+b%20c" }, + { "a%20b%20c", TRUE, QUERY_NO, "a%20b%20c" }, + { "a%aab%aac", TRUE, QUERY_NO, "a%AAb%AAc" }, + { "a%aab%AAc", TRUE, QUERY_NO, "a%AAb%AAc" }, + { "w%w%x", TRUE, QUERY_NO, "w%w%x" }, + { "w%wf%xf", TRUE, QUERY_NO, "w%wf%xf" }, + { "w%fw%fw", TRUE, QUERY_NO, "w%fw%fw" }, + { "a+b c", TRUE, QUERY_YES, "a+b+c" }, + { "/foo/bar", TRUE, QUERY_NO, "/foo/bar" }, + { "/foo/bar", TRUE, QUERY_YES, "/foo/bar" }, + { "/foo/ bar", TRUE, QUERY_NO, "/foo/%20bar" }, + { "/foo/ bar", TRUE, QUERY_YES, "/foo/+bar" }, + { "~-._", TRUE, QUERY_NO, "~-._" }, + { "~-._", TRUE, QUERY_YES, "~-._" }, + { "foo bar?foo bar", TRUE, QUERY_NO, "foo%20bar?foo%20bar" }, + { "foo bar?foo bar", TRUE, QUERY_NOT_YET, "foo%20bar?foo+bar" }, }; curlx_dyn_init(&out, 256); @@ -206,11 +206,11 @@ static CURLcode test_unit1675(const char *arg) const char *out_zone; }; const struct ipv6_test tests[] = { - {"[::1]", "[::1]", NULL}, - {"[fe80::1%eth0]", "[fe80::1]", "eth0"}, - {"[fe80::1%25eth0]", "[fe80::1]", "eth0"}, - {"[::1", NULL, NULL}, /* missing bracket */ - {"[]", NULL, NULL}, /* empty */ + { "[::1]", "[::1]", NULL }, + { "[fe80::1%eth0]", "[fe80::1]", "eth0" }, + { "[fe80::1%25eth0]", "[fe80::1]", "eth0" }, + { "[::1", NULL, NULL }, /* missing bracket */ + { "[]", NULL, NULL }, /* empty */ }; for(i = 0; i < CURL_ARRAYSIZE(tests); i++) { @@ -264,16 +264,16 @@ static CURLcode test_unit1675(const char *arg) bool fine; }; const struct file_test tests[] = { - {"file:///etc/hosts", "/etc/hosts", TRUE}, - {"file://localhost/etc/hosts", "/etc/hosts", TRUE}, - {"file://apple/etc/hosts", "/etc/hosts", FALSE}, - {"file:foo", NULL, FALSE}, - {"file:./", NULL, FALSE}, - {"file:?q", NULL, FALSE}, - {"file:#f", NULL, FALSE}, + { "file:///etc/hosts", "/etc/hosts", TRUE }, + { "file://localhost/etc/hosts", "/etc/hosts", TRUE }, + { "file://apple/etc/hosts", "/etc/hosts", FALSE }, + { "file:foo", NULL, FALSE }, + { "file:./", NULL, FALSE }, + { "file:?q", NULL, FALSE }, + { "file:#f", NULL, FALSE }, #ifdef _WIN32 - {"file:///c:/windows/system32", "c:/windows/system32", TRUE}, - {"file://localhost/c:/windows/system32", "c:/windows/system32", TRUE}, + { "file:///c:/windows/system32", "c:/windows/system32", TRUE }, + { "file://localhost/c:/windows/system32", "c:/windows/system32", TRUE }, #endif }; @@ -321,16 +321,16 @@ static CURLcode test_unit1675(const char *arg) bool expect_match; }; const struct origin_test tests[] = { - {"http://host:123/x", "http", "host", "123", "/y", TRUE}, - {"http://host:123/x", NULL, "host", "123", "/y", TRUE}, - {"http://host:123/x", NULL, NULL, NULL, "/y", TRUE}, - {"http://host:80/x", "http", "host", "123", "/y", FALSE}, - {"http://host:80/x", "http", "host", NULL, "/y", TRUE}, - {"http://host/x", "http", "host", "80", "/y", TRUE}, + { "http://host:123/x", "http", "host", "123", "/y", TRUE }, + { "http://host:123/x", NULL, "host", "123", "/y", TRUE }, + { "http://host:123/x", NULL, NULL, NULL, "/y", TRUE }, + { "http://host:80/x", "http", "host", "123", "/y", FALSE }, + { "http://host:80/x", "http", "host", NULL, "/y", TRUE }, + { "http://host/x", "http", "host", "80", "/y", TRUE }, #ifdef USE_SSL - {"http://host:123/x", "https", "host", "123", "/y", FALSE}, - {"https://host/x", "http", "host", "443", "/y", FALSE}, - {"https://host/x", "https", "host", "443", "/y", TRUE}, + { "http://host:123/x", "https", "host", "123", "/y", FALSE }, + { "https://host/x", "http", "host", "443", "/y", FALSE }, + { "https://host/x", "https", "host", "443", "/y", TRUE }, #endif }; diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 213fd0b178db..fc4d8b7d2c05 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -284,7 +284,7 @@ static void check_result(const struct test_case *tc, struct test_result *tr) fail(msg); } if(tr->cf6.creations && tr->cf4.creations && tc->pref_family) { - /* did ipv4 and ipv6 both, expect the preferred family to start right arway + /* did ipv4 and ipv6 both, expect the preferred family to start right away * with the other being delayed by the happy_eyeball_timeout */ struct ai_family_stats *stats1 = !strcmp(tc->pref_family, "v6") ? &tr->cf6 : &tr->cf4; From 3b9f0972e2e874657a4052a78b1336fd8092d0e6 Mon Sep 17 00:00:00 2001 From: Vasiliy-Kkk <61242428+Vasiliy-Kkk@users.noreply.github.com> Date: Tue, 26 May 2026 16:55:39 +0300 Subject: [PATCH 0314/1062] schannel_verify: simplify CryptQueryObject use - Specify that the content is base64 encoded, rather than rely on auto-detect. - Remove unnecessary sanity check of the returned content type. Closes https://github.com/curl/curl/pull/21760 --- lib/vtls/schannel_verify.c | 57 +++++++++++--------------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 127fcf2b3f60..1aa75fd84cfb 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -152,7 +152,6 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, CERT_BLOB cert_blob; const CERT_CONTEXT *cert_context = NULL; BOOL add_cert_result = FALSE; - DWORD actual_content_type = 0; DWORD cert_size = (DWORD)((end_cert_ptr + end_cert_len) - begin_cert_ptr); @@ -162,10 +161,10 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &cert_blob, CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, + CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED, 0, NULL, - &actual_content_type, + NULL, NULL, NULL, NULL, @@ -182,51 +181,27 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, else { current_ca_file_ptr = begin_cert_ptr + cert_size; - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { + add_cert_result = + CertAddCertificateContextToStore(trust_store, + cert_context, + CERT_STORE_ADD_ALWAYS, + NULL); + if(!add_cert_result) { + char buffer[WINAPI_ERROR_LEN]; failf(data, - "schannel: unexpected content type '%lu' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file_text); + "schannel: failed to add certificate from CA file '%s' " + "to certificate store: %s", + ca_file_text, + curlx_winapi_strerror(GetLastError(), buffer, + sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; more_certs = 0; } else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - if(!add_cert_result) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file_text, - curlx_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } + num_certs++; } - switch(actual_content_type) { - case CERT_QUERY_CONTENT_CERT: - case CERT_QUERY_CONTENT_SERIALIZED_CERT: - CertFreeCertificateContext(cert_context); - break; - case CERT_QUERY_CONTENT_CRL: - case CERT_QUERY_CONTENT_SERIALIZED_CRL: - CertFreeCRLContext((PCCRL_CONTEXT)cert_context); - break; - case CERT_QUERY_CONTENT_CTL: - case CERT_QUERY_CONTENT_SERIALIZED_CTL: - CertFreeCTLContext((PCCTL_CONTEXT)cert_context); - break; - } + CertFreeCertificateContext(cert_context); } } } From c3c2cfb65d25619c7e08407b13750e566f047df6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 6 Jun 2026 17:27:52 +0200 Subject: [PATCH 0315/1062] http: reject spurious CR bytes in headers Verified by test 2105 Closes #21882 --- lib/http.c | 8 +++++++ tests/data/Makefile.am | 2 +- tests/data/test2105 | 49 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2105 diff --git a/lib/http.c b/lib/http.c index e16d15a446ff..f8ca40b0a494 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3818,6 +3818,14 @@ static CURLcode verify_header(struct Curl_easy *data, failf(data, "Nul byte in header"); return CURLE_WEIRD_SERVER_REPLY; } + if(hdlen > 2) { + ptr = memchr(hd, '\r', hdlen - 2); + if(ptr) { + /* CR may only precede the LF, nothing else */ + failf(data, "Carriage return found in header"); + return CURLE_WEIRD_SERVER_REPLY; + } + } if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index da4bdbfbce7a..f04cac6ea4cb 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -253,7 +253,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ -test2100 test2101 test2102 test2103 test2104 \ +test2100 test2101 test2102 test2103 test2104 test2105 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ \ diff --git a/tests/data/test2105 b/tests/data/test2105 new file mode 100644 index 000000000000..8bd26caff12d --- /dev/null +++ b/tests/data/test2105 @@ -0,0 +1,49 @@ + + + + +HTTP +HTTP GET + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/%CRfake +Content-Length: 6 +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +HTTP with spurious CR in received header + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +8 + + + From f7f1666ee2b46c621c4ddaa5a12facdb2d38b92f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 6 Jun 2026 23:34:15 +0200 Subject: [PATCH 0316/1062] CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only Closes #21883 --- docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md index 838b2e5a5f07..c04ebe305156 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md @@ -37,7 +37,8 @@ struct curl_fileinfo { char *perm; char *user; char *group; - char *target; /* pointer to the target filename of a symlink */ + char *target; /* pointer to the target filename of a symlink, only + available for CURLFILETYPE_SYMLINK */ } strings; unsigned int flags; From 38b72f3b56b378c03276b150ddb31899581ece06 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 6 Jun 2026 23:50:47 +0200 Subject: [PATCH 0317/1062] CURLOPT_PINNEDPUBLICKEY.md: does not apply for other origins Clarify Closes #21885 --- docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md index 82dd1626d140..ed4011d57fa3 100644 --- a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md +++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md @@ -36,9 +36,9 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PINNEDPUBLICKEY, # DESCRIPTION Pass a pointer to a null-terminated string as parameter. The string can be the -filename of your pinned public key. The file format expected is "PEM" or -"DER". The string can also be any number of base64 encoded sha256 hashes -preceded by "sha256//" and separated by ";" +filename of your pinned public key. The file format expected is `PEM` or +`DER`. The string can also be any number of base64 encoded sha256 hashes +preceded by `sha256//` and separated by `;`. When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. A public key is extracted from this certificate and @@ -53,6 +53,10 @@ On mismatch, *CURLE_SSL_PINNEDPUBKEYNOTMATCH* is returned. The application does not have to keep the string around after setting this option. +The pinned public key is used to verify the initial origin used in a transfer. +If the transfer is set to follow redirects to other origins, they are *not* +checked against this key. + This option has no effect on LDAP connections when libcurl uses the legacy LDAP backend. That backend manages TLS independently of curl's TLS layer. When libcurl is built with USE_OPENLDAP, the OpenLDAP backend routes TLS through From 317bf7e8a8e71b60753a1efacfa323695431bd4e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 6 Jun 2026 23:38:22 +0200 Subject: [PATCH 0318/1062] ftplistparser: clear strings.target if not symlink When the struct is passed to the CURLOPT_CHUNK_BGN_FUNCTION callback, clear the pointer if the provided data is not a symlink. Closes #21884 --- lib/ftplistparser.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 0c8cea4dc90b..a4fffc86dc84 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -310,8 +310,9 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, str + parser->offsets.group : NULL; finfo->strings.perm = parser->offsets.perm ? str + parser->offsets.perm : NULL; - finfo->strings.target = parser->offsets.symlink_target ? - str + parser->offsets.symlink_target : NULL; + finfo->strings.target = parser->offsets.symlink_target && + (finfo->filetype == CURLFILETYPE_SYMLINK) ? + str + parser->offsets.symlink_target : NULL; finfo->strings.time = str + parser->offsets.time; finfo->strings.user = parser->offsets.user ? str + parser->offsets.user : NULL; From fbcf10ab84d75e76029e50d0b22793a218181166 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 2 Jun 2026 11:10:10 +0200 Subject: [PATCH 0319/1062] progress: fx CURLINFO time reporting Whack the times reported for a transfer (see https://curl.se/libcurl/c/curl_easy_getinfo.html#TIMES) into order for all variations of up-/download, http/ftp etc. Make sure they are reported in the documented order. There is still the *possibility* of PRETRANSFER being longer then POSTTRANSFER, if a server sends a response before an upload is done. POST is the time the first response byte is received, and PRE is the time the last byte was sent by curl. This may happen with more likelihood on HTTP/2 and 3 for a server rejected upload. But for successful uploads, the answer will almost over come afterwards. Undo the previous twists in lib500.c tests, adjust pytest timeline checks. Fixes #21828 Reported-by: BazaarAcc32 on github Closes #21843 --- lib/multi.c | 110 ++++++++++++++++++++----------------- lib/progress.c | 43 +++++++++++++-- lib/progress.h | 2 + lib/request.c | 3 +- lib/sendf.c | 3 +- tests/http/testenv/curl.py | 19 +------ tests/libtest/lib500.c | 5 +- tests/unit/unit1399.c | 83 +++++++++++++++++----------- 8 files changed, 159 insertions(+), 109 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 1948bbda7705..fe9bcfaeaf9d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -106,20 +106,56 @@ static const struct curltime *multi_now(struct Curl_multi *multi) return &multi->now; } -/* function pointer called once when switching TO a state */ -typedef void (*init_multistate_func)(struct Curl_easy *data); +/* function pointer called once when entering a state */ +typedef void (*mstate_enter_func)(struct Curl_easy *data, + CURLMstate from_state); -/* called in DID state, before PERFORMING state */ -static void before_perform(struct Curl_easy *data) +static void mstate_enter_connect(struct Curl_easy *data, + CURLMstate from_state) { + (void)from_state; + Curl_init_CONNECT(data); +} + +static void mstate_enter_did(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; data->req.chunk = FALSE; Curl_pgrsTime(data, TIMER_PRETRANSFER); + if(!CURL_REQ_WANT_SEND(data)) + Curl_pgrsTime(data, TIMER_POSTRANSFER); +} + +static void mstate_enter_done(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); } -static void init_completed(struct Curl_easy *data) +static void mstate_enter_completed(struct Curl_easy *data, + CURLMstate from_state) { - /* this is a completed transfer */ + /* we sometimes directly jump to COMPLETED, trigger things + * we then missed. */ + if(from_state < MSTATE_DID) { + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_pgrsTime(data, TIMER_POSTRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + } + Curl_pgrsCompleted(data); + if(from_state < MSTATE_DONE) + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); + /* changing to COMPLETED means it is in process and needs to go */ + DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid)); + Curl_uint32_bset_remove(&data->multi->process, data->mid); + Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */ + if(Curl_uint32_bset_empty(&data->multi->process)) { + /* free the transfer buffer when we have no more active transfers */ + multi_xfer_bufs_free(data->multi); + } /* Important: reset the conn pointer so that we do not point to memory that could be freed anytime */ Curl_detach_connection(data); @@ -134,23 +170,23 @@ static void mstate(struct Curl_easy *data, CURLMstate state ) { CURLMstate oldstate = data->mstate; - static const init_multistate_func finit[MSTATE_LAST] = { - NULL, /* INIT */ - NULL, /* PENDING */ - NULL, /* SETUP */ - Curl_init_CONNECT, /* CONNECT */ - NULL, /* CONNECTING */ - NULL, /* PROTOCONNECT */ - NULL, /* PROTOCONNECTING */ - NULL, /* DO */ - NULL, /* DOING */ - NULL, /* DOING_MORE */ - before_perform, /* DID */ - NULL, /* PERFORMING */ - NULL, /* RATELIMITING */ - NULL, /* DONE */ - init_completed, /* COMPLETED */ - NULL /* MSGSENT */ + static const mstate_enter_func state_enter[MSTATE_LAST] = { + NULL, /* INIT */ + NULL, /* PENDING */ + NULL, /* SETUP */ + mstate_enter_connect, /* CONNECT */ + NULL, /* CONNECTING */ + NULL, /* PROTOCONNECT */ + NULL, /* PROTOCONNECTING */ + NULL, /* DO */ + NULL, /* DOING */ + NULL, /* DOING_MORE */ + mstate_enter_did, /* DID */ + NULL, /* PERFORMING */ + NULL, /* RATELIMITING */ + mstate_enter_done, /* DONE */ + mstate_enter_completed, /* COMPLETED */ + NULL /* MSGSENT */ }; if(oldstate == state) @@ -166,32 +202,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state /* really switching state */ data->mstate = state; - switch(state) { - case MSTATE_DONE: - CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); - break; - case MSTATE_COMPLETED: - /* we sometimes directly jump to COMPLETED, trigger also a notification - * in that case. */ - if(oldstate < MSTATE_DONE) - CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); - /* changing to COMPLETED means it is in process and needs to go */ - DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid)); - Curl_uint32_bset_remove(&data->multi->process, data->mid); - Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */ - - if(Curl_uint32_bset_empty(&data->multi->process)) { - /* free the transfer buffer when we have no more active transfers */ - multi_xfer_bufs_free(data->multi); - } - break; - default: - break; - } - - /* if this state has an init-function, run it */ - if(finit[state]) - finit[state](data); + if(state_enter[state]) + state_enter[state](data, oldstate); } #ifndef DEBUGBUILD diff --git a/lib/progress.c b/lib/progress.c index 919c151e75e4..d34b155e32fa 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -240,6 +240,30 @@ void Curl_pgrsSendPause(struct Curl_easy *data, bool enable) } } +#ifdef CURLVERBOSE +static const char * const pgrs_timer_names[] = { + "PGRS-NONE", + "PGRS-STARTOP", + "PGRS-STARTSINGLE", + "PGRS-POSTQUEUE", + "PGRS-NAMELOOKUP", + "PGRS-CONNECT", + "PGRS-APPCONNECT", + "PGRS-PRETRANSFER", + "PGRS-STARTTRANSFER", + "PGRS-POSTRANSFER", + "PGRS-STARTACCEPT", + "PGRS-REDIRECT", +}; + +static const char *pgrs_timer_name(timerid timer) +{ + if((size_t)timer < CURL_ARRAYSIZE(pgrs_timer_names)) + return pgrs_timer_names[(size_t)timer]; + return "?"; +} + +#endif /* CURLVERBOSE */ /* * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ @@ -285,7 +309,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, delta = &data->progress.t_pretransfer; break; case TIMER_STARTTRANSFER: - delta = &data->progress.t_starttransfer; /* prevent updating t_starttransfer unless: * 1. this is the first time we are setting t_starttransfer * 2. a redirect has occurred since the last time t_starttransfer was set @@ -293,12 +316,12 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, * changing the t_starttransfer time. */ if(data->progress.is_t_startransfer_set) { + CURL_TRC_M(data, "[%s] ignored", pgrs_timer_name(timer)); return; } - else { - data->progress.is_t_startransfer_set = TRUE; - break; - } + data->progress.is_t_startransfer_set = TRUE; + delta = &data->progress.t_starttransfer; + break; case TIMER_POSTRANSFER: delta = &data->progress.t_posttransfer; break; @@ -314,7 +337,11 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; + CURL_TRC_M(data, "[%s] added %" FMT_TIMEDIFF_T "ns", + pgrs_timer_name(timer), us); } + else + CURL_TRC_M(data, "[%s] set", pgrs_timer_name(timer)); } /* @@ -703,3 +730,9 @@ void Curl_pgrsUpdate_nometer(struct Curl_easy *data) { (void)progress_calc(data, Curl_pgrs_now(data)); } + +void Curl_pgrsCompleted(struct Curl_easy *data) +{ + struct Progress * const p = &data->progress; + p->timespent = curlx_ptimediff_us(Curl_pgrs_now(data), &p->start); +} diff --git a/lib/progress.h b/lib/progress.h index 7d419ecb8ab0..1f2a4e71c305 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -83,4 +83,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); +void Curl_pgrsCompleted(struct Curl_easy *data); + #endif /* HEADER_CURL_PROGRESS_H */ diff --git a/lib/request.c b/lib/request.c index 4ecb462951e1..de07c6aa1354 100644 --- a/lib/request.c +++ b/lib/request.c @@ -271,7 +271,8 @@ static CURLcode req_set_upload_done(struct Curl_easy *data) data->req.upload_done = TRUE; CURL_REQ_CLEAR_SEND(data); - Curl_pgrsTime(data, TIMER_POSTRANSFER); + if(data->mstate >= MSTATE_DID) + Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); if(data->req.upload_aborted) { diff --git a/lib/sendf.c b/lib/sendf.c index c5936514a100..38abb0495510 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -183,7 +183,8 @@ static CURLcode cw_download_write(struct Curl_easy *data, bool is_connect = !!(type & CLIENTWRITE_CONNECT); if(!ctx->started_response && - !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { + !(type & CLIENTWRITE_CONNECT) && + (!(type & CLIENTWRITE_INFO) || data->req.upload_done)) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); ctx->started_response = TRUE; } diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 8fb17349ab98..64c1cacd46fa 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -540,23 +540,8 @@ def check_stats_timeline(self, idx): ref_tl += ['time_namelookup', 'time_connect'] if url.startswith('https:'): ref_tl += ['time_appconnect'] - # what kind of transfer was it? - if s['size_upload'] == 0 and s['size_download'] > 0: - # this is a download - dl_tl = ['time_pretransfer'] - if s['size_request'] > 0: - dl_tl = ['time_posttransfer'] + dl_tl - ref_tl += dl_tl - # the first byte of the response may arrive before we - # track the other times when the client is slow (CI). - somewhere_keys.extend(['time_starttransfer']) - elif s['size_upload'] > 0 and s['size_download'] == 0: - # this is an upload - ul_tl = ['time_pretransfer', 'time_posttransfer'] - ref_tl += ul_tl - else: - # could be a 0-length upload or 0-length download, not sure - exact_match = False + ref_tl += ['time_pretransfer', 'time_posttransfer'] + somewhere_keys.extend(['time_starttransfer']) # always there at the end ref_tl += ['time_total'] diff --git a/tests/libtest/lib500.c b/tests/libtest/lib500.c index 71b1eaed8fe3..6e64704ee0df 100644 --- a/tests/libtest/lib500.c +++ b/tests/libtest/lib500.c @@ -127,10 +127,7 @@ static CURLcode test_lib500(const char *URL) (time_pretransfer / 1000000), (long)(time_pretransfer % 1000000)); } - if(time_posttransfer > time_pretransfer) { - /* counter-intuitive: on a GET request, all bytes are sent *before* - * PRETRANSFER happens. Thus POSTTRANSFER has to be smaller. - * The reverse would be true for a POST/PUT. */ + if(time_pretransfer > time_posttransfer) { curl_mfprintf(moo, "pretransfer vs posttransfer: %" CURL_FORMAT_CURL_OFF_T ".%06ld %" CURL_FORMAT_CURL_OFF_T ".%06ld\n", diff --git a/tests/unit/unit1399.c b/tests/unit/unit1399.c index e1e472a53682..909b13e8b2cf 100644 --- a/tests/unit/unit1399.c +++ b/tests/unit/unit1399.c @@ -25,6 +25,25 @@ #include "urldata.h" #include "progress.h" +static CURLcode t1399_setup(struct Curl_easy **easy) +{ + CURLcode result = CURLE_OK; + + global_init(CURL_GLOBAL_ALL); + *easy = curl_easy_init(); + if(!*easy) { + curl_global_cleanup(); + return CURLE_OUT_OF_MEMORY; + } + return result; +} + +static void t1399_stop(struct Curl_easy *easy) +{ + curl_easy_cleanup(easy); + curl_global_cleanup(); +} + /* * Invoke Curl_pgrsTime for TIMER_STARTSINGLE to trigger the behavior that * manages is_t_startransfer_set, but fake the t_startsingle time for purposes @@ -72,45 +91,45 @@ static void expect_timer_seconds(struct Curl_easy *data, int seconds) * be 3 seconds. */ static CURLcode test_unit1399(const char *arg) { - UNITTEST_BEGIN_SIMPLE - - struct Curl_easy data; + struct Curl_easy *data; struct curltime now = curlx_now(); - data.multi = NULL; - data.progress.now = now; - data.progress.t_nslookup = 0; - data.progress.t_connect = 0; - data.progress.t_appconnect = 0; - data.progress.t_pretransfer = 0; - data.progress.t_starttransfer = 0; - data.progress.t_redirect = 0; - data.progress.start.tv_sec = now.tv_sec - 2; - data.progress.start.tv_usec = now.tv_usec; - fake_t_startsingle_time(&data, now, -2); - - Curl_pgrsTime(&data, TIMER_NAMELOOKUP); - Curl_pgrsTime(&data, TIMER_CONNECT); - Curl_pgrsTime(&data, TIMER_APPCONNECT); - Curl_pgrsTime(&data, TIMER_PRETRANSFER); - Curl_pgrsTime(&data, TIMER_STARTTRANSFER); - - expect_timer_seconds(&data, 2); + UNITTEST_BEGIN(t1399_setup(&data)) + + data->multi = NULL; + data->progress.now = now; + data->progress.t_nslookup = 0; + data->progress.t_connect = 0; + data->progress.t_appconnect = 0; + data->progress.t_pretransfer = 0; + data->progress.t_starttransfer = 0; + data->progress.t_redirect = 0; + data->progress.start.tv_sec = now.tv_sec - 2; + data->progress.start.tv_usec = now.tv_usec; + fake_t_startsingle_time(data, now, -2); + + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + Curl_pgrsTime(data, TIMER_CONNECT); + Curl_pgrsTime(data, TIMER_APPCONNECT); + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + + expect_timer_seconds(data, 2); /* now simulate the redirect */ - data.progress.t_redirect = data.progress.t_starttransfer + 1; - fake_t_startsingle_time(&data, now, -1); + data->progress.t_redirect = data->progress.t_starttransfer + 1; + fake_t_startsingle_time(data, now, -1); - Curl_pgrsTime(&data, TIMER_NAMELOOKUP); - Curl_pgrsTime(&data, TIMER_CONNECT); - Curl_pgrsTime(&data, TIMER_APPCONNECT); - Curl_pgrsTime(&data, TIMER_PRETRANSFER); + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + Curl_pgrsTime(data, TIMER_CONNECT); + Curl_pgrsTime(data, TIMER_APPCONNECT); + Curl_pgrsTime(data, TIMER_PRETRANSFER); /* ensure t_starttransfer is only set on the first invocation by attempting * to set it twice */ - Curl_pgrsTime(&data, TIMER_STARTTRANSFER); - Curl_pgrsTime(&data, TIMER_STARTTRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); - expect_timer_seconds(&data, 3); + expect_timer_seconds(data, 3); - UNITTEST_END_SIMPLE + UNITTEST_END(t1399_stop(data)) } From c4c12843df9560d47fa0bd561837ddb06732dcbe Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 7 Jun 2026 00:09:30 +0200 Subject: [PATCH 0320/1062] CURLOPT_PORT.md: use stronger language This option should not be used. Closes #21886 --- docs/libcurl/opts/CURLOPT_PORT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_PORT.md b/docs/libcurl/opts/CURLOPT_PORT.md index 334f01b3d719..567d6a975a79 100644 --- a/docs/libcurl/opts/CURLOPT_PORT.md +++ b/docs/libcurl/opts/CURLOPT_PORT.md @@ -27,8 +27,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PORT, long number); # DESCRIPTION -We discourage using this option since its scope is not obvious and hard to -predict. Set the preferred port number in the URL instead. +We strongly discourage using this unreliable option since its scope is not +obvious and hard to predict. Set the preferred port number in the URL instead. This option sets *number* to be the remote port number to connect to, instead of the one specified in the URL or the default port for the used From 7bb7b2c2a4ec28747d4c0cf9b28ca673895dbd40 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 7 Jun 2026 00:22:53 +0200 Subject: [PATCH 0321/1062] tool: warn when --ssl and --ftp-ssl-control override each other and mention this properly in the docs. Closes #21887 --- docs/cmdline-opts/ftp-ssl-control.md | 2 ++ docs/cmdline-opts/ssl.md | 2 ++ src/tool_getparam.c | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/docs/cmdline-opts/ftp-ssl-control.md b/docs/cmdline-opts/ftp-ssl-control.md index a68359a7b635..fa85de24f187 100644 --- a/docs/cmdline-opts/ftp-ssl-control.md +++ b/docs/cmdline-opts/ftp-ssl-control.md @@ -18,3 +18,5 @@ Example: Require SSL/TLS for the FTP login, clear for transfer. Allows secure authentication, but non-encrypted data transfers for efficiency. Fails the transfer if the server does not support SSL/TLS. + +If set, this option overrides --ssl. diff --git a/docs/cmdline-opts/ssl.md b/docs/cmdline-opts/ssl.md index 5951d0199123..b2345858091d 100644 --- a/docs/cmdline-opts/ssl.md +++ b/docs/cmdline-opts/ssl.md @@ -30,5 +30,7 @@ OpenLDAP backend and ignored by the generic ldap backend. Please note that a server may close the connection if the negotiation fails. +If set, this option overrides --ftp-ssl-control. + This option was formerly known as --ftp-ssl (added in 7.11.0). That option name can still be used but might be removed in a future version. diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 00fd8515a583..35c01b1d92d2 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -1908,6 +1908,10 @@ static ParameterError opt_bool(struct OperationConfig *config, if(config->ftp_ssl) warnf("--%s is an insecure option, consider --ssl-reqd instead", a->lname); + if(toggle && config->ftp_ssl_control) { + config->ftp_ssl_control = FALSE; + warnf("--%s overrides --ftp-ssl-control", a->lname); + } break; case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */ config->ftp_ssl_ccc = toggle; @@ -1959,6 +1963,10 @@ static ParameterError opt_bool(struct OperationConfig *config, break; case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ config->ftp_ssl_control = toggle; + if(toggle && config->ftp_ssl) { + config->ftp_ssl = FALSE; + warnf("--%s overrides --ssl", a->lname); + } break; case C_RAW: /* --raw */ config->raw = toggle; From e2cb3cc78ec9eeff2b3f2fccdb938aa2dd704edb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 7 Jun 2026 14:48:50 +0200 Subject: [PATCH 0322/1062] CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only Closes #21890 --- docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md b/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md index 7435f9866840..e896b8b685ba 100644 --- a/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md +++ b/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md @@ -29,12 +29,14 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_DISALLOW_USERNAME_IN_URL, # DESCRIPTION -A long parameter set to 1 tells the library to not allow URLs that include a -username. +A long parameter set to 1 tells the library to not allow URLs set with +CURLOPT_URL(3) that include a username. This is the equivalent to the *CURLU_DISALLOW_USER* flag for the curl_url_set(3) function. +Note that this option does not affect URLs set with CURLOPT_CURLU(3). + # DEFAULT 0 (disabled) From 1a1ec74b0bfa69d94465527581f80e1a5da52f63 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 7 Jun 2026 23:19:55 +0200 Subject: [PATCH 0323/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 70 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index b6dd267fb953..8e8be87d454b 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1482 - Contributors: 3706 + Authors: 1483 + Contributors: 3710 This release includes the following changes: @@ -20,6 +20,7 @@ This release includes the following bugfixes: o asyn-thrdd: fix result processing without wakeup socketpair [2] o autotools: mbedtls detection fixes [163] + o BINDINGS: Update Hollywood link [181] o BUFQ.md: re-sync with source code [111] o build: omit zlib pkg-config reference for Android [130] o cf-h2-prox: fix peer leak [132] @@ -47,9 +48,13 @@ This release includes the following bugfixes: o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] o curl_sha512_256: fix result code on error [166] + o CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only [156] + o CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only [61] o CURLOPT_ECH.md: simplify the description language [18] o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] + o CURLOPT_PINNEDPUBLICKEY.md: does not apply for other origins [152] + o CURLOPT_PORT.md: use stronger language [133] o CURLOPT_SHARE: warn about early remove [51] o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] o delta: harden external command invocations [98] @@ -66,18 +71,22 @@ This release includes the following bugfixes: o ftp: avoid accessing EPSV response one byte past the NULL [9] o ftp: remove 2 Curl_resolv_blocking() calls [30] o ftp: remove bits.ftp_use_control_ssl [28] + o ftplistparser: clear strings.target if not symlink [148] o gnutls: allow building with nettle 4.0 [96] o gnutls: fix more nettle 4+ compatibility issues [94] o GnuTLS: require 3.7.2 for earlydata [103] o gsasl: fix potential double free [56] o gtls: fix ignored return and uninitialized status in OCSP check [49] o gtls: fix some typos [15] + o gtls: minor fixes and improvements [190] o gtls: use the correct return code in trace output [173] o gtls: verify OCSP response signature in gtls_verify_ocsp_status [86] o h3-proxy: fix callback return values, and a typo in tests [139] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] + o hsts.md: mention multiple curl invokes effect [189] o http: don't pass on set cookies to new origins [140] o http: prefer chunked encoding over Content-Length: 0 [146] + o http: reject spurious CR bytes in headers [157] o idn: replace header guards with forward declaration [100] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o KNOWN_BUGS: remove stale Threads::Threads entry [135] @@ -88,6 +97,10 @@ This release includes the following bugfixes: o lib: make `__STDC_VERSION__` literals `L` (where missing) o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] + o libssh2: do not use deprecated macros when unavailable [177] + o libssh2: replace macro names with non-misspelled alternatives [169] + o libssh2: sync version check with INTERNALS.md [176] + o libssh2: use non-deprecated `libssh2_knownhost_addc()` [178] o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] o m4: drop redundant conditions in TLS library detections [155] o Makefile.am: drop test1190 listed twice [144] @@ -100,9 +113,11 @@ This release includes the following bugfixes: o netrc: scanner refactor [121] o ngtcp2: fail handshake directly [138] o os400sys: fix theoretical length overflows [141] + o progress: fix CURLINFO time reporting [145] o pytest: pass `--disable` to curl [175] o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] + o quic: count zero length packets against max [179] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] @@ -111,7 +126,9 @@ This release includes the following bugfixes: o schannel: enforce Extended Key Usage for custom CA roots [29] o schannel: error on TLS 1.3-only with cipher list [136] o schannel: fix revoke_best_effort setting for proxy [70] + o schannel: use fopen instead CreateFile [191] o schannel_verify: avoid out of blob access [11] + o schannel_verify: simplify CryptQueryObject use [159] o scripts: catch Credits-to contributors [127] o setopt: changing the proxy port is also a proxy change [23] o setopt: clear proxy auth properly on NULL [81] @@ -129,14 +146,17 @@ This release includes the following bugfixes: o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] o test1588: use %TESTNUMBER, not hard-coded number [118] o test1981: explicitly set the locale [85] + o tests: add `cookies` feature to some tests [182] o tests: add an assert to avoid IPC blocking [69] o tests: fix unit1636 with --disable-progress-meter [37] o tftp: avoid the timeout calc if the timeout is crazy [151] o tftp: stricter option name checks [90] o tidy-up: add space around operators, where missing [147] o tidy-up: apply clang-format fixes [153] + o tidy-up: drop stray casts for allocated pointers [174] o tidy-up: miscellaneous [106] o tls: fix incomplete mTLS config in conn reuse and session cache [108] + o tool: warn when --ssl and --ftp-ssl-control override each other [129] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] o tool_formparse: tool2curlparts is no longer recursive [33] @@ -167,10 +187,12 @@ This release includes the following bugfixes: o urlapi: forbid '|' in host [172] o urlapi: handle redirect without set scheme with default-scheme [38] o user-agent.md: mention double quotes too [3] + o vquic: drop stray casts for `iovec.iov_len` [162] o vtls: more large buffer support and error checks for SHA-256 [164] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] o vtls_scache: include signature_algorithms in the SSL peer cache key [123] o vtls_spack: drop redundant macro fallbacks [167] + o VULN-DISCLOSURE-POLICY.md: emphasize comm as a human [180] o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] o websockets: auto-tunnel through http proxy [102] @@ -199,21 +221,23 @@ This release would not have looked like this without help, code, reports and advice from friends like these: 0xN3R3K3, 11soda11, Ady Elouej, Alan De Smet, ambikeesshh, amitbidlan, - Andrei Rybak, Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, - Bartel Sielski, Bastian Jesuiter, Bill Mill, chrizilla on github, - co-authors in libssh2, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, - Dario Vinella, dependabot[bot], Earnestly on github, Elise Vance, - Emanuel Krollmann, Eunsoo Kim, Fabian Keil, Gao Liyou, Guancheng Li, - Guannan Wang, Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, - Jiashuo Liang, Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, + Andreas Falkenhahn, Andrei Rybak, Andrew Nesbitt, Aritra Basu, + azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, + BazaarAcc32 on github, Bill Mill, chrizilla on github, co-authors in libssh2, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, + dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, + Eunsoo Kim, Fabian Keil, Gao Liyou, Guancheng Li, Guannan Wang, + Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, Jiashuo Liang, + Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, Mark Esler, Max Dymond, mik, Mike-menny on github, Muhamad Arga Reksapati, mulan_dh on hackerone, parasol-aser, penpal, Peter Krefting, Randall S. Becker, Raymond Steen, Ray Satiro, renjian on hackerone, renovate[bot], Ross Burton, Sergio Correia, sfan5 on github, Shintomon Mathew, Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, - tiymat, vegagent on hackerone, Viktor Szakats, Will Cosgrove, Xi Ruoyao, - x-xiang on github, Zhanpeng Liu - (66 contributors) + tiymat, Vasiliy-Kkk, vectorqueue on hackerone, vegagent on hackerone, + Viktor Szakats, Will Cosgrove, Xi Ruoyao, x-xiang on github, + zhanhb on github, Zhanpeng Liu + (72 contributors) References to bug reports and discussions on issues: @@ -277,6 +301,7 @@ References to bug reports and discussions on issues: [58] = https://curl.se/bug/?i=21622 [59] = https://curl.se/bug/?i=21614 [60] = https://curl.se/bug/?i=21621 + [61] = https://curl.se/bug/?i=21890 [62] = https://curl.se/bug/?i=21617 [63] = https://curl.se/bug/?i=21820 [64] = https://curl.se/bug/?i=21745 @@ -344,9 +369,11 @@ References to bug reports and discussions on issues: [126] = https://curl.se/bug/?i=21654 [127] = https://curl.se/bug/?i=21653 [128] = https://curl.se/bug/?i=21649 + [129] = https://curl.se/bug/?i=21887 [130] = https://curl.se/bug/?i=21647 [131] = https://curl.se/bug/?i=21650 [132] = https://curl.se/bug/?i=21602 + [133] = https://curl.se/bug/?i=21886 [134] = https://curl.se/bug/?i=21841 [135] = https://curl.se/bug/?i=21734 [136] = https://curl.se/bug/?i=21702 @@ -358,25 +385,44 @@ References to bug reports and discussions on issues: [142] = https://curl.se/bug/?i=21836 [143] = https://curl.se/bug/?i=21837 [144] = https://curl.se/bug/?i=21839 + [145] = https://curl.se/bug/?i=21828 [146] = https://curl.se/bug/?i=21706 [147] = https://curl.se/bug/?i=21793 + [148] = https://curl.se/bug/?i=21884 [149] = https://curl.se/bug/?i=21743 [150] = https://curl.se/bug/?i=21669 [151] = https://curl.se/bug/?i=21782 + [152] = https://curl.se/bug/?i=21885 [153] = https://curl.se/bug/?i=21786 [154] = https://curl.se/bug/?i=21784 [155] = https://curl.se/bug/?i=21781 + [156] = https://curl.se/bug/?i=21883 + [157] = https://curl.se/bug/?i=21882 [158] = https://curl.se/bug/?i=21776 + [159] = https://curl.se/bug/?i=21760 [160] = https://curl.se/bug/?i=21774 [161] = https://curl.se/bug/?i=21829 + [162] = https://curl.se/bug/?i=21877 [163] = https://curl.se/bug/?i=21727 [164] = https://curl.se/bug/?i=21771 [165] = https://curl.se/bug/?i=21739 [166] = https://curl.se/bug/?i=21767 [167] = https://curl.se/bug/?i=21768 [168] = https://curl.se/bug/?i=21826 + [169] = https://curl.se/bug/?i=21876 [170] = https://curl.se/bug/?i=21603 [171] = https://curl.se/bug/?i=21756 [172] = https://curl.se/bug/?i=21762 [173] = https://curl.se/bug/?i=21766 + [174] = https://curl.se/bug/?i=21865 [175] = https://curl.se/bug/?i=21816 + [176] = https://curl.se/bug/?i=21868 + [177] = https://curl.se/bug/?i=21867 + [178] = https://curl.se/bug/?i=21866 + [179] = https://curl.se/bug/?i=21869 + [180] = https://curl.se/bug/?i=21870 + [181] = https://curl.se/bug/?i=21862 + [182] = https://curl.se/bug/?i=21858 + [189] = https://curl.se/bug/?i=21851 + [190] = https://curl.se/bug/?i=21850 + [191] = https://curl.se/bug/?i=21773 From 9c1ebea35914e8dfbd21f9c4a8a591e384baf143 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 8 Jun 2026 00:28:41 +0200 Subject: [PATCH 0324/1062] lib1587: drop redundant includes Closes #21892 --- tests/libtest/lib1587.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/libtest/lib1587.c b/tests/libtest/lib1587.c index a0739cb22153..5789804acb47 100644 --- a/tests/libtest/lib1587.c +++ b/tests/libtest/lib1587.c @@ -21,14 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "first.h" #ifdef USE_OPENSSL - -#include -#include -#include #include #ifdef HAVE_BORINGSSL_LIKE From 7c34365ccea19949317878c7fcd5f7376e2e09f1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 16:39:20 +0200 Subject: [PATCH 0325/1062] urlapi: fix memleaks on error in `parse_hostname_login()` Detected by GitHub Code Quality Follow-up to acd82c8bfd743d0f743a1c1296890738832ac83e #11006 Follow-up to 4183b8fe9a8558b8f62c9dbf8271deed75bff28b #8049 Closes #21879 --- lib/urlapi.c | 26 +++++++++++-------- tests/unit/unit1675.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 8151da95916b..1799625ffdf4 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -253,12 +253,18 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace) * Parse the login details (username, password and options) from the URL and * strip them out of the hostname * + * @unittest 1675 */ -static CURLUcode parse_hostname_login(struct Curl_URL *u, - const char *login, - size_t len, - unsigned int flags, - size_t *offset) /* to the hostname */ +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset); +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset) { CURLUcode ures = CURLUE_OK; CURLcode result; @@ -278,7 +284,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, DEBUGASSERT(login); - *offset = 0; + *hostname_offset = 0; ptr = memchr(login, '@', len); if(!ptr) goto out; @@ -326,7 +332,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, } /* the hostname starts at this offset */ - *offset = ptr - login; + *hostname_offset = ptr - login; return CURLUE_OK; out: @@ -334,9 +340,9 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, curlx_free(userp); curlx_free(passwdp); curlx_free(optionsp); - u->user = NULL; - u->password = NULL; - u->options = NULL; + curlx_safefree(u->user); + curlx_safefree(u->password); + curlx_safefree(u->options); return ures; } diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index ad5e92a25ead..f7a965e38aac 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -383,5 +383,64 @@ static CURLcode test_unit1675(const char *arg) } #endif /* !CURL_DISABLE_HTTP */ + /* Test parse_hostname_login */ + { + struct Curl_URL u; + int fails = 0; + unsigned int i; + struct test { + CURLUcode uc; + const char *in; + const char *scheme; + unsigned int flags; + const char *user; + const char *password; + const char *options; + size_t offset; + }; + const struct test tests[] = { + { CURLUE_OK, "foo:bar@host", NULL, 0, "foo", "bar", "o", 8 }, + { CURLUE_OK, "foo:bar;abc@host", "imap", 0, "foo", "bar", "abc", 12 }, + { CURLUE_OK, "foo:bar;abc@host", NULL, 0, "foo", "bar;abc", "o", 12 }, + { CURLUE_USER_NOT_ALLOWED, "foo:bar@host", NULL, CURLU_DISALLOW_USER, + NULL, NULL, NULL, 0 }, + { CURLUE_OK, "host", NULL, 0, NULL, NULL, NULL, 0 }, + }; + + for(i = 0; i < CURL_ARRAYSIZE(tests); i++) { + CURLUcode uc; + size_t offset = 0; + memset(&u, 0, sizeof(u)); + u.scheme = CURL_UNCONST(tests[i].scheme); + u.user = curlx_strdup("u"); + u.password = curlx_strdup("p"); + u.options = curlx_strdup("o"); + uc = parse_hostname_login(&u, tests[i].in, strlen(tests[i].in), + tests[i].flags, &offset); + if(uc != tests[i].uc || + !!u.user != !!tests[i].user || + (u.user && tests[i].user && + strcmp(u.user, tests[i].user)) || + !!u.password != !!tests[i].password || + (u.password && tests[i].password && + strcmp(u.password, tests[i].password)) || + !!u.options != !!tests[i].options || + (u.options && tests[i].options && + strcmp(u.options, tests[i].options)) || + offset != tests[i].offset) { + curl_mfprintf(stderr, "%d: parse_hostname_login('%s') host failed:" + " expected '%d/%s/%s/%s/%zu', got '%d/%s/%s/%s/%zu'\n", + i, tests[i].in, (int)tests[i].uc, tests[i].user, + tests[i].password, tests[i].options, tests[i].offset, + (int)uc, u.user, u.password, u.options, offset); + fails++; + } + curlx_safefree(u.user); + curlx_safefree(u.password); + curlx_safefree(u.options); + } + abort_if(fails, "parse_hostname_login tests failed"); + } + UNITTEST_END_SIMPLE } From 0618ffe50d0e69e247d88a8050c49a5b746a19bd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 07:54:50 +0200 Subject: [PATCH 0326/1062] Revert "url: remove ssh_config_matches" This reverts commit 3e9817cd1bb6aa53d3d3bf10572bb245d064870c. The change was incorrect as the check was not for the options the commit message mentions. Reported-by: ByteRay on hackerone Closes #21899 --- lib/url.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/url.c b/lib/url.c index d7af1560f9ea..99463551e8c4 100644 --- a/lib/url.c +++ b/lib/url.c @@ -671,6 +671,19 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, return result; } +#ifdef USE_SSH +static bool ssh_config_matches(struct connectdata *one, + struct connectdata *two) +{ + struct ssh_conn *sshc1, *sshc2; + + sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); + sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); + return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && + Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); +} +#endif + struct url_conn_match { struct connectdata *found; struct Curl_easy *data; @@ -927,6 +940,12 @@ static bool url_match_proto_config(struct connectdata *conn, if(!url_match_http_version(conn, m)) return FALSE; +#ifdef USE_SSH + if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_SSH) { + if(!ssh_config_matches(m->needle, conn)) + return FALSE; + } +#endif #ifndef CURL_DISABLE_FTP else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) { if(!ftp_conns_match(m->needle, conn)) From 9b69cfb937850e04fa83294fc79b439c4225d4be Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 08:11:34 +0200 Subject: [PATCH 0327/1062] var: use a dedicated pointer for the alloc As the 'c' pointer might actually get modified before it is time to free the memory. Verify in test 2310 Reported-by: Eunsoo Kim Fixes #21898 Closes #21900 --- src/var.c | 13 +++++------ tests/data/Makefile.am | 2 +- tests/data/test2310 | 50 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 tests/data/test2310 diff --git a/src/var.c b/src/var.c index 93408be28f9e..bf1bc4e9eb00 100644 --- a/src/var.c +++ b/src/var.c @@ -78,7 +78,7 @@ static ParameterError varfunc(char *c, /* content */ size_t flen, /* function string length */ struct dynbuf *out) { - bool alloc = FALSE; + char *allocptr = NULL; ParameterError err = PARAM_OK; const char *finput = f; @@ -185,19 +185,18 @@ static ParameterError varfunc(char *c, /* content */ err = PARAM_EXPAND_ERROR; break; } - if(alloc) - curlx_free(c); + if(allocptr) + curlx_free(allocptr); clen = curlx_dyn_len(out); - c = curlx_memdup0(curlx_dyn_ptr(out), clen); + allocptr = c = curlx_memdup0(curlx_dyn_ptr(out), clen); if(!c) { err = PARAM_NO_MEM; break; } - alloc = TRUE; } - if(alloc) - curlx_free(c); + if(allocptr) + curlx_free(allocptr); if(err) curlx_dyn_free(out); return err; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f04cac6ea4cb..211c96f846ad 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -258,7 +258,7 @@ test2100 test2101 test2102 test2103 test2104 test2105 \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ \ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ -test2309 \ +test2309 test2310 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ test2408 test2409 test2410 test2411 \ diff --git a/tests/data/test2310 b/tests/data/test2310 new file mode 100644 index 000000000000..0446c374a0ef --- /dev/null +++ b/tests/data/test2310 @@ -0,0 +1,50 @@ + + + + +variables + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: something-close, close-something, close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +variable decode and trim + + +--variable 'VAR=IA==' --expand-url 'http://%HOSTIP:%HTTPPORT/{{VAR:64dec:trim}}' + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + From 5df33efab41c5888ac8d70c4546ba5f9b2d479aa Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 09:29:24 +0200 Subject: [PATCH 0328/1062] setopt: claer the "custom" CA booleans when set to NULL Mark them as custom choices only when pointer is passed, and clear them again when set to NULL. Closes #21901 --- lib/setopt.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 368548f704f5..d1a140c2406e 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1782,8 +1782,9 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * Set CA info SSL connection for proxy. Specify filename of the * CA certificate */ - s->proxy_ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + s->proxy_ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE_PROXY]; + return result; case CURLOPT_PROXY_CRLFILE: /* * Set CRL file info for SSL connection for proxy. Specify filename of the @@ -1807,8 +1808,9 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - s->proxy_ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + s->proxy_ssl.custom_capath = !!s->str[STRING_SSL_CAPATH_PROXY]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -1915,8 +1917,9 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * Set CA info for SSL connection. Specify filename of the CA certificate */ - s->ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + s->ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE]; + return result; case CURLOPT_CAPATH: /* * Set CA path info for SSL connection. Specify directory name of the CA @@ -1925,8 +1928,9 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - s->ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + s->ssl.custom_capath = !!s->str[STRING_SSL_CAPATH]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -2845,8 +2849,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - s->proxy_ssl.custom_cablob = TRUE; - return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + s->proxy_ssl.custom_cablob = !!s->blobs[BLOB_CAINFO_PROXY]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -2870,8 +2875,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - s->ssl.custom_cablob = TRUE; - return Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + s->ssl.custom_cablob = !!s->blobs[BLOB_CAINFO]; + return result; } #endif return CURLE_NOT_BUILT_IN; From 39d5cead0d23ba83e7effd43f62ad3fd678b4224 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 15:56:17 +0200 Subject: [PATCH 0329/1062] libssh2: save non-standard port to `known_hosts` Reported-by: dyingc on github Fixes #21863 Closes #21874 --- lib/vssh/libssh2.c | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 32c60f110592..c4b72fbbb4bc 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -429,31 +429,42 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, case CURLKHSTAT_FINE_ADD_TO_FILE: /* proceed */ if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - /* the found host+key did not match but has been told to be fine - anyway so we add it in memory */ - int addrc = libssh2_knownhost_addc(sshc->kh, - conn->origin->hostname, NULL, - remotekey, keylen, - NULL, 0, - LIBSSH2_KNOWNHOST_TYPE_PLAIN | - LIBSSH2_KNOWNHOST_KEYENC_RAW | - keybit, NULL); - if(addrc) - infof(data, "WARNING: adding the known host %s failed", - conn->origin->hostname); - else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || - rc == CURLKHSTAT_FINE_REPLACE) { - /* now we write the entire in-memory list of known hosts to the - known_hosts file */ - int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(wrc) { - infof(data, "WARNING: writing %s failed", - data->set.str[STRING_SSH_KNOWNHOSTS]); + int addrc; + const char *hostbuf; + char *hostport = NULL; + if(conn->origin->port != PORT_SSH) { + hostbuf = hostport = curl_maprintf("[%s]:%u", conn->origin->hostname, + conn->origin->port); + if(!hostbuf) + infof(data, "WARNING: failed allocating buffer for [host]:port"); + } + else + hostbuf = conn->origin->hostname; + if(hostbuf) { + /* the found host+key did not match but has been told to be fine + anyway so we add it in memory */ + addrc = libssh2_knownhost_addc(sshc->kh, hostbuf, NULL, + remotekey, keylen, NULL, 0, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, NULL); + if(addrc) + infof(data, "WARNING: adding the known host %s failed", hostbuf); + else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || + rc == CURLKHSTAT_FINE_REPLACE) { + /* now we write the entire in-memory list of known hosts to the + known_hosts file */ + int wrc = + libssh2_knownhost_writefile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(wrc) { + infof(data, "WARNING: writing %s failed", + data->set.str[STRING_SSH_KNOWNHOSTS]); + } } } + curlx_free(hostport); } break; } From cdce2460b330d4c1084849ec50149efe3cc1fc7b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 8 Jun 2026 12:37:48 +0200 Subject: [PATCH 0330/1062] runtests: allow skipping tests on torture, use for test 357 Some tests may take a long time in torture mode. Make it possible to skip individual tests when runtests in running in torture mode. Also: - skip test 357 for the reason above. Saved 1-3 minutes for the Linux CI torture job, 1-1.5m on Windows. No savings on macOS. Reported-by: Stefan Eissing Fixes #21873 Closes #21906 --- docs/tests/FILEFORMAT.md | 1 + docs/tests/TEST-SUITE.md | 3 +++ tests/data/test357 | 3 +++ tests/runtests.pl | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 26e32ecd7fa9..0518be11fd7d 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -539,6 +539,7 @@ Features testable here are: - `SSPI` - `threaded-resolver` - `TLS-SRP` +- `torture` - if runtests is running in memory test mode - `TrackMemory` - `typecheck` - `threadsafe` diff --git a/docs/tests/TEST-SUITE.md b/docs/tests/TEST-SUITE.md index 51a3f20b2288..6856eb5f8a21 100644 --- a/docs/tests/TEST-SUITE.md +++ b/docs/tests/TEST-SUITE.md @@ -188,6 +188,9 @@ that memory leaks do not occur even in those situations. It can help to compile curl with `CPPFLAGS=-DMEMDEBUG_LOG_SYNC` when using this option, to ensure that the memory log file is properly written even if curl crashes. +If a specific test takes a long time to run in memory test mode, you can +disable it individually by adding `!torture` to its `` section. + ### Debug If a test case fails, you can conveniently get the script to invoke the diff --git a/tests/data/test357 b/tests/data/test357 index a763fd22556d..c6b11bd83c75 100644 --- a/tests/data/test357 +++ b/tests/data/test357 @@ -48,6 +48,9 @@ no-expect http + +!torture + HTTP PUT with Expect: 100-continue and 417 response diff --git a/tests/runtests.pl b/tests/runtests.pl index cd0572a59e17..a8072e32cd93 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -759,6 +759,10 @@ sub checksystemfeatures { } } + if($torture) { + $feature{"torture"} = 1; + } + if(!$curl) { logmsg "unable to get curl's version, further details are:\n"; logmsg "issued command: \n"; From a89fd1ffd4982c283864c30538d400f704e87bc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 05:13:07 +0000 Subject: [PATCH 0331/1062] GHA: update dependency pizlonator/fil-c to v0.679 Closes #21897 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 61e6470ed32d..c8e5fe05ac81 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260526.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.678 + FIL_C_VERSION: 0.679 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com From ff7086874ed22b52baa7e73e66305ff045ac9bb6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 12:40:55 +0200 Subject: [PATCH 0332/1062] _ENVIRONMENT.md. Windows does case insensitive env variables Closes #21907 --- docs/cmdline-opts/_ENVIRONMENT.md | 3 ++- docs/libcurl/libcurl-env.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/cmdline-opts/_ENVIRONMENT.md b/docs/cmdline-opts/_ENVIRONMENT.md index 1ac85fb12841..a3c13ae0c00d 100644 --- a/docs/cmdline-opts/_ENVIRONMENT.md +++ b/docs/cmdline-opts/_ENVIRONMENT.md @@ -3,7 +3,8 @@ # ENVIRONMENT The environment variables can be specified in lower case or upper case. The lower case version has precedence. `http_proxy` is an exception as it is only -available in lower case. +available in lower case. (Note that some systems, like Windows, do not +differentiate between environment variables using different case.) Using an environment variable to set the proxy has the same effect as using the --proxy option. diff --git a/docs/libcurl/libcurl-env.md b/docs/libcurl/libcurl-env.md index 6ef11ac9a3dd..5ce846df8aa6 100644 --- a/docs/libcurl/libcurl-env.md +++ b/docs/libcurl/libcurl-env.md @@ -32,8 +32,8 @@ uses the **ftp_proxy** variable. These proxy variables are also checked for in their uppercase versions, except the **http_proxy** one which is only used lowercase. Note also that some -systems actually have a case insensitive handling of environment variables and -then of course **HTTP_PROXY** still works. +systems (like Windows) have a case insensitive handling of environment +variables and then of course **HTTP_PROXY** still works. An exception exists for the WebSocket **ws** and **wss** URL schemes, where libcurl first checks **ws_proxy** or **wss_proxy** but if they are not set, it From e786a4e9159b11166b1c870f2c02df37c480cb73 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 12:55:40 +0200 Subject: [PATCH 0333/1062] CURLOPT_DOH_URL.md: does not inherit proxy options Closes #21904 --- docs/libcurl/opts/CURLOPT_DOH_URL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_DOH_URL.md b/docs/libcurl/opts/CURLOPT_DOH_URL.md index 1c463f13caff..d82d71d8c0c6 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_URL.md +++ b/docs/libcurl/opts/CURLOPT_DOH_URL.md @@ -47,6 +47,8 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +DoH lookups do not inherit proxy options from its parent transfer. + # INHERIT OPTIONS DoH lookups use SSL and some SSL settings from your transfer are inherited, From 435fb96dcf787335626ada95dc02eb26afcf7bca Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 8 Jun 2026 11:28:57 +0200 Subject: [PATCH 0334/1062] netrc: remember and check filename loaded Remember the filename of a loaded netrc file to detect changed configurations in a reused easy handle. Closes #21903 --- lib/netrc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/netrc.c b/lib/netrc.c index 599d8c69966b..7c8e1fb2ab3f 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -503,6 +503,7 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + curlx_dyn_reset(filebuf); if(file) { curlx_struct_stat stat; if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { @@ -545,12 +546,20 @@ static NETRCcode netrc_scan_file(struct Curl_easy *data, { struct dynbuf *filebuf = &store->filebuf; - if(!store->loaded) { - NETRCcode ret = file2memory(netrcfile, filebuf); + if(!store->loaded || strcmp(netrcfile, store->filename)) { + NETRCcode ret; + store->loaded = FALSE; + ret = file2memory(netrcfile, filebuf); if(ret) { CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile); return ret; } + curlx_free(store->filename); + store->filename = curlx_strdup(netrcfile); + if(!store->filename) { + curlx_dyn_reset(&store->filebuf); + return NETRC_OUT_OF_MEMORY; + } store->loaded = TRUE; } @@ -571,7 +580,6 @@ NETRCcode Curl_netrc_scan(struct Curl_easy *data, struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; - char *filealloc = NULL; CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'", netrcfile, hostname, user); @@ -579,6 +587,7 @@ NETRCcode Curl_netrc_scan(struct Curl_easy *data, if(!netrcfile) { char *home = NULL; char *homea = NULL; + char *filealloc = NULL; #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) char pwbuf[1024]; #endif @@ -656,10 +665,12 @@ void Curl_netrc_init(struct store_netrc *store) { curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); store->loaded = FALSE; + store->filename = NULL; } void Curl_netrc_cleanup(struct store_netrc *store) { curlx_dyn_free(&store->filebuf); + curlx_safefree(store->filename); store->loaded = FALSE; } From d69bfad3fa3daf5e72331f6870667607828d5891 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 8 Jun 2026 10:11:30 +0200 Subject: [PATCH 0335/1062] ssl native_ca_store: always reinit Add bit `native_ca_store_opt` to keep the setting of CURLOPT_(PROXY_)SSL_OPTIONS and use that to calculate every easy transfer if a native CA store shall be used or not. This avoids `native_ca_store` getting stuck on TRUE after being set once. Closes #21902 --- lib/doh.c | 3 ++- lib/setopt.c | 20 ++------------------ lib/vtls/vtls_config.c | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/lib/doh.c b/lib/doh.c index 30441358ca54..e94a2371d2df 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -418,7 +418,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, } (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, - (long)data->set.ssl.primary.ssl_options); + ((long)data->set.ssl.primary.ssl_options & + ~CURLSSLOPT_AUTO_CLIENT_CERT)); doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ diff --git a/lib/setopt.c b/lib/setopt.c index d1a140c2406e..c01221ba7a0a 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -399,22 +399,6 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) } #endif /* !CURL_DISABLE_RTSP */ -#ifdef USE_SSL -static void set_ssl_options(struct ssl_config_data *ssl, - struct ssl_primary_config *config, - long arg) -{ - config->ssl_options = (unsigned char)(arg & 0xff); - ssl->enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - ssl->no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - ssl->no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - ssl->revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - ssl->native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - ssl->auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - ssl->earlydata = !!(arg & CURLSSLOPT_EARLYDATA); -} -#endif - static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, long arg) { @@ -994,11 +978,11 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, s->use_ssl = (unsigned char)arg; break; case CURLOPT_SSL_OPTIONS: - set_ssl_options(&s->ssl, &s->ssl.primary, arg); + s->ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_OPTIONS: - set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); + s->proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #endif case CURLOPT_SSL_ENABLE_NPN: diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index 771c6101ae20..0d294da83a91 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -234,6 +234,25 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, return TRUE; } +static void ssl_easy_config_compl_options(struct Curl_peer *origin, + struct Curl_peer *initial_origin, + struct ssl_config_data *sslc) +{ + uint8_t options = sslc->primary.ssl_options; + /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it. + * If not, we switch it on for supported backends if no custom + * ca settings exist. */ + sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); + sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST); + sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN); + sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE); + sslc->revoke_best_effort = !!(options & CURLSSLOPT_REVOKE_BEST_EFFORT); + sslc->earlydata = !!(options & CURLSSLOPT_EARLYDATA); + + sslc->auto_client_cert = Curl_peer_equal(origin, initial_origin) && + !!(options & CURLSSLOPT_AUTO_CLIENT_CERT); +} + CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, struct Curl_peer *origin) { @@ -243,6 +262,8 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, CURLcode result; #endif + ssl_easy_config_compl_options(origin, data->state.initial_origin, sslc); + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) @@ -308,6 +329,9 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY sslc = &data->set.proxy_ssl; + /* no initial origin for proxy, it is not changed for redirects */ + ssl_easy_config_compl_options(NULL, NULL, sslc); + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) From 7de0a7e71aad984cb7f514fb2987662e2fe8115e Mon Sep 17 00:00:00 2001 From: alhudz Date: Mon, 8 Jun 2026 10:37:34 +0530 Subject: [PATCH 0336/1062] chunked: reject invalid bytes in trailer Trailers are delivered to the application as headers via CLIENTWRITE_TRAILER, but unlike regular response headers they skipped the verify_header() checks, so a server could smuggle a nul byte (or stray CR) into a header reaching CURLOPT_HEADERFUNCTION and curl_easy_header(). Run each assembled trailer line through Curl_verify_header(), the same validation used for normal headers. Covered by the new test 2106. Closes #21896 --- lib/http.c | 6 ++--- lib/http.h | 5 ++++ lib/http_chunks.c | 14 ++++++++++- tests/data/Makefile.am | 2 +- tests/data/test2106 | 53 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 tests/data/test2106 diff --git a/lib/http.c b/lib/http.c index f8ca40b0a494..cf9177922de6 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3808,8 +3808,8 @@ static CURLcode http_size(struct Curl_easy *data) return CURLE_OK; } -static CURLcode verify_header(struct Curl_easy *data, - const char *hd, size_t hdlen) +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; const char *ptr = memchr(hd, 0x00, hdlen); @@ -4359,7 +4359,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, } } - result = verify_header(data, hd, hdlen); + result = Curl_verify_header(data, hd, hdlen); if(result) return result; diff --git a/lib/http.h b/lib/http.h index ed93d265e308..5050215743f2 100644 --- a/lib/http.h +++ b/lib/http.h @@ -106,6 +106,11 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, const char *hd, size_t hdlen, bool is_eos); +/* check a received header line for forbidden bytes/format, the same checks + applied to regular response headers */ +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen); + /* These functions are in http.c */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth); diff --git a/lib/http_chunks.c b/lib/http_chunks.c index aa45c79e3853..9e3e3bd1fece 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -27,6 +27,7 @@ #include "urldata.h" /* it includes http_chunks.h */ #include "curl_trc.h" +#include "http.h" /* for Curl_verify_header */ #include "sendf.h" /* for the client write stuff */ #include "curlx/dynbuf.h" #include "multiif.h" @@ -247,6 +248,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, there was no trailer and we move on */ if(tr) { + size_t trlen; result = curlx_dyn_addn(&ch->trailer, STRCONST("\x0d\x0a")); if(result) { ch->state = CHUNK_FAILED; @@ -254,8 +256,18 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, return result; } tr = curlx_dyn_ptr(&ch->trailer); + trlen = curlx_dyn_len(&ch->trailer); + + /* a trailer is delivered to the client as a header, so it must pass + the same checks as a regular response header */ + result = Curl_verify_header(data, tr, trlen); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + return result; + } + if(!data->set.http_te_skip) { - size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_HEADER | diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 211c96f846ad..1fb219832e73 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -253,7 +253,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ -test2100 test2101 test2102 test2103 test2104 test2105 \ +test2100 test2101 test2102 test2103 test2104 test2105 test2106 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ \ diff --git a/tests/data/test2106 b/tests/data/test2106 new file mode 100644 index 000000000000..40d2dc8b62b6 --- /dev/null +++ b/tests/data/test2106 @@ -0,0 +1,53 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + + +# Server-side + + +HTTP/1.1 200 OK%CR +Server: test%CR +Transfer-Encoding: chunked%CR +Trailer: chunky-trailer%CR +%CR +6%CR +-foo-%CR +0%CR +chunky-trailer: he%hex[%00]hex%llo%CR +%CR + + + +# Client-side + + +http + + +HTTP chunked response with a nul byte in the trailer + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +8 + + + From 2dfd265d668eb184a0feb0cbda5357fb076d9f0d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 8 Jun 2026 13:56:49 +0200 Subject: [PATCH 0337/1062] checksrc-all.pl: do not check files multiple times Restrict `git ls-files` to return `*.[ch]` files within `$dir` only. Before this patch it returned files in subdirectories too, which did double work and may have made `checksrc.pl` pick `.checksrc` from the first such subdirectory, masking the one in `$dir`. (current curl tree is not affected) Ref: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec Follow-up to 33f606cd51995b68a0f68ac478f7395d8acda17b #20439 Closes #21909 --- scripts/checksrc-all.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checksrc-all.pl b/scripts/checksrc-all.pl index 5b1cba7af624..5982384fe115 100755 --- a/scripts/checksrc-all.pl +++ b/scripts/checksrc-all.pl @@ -34,7 +34,7 @@ for my $dir (@dirs) { if($is_git) { @files = (); - open(O, '-|', 'git', 'ls-files', "$dir/*.[ch]") || die; push @files, ; close(O); + open(O, '-|', 'git', 'ls-files', ":(glob)$dir/*.[ch]") || die; push @files, ; close(O); chomp(@files); } else { From 8145476d5dd97d0ec704e9ea65b2f2028b8a945c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 8 Jun 2026 23:27:32 +0200 Subject: [PATCH 0338/1062] pytest: close file handles after use, and two minor tidy-ups Also: - drop two unreachable return statements. - test_17_ssl_use: avoid implicit string concatenations in lists. Reported by GitHub CodeQL Closes #21916 --- tests/http/test_02_download.py | 6 ++- tests/http/test_07_upload.py | 75 ++++++++++++++++++++----------- tests/http/test_08_caddy.py | 12 +++-- tests/http/test_10_proxy.py | 13 +++--- tests/http/test_13_proxy_auth.py | 1 - tests/http/test_17_ssl_use.py | 12 ++--- tests/http/test_30_vsftpd.py | 15 ++++--- tests/http/test_31_vsftpds.py | 21 ++++++--- tests/http/test_32_ftps_vsftpd.py | 21 ++++++--- tests/http/test_40_socks.py | 6 ++- tests/http/test_50_scp.py | 12 +++-- tests/http/test_51_sftp.py | 12 +++-- tests/http/testenv/client.py | 11 +++-- tests/http/testenv/curl.py | 10 +++-- tests/http/testenv/sshd.py | 6 ++- 15 files changed, 149 insertions(+), 84 deletions(-) diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index 69b31183d546..c75f932b7218 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -473,8 +473,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index fee50387c59d..6cb4d943e155 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -57,7 +57,8 @@ def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) r.check_stats(count=1, http_status=200, exitcode=0) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data, check that this is what was echoed @@ -68,8 +69,9 @@ def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # upload data sequentially, check that they were echoed @@ -82,7 +84,8 @@ def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, proto): r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload data parallel, check that they were echoed @@ -97,7 +100,8 @@ def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data sequentially, check that this is what was echoed @@ -109,10 +113,12 @@ def test_07_12_upload_seq_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_response(count=count, http_status=200) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # upload very large data sequentially, check that this is what was echoed @@ -124,9 +130,11 @@ def test_07_13_upload_seq_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # upload from stdin, issue #14870 @@ -141,7 +149,8 @@ def test_07_14_upload_stdin(self, env: Env, httpd, nghttpx, proto, indata): r = curl.http_put(urls=[url], data=indata, alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [f'{len(indata)}'] @pytest.mark.parametrize("proto", Env.http_protos()) @@ -198,7 +207,8 @@ def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data parallel, check that this is what was echoed @@ -243,7 +253,8 @@ def test_07_30_put_100k(self, env: Env, httpd, nghttpx, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data # PUT 10m @@ -259,7 +270,8 @@ def test_07_31_put_10m(self, env: Env, httpd, nghttpx, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data # issue #10591 @@ -351,8 +363,9 @@ def test_07_35_h1_h2_upgrade_upload(self, env: Env, httpd, nghttpx): r.check_response(count=1, http_status=200) # apache does not Upgrade on request with a body assert r.stats[0]['http_version'] == '1.1', f'{r}' - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # upload to a 301,302,303 response @@ -368,7 +381,8 @@ def test_07_36_upload_30x(self, env: Env, httpd, nghttpx, redir, proto): '-L', '--trace-config', 'http/2,http/3' ]) r.check_response(count=1, http_status=200) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [] # was transformed to a GET # upload to a 307 response @@ -383,7 +397,8 @@ def test_07_37_upload_307(self, env: Env, httpd, nghttpx, proto): '-L', '--trace-config', 'http/2,http/3' ]) r.check_response(count=1, http_status=200) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [data] # was POST again # POST form data, yet another code path in transfer @@ -406,8 +421,9 @@ def test_07_39_post_urlenc_small(self, env: Env, httpd, nghttpx, proto): '--trace-config', 'http/2,http/3' ]) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # POST data urlencoded, large enough to be sent separate from request headers @@ -420,8 +436,9 @@ def test_07_40_post_urlenc_large(self, env: Env, httpd, nghttpx, proto): '--trace-config', 'http/2,http/3' ]) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # POST data urlencoded, small enough to be sent with request headers @@ -442,8 +459,9 @@ def test_07_41_post_urlenc_small(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=extra_args) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata def check_download(self, r: ExecResult, count: int, srcfile: Union[str, os.PathLike], curl: CurlClient): @@ -451,8 +469,10 @@ def check_download(self, r: ExecResult, count: int, srcfile: Union[str, os.PathL dfile = curl.download_file(i) assert os.path.exists(dfile), f'download {dfile} missing\n{r.dump_logs()}' if not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -699,8 +719,9 @@ def check_downloads(self, client, r, source: List[str], count: int, dfile = client.download_file(i) assert os.path.exists(dfile), f'download {dfile} missing\n{r.dump_logs()}' if complete: - diff = "".join(difflib.unified_diff(a=source, - b=open(dfile).readlines(), + with open(dfile) as fb: + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=source, b=b, fromfile='-', tofile=dfile, n=1)) diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index efee4bc48b76..23cea4582ab8 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -151,7 +151,8 @@ def test_08_06_post_parallel(self, env: Env, httpd, caddy, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # put large file, check that they length were echoed @@ -166,7 +167,8 @@ def test_08_07_put_large(self, env: Env, httpd, caddy, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data @pytest.mark.parametrize("proto", Env.http_protos()) @@ -210,8 +212,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index f81260a28f21..94a13ef49084 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -57,7 +57,6 @@ def get_tunnel_proto_used(self, r: ExecResult): if m: return m.group(1) assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}' - return None # download via http: proxy (no tunnel) def test_10_01_proxy_http(self, env: Env, httpd): @@ -105,9 +104,11 @@ def test_10_02_proxys_up(self, env: Env, httpd, nghttpx, proto, extra_args=xargs) r.check_response(count=count, http_status=200, protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1') - indata = open(srcfile).readlines() + with open(srcfile) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # download http: via http: proxytunnel @@ -229,9 +230,11 @@ def test_10_08_upload_seq_large(self, env: Env, httpd, nghttpx, proto, assert self.get_tunnel_proto_used(r) == tunnel r.check_response(count=count, http_status=200) assert r.total_connects == 1, r.dump_logs() - indata = open(srcfile).readlines() + with open(srcfile) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata, f'response {i} differs' @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") diff --git a/tests/http/test_13_proxy_auth.py b/tests/http/test_13_proxy_auth.py index d74a9f280b76..50e45960575d 100644 --- a/tests/http/test_13_proxy_auth.py +++ b/tests/http/test_13_proxy_auth.py @@ -48,7 +48,6 @@ def get_tunnel_proto_used(self, r: ExecResult): if m: return m.group(1) assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}' - return None # download via http: proxy (no tunnel), no auth def test_13_01_proxy_no_auth(self, env: Env, httpd, configures_httpd): diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 0f3b2ccb623f..4179925f42eb 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -243,10 +243,10 @@ def test_17_07_ssl_ciphers(self, env: Env, httpd, configures_httpd, succeed13, succeed12): # to test setting cipher suites, the AES 256 ciphers are disabled in the test server httpd.set_extra_config('base', [ - 'SSLCipherSuite SSL' - ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + 'SSLCipherSuite SSL' + + ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + ':ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305', - 'SSLCipherSuite TLSv1.3' + 'SSLCipherSuite TLSv1.3' + ' TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256', f'SSLProtocol {tls_proto}' ]) @@ -525,10 +525,10 @@ def test_17_17_h1_ignore_ciphers13(self, env: Env, httpd): def test_17_18_gnutls_priority(self, env: Env, httpd, configures_httpd, priority, tls_proto, ciphers, success): # to test setting cipher suites, the AES 256 ciphers are disabled in the test server httpd.set_extra_config('base', [ - 'SSLCipherSuite SSL' - ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + 'SSLCipherSuite SSL' + + ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + ':ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305', - 'SSLCipherSuite TLSv1.3' + 'SSLCipherSuite TLSv1.3' + ' TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256', ]) httpd.reload_if_config_changed() diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index a57882f5202f..d625852249a8 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -77,7 +77,8 @@ def test_30_01_list_dir(self, env: Env, vsftpd: VsFTPD): url = f'ftp://{env.ftp_domain}:{vsftpd.port}/' r = curl.ftp_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 5, f'list: {lines}' r.check_stats_timelines() @@ -251,8 +252,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -264,8 +267,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname, binary=True): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 5858d9e461fa..ac4c154c4722 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -82,7 +82,8 @@ def test_31_01_list_dir(self, env: Env, vsftpds: VsFTPD): url = f'ftp://{env.ftp_domain}:{vsftpds.port}/' r = curl.ftp_ssl_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 4, f'list: {lines}' r.check_stats_timelines() @@ -203,7 +204,8 @@ def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): r.check_stats(count=count, http_status=226) # expect the uploaded file to be number of converted newlines larger dstsize = os.path.getsize(dstfile) - newlines = len(open(srcfile).readlines()) + with open(srcfile) as fd: + newlines = len(fd.readlines()) assert (srcsize + newlines) == dstsize, \ f'expected source with {newlines} lines to be that much larger,'\ f'instead srcsize={srcsize}, upload size={dstsize}, diff={dstsize-srcsize}' @@ -248,7 +250,8 @@ def test_31_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata): r = curl.ftp_ssl_upload(urls=[url], updata=indata, with_stats=True) r.check_stats(count=count, http_status=226) assert os.path.exists(dstfile) - destdata = open(dstfile).readlines() + with open(dstfile) as fd: + destdata = fd.readlines() expdata = [indata] if len(indata) else [] assert expdata == destdata, f'expected: {expdata}, got: {destdata}' @@ -300,8 +303,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -313,8 +318,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index b16766466139..1181bb3211ce 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -82,7 +82,8 @@ def test_32_01_list_dir(self, env: Env, vsftpds: VsFTPD): url = f'ftps://{env.ftp_domain}:{vsftpds.port}/' r = curl.ftp_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 4, f'list: {lines}' r.check_stats_timelines() @@ -216,7 +217,8 @@ def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): r.check_stats(count=count, http_status=226) # expect the uploaded file to be number of converted newlines larger dstsize = os.path.getsize(dstfile) - newlines = len(open(srcfile).readlines()) + with open(srcfile) as fd: + newlines = len(fd.readlines()) assert (srcsize + newlines) == dstsize, \ f'expected source with {newlines} lines to be that much larger,'\ f'instead srcsize={srcsize}, upload size={dstsize}, diff={dstsize-srcsize}' @@ -261,7 +263,8 @@ def test_32_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata): r = curl.ftp_upload(urls=[url], updata=indata, with_stats=True) r.check_stats(count=count, http_status=226) assert os.path.exists(dstfile) - destdata = open(dstfile).readlines() + with open(dstfile) as fd: + destdata = fd.readlines() expdata = [indata] if len(indata) else [] assert expdata == destdata, f'expected: {expdata}, got: {destdata}' @@ -289,8 +292,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -302,8 +307,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_40_socks.py b/tests/http/test_40_socks.py index 9702aa111060..471f1948fbc2 100644 --- a/tests/http/test_40_socks.py +++ b/tests/http/test_40_socks.py @@ -96,7 +96,9 @@ def test_40_04_ul_serial(self, env: Env, httpd, danted, proto, sproto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata diff --git a/tests/http/test_50_scp.py b/tests/http/test_50_scp.py index 5bfba23f338c..831ee5b3f80e 100644 --- a/tests/http/test_50_scp.py +++ b/tests/http/test_50_scp.py @@ -191,8 +191,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -202,8 +204,10 @@ def check_upload(self, sshd: Sshd, srcfile, destfile, binary=True): assert os.path.exists(srcfile) assert os.path.exists(destfile) if not filecmp.cmp(srcfile, destfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(destfile).readlines(), + with open(srcfile) as fa, open(destfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=destfile, n=1)) diff --git a/tests/http/test_51_sftp.py b/tests/http/test_51_sftp.py index 76e8727b99ee..acf76ef68356 100644 --- a/tests/http/test_51_sftp.py +++ b/tests/http/test_51_sftp.py @@ -191,8 +191,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -202,8 +204,10 @@ def check_upload(self, sshd: Sshd, srcfile, destfile, binary=True): assert os.path.exists(srcfile) assert os.path.exists(destfile) if not filecmp.cmp(srcfile, destfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(destfile).readlines(), + with open(srcfile) as fa, open(destfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=destfile, n=1)) diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 36ac8a599e6d..1b4e303ac609 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -104,8 +104,9 @@ def run(self, args): log.warning(f'Timeout after {self._timeout}s: {args}') exitcode = -1 exception = 'TimeoutExpired' - coutput = open(self._stdoutfile).readlines() - cerrput = open(self._stderrfile).readlines() + with open(self._stdoutfile) as fout, open(self._stderrfile) as ferr: + coutput = fout.readlines() + cerrput = ferr.readlines() return ExecResult(args=myargs, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, duration=datetime.now() - start) @@ -113,8 +114,10 @@ def run(self, args): def dump_logs(self): lines = [] lines.append('>>--stdout ----------------------------------------------\n') - lines.extend(open(self._stdoutfile).readlines()) + with open(self._stdoutfile) as cstdout: + lines.extend(cstdout.readlines()) lines.append('>>--stderr ----------------------------------------------\n') - lines.extend(open(self._stderrfile).readlines()) + with open(self._stderrfile) as cstderr: + lines.extend(cstderr.readlines()) lines.append('<<-------------------------------------------------------\n') return ''.join(lines) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 64c1cacd46fa..0864e2899eb5 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -393,7 +393,7 @@ def check_exit_code(self, code: Union[int, bool]): f'got {self.exit_code}\n{self.dump_logs()}' elif code is False: assert self.exit_code != 0, f'expected exit code {code}, '\ - f'got {self.exit_code}\n{self.dump_logs()}' + f'got {self.exit_code}\n{self.dump_logs()}' else: assert self.exit_code == code, f'expected exit code {code}, '\ f'got {self.exit_code}\n{self.dump_logs()}' @@ -1072,8 +1072,9 @@ def _run(self, args, intext='', with_stats: bool = False, dtrace.finish() if self._with_flame: self._generate_flame(args, dtrace=dtrace, perf=perf) - coutput = open(self._stdoutfile).readlines() - cerrput = open(self._stderrfile).readlines() + with open(self._stdoutfile) as fout, open(self._stderrfile) as ferr: + coutput = fout.readlines() + cerrput = ferr.readlines() return ExecResult(args=args, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, duration=ended_at - started_at, @@ -1167,7 +1168,8 @@ def _complete_args(self, urls, timeout=None, options=None, return args def _parse_headerfile(self, headerfile: str, r: Optional[ExecResult] = None) -> ExecResult: - lines = open(headerfile).readlines() + with open(headerfile) as fd: + lines = fd.readlines() if r is None: r = ExecResult(args=[], exit_code=0, stdout=[], stderr=[]) diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index e800ced17b67..83accd74920b 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -132,7 +132,8 @@ def mk_host_keys(self): self._host_key_files.append(key_file) pub_file = f'{key_file}.pub' self._host_pub_files.append(pub_file) - pubkey = open(pub_file).read() + with open(pub_file) as fp: + pubkey = fp.read() # fd_known.write(f'[127.0.0.1]:{self.port} {pubkey}') fd_known.write(f'[{self.env.domain1.lower()}]:{self.port} {pubkey}') fd_unknown.write(f'dummy.invalid {pubkey}') @@ -159,7 +160,8 @@ def mk_user_keys(self): self._user_pub_files.append(f'{key_file}.pub') with open(self._auth_keys, 'w') as fd: os.chmod(self._auth_keys, stat.S_IRUSR | stat.S_IWUSR) - pubkey = open(self._user_pub_files[0]).read() + with open(self._user_pub_files[0]) as fp: + pubkey = fp.read() fd.write(pubkey) def clear_logs(self): From 04afd160767d22c9e8b95a8113564c2a8f1d3d29 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 08:18:18 +0200 Subject: [PATCH 0339/1062] urlapi: URL decode hostname before IP address normalization With this, IPv6 addresses that end with '%25' with no following zone id are considered invalid. Extend test 1560 to verify Reported-by: Hem Parekh Closes #21918 --- lib/urlapi.c | 17 +++++++++-------- tests/libtest/lib1560.c | 11 +++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 1799625ffdf4..22b9304ed0dc 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -658,20 +658,24 @@ static CURLUcode parse_authority(struct Curl_URL *u, */ uc = parse_hostname_login(u, auth, authlen, flags, &offset); if(uc) - goto out; + return uc; result = curlx_dyn_addn(host, auth + offset, authlen - offset); if(result) { uc = cc2cu(result); - goto out; + return uc; } uc = parse_port(u, host, has_scheme); if(uc) - goto out; + return uc; if(!curlx_dyn_len(host)) - return CURLUE_NO_HOST; + uc = CURLUE_NO_HOST; + else + uc = urldecode_host(host); + if(uc) + return uc; switch(ipv4_normalize(host)) { case HOST_IPV4: @@ -680,9 +684,7 @@ static CURLUcode parse_authority(struct Curl_URL *u, uc = ipv6_parse(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_NAME: - uc = urldecode_host(host); - if(!uc) - uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); + uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_ERROR: uc = CURLUE_OUT_OF_MEMORY; @@ -692,7 +694,6 @@ static CURLUcode parse_authority(struct Curl_URL *u, break; } -out: return uc; } diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index e66ae84b5629..62c163c7cb5c 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -626,6 +626,13 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + /* percent-encoded IP addresses */ + {"https://127.0.0.%31.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.0%78f%46.", "https://127.0.0.255/", 0, 0, CURLUE_OK}, + {"https://%30%31%37%37%2e%31", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://[fe80%3A%3A20c%3A29ff%3Afe9c%3A409b]/", + "https://[fe80::20c:29ff:fe9c:409b]/", 0, 0, CURLUE_OK }, + /* IPvFuture format */ {"http://[v1.fe80::abcd]/", "", 0, 0, CURLUE_BAD_IPV6}, @@ -842,8 +849,8 @@ static const struct urltestcase get_url_list[] = { "", 0, 0, CURLUE_BAD_IPV6}, {"https://[fe80::20c:29ff:fe9c:409b%25]:1234", - "https://[fe80::20c:29ff:fe9c:409b%2525]:1234/", - 0, 0, CURLUE_OK}, + "", + 0, 0, CURLUE_BAD_IPV6}, {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234", "https://[fe80::20c:29ff:fe9c:409b%25eth0]:1234/", 0, 0, CURLUE_OK}, From a2b943b115ab55e25464b555aed746b2e67c8dfe Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 23:21:55 +0200 Subject: [PATCH 0340/1062] digest: escape control codes too Since the username is decoded when used and control codes are accepted in HTTP usernames in general, the username encoding for the Digest auth needs to percent encode such bytes. Verified by test 3221 Reported-by: Trail of Bits Closes #21915 --- lib/vauth/digest.c | 6 ++++ tests/data/Makefile.am | 2 +- tests/data/test3221 | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/data/test3221 diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 6d935fc99ebf..6cc4edbee126 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -36,6 +36,7 @@ #include "curl_sha512_256.h" #include "curlx/strparse.h" #include "rand.h" +#include "escape.h" #ifndef USE_WINDOWS_SSPI #define SESSION_ALGO 1 /* for algos with this bit set */ @@ -163,6 +164,11 @@ static char *auth_digest_string_quoted(const char *s) if(!result) result = curlx_dyn_addn(&out, s, 1); } + else if((*s < ' ') || (*s > 0x7e)) { + unsigned char buf[3] = { '%' }; + Curl_hexbyte(&buf[1], (unsigned char)*s); + result = curlx_dyn_addn(&out, buf, 3); + } else result = curlx_dyn_addn(&out, s, 1); if(result) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 1fb219832e73..b0caa11346f8 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -281,7 +281,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ -test3216 test3217 test3218 test3219 test3220 \ +test3216 test3217 test3218 test3219 test3220 test3221 \ \ test3300 test3301 test3302 test3303 test3304 \ \ diff --git a/tests/data/test3221 b/tests/data/test3221 new file mode 100644 index 000000000000..321213ab0919 --- /dev/null +++ b/tests/data/test3221 @@ -0,0 +1,74 @@ + + + + +HTTP +HTTP GET +digest + + + +# Server-side + + +HTTP/1.1 401 Authorization Required +WWW-Authenticate: Digest realm="testrealm%0a%0d", nonce="1053604145" +Content-Length: 4 + +hej + + + +HTTP/1.1 200 OK +Content-Length: 23 + +This IS the real page! + + + +HTTP/1.1 401 Authorization Required +WWW-Authenticate: Digest realm="testrealm%0a%0d", nonce="1053604145" +Content-Length: 4 + +HTTP/1.1 200 OK +Content-Length: 23 + +This IS the real page! + + + +# Client-side + + +http + + +!SSPI +crypto +digest + + +HTTP Digest with CRLF in username + + +http://hello%0a%0d:there@%HOSTIP:%HTTPPORT/ --digest + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Digest username="hello%0A%0D", realm="testrealm%0a%0d", nonce="1053604145", uri="/", response="64e5ae1b90f05309847ac483c1094284" +User-Agent: curl/%VERSION +Accept: */* + + + + From 7b9d74abf6fb446d5c388c75755100998c67944a Mon Sep 17 00:00:00 2001 From: Yedaya Katsman Date: Fri, 5 Jun 2026 17:15:59 +0300 Subject: [PATCH 0341/1062] resolve: Mention in error that IP address is expected If you try using a DNS name like connect-to supports it can be confusing that it is illegal. Also make it a bit more readable Closes #21913 --- lib/dnscache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dnscache.c b/lib/dnscache.c index 82114b492025..7432a1a3013d 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -775,7 +775,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) result = Curl_str2addr(address, port, &ai); if(result) { - infof(data, "Resolve address '%s' found illegal", address); + infof(data, "Resolve IP address '%s' found is illegal", address); goto err; } From cb4465bfe67ec9c75722ea10923a6a75005e8f68 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 02:08:30 +0200 Subject: [PATCH 0342/1062] pytest: close file handles after use (cont.), and tidy-ups - dante.py, dnsd.py, sshd.py: drop redundant conditions. Spotted in sshd by GitHub Code Quality. - curl.py: comment out `if` to silence CodeQL warning. Reported by GitHub CodeQL Follow-up to 8145476d5dd97d0ec704e9ea65b2f2028b8a945c #21916 Closes #21917 --- tests/http/testenv/caddy.py | 12 ++++++++++-- tests/http/testenv/client.py | 8 ++++---- tests/http/testenv/curl.py | 22 ++++++++++++---------- tests/http/testenv/dante.py | 12 +++++++++--- tests/http/testenv/dnsd.py | 12 +++++++++--- tests/http/testenv/h2o.py | 14 ++++++++++++-- tests/http/testenv/nghttpx.py | 16 ++++++++++++---- tests/http/testenv/sshd.py | 18 +++++++++++++----- tests/http/testenv/vsftpd.py | 12 ++++++++++-- 9 files changed, 91 insertions(+), 35 deletions(-) diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index eece1a5a394a..1d554b1906f4 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -55,6 +55,7 @@ def __init__(self, env: Env): self._conf_file = os.path.join(self._caddy_dir, 'Caddyfile') self._error_log = os.path.join(self._caddy_dir, 'caddy.log') self._tmp_dir = os.path.join(self._caddy_dir, 'tmp') + self._error_fd = None self._process = None self._http_port = 0 self._https_port = 0 @@ -68,6 +69,11 @@ def docs_dir(self): def port(self) -> int: return self._https_port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) @@ -107,8 +113,8 @@ def start(self, wait_live=True): args = [ self._caddy, 'run' ] - caddyerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, cwd=self._caddy_dir, stderr=caddyerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, cwd=self._caddy_dir, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) @@ -122,7 +128,9 @@ def stop(self, wait_dead=True): except Exception: self._process.kill() self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 1b4e303ac609..76c4822cbc8d 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -114,10 +114,10 @@ def run(self, args): def dump_logs(self): lines = [] lines.append('>>--stdout ----------------------------------------------\n') - with open(self._stdoutfile) as cstdout: - lines.extend(cstdout.readlines()) + with open(self._stdoutfile) as fd: + lines.extend(fd.readlines()) lines.append('>>--stderr ----------------------------------------------\n') - with open(self._stderrfile) as cstderr: - lines.extend(cstderr.readlines()) + with open(self._stderrfile) as fd: + lines.extend(fd.readlines()) lines.append('<<-------------------------------------------------------\n') return ''.join(lines) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 0864e2899eb5..308e29f11067 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -191,13 +191,14 @@ def get_rsts(self, ports: List[int]|None = None) -> Optional[List[str]]: if self._proc: raise Exception('tcpdump still running') lines = [] - for line in open(self._stdoutfile): - m = re.match(r'.* IP 127\.0\.0\.1\.(\d+) [<>] 127\.0\.0\.1\.(\d+):.*', line) - if m: - sport = int(m.group(1)) - dport = int(m.group(2)) - if ports is None or sport in ports or dport in ports: - lines.append(line) + with open(self._stdoutfile) as fd: + for line in fd: + m = re.match(r'.* IP 127\.0\.0\.1\.(\d+) [<>] 127\.0\.0\.1\.(\d+):.*', line) + if m: + sport = int(m.group(1)) + dport = int(m.group(2)) + if ports is None or sport in ports or dport in ports: + lines.append(line) return lines @property @@ -208,7 +209,8 @@ def stats(self) -> Optional[List[str]]: def stderr(self) -> List[str]: if self._proc: raise Exception('tcpdump still running') - return open(self._stderrfile).readlines() + with open(self._stderrfile) as fd: + return fd.readlines() def sample(self): # not sure how to make that detection reliable for all platforms @@ -1027,8 +1029,8 @@ def _run(self, args, intext='', with_stats: bool = False, cwd=self._run_dir, shell=False, env=self._run_env) profile = RunProfile(p.pid, started_at, self._run_dir) - if intext is not None and False: - p.communicate(input=intext.encode(), timeout=1) + #if intext is not None and False: + # p.communicate(input=intext.encode(), timeout=1) if self._with_perf: perf = PerfProfile(p.pid, self._run_dir) perf.start() diff --git a/tests/http/testenv/dante.py b/tests/http/testenv/dante.py index 2feb42eb7cca..b2ea4dd0cfe4 100644 --- a/tests/http/testenv/dante.py +++ b/tests/http/testenv/dante.py @@ -57,6 +57,7 @@ def __init__(self, env: Env): self._dante_log = os.path.join(self._dante_dir, 'dante.log') self._error_log = os.path.join(self._dante_dir, 'error.log') self._pid_file = os.path.join(self._dante_dir, 'dante.pid') + self._error_fd = None self._process = None self.clear_logs() @@ -65,6 +66,11 @@ def __init__(self, env: Env): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) self._rmf(self._dante_log) @@ -89,7 +95,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -122,8 +128,8 @@ def start(self, wait_live=True): '-p', f'{self._pid_file}', '-d', '0', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index b8ff097280a3..a7dc0885571f 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -56,6 +56,7 @@ def __init__(self, env: Env): self._conf_file = os.path.join(self._log_dir, 'dnsd.cmd') self._pid_file = os.path.join(self._log_dir, 'dnsd.pid') self._error_log = os.path.join(self._log_dir, 'dnsd.err.log') + self._error_fd = None self._process = None self.clear_logs() @@ -64,6 +65,11 @@ def __init__(self, env: Env): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._log_file) self._rmf(self._error_log) @@ -87,7 +93,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -122,8 +128,8 @@ def start(self, wait_live=True): '--logfile', f'{self._log_file}', '--pidfile', f'{self._pid_file}', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index 279cff8b90c3..deb3608d3a8c 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -48,6 +48,7 @@ def __init__(self, env: Env, name: str, domain: str, cred_name: str): self._port = 0 # defaults to h3_port self._cred_name = cred_name self._loaded_cred_name = None + self._error_fd = None self._process = None self._tmp_dir = os.path.join(self.env.gen_dir, self._name) self._run_dir = os.path.join(self._tmp_dir, "run") @@ -72,6 +73,11 @@ def h1_port(self) -> Optional[int]: def h2_port(self) -> Optional[int]: return getattr(self, "_h2_port", None) + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) self._rmf(self._stderr) @@ -127,8 +133,8 @@ def start(self, wait_live=True): self._loaded_cred_name = self._cred_name self.write_config() args = [self._cmd, "-c", self._conf_file] - ngerr = open(self._stderr, "a") - self._process = subprocess.Popen(args=args, stderr=ngerr) + self._error_fd = open(self._stderr, "a") + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False if wait_live: @@ -155,15 +161,19 @@ def stop(self, wait_dead=True): self._process.kill() self._process.wait(timeout=2) self._process = None + self.close_log() return not wait_dead or self.wait_for_state( live=False, timeout=timedelta(seconds=5) ) + self.close_log() return True def kill(self, wait_dead=True): if self._process: self._process.kill() + self.close_log() return True + self.close_log() return False def restart(self): diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index c72a7f7f6de3..37c72ad9451f 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -55,6 +55,7 @@ def __init__(self, env: Env, name: str, domain: str, cred_name: str): self._error_log = os.path.join(self._run_dir, 'nghttpx.log') self._stderr = os.path.join(self._run_dir, 'nghttpx.stderr') self._tmp_dir = os.path.join(self._run_dir, 'tmp') + self._error_fd = None self._process: Optional[subprocess.Popen] = None self._cred_name = self._def_cred_name = cred_name self._loaded_cred_name = '' @@ -86,6 +87,11 @@ def port_is_quic(self): def exists(self): return self._cmd and os.path.exists(self._cmd) + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) self._rmf(self._stderr) @@ -116,7 +122,9 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): @@ -262,8 +270,8 @@ def start(self, wait_live=True): '--frontend-http3-max-connection-window-size=100M', # f'--frontend-quic-debug-log', ]) - ngerr = open(self._stderr, 'a') - self._process = subprocess.Popen(args=args, stderr=ngerr) + self._error_fd = open(self._stderr, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) @@ -312,8 +320,8 @@ def start(self, wait_live=True): creds.pkey_file, creds.cert_file, ] - ngerr = open(self._stderr, 'a') - self._process = subprocess.Popen(args=args, stderr=ngerr) + self._error_fd = open(self._stderr, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index 83accd74920b..de8078dd265c 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -74,6 +74,7 @@ def __init__(self, env: Env): ] self._user_key_files = [] self._user_pub_files = [] + self._error_fd = None self._process = None self.clear_logs() @@ -164,14 +165,21 @@ def mk_user_keys(self): pubkey = fp.read() fd.write(pubkey) + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._sshd_log) def dump_log(self): lines = ['>>--sshd log ----------------------------------------------\n'] - lines.extend(open(self._sshd_log)) + with open(self._sshd_log) as fd: + lines.extend(fd.readlines()) lines.extend(['>>--curl log ----------------------------------------------\n']) - lines.extend(open(os.path.join(self._tmp_dir, 'curl.stderr'))) + with open(os.path.join(self._tmp_dir, 'curl.stderr')) as fd: + lines.extend(fd.readlines()) lines.append('<<-------------------------------------------------------\n') return ''.join(lines) @@ -195,7 +203,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -233,8 +241,8 @@ def start(self, wait_live=True): run_env = os.environ.copy() # does not have any effect, sadly # run_env['HOME'] = f'{self._home_dir}' - procerr = open(self._sshd_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr, env=run_env) + self._error_fd = open(self._sshd_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd, env=run_env) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index ace2884c7d35..6c2d1db8c5f9 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -68,6 +68,7 @@ def __init__(self, env: Env, with_ssl=False, ssl_implicit=False): self._conf_file = os.path.join(self._vsftpd_dir, 'test.conf') self._pid_file = os.path.join(self._vsftpd_dir, 'vsftpd.pid') self._error_log = os.path.join(self._vsftpd_dir, 'vsftpd.log') + self._error_fd = None self._process = None self.clear_logs() @@ -84,6 +85,11 @@ def docs_dir(self): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) @@ -107,7 +113,9 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): @@ -138,8 +146,8 @@ def start(self, wait_live=True): self._cmd, f'{self._conf_file}', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) From b9b2c0cbb854908a2580e4424b0315983f7cf3da Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 8 Jun 2026 18:39:27 +0200 Subject: [PATCH 0343/1062] docs: returned header size reflects HTTP/1-style format Ref: #21889 Closes #21912 --- docs/cmdline-opts/write-out.md | 3 ++- docs/libcurl/opts/CURLINFO_HEADER_SIZE.md | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/cmdline-opts/write-out.md b/docs/cmdline-opts/write-out.md index df0d3d5c84f1..5cf9d91f8704 100644 --- a/docs/cmdline-opts/write-out.md +++ b/docs/cmdline-opts/write-out.md @@ -202,7 +202,8 @@ The total amount of bytes that were downloaded. This is the size of the body/data that was transferred, excluding headers. ## `size_header` -The total amount of bytes of the downloaded headers. +The total amount of bytes of the downloaded headers, as represented in +HTTP/1-style header format. ## `size_request` The total amount of bytes that were sent in the HTTP request. diff --git a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md index c27856809ee5..f0ce241e3f00 100644 --- a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md +++ b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md @@ -29,11 +29,15 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HEADER_SIZE, long *sizep); # DESCRIPTION Pass a pointer to a long to receive the total size of all the headers -received. Measured in number of bytes. +received, represented in HTTP/1-style header format. Measured in number of +bytes. The total includes the size of any received headers suppressed by CURLOPT_SUPPRESS_CONNECT_HEADERS(3). +The number of bytes transferred over the wire (or to the TLS backend) is +different when using HTTP/2 or greater. + # %PROTOCOLS% # EXAMPLE From fb9a520873133e369fa86ef63b4e4f0fd2fc1f68 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 10:38:18 +0200 Subject: [PATCH 0344/1062] peer.h: fix typo in comment Closes #21920 --- lib/peer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/peer.h b/lib/peer.h index 7946735a23e5..c18bad4501bc 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -51,7 +51,7 @@ struct Curl_peer { * + stripping of surrounding '[]' for URL formatted ipv6 addresses * + the path alone in case of a unix domain socket, e.g. hostname * starts with CURL_PEER_UDS_PREFIX and is longer - * Will scam for IPv6 addresses even without surrounding '[]'. + * Scans for IPv6 addresses even without surrounding '[]'. * - `zoneid` ipv6 zone identifier or NULL * - `scopeid` ipv6 scopeid of zoneid, when known. */ From 849317ff5c5a5e13f50ec3d001e46ddffa77d8a4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 8 Jun 2026 16:57:01 +0200 Subject: [PATCH 0345/1062] ws: make pong sending lazy Do not send PONG frames unless there is sufficient space left in the websocket send buffer. A server might be lazy in reading our data and intermediary PONG frames can be skipped by a client (RFC 6455, ch. 5.5.3). Add test case measuring no real RSS increase on a server blasting with PING frames. Closes #21911 --- lib/ws.c | 33 +++++++++----- tests/http/test_20_websockets.py | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/lib/ws.c b/lib/ws.c index d7840f1ffb8d..d00891b83fb0 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -632,6 +632,7 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, size_t plen, unsigned int frame_type) { + (void)data; DEBUGASSERT(plen <= WS_MAX_CNTRL_LEN); if(plen > WS_MAX_CNTRL_LEN) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -641,13 +642,6 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, ws->pending.type = frame_type; ws->pending.payload_len = plen; memcpy(ws->pending.payload, payload, plen); - - if(!ws->enc.payload_remain) { /* not in the middle of another frame */ - CURLcode result = ws_enc_add_pending(data, ws); - if(!result) - (void)ws_flush(data, ws, Curl_is_in_callback(data)); - return result; - } return CURLE_OK; } @@ -716,7 +710,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, { struct ws_cw_ctx *ctx = writer->ctx; struct websocket *ws; - CURLcode result; + CURLcode result = CURLE_OK; CURL_TRC_WRITE(data, "ws_cw_write(len=%zu, type=%d)", nbytes, type); if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) @@ -749,7 +743,8 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if(result == CURLE_AGAIN) { /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ - return CURLE_OK; + result = CURLE_OK; + goto out; } else if(result) { failf(data, "[WS] decode payload error %d", (int)result); @@ -760,10 +755,16 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { failf(data, "[WS] decode ending with %zu frame bytes remaining", Curl_bufq_len(&ctx->buf)); - return CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; } - return CURLE_OK; +out: + if(!result) { + result = ws_flush(data, ws, Curl_is_in_callback(data)); + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; } /* WebSocket payload decoding client writer. */ @@ -1627,8 +1628,16 @@ CURLcode curl_ws_recv(CURL *curl, void *buffer, static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, bool blocking) { + CURLcode result; + + /* If there is space, add any pending control frame */ + if(Curl_bufq_len(&ws->sendbuf) < ws->sendbuf.chunk_size) { + result = ws_enc_add_pending(data, ws); + if(result && (result != CURLE_AGAIN)) + return result; + } + if(!Curl_bufq_is_empty(&ws->sendbuf)) { - CURLcode result; const uint8_t *out; size_t outlen, n; #ifdef DEBUGBUILD diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 3a55d41b2bcf..00fc394a3bde 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -24,11 +24,15 @@ # ########################################################################### # +import base64 +import hashlib import logging import os +import re import shutil import socket import subprocess +import threading import time from datetime import datetime, timedelta from typing import Dict @@ -220,3 +224,77 @@ def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): # The CONNECT through the proxy fails as it does not allow it r.check_exit_code(7) # CURLE_COULDNT_CONNECT assert r.stats[0]['http_connect'] == 403, f'{r}' + + def test_20_11_crazy_pings(self, env: Env): + st = {} + send_rounds = 1 + + def srv(): + try: + with socket.socket() as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(("127.0.0.1", 0)) + s.listen(1) + st["p"] = s.getsockname()[1] + + c, _ = s.accept() + c.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096) + c.settimeout(Env.SERVER_TIMEOUT) + req = b"" + while b"\r\n\r\n" not in req: + req += c.recv(4096) + + k = re.search(rb"(?im)^Sec-WebSocket-Key:\s*(\S+)", req).group(1) + a = base64.b64encode( + hashlib.sha1(k + b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest() + ).decode() + c.sendall( + ( + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + f"Sec-WebSocket-Accept: {a}\r\n\r\n" + ).encode() + ) + + f = b"\x89\x00" * 65536 # PING frames, many + try: + for _ in range(send_rounds): + c.sendall(f) + f = b"\x88\x00" # CLOSE frame + c.sendall(f) + except OSError: + pass + time.sleep(1) + c.close() + except OSError as e: + st["err"] = e + + curl = CurlClient(env=env) + send_rounds = 2 + threading.Thread(target=srv, daemon=True).start() + while "p" not in st and "err" not in st: + time.sleep(0.01) + assert "err" not in st, f'ws-ping server failed to start: {st["err"]}' + + url = f'ws://127.0.0.1:{st["p"]}/' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + with_profile=True) + assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR + assert r.profile, f'{r}' + rss1 = r.profile.stats['rss'] / (1024 * 1024) + + st.clear() + send_rounds = 10 + threading.Thread(target=srv, daemon=True).start() + while "p" not in st and "err" not in st: + time.sleep(0.01) + assert "err" not in st, f'ws-ping server failed to start: {st["err"]}' + + url = f'ws://127.0.0.1:{st["p"]}/' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + with_profile=True) + assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR + assert r.profile, f'{r}' + rss2 = r.profile.stats['rss'] / (1024 * 1024) + assert (rss1 * 1.1) >= rss2, 'bad memory increase' From 952b04474cb1fc18bf66ddf3b41587535b71ca1d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 5 Jun 2026 23:02:11 +0200 Subject: [PATCH 0346/1062] tidy-up: miscellaneous - badwords: replace stray synonyms with 'null-terminator'. - tests/FILEFORMAT.md: tidy up feature descriptions. - printf: replace stray `%i` masks with `%d` for consistency. - pytest: add comments for empty excepts to try silencing GitHub CodeQL warnings. - tool1394, unit1675: merge nested `if`s. - dnscache: fix typo in comment. - fix whitespace, indent and newlines. Closes #21921 --- docs/CODE_REVIEW.md | 10 +++++----- docs/examples/evhiperfifo.c | 6 +++--- docs/internals/DYNBUF.md | 6 +++--- docs/libcurl/curl_mprintf.md | 6 +++--- docs/libcurl/opts/CURLOPT_DOH_URL.md | 6 +++--- docs/tests/FILEFORMAT.md | 10 +++++----- include/curl/curl.h | 16 ++++++++-------- lib/cf-haproxy.c | 2 +- lib/cf-socket.c | 4 ++-- lib/dnscache.c | 4 ++-- lib/doh.c | 2 +- lib/progress.c | 2 +- lib/rand.c | 4 ++-- lib/rand.h | 4 ++-- lib/tftp.c | 2 +- lib/urlapi.c | 3 +-- lib/vssh/libssh2.c | 2 +- lib/vtls/apple.c | 4 ++-- lib/vtls/openssl.c | 2 +- m4/curl-compilers.m4 | 10 ++++++++++ scripts/badwords.txt | 1 + tests/http/test_05_errors.py | 1 + tests/http/testenv/curl.py | 2 ++ tests/http/testenv/ws_echo_server.py | 1 + tests/libtest/lib530.c | 4 ++-- tests/libtest/lib582.c | 4 ++-- tests/libtest/lib758.c | 4 ++-- tests/server/sockfilt.c | 4 ---- tests/tunit/tool1394.c | 20 ++++++++------------ tests/unit/unit1655.c | 10 ++++------ tests/unit/unit1675.c | 24 ++++++++++-------------- 31 files changed, 90 insertions(+), 90 deletions(-) diff --git a/docs/CODE_REVIEW.md b/docs/CODE_REVIEW.md index da7bb2c9d78b..a80d2eec3ce2 100644 --- a/docs/CODE_REVIEW.md +++ b/docs/CODE_REVIEW.md @@ -156,14 +156,14 @@ Maybe use of `realloc()` should rather use the dynbuf functions? Do not allow new code that grows buffers without using dynbuf. -Use of C functions that rely on a terminating zero must only be used on data -that really do have a null-terminating zero. +Use of C functions that rely on a null-terminator must only be used on data +that really do have a null-terminator (`\0` byte). ## Dangerous "data styles" -Make extra precautions and verify that memory buffers that need a terminating -zero always have exactly that. Buffers *without* a null-terminator must not be -used as input to string functions. +Make extra precautions and verify that memory buffers that need +null-terminator always have exactly that. Buffers *without* a null-terminator +must not be used as input to string functions. # Commit messages diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index 37e41b726e9f..c5ddbf56ba5b 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -173,7 +173,7 @@ static void timer_cb(EV_P_ struct ev_timer *w, int revents) CURLMcode mresult; struct GlobalInfo *g; - printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); + printf("%s w %p revents %d\n", __PRETTY_FUNCTION__, (void *)w, revents); g = (struct GlobalInfo *)w->data; @@ -207,7 +207,7 @@ static void event_cb(EV_P_ struct ev_io *w, int revents) int action = ((revents & EV_READ) ? CURL_POLL_IN : 0) | ((revents & EV_WRITE) ? CURL_POLL_OUT : 0); - printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); + printf("%s w %p revents %d\n", __PRETTY_FUNCTION__, (void *)w, revents); g = (struct GlobalInfo *)w->data; mresult = curl_multi_socket_action(g->multi, w->fd, action, @@ -269,7 +269,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) struct SockInfo *fdp = (struct SockInfo *)sockp; const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; - printf("%s e %p s %i what %i cbp %p sockp %p\n", + printf("%s e %p s %d what %d cbp %p sockp %p\n", __PRETTY_FUNCTION__, e, s, what, cbp, sockp); fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); diff --git a/docs/internals/DYNBUF.md b/docs/internals/DYNBUF.md index 1ae7131f977e..d28b02809dc9 100644 --- a/docs/internals/DYNBUF.md +++ b/docs/internals/DYNBUF.md @@ -9,7 +9,7 @@ SPDX-License-Identifier: curl This is the internal module for creating and handling "dynamic buffers". This means buffers that can be appended to, dynamically and grow to adapt. -There is always a terminating zero put at the end of the dynamic buffer. +There is always a null-terminator put at the end of the dynamic buffer. The `struct dynbuf` is used to hold data for each instance of a dynamic buffer. The members of that struct **MUST NOT** be accessed or modified @@ -120,8 +120,8 @@ trusted or used anymore after the next buffer manipulation call. size_t curlx_dyn_len(const struct dynbuf *s); ``` -Returns the length of the buffer in bytes. Does not include the terminating -zero byte. +Returns the length of the buffer in bytes. Does not include the +null-terminator byte. ## `curlx_dyn_setlen` diff --git a/docs/libcurl/curl_mprintf.md b/docs/libcurl/curl_mprintf.md index 72ee0a1f0229..7c9def535c8a 100644 --- a/docs/libcurl/curl_mprintf.md +++ b/docs/libcurl/curl_mprintf.md @@ -54,7 +54,7 @@ write output to stdout, the standard output stream; **curl_mfprintf()** and **curl_mvsnprintf()** write to the character string **buffer**. The functions **curl_msnprintf()** and **curl_mvsnprintf()** write at most -*maxlength* bytes (including the terminating null byte ('0')) to +*maxlength* bytes (including the null-terminator byte ('0')) to *buffer*. The functions **curl_mvprintf()**, **curl_mvfprintf()**, @@ -246,10 +246,10 @@ is written. The *const char ** argument is expected to be a pointer to an array of character type (pointer to a string). Characters from the array are written up -to (but not including) a terminating null byte. If a precision is specified, +to (but not including) a null-terminator byte. If a precision is specified, no more than the number specified are written. If a precision is given, no null byte need be present; if the precision is not specified, or is greater -than the size of the array, the array must contain a terminating null byte. +than the size of the array, the array must contain a null-terminator byte. ## p diff --git a/docs/libcurl/opts/CURLOPT_DOH_URL.md b/docs/libcurl/opts/CURLOPT_DOH_URL.md index d82d71d8c0c6..696903c6baeb 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_URL.md +++ b/docs/libcurl/opts/CURLOPT_DOH_URL.md @@ -99,6 +99,6 @@ curl_easy_setopt(3) returns a CURLcode indicating success or error. CURLE_OK (0) means everything was OK, non-zero means an error occurred, see libcurl-errors(3). -Note that curl_easy_setopt(3) does immediately parse the given string so when -given a bad DoH URL, libcurl might not detect the problem until it later tries -to resolve a name with it. +Note that curl_easy_setopt(3) does not immediately parse the given string so +when given a bad DoH URL, libcurl might not detect the problem until it later +tries to resolve a name with it. diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 0518be11fd7d..b19cd7f87f96 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -487,7 +487,7 @@ Features testable here are: - `brotli` - `c-ares` - c-ares is used for (all) name resolves - `CharConv` -- `codeset-utf8`. If the running codeset is UTF-8 capable. +- `codeset-utf8` - if the running codeset is UTF-8 capable. - `cookies` - `crypto` - `cygwin` @@ -509,13 +509,13 @@ Features testable here are: - `IPv6` - `Kerberos` - `Largefile` -- `large-time` (time_t is larger than 32-bit) -- `large-size` (size_t is larger than 32-bit) +- `large-time` - time_t is larger than 32-bit +- `large-size` - size_t is larger than 32-bit - `libssh2` - `libssh` -- `badlibssh` (libssh configuration incompatible with the test suite) +- `badlibssh` - libssh configuration incompatible with the test suite - `libz` -- `local-http`. The HTTP server runs on 127.0.0.1 +- `local-http` - the HTTP server runs on 127.0.0.1 - `manual` - `mbedtls` - `Mime` diff --git a/include/curl/curl.h b/include/curl/curl.h index 9b2d0c855e26..cd53dedbc694 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -904,16 +904,16 @@ enum curl_khmatch { }; typedef int - (*curl_sshkeycallback) (CURL *easy, /* easy handle */ - const struct curl_khkey *knownkey, /* known */ - const struct curl_khkey *foundkey, /* found */ - enum curl_khmatch, /* libcurl's view on the keys */ - void *clientp); /* custom pointer passed with */ - /* CURLOPT_SSH_KEYDATA */ + (*curl_sshkeycallback)(CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed with */ + /* CURLOPT_SSH_KEYDATA */ typedef int - (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ - /* with CURLOPT_SSH_HOSTKEYDATA */ + (*curl_sshhostkeycallback)(void *clientp,/* custom pointer passed */ + /* with CURLOPT_SSH_HOSTKEYDATA */ int keytype, /* CURLKHTYPE */ const char *key, /* hostkey to check */ size_t keylen); /* length of the key */ diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 1b9e0791f15a..16ee25066c0e 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -91,7 +91,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, client_dest_ip = ipquad.remote_ip; } - result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", + result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %d %d\r\n", is_ipv6 ? "TCP6" : "TCP4", client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 8673562de280..f2d867276b87 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -667,7 +667,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, * We now have the numerical IP address in the 'myhost' buffer */ host = myhost; - infof(data, "Local Interface %s is ip %s using address family %i", + infof(data, "Local Interface %s is ip %s using address family %d", iface, host, af); done = 1; break; @@ -693,7 +693,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); - infof(data, "Name '%s' family %i resolved to '%s' family %i", + infof(data, "Name '%s' family %d resolved to '%s' family %d", host, af, myhost, h_af); Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { diff --git a/lib/dnscache.c b/lib/dnscache.c index 7432a1a3013d..9c8341e56fb4 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -351,7 +351,7 @@ UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, if(num_addrs > 1) { struct Curl_addrinfo **nodes; - CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + CURL_TRC_DNS(data, "Shuffling %d addresses", num_addrs); nodes = curlx_malloc(num_addrs * sizeof(*nodes)); if(nodes) { @@ -828,7 +828,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); } - /* put this new host in the cache, an overridy for ALL dns queries */ + /* put this new host in the cache, an override for ALL dns queries */ dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, &head, curlx_str(&source), curlx_strlen(&source), port, permanent); diff --git a/lib/doh.c b/lib/doh.c index e94a2371d2df..b5526b4a836f 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -419,7 +419,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, ((long)data->set.ssl.primary.ssl_options & - ~CURLSSLOPT_AUTO_CLIENT_CERT)); + ~CURLSSLOPT_AUTO_CLIENT_CERT)); doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ diff --git a/lib/progress.c b/lib/progress.c index d34b155e32fa..969b29a275ca 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -262,8 +262,8 @@ static const char *pgrs_timer_name(timerid timer) return pgrs_timer_names[(size_t)timer]; return "?"; } - #endif /* CURLVERBOSE */ + /* * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ diff --git a/lib/rand.c b/lib/rand.c index dd82750ba6b4..3260fe3345bf 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -186,7 +186,7 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ @@ -214,7 +214,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num) /* * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ static const char alnum[] = diff --git a/lib/rand.h b/lib/rand.h index afccd0aac13b..a02717074fdb 100644 --- a/lib/rand.h +++ b/lib/rand.h @@ -37,14 +37,14 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num); /* * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); diff --git a/lib/tftp.c b/lib/tftp.c index 039b7dd393d0..08345532c54d 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -491,7 +491,7 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) break; default: - failf(data, "tftp_tx: internal error, event: %i", (int)event); + failf(data, "tftp_tx: internal error, event: %d", (int)event); break; } diff --git a/lib/urlapi.c b/lib/urlapi.c index 22b9304ed0dc..b96f8ba19bde 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -509,7 +509,6 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * * @unittest 1675 */ - UNITTEST int ipv4_normalize(struct dynbuf *host); UNITTEST int ipv4_normalize(struct dynbuf *host) { @@ -1040,7 +1039,7 @@ static CURLUcode handle_fragment(CURLU *u, const char *fragment, CURLUcode ures; u->fragment_present = TRUE; if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ + /* skip the leading '#' in the copy but include the null-terminator */ if(flags & CURLU_URLENCODE) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index c4b72fbbb4bc..bc313b122765 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -713,7 +713,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, failf(data, "Found host key type RSA1 which is not supported"); return CURLE_SSH; default: - failf(data, "Unknown host key type: %i", + failf(data, "Unknown host key type: %d", (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); return CURLE_SSH; } diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index f9ffa2f5c2d8..8ad77bd7fd53 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -208,7 +208,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustSetOCSPResponse(trust, ocspdata); CFRelease(ocspdata); if(status != noErr) { - failf(data, "Apple SecTrust: failed to set OCSP response: %i", + failf(data, "Apple SecTrust: failed to set OCSP response: %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; goto out; @@ -254,7 +254,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustEvaluate(trust, &sec_result); if(status != noErr) { - failf(data, "Apple SecTrust verification failed: error %i", (int)status); + failf(data, "Apple SecTrust verification failed: error %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; } else if((sec_result == kSecTrustResultUnspecified) || diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 93409eb20999..520ba95fa9b5 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2192,7 +2192,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((cnlen <= 0) || !cn) result = CURLE_OUT_OF_MEMORY; else if((size_t)cnlen != strlen((char *)cn)) { - /* there was a terminating zero before the end of string, this + /* there was a null-terminator before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); result = CURLE_PEER_FAILED_VERIFICATION; diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index 3c96093abbc3..afe7a335b9ae 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -862,6 +862,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [conditional-uninitialized]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [language-extension-token]) fi + dnl Only clang 3.1 or later if test "$compiler_num" -ge "301"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-non-iso]) @@ -883,6 +884,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ ;; esac fi + dnl Only clang 3.3 or later if test "$compiler_num" -ge "303"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-documentation-unknown-command" @@ -914,38 +916,46 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -Wno-varargs" fi fi + dnl clang 7 or later if test "$compiler_num" -ge "700"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [assign-enum]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [extra-semi-stmt]) fi + dnl clang 10 or later if test "$compiler_num" -ge "1000"; then tmp_CFLAGS="$tmp_CFLAGS -Wimplicit-fallthrough" # we have silencing markup for clang 10.0 and above only CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [xor-used-as-pow]) fi + dnl clang 13 or later if test "$compiler_num" -ge "1300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-function-type]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [reserved-identifier]) # Keep it before -Wno-reserved-macro-identifier tmp_CFLAGS="$tmp_CFLAGS -Wno-reserved-macro-identifier" # Sometimes such external macros need to be set fi + dnl clang 16 or later if test "$compiler_num" -ge "1600"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-unsafe-buffer-usage" fi + dnl clang 17 or later if test "$compiler_num" -ge "1700"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-function-type-strict]) # with Apple clang it requires 16.0 or above fi + dnl clang 19 or later if test "$compiler_num" -ge "1901"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-format-signedness" fi + dnl clang 20 or later if test "$compiler_num" -ge "2001"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [array-compare]) fi + dnl clang 21 or later if test "$compiler_num" -ge "2101"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [c++-hidden-decl]) diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 38efb03935cd..c55fcbe19f7d 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -29,6 +29,7 @@ nul terminated:null-terminated null terminated:null-terminated NULL-terminated=null-terminated zero terminated:null-terminated +zero-terminated:null-terminated nul terminator:null-terminator null terminator:null-terminator zero terminator:null-terminator diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index 19cada0c9e57..76c12bc8e551 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -197,6 +197,7 @@ def accept_and_close(): conn.recv(1) # wait for ClientHello conn.close() except Exception: + # ignore expected socket error pass t = threading.Thread(target=accept_and_close) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 308e29f11067..779fd48bf96a 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -89,6 +89,7 @@ def sample(self): 'rss': mem.rss, }) except psutil.NoSuchProcess: + # process may exit between sampling ticks: ignore this pass def finish(self): @@ -238,6 +239,7 @@ def sample(self): try: self._proc.wait(timeout=1) except subprocess.TimeoutExpired: + # timeout means tcpdump is still running pass except Exception: log.exception('Tcpdump') diff --git a/tests/http/testenv/ws_echo_server.py b/tests/http/testenv/ws_echo_server.py index 99eaa628d239..f3f659a27f78 100755 --- a/tests/http/testenv/ws_echo_server.py +++ b/tests/http/testenv/ws_echo_server.py @@ -37,6 +37,7 @@ async def echo(websocket): async for message in websocket: await websocket.send(message) except ConnectionClosedError: + # websocket connection closed by client pass diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index 31cef1b702f6..e18a0191abed 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -203,7 +203,7 @@ static int t530_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n", + curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", t530_tag(), message->msg); result = 1; *success = 0; @@ -247,7 +247,7 @@ static CURLMcode socket_action(CURLM *multi, curl_socket_t s, int evBitmask, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "%s curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "%s curl error on %s (%d) %s\n", t530_tag(), info, mresult, curl_multi_strerror(mresult)); } return mresult; diff --git a/tests/libtest/lib582.c b/tests/libtest/lib582.c index 3a4e278e383f..408053ebc8d6 100644 --- a/tests/libtest/lib582.c +++ b/tests/libtest/lib582.c @@ -150,7 +150,7 @@ static int t582_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "Got an unexpected message from curl: %i\n", + curl_mfprintf(stderr, "Got an unexpected message from curl: %d\n", message->msg); result = 1; *success = 0; @@ -194,7 +194,7 @@ static void notifyCurl(CURLM *multi, curl_socket_t s, int evBitmask, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "curl error on %s (%d) %s\n", info, mresult, curl_multi_strerror(mresult)); } } diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index b6642cb130aa..0497754109a0 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -249,7 +249,7 @@ static int t758_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n", + curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", t758_tag(), message->msg); result = 1; *success = 0; @@ -293,7 +293,7 @@ static CURLMcode t758_saction(CURLM *multi, curl_socket_t s, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "%s curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "%s curl error on %s (%d) %s\n", t758_tag(), info, mresult, curl_multi_strerror(mresult)); } return mresult; diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index fabf6864e04e..af6dae96cb08 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -180,7 +180,6 @@ static ssize_t write_wincon(int fd, const void *buf, size_t count) * in nbytes or it fails with a condition that cannot be handled with a simple * retry of the read call. */ - static ssize_t fullread(int filedes, void *buffer, size_t nbytes) { int error; @@ -234,7 +233,6 @@ static ssize_t fullread(int filedes, void *buffer, size_t nbytes) * indicated in nbytes or it fails with a condition that cannot be handled * with a simple retry of the write call. */ - static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) { int error; @@ -283,7 +281,6 @@ static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) * read or FALSE when an unrecoverable error has been detected. Failure of this * function is an indication that the sockfilt process should terminate. */ - static bool read_stdin(void *buffer, size_t nbytes) { ssize_t nread = fullread(fileno(stdin), buffer, nbytes); @@ -300,7 +297,6 @@ static bool read_stdin(void *buffer, size_t nbytes) * written or FALSE when an unrecoverable error has been detected. Failure of * this function is an indication that the sockfilt process should terminate. */ - static bool write_stdout(const void *buffer, size_t nbytes) { ssize_t nwrite; diff --git a/tests/tunit/tool1394.c b/tests/tunit/tool1394.c index 33a5340cd638..8c29f2c37541 100644 --- a/tests/tunit/tool1394.c +++ b/tests/tunit/tool1394.c @@ -88,12 +88,10 @@ static CURLcode test_tool1394(const char *arg) fail("assertion failure"); } } - else { - if(certname) { - curl_mprintf("expected certname NULL but got '%s' " - "for -E param '%s'\n", certname, p->param); - fail("assertion failure"); - } + else if(certname) { + curl_mprintf("expected certname NULL but got '%s' " + "for -E param '%s'\n", certname, p->param); + fail("assertion failure"); } if(p->passwd) { if(passphrase) { @@ -109,12 +107,10 @@ static CURLcode test_tool1394(const char *arg) fail("assertion failure"); } } - else { - if(passphrase) { - curl_mprintf("expected passphrase NULL but got '%s' " - "for -E param '%s'\n", passphrase, p->param); - fail("assertion failure"); - } + else if(passphrase) { + curl_mprintf("expected passphrase NULL but got '%s' " + "for -E param '%s'\n", passphrase, p->param); + fail("assertion failure"); } if(certname) curlx_free(certname); diff --git a/tests/unit/unit1655.c b/tests/unit/unit1655.c index 78a08a2d8b53..ff3dc5348181 100644 --- a/tests/unit/unit1655.c +++ b/tests/unit/unit1655.c @@ -117,12 +117,10 @@ static CURLcode test_unit1655(const char *arg) fail_unless(victim.canary3 == 41, "three-byte buffer overwrite has happened"); } - else { - if(d == DOH_OK) { - fail_unless(olen <= sizeof(victim.dohbuffer), - "wrote outside bounds"); - fail_unless(olen > strlen(name), "unrealistic low size"); - } + else if(d == DOH_OK) { + fail_unless(olen <= sizeof(victim.dohbuffer), + "wrote outside bounds"); + fail_unless(olen > strlen(name), "unrealistic low size"); } } } while(0); diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index f7a965e38aac..c125ea9706dd 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -29,7 +29,7 @@ static CURLcode test_unit1675(const char *arg) { UNITTEST_BEGIN_SIMPLE - /* Test ipv4_normalize */ + /* Test ipv4_normalize */ { struct dynbuf host; int fails = 0; @@ -123,13 +123,11 @@ static CURLcode test_unit1675(const char *arg) fails++; } } - else { - if(rc == HOST_IPV4) { - curl_mfprintf(stderr, "ipv4_normalize('%s') succeeded unexpectedly:" - " got '%s'\n", - tests[i].in, curlx_dyn_ptr(&host)); - fails++; - } + else if(rc == HOST_IPV4) { + curl_mfprintf(stderr, "ipv4_normalize('%s') succeeded unexpectedly:" + " got '%s'\n", + tests[i].in, curlx_dyn_ptr(&host)); + fails++; } } curlx_dyn_free(&host); @@ -238,12 +236,10 @@ static CURLcode test_unit1675(const char *arg) } } } - else { - if(!uc) { - curl_mfprintf(stderr, "ipv6_parse('%s') succeeded unexpectedly\n", - tests[i].in); - fails++; - } + else if(!uc) { + curl_mfprintf(stderr, "ipv6_parse('%s') succeeded unexpectedly\n", + tests[i].in); + fails++; } curlx_free(u.host); curlx_free(u.zoneid); From e66b81a53283100b0d53c840844d3865b3eba951 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2026 16:37:44 +0200 Subject: [PATCH 0347/1062] cookie: tailmatch the domains for secure override If a SECURE cookie is set for a sub-domain (`example.com`) and is then attempted to get set again for more specific part of that domain (`www.example.com`) without the SECURE property, the second occurance should not be allowed. Reported-by: Trail of Bits Verified by test 3305 Closes #21910 --- lib/cookie.c | 6 ++- tests/data/Makefile.am | 2 +- tests/data/test3305 | 84 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tests/data/test3305 diff --git a/lib/cookie.c b/lib/cookie.c index b288a2c1d06a..e6b6e4f13296 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -863,8 +863,10 @@ static bool replace_existing(struct Curl_easy *data, bool matching_domains = FALSE; if(clist->domain && co->domain) { - if(curl_strequal(clist->domain, co->domain)) - /* The domains are identical */ + if(cookie_tailmatch(clist->domain, strlen(clist->domain), + co->domain) || + cookie_tailmatch(co->domain, strlen(co->domain), clist->domain)) + /* The existing one is a tail of the new or vice versa */ matching_domains = TRUE; } else if(!clist->domain && !co->domain) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b0caa11346f8..393f7531bdb2 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -283,7 +283,7 @@ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 test3221 \ \ -test3300 test3301 test3302 test3303 test3304 \ +test3300 test3301 test3302 test3303 test3304 test3305 \ \ test3400 \ \ diff --git a/tests/data/test3305 b/tests/data/test3305 new file mode 100644 index 000000000000..7a6d63beeeca --- /dev/null +++ b/tests/data/test3305 @@ -0,0 +1,84 @@ + + + + +HTTP +cookies +--resolve + + + +# Server-side + + +HTTP/1.1 301 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Set-Cookie: this=secret; domain=example.com; secure; path=/ +Set-Cookie: that=secret; domain=www.example.com; secure; path=/ +Set-Cookie: second=fine; + +-foo- + + +# The cookie 'this' should not be accepted since it would be the same as already +# set with a 'secure' flag. +# The cookie 'second' is however not secure so it is fair game to override + +HTTP/1.1 301 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Set-Cookie: this=open; path=/ +Set-Cookie: that=open; path=/; domain=example.com +Set-Cookie: second=override + +-foo- + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 + +-foo- + + + +# Client-side + + +http +https + + +same-name cookie over HTTPS and HTTP with different domains + + +https://www.example.com:%HTTPSPORT/ http://www.example.com:%HTTPPORT/%TESTNUMBER0002 https://www.example.com:%HTTPSPORT/%TESTNUMBER0003 --insecure -c %LOGDIR/cookie%TESTNUMBER --resolve www.example.com:%HTTPSPORT:%HOSTIP --resolve www.example.com:%HTTPPORT:%HOSTIP + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: www.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: www.example.com:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: second=fine + +GET /%TESTNUMBER0003 HTTP/1.1 +Host: www.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: second=override; that=secret; this=secret + + + + From feb609f28bc038b158d6e5f00e2aa30473b31d6e Mon Sep 17 00:00:00 2001 From: Yedaya Katsman Date: Mon, 8 Jun 2026 22:48:45 +0300 Subject: [PATCH 0348/1062] cf-socket: store errno from do_connect in ctx->error This fixes a misleading log in verbose mode when ipv6 connectivity isn't available, presumably also in other cases: ``` * Immediate connect fail for 2a00:1450:4028:806::200e: Network is unreachable * connect to 2a00:1450:4028:806::200e port 443 from :: port 0 failed: Success ``` Closes #21914 --- lib/cf-socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index f2d867276b87..9496e0e9e884 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1319,6 +1319,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "local address %s port %d...", ctx->ip.local_ip, ctx->ip.local_port); if(rc == -1) { + ctx->error = error; result = socket_connect_result(data, ctx->ip.remote_ip, error); goto out; } From 4aa8cc3c4ad567ba5e96d9901ff22238fbe01a0c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 11:14:17 +0200 Subject: [PATCH 0349/1062] pytest: fix remaining code checker warnings - curl.py: delete commented no-op code. - certs.py, curl.py: narrow down exceptions to fix: Except block handles 'BaseException' - test_20_websockets: add comment to empty except branch. Reported by GitHub CodeQL Closes #21924 --- tests/http/test_20_websockets.py | 2 ++ tests/http/testenv/certs.py | 3 +-- tests/http/testenv/curl.py | 8 ++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 00fc394a3bde..0a86e6439b1e 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -264,6 +264,8 @@ def srv(): f = b"\x88\x00" # CLOSE frame c.sendall(f) except OSError: + # Client may close/reset while we intentionally flood frames. + # Send errors are expected here, ignore them. pass time.sleep(1) c.close() diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 3e206efcdc80..85012634a5a7 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -484,8 +484,7 @@ def _add_leaf_usages(csr: Any, domains: List[str], issuer: Credentials) -> Any: else: try: names.append(x509.IPAddress(ipaddress.ip_address(name))) - # TODO: specify specific exceptions here - except: # noqa: E722 + except ValueError: names.append(x509.DNSName(name)) return csr.add_extension( diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 779fd48bf96a..d8e6a194e0e4 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -284,11 +284,10 @@ def __init__(self, args: List[str], exit_code: int, if with_stats: self._parse_stats() else: - # noinspection PyBroadException try: out = ''.join(self._stdout) self._json_out = json.loads(out) - except: # noqa: E722 + except (json.JSONDecodeError, TypeError, ValueError): pass def __repr__(self): @@ -300,8 +299,7 @@ def _parse_stats(self): for line in self._stdout: try: self._stats.append(json.loads(line)) - # TODO: specify specific exceptions here - except: # noqa: E722 + except (json.JSONDecodeError, TypeError, ValueError): log.exception(f'not a JSON stat: {line}') break @@ -1031,8 +1029,6 @@ def _run(self, args, intext='', with_stats: bool = False, cwd=self._run_dir, shell=False, env=self._run_env) profile = RunProfile(p.pid, started_at, self._run_dir) - #if intext is not None and False: - # p.communicate(input=intext.encode(), timeout=1) if self._with_perf: perf = PerfProfile(p.pid, self._run_dir) perf.start() From bbb226b22603cf52e5997bfc82db3d3aba46ee34 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 11:28:06 +0200 Subject: [PATCH 0350/1062] unit1675: fix potential memory leak on dynbuf fail path Spotted by GitHub Code Quality Closes #21922 --- tests/unit/unit1675.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index c125ea9706dd..29b76e61c8f6 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -111,6 +111,7 @@ static CURLcode test_unit1675(const char *arg) int rc; curlx_dyn_reset(&host); if(curlx_dyn_add(&host, tests[i].in)) { + curlx_dyn_free(&host); return CURLE_OUT_OF_MEMORY; } rc = ipv4_normalize(&host); From 9dcc57b801385fc598c2a658d83bdef68904a050 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 12:33:24 +0200 Subject: [PATCH 0351/1062] pytest: add comment to empty except branch To silence GitHub CodeQL. Follow-up to 4aa8cc3c4ad567ba5e96d9901ff22238fbe01a0c #21924 --- tests/http/testenv/curl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index d8e6a194e0e4..45fa899ad243 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -288,6 +288,7 @@ def __init__(self, args: List[str], exit_code: int, out = ''.join(self._stdout) self._json_out = json.loads(out) except (json.JSONDecodeError, TypeError, ValueError): + # stdout not guaranteed to be JSON, keep _json_out as None pass def __repr__(self): From 847aac066d45f0b79c96f76ea3f1c891978f1c43 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 12:09:46 +0200 Subject: [PATCH 0352/1062] tidy-up: use uppercase `TRUE`/`FALSE` where missing Keep it only in external API calls and C++ code. Also: - curlx/fopen: replace with `!!`. Spotted by GitHub Code Quality in cf-socket.c. Closes #21925 --- lib/cf-socket.c | 4 ++-- lib/curlx/fopen.c | 2 +- lib/http_aws_sigv4.c | 5 ++--- lib/imap.c | 4 ++-- lib/vtls/vtls.c | 2 +- src/tool_cb_wrt.c | 8 ++++---- src/tool_doswin.c | 4 ++-- src/tool_writeout.c | 40 ++++++++++++++++++++-------------------- src/tool_writeout_json.c | 2 +- src/var.c | 2 +- tests/libtest/lib1565.c | 4 ++-- tests/libtest/lib2700.c | 6 +++--- tests/libtest/lib3102.c | 6 +++--- tests/server/dnsd.c | 2 +- tests/server/rtspd.c | 4 ++-- tests/server/sockfilt.c | 4 ++-- tests/server/socksd.c | 8 ++++---- tests/server/sws.c | 12 ++++++------ tests/server/tftpd.c | 8 ++++---- tests/unit/unit1607.c | 16 ++++++++-------- tests/unit/unit1609.c | 12 ++++++------ tests/unit/unit1979.c | 22 +++++++++++----------- tests/unit/unit3205.c | 4 ++-- 23 files changed, 90 insertions(+), 91 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 9496e0e9e884..354f43e7eaa4 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -2176,10 +2176,10 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, int error = 0; /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); + Curl_set_in_callback(data, TRUE); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, FALSE); if(error) return CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 25dc653e496a..37ca02671afe 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -247,7 +247,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out) CURLX_FREE(ibuf); CURLX_FREE(obuf); #endif - return *out ? true : false; + return !!*out; } #ifndef CURL_WINDOWS_UWP diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index ed5dcf8f8ff0..308970bb89d2 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -271,13 +271,12 @@ static bool should_urlencode(struct Curl_str *service_name) * should_urlencode == true is equivalent to should_urlencode_uri_path * from the AWS SDK. Urls are already normalized by the curl URL parser */ - if(curlx_str_cmp(service_name, "s3") || curlx_str_cmp(service_name, "s3-express") || curlx_str_cmp(service_name, "s3-outposts")) { - return false; + return FALSE; } - return true; + return TRUE; } /* maximum length for the aws sivg4 parts */ diff --git a/lib/imap.c b/lib/imap.c index 1898e33adbcc..87d33c9bce84 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1188,9 +1188,9 @@ static bool is_custom_fetch_listing_match(const char *params) return FALSE; } if(*params == ':') - return true; + return TRUE; if(*params == ',') - return true; + return TRUE; return FALSE; } diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 4c456a7fcb54..78a956a16608 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1452,7 +1452,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } #endif - ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, FALSE, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index d514e34966ef..133573a8b38e 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -125,12 +125,12 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, /* attempt to complete an incomplete UTF-8 sequence from previous call. the sequence does not have to be well-formed. */ if(outs->utf8seq[0] && rlen) { - bool complete = false; + bool complete = FALSE; /* two byte sequence (lead byte 110yyyyy) */ if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) { outs->utf8seq[1] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } /* three byte sequence (lead byte 1110zzzz) */ else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) { @@ -141,7 +141,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, if(rlen && !outs->utf8seq[2]) { outs->utf8seq[2] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } } /* four byte sequence (lead byte 11110uuu) */ @@ -157,7 +157,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, if(rlen && !outs->utf8seq[3]) { outs->utf8seq[3] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } } diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 76c5ba4e3f82..577270cdb078 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -678,7 +678,7 @@ static void init_terminal(void) return; if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) - tool_term_has_bold = true; + tool_term_has_bold = TRUE; else { /* The signal handler is set before attempting to change the console mode because otherwise a signal would not be caught after the change but @@ -688,7 +688,7 @@ static void init_terminal(void) if(SetConsoleMode(TerminalSettings.hStdOut, (TerminalSettings.dwOutputMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { - tool_term_has_bold = true; + tool_term_has_bold = TRUE; atexit(restore_terminal); } else { diff --git a/src/tool_writeout.c b/src/tool_writeout.c index ad8c77c962e9..8023fc47ab31 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -45,7 +45,7 @@ static int writeTime(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; curl_off_t us = 0; (void)per; @@ -54,7 +54,7 @@ static int writeTime(FILE *stream, const struct writeoutvar *wovar, if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &us)) - valid = true; + valid = TRUE; } else { DEBUGASSERT(0); @@ -173,7 +173,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; const char *strinfo = NULL; const char *freestr = NULL; struct dynbuf buf; @@ -189,7 +189,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, while(m->str) { if(m->num == version) { strinfo = m->str; - valid = true; + valid = TRUE; break; } m++; @@ -198,7 +198,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, } else { if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo) - valid = true; + valid = TRUE; } } else { @@ -243,7 +243,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(!strinfo) /* maybe not a TLS protocol */ strinfo = ""; - valid = true; + valid = TRUE; } } else @@ -253,19 +253,19 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(per_result) { strinfo = (per->errorbuffer[0]) ? per->errorbuffer : curl_easy_strerror(per_result); - valid = true; + valid = TRUE; } break; case VAR_EFFECTIVE_FILENAME: if(per->outs.filename) { strinfo = per->outs.filename; - valid = true; + valid = TRUE; } break; case VAR_INPUT_URL: if(per->url) { strinfo = per->url; - valid = true; + valid = TRUE; } break; case VAR_INPUT_URLSCHEME: @@ -291,7 +291,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(per->url) { if(!urlpart(per, wovar->id, &strinfo)) { freestr = strinfo; - valid = true; + valid = TRUE; } } break; @@ -324,33 +324,33 @@ static int writeLong(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; long longinfo = 0; DEBUGASSERT(wovar->writefunc == writeLong); if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo)) - valid = true; + valid = TRUE; } else { switch(wovar->id) { case VAR_NUM_RETRY: longinfo = per->num_retries; - valid = true; + valid = TRUE; break; case VAR_NUM_CERTS: certinfo(per); longinfo = per->certinfo ? per->certinfo->num_of_certs : 0; - valid = true; + valid = TRUE; break; case VAR_NUM_HEADERS: longinfo = per->num_headers; - valid = true; + valid = TRUE; break; case VAR_EXITCODE: longinfo = (long)per_result; - valid = true; + valid = TRUE; break; default: DEBUGASSERT(0); @@ -380,7 +380,7 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; curl_off_t offinfo = 0; (void)per; @@ -389,14 +389,14 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo)) - valid = true; + valid = TRUE; } else { switch(wovar->id) { case VAR_URLNUM: if(per->urlnum <= INT_MAX) { offinfo = per->urlnum; - valid = true; + valid = TRUE; } break; default: @@ -787,7 +787,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, headerJSON(stream, per); break; default: - (void)wv->writefunc(stream, wv, per, per_result, false); + (void)wv->writefunc(stream, wv, per, per_result, FALSE); break; } } diff --git a/src/tool_writeout_json.c b/src/tool_writeout_json.c index 31f528ac4710..7ea18860a5b1 100644 --- a/src/tool_writeout_json.c +++ b/src/tool_writeout_json.c @@ -105,7 +105,7 @@ void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[], for(i = 0; i < nentries; i++) { if(mappings[i].writefunc && - mappings[i].writefunc(stream, &mappings[i], per, per_result, true)) + mappings[i].writefunc(stream, &mappings[i], per, per_result, TRUE)) fputs(",", stream); } diff --git a/src/var.c b/src/var.c index bf1bc4e9eb00..8f9dbbb5b072 100644 --- a/src/var.c +++ b/src/var.c @@ -316,7 +316,7 @@ ParameterError varexpand(const char *line, struct dynbuf *out, bool *replaced) if(result) return PARAM_NO_MEM; - added = true; + added = TRUE; } } line = &clp[2]; diff --git a/tests/libtest/lib1565.c b/tests/libtest/lib1565.c index e6c266a7e5f4..c3fedd6e217f 100644 --- a/tests/libtest/lib1565.c +++ b/tests/libtest/lib1565.c @@ -93,7 +93,7 @@ static CURLcode test_lib1565(const char *URL) int started_num = 0; int finished_num = 0; pthread_t tid = 0; - bool tid_valid = false; + bool tid_valid = FALSE; struct CURLMsg *message; start_test_timing(); @@ -106,7 +106,7 @@ static CURLcode test_lib1565(const char *URL) rc = pthread_create(&tid, NULL, t1565_run_thread, NULL); if(!rc) - tid_valid = true; + tid_valid = TRUE; else { curl_mfprintf(stderr, "%s:%d Could not create thread, errno %d\n", __FILE__, __LINE__, rc); diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index 5eac6011dc06..b3782972ea2f 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -39,7 +39,7 @@ static const char *descr_flags(int flags) return "pong"; if(flags & CURLWS_CLOSE) return "close"; - assert(false); + assert(FALSE); return ""; } @@ -200,7 +200,7 @@ static CURLcode recv_frame(CURL *curl, bool *stop) } if(flags & CURLWS_CLOSE) - *stop = true; + *stop = TRUE; curl_mfprintf(stdout, "\n"); @@ -212,7 +212,7 @@ static CURLcode test_lib2700(const char *URL) { #ifndef CURL_DISABLE_WEBSOCKETS CURLcode result = CURLE_OK; - bool stop = false; + bool stop = FALSE; CURL *curl; global_init(CURL_GLOBAL_ALL); diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index 765488f36ec6..c083896381ca 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -34,7 +34,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) /* Chains with only a single certificate are always in order */ if(cert_info->num_of_certs <= 1) - return true; + return TRUE; /* Enumerate each certificate in the chain */ for(cert = 0; cert < cert_info->num_of_certs; cert++) { @@ -68,7 +68,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) "cert %d issuer does not match cert %d subject\n", cert - 1, cert); curl_mfprintf(stderr, "certificate chain is not in order\n"); - return false; + return FALSE; } } } @@ -77,7 +77,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) } curl_mprintf("certificate chain is in order\n"); - return true; + return TRUE; } static size_t wrfu(char *ptr, size_t size, size_t nmemb, void *stream) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index aab3c8c251fb..8726897fd66a 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -1047,7 +1047,7 @@ static int test_dnsd(int argc, const char **argv) } clear_resp_queue(); - restore_signal_handlers(true); + restore_signal_handlers(TRUE); if(got_exit_signal) { logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 3684cf0f9291..01c6155cb599 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -1106,7 +1106,7 @@ static int test_rtspd(int argc, const char *argv[]) snprintf(loglockfile, sizeof(loglockfile), "%s/%s/rtsp-%s.lock", logdir, SERVERLOGS_LOCKDIR, ipv_inuse); - install_signal_handlers(false); + install_signal_handlers(FALSE); #ifdef USE_IPV6 if(!use_ipv6) @@ -1343,7 +1343,7 @@ static int test_rtspd(int argc, const char *argv[]) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index af6dae96cb08..22aec2a02c78 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -1288,7 +1288,7 @@ static int test_sockfilt(int argc, const char *argv[]) CURL_BINMODE(stdout); CURL_BINMODE(stderr); - install_signal_handlers(false); + install_signal_handlers(FALSE); sock = socket(socket_domain, SOCK_STREAM, 0); @@ -1389,7 +1389,7 @@ static int test_sockfilt(int argc, const char *argv[]) if(wroteportfile) unlink(portname); - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("============> sockfilt exits with signal (%d)", exit_signal); diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 20fdffa1bacb..331b67ca1d24 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -738,7 +738,7 @@ static int test_socksd(int argc, const char *argv[]) const char *unix_socket = NULL; #ifdef USE_UNIX_SOCKETS - bool unlink_socket = false; + bool unlink_socket = FALSE; #endif pidname = ".socksd.pid"; @@ -861,7 +861,7 @@ static int test_socksd(int argc, const char *argv[]) CURL_BINMODE(stdout); CURL_BINMODE(stderr); - install_signal_handlers(false); + install_signal_handlers(FALSE); sock = socket(socket_domain, SOCK_STREAM, 0); @@ -879,7 +879,7 @@ static int test_socksd(int argc, const char *argv[]) goto socks5_cleanup; } #ifdef USE_UNIX_SOCKETS - unlink_socket = true; + unlink_socket = TRUE; #endif msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ } @@ -930,7 +930,7 @@ static int test_socksd(int argc, const char *argv[]) if(wroteportfile) unlink(portname); - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("============> socksd exits with signal (%d)", exit_signal); diff --git a/tests/server/sws.c b/tests/server/sws.c index 7dfa8b79e2fd..db32c211c670 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -195,10 +195,10 @@ static bool socket_domain_is_ip(void) #ifdef USE_IPV6 case AF_INET6: #endif - return true; + return TRUE; default: /* case AF_UNIX: */ - return false; + return FALSE; } } #endif @@ -1971,7 +1971,7 @@ static int test_sws(int argc, const char *argv[]) unsigned short port = 8999; #ifdef USE_UNIX_SOCKETS const char *unix_socket = NULL; - bool unlink_socket = false; + bool unlink_socket = FALSE; #endif struct sws_httprequest *req = NULL; int rc = 0; @@ -2135,7 +2135,7 @@ static int test_sws(int argc, const char *argv[]) logdir, SERVERLOGS_LOCKDIR, protocol_type, is_proxy ? "-proxy" : "", socket_type); - install_signal_handlers(false); + install_signal_handlers(FALSE); req = calloc(1, sizeof(*req)); if(!req) @@ -2271,7 +2271,7 @@ static int test_sws(int argc, const char *argv[]) #ifdef USE_UNIX_SOCKETS /* listen succeeds, so let's assume a valid listening Unix socket */ - unlink_socket = true; + unlink_socket = TRUE; #endif /* @@ -2465,7 +2465,7 @@ static int test_sws(int argc, const char *argv[]) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)", diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index d81d3d39d256..1bfd2f3c94ce 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -918,10 +918,10 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) cp = (char *)&tp->th_stuff; filename = cp; do { - bool endofit = true; + bool endofit = TRUE; while(cp < &trsbuf.storage[size]) { if(*cp == '\0') { - endofit = false; + endofit = FALSE; break; } cp++; @@ -1109,7 +1109,7 @@ static int test_tftpd(int argc, const char **argv) snprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock", logdir, SERVERLOGS_LOCKDIR, ipv_inuse); - install_signal_handlers(true); + install_signal_handlers(TRUE); #ifdef USE_IPV6 if(!use_ipv6) @@ -1327,7 +1327,7 @@ static int test_tftpd(int argc, const char **argv) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(true); + restore_signal_handlers(TRUE); if(got_exit_signal) { logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index 1380f9547e97..303f9849507e 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -110,7 +110,7 @@ static CURLcode test_unit1607(const char *arg) struct Curl_addrinfo *addr; struct Curl_dns_entry *dns; void *entry_id; - bool problem = false; + bool problem = FALSE; easy = curl_easy_init(); if(!easy) goto error; @@ -151,7 +151,7 @@ static CURLcode test_unit1607(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. " "getaddressinfo failed.\n", __FILE__, __LINE__, i); - problem = true; + problem = TRUE; break; } @@ -159,7 +159,7 @@ static CURLcode test_unit1607(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. the retrieved addr " "is %s but tests[%zu].address[%zu] is NULL.\n", __FILE__, __LINE__, i, ipaddress, i, j); - problem = true; + problem = TRUE; break; } @@ -167,7 +167,7 @@ static CURLcode test_unit1607(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. the retrieved addr " "is NULL but tests[%zu].address[%zu] is %s.\n", __FILE__, __LINE__, i, i, j, tests[i].address[j]); - problem = true; + problem = TRUE; break; } @@ -176,7 +176,7 @@ static CURLcode test_unit1607(const char *arg) "%s is not equal to tests[%zu].address[%zu] %s.\n", __FILE__, __LINE__, i, ipaddress, i, j, tests[i].address[j]); - problem = true; + problem = TRUE; break; } @@ -185,7 +185,7 @@ static CURLcode test_unit1607(const char *arg) "for tests[%zu].address[%zu] is %d " "but tests[%zu].port is %d.\n", __FILE__, __LINE__, i, i, j, port, i, tests[i].port); - problem = true; + problem = TRUE; break; } @@ -194,7 +194,7 @@ static CURLcode test_unit1607(const char *arg) "%s:%d tests[%zu] failed. the timestamp is not zero " "but tests[%zu].permanent is TRUE\n", __FILE__, __LINE__, i, i); - problem = true; + problem = TRUE; break; } @@ -202,7 +202,7 @@ static CURLcode test_unit1607(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. the timestamp is zero " "but tests[%zu].permanent is FALSE\n", __FILE__, __LINE__, i, i); - problem = true; + problem = TRUE; break; } diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index 356fa2a6c985..c09edd22caa4 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -106,7 +106,7 @@ static CURLcode test_unit1609(const char *arg) struct Curl_addrinfo *addr; struct Curl_dns_entry *dns; void *entry_id; - bool problem = false; + bool problem = FALSE; easy = curl_easy_init(); if(!easy) { curl_global_cleanup(); @@ -150,7 +150,7 @@ static CURLcode test_unit1609(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. Curl_addr2string failed.\n", __FILE__, __LINE__, i); - problem = true; + problem = TRUE; break; } @@ -158,7 +158,7 @@ static CURLcode test_unit1609(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. the retrieved addr " "is %s but tests[%zu].address[%zu] is NULL.\n", __FILE__, __LINE__, i, ipaddress, i, j); - problem = true; + problem = TRUE; break; } @@ -166,7 +166,7 @@ static CURLcode test_unit1609(const char *arg) curl_mfprintf(stderr, "%s:%d tests[%zu] failed. the retrieved addr " "is NULL but tests[%zu].address[%zu] is %s.\n", __FILE__, __LINE__, i, i, j, tests[i].address[j]); - problem = true; + problem = TRUE; break; } @@ -175,7 +175,7 @@ static CURLcode test_unit1609(const char *arg) "%s is not equal to tests[%zu].address[%zu] %s.\n", __FILE__, __LINE__, i, ipaddress, i, j, tests[i].address[j]); - problem = true; + problem = TRUE; break; } @@ -184,7 +184,7 @@ static CURLcode test_unit1609(const char *arg) "for tests[%zu].address[%zu] is %d " "but tests[%zu].port is %d.\n", __FILE__, __LINE__, i, i, j, port, i, tests[i].port); - problem = true; + problem = TRUE; break; } diff --git a/tests/unit/unit1979.c b/tests/unit/unit1979.c index b40bdad82757..76e65fb70b60 100644 --- a/tests/unit/unit1979.c +++ b/tests/unit/unit1979.c @@ -39,19 +39,19 @@ static CURLcode test_unit1979(const char *arg) static const struct testcase testcases[] = { { "test-equals-encode", - true, + TRUE, "/a=b", "/a%3Db" }, { "test-equals-noencode", - false, + FALSE, "/a=b", "/a=b" }, { "test-s3-tables", - true, + TRUE, "/tables/arn%3Aaws%3As3tables%3Aus-east-1%3A022954301426%3Abucket%2Fja" "soehartablebucket/jasoeharnamespace/jasoehartable/encryption", "/tables/arn%253Aaws%253As3tables%253Aus-east-1%253A022954301426%253Ab" @@ -60,49 +60,49 @@ static CURLcode test_unit1979(const char *arg) }, { "get-vanilla", - true, + TRUE, "/", "/" }, { "get-unreserved", - true, + TRUE, "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" }, { "get-slashes-unnormalized", - false, + FALSE, "//example//", "//example//" }, { "get-space-normalized", - true, + TRUE, "/example space/", "/example%20space/" }, { "get-plus-normalized", - true, + TRUE, "/example+space/", "/example%2Bspace/" }, { "get-slash-dot-slash-unnormalized", - false, + FALSE, "/./", "/./" }, { "get-slash-unnormalized", - false, + FALSE, "//", "//" }, { "get-relative-relative-unnormalized", - false, + FALSE, "/example1/example2/../..", "/example1/example2/../.." } diff --git a/tests/unit/unit3205.c b/tests/unit/unit3205.c index e7de755331d2..686620c292fe 100644 --- a/tests/unit/unit3205.c +++ b/tests/unit/unit3205.c @@ -542,7 +542,7 @@ static CURLcode test_unit3205(const char *arg) buf[0] = '\0'; expect = test->rfc ? test->rfc : test->openssl; - Curl_cipher_suite_get_str(test->id, buf, sizeof(buf), true); + Curl_cipher_suite_get_str(test->id, buf, sizeof(buf), TRUE); if(expect && strcmp(buf, expect) != 0) { curl_mfprintf(stderr, "Curl_cipher_suite_get_str FAILED for 0x%04x, " @@ -555,7 +555,7 @@ static CURLcode test_unit3205(const char *arg) buf[0] = '\0'; expect = test->openssl ? test->openssl : test->rfc; - Curl_cipher_suite_get_str(test->id, buf, sizeof(buf), false); + Curl_cipher_suite_get_str(test->id, buf, sizeof(buf), FALSE); /* suites matched by EDH alias will return the DHE name */ if(test->id >= 0x0011 && test->id < 0x0017) { From 056dcd9e71fcdce8b123de7ac4f43fafb1ccadcb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 12:46:17 +0200 Subject: [PATCH 0353/1062] pytest: use `Optional[]`, adjust whitespace Reported by GitHub Code Quality Closes #21928 --- tests/http/testenv/curl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 45fa899ad243..3071d7dd4aa9 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -188,7 +188,7 @@ def __init__(self, env, run_dir): self._stdoutfile = os.path.join(self._run_dir, 'tcpdump.out') self._stderrfile = os.path.join(self._run_dir, 'tcpdump.err') - def get_rsts(self, ports: List[int]|None = None) -> Optional[List[str]]: + def get_rsts(self, ports: Optional[List[int]] = None) -> Optional[List[str]]: if self._proc: raise Exception('tcpdump still running') lines = [] @@ -725,7 +725,7 @@ def http_download(self, urls: List[str], no_save: bool = False, limit_rate: Optional[str] = None, extra_args: Optional[List[str]] = None, - url_options: Optional[Dict[str,List[str]]] = None): + url_options: Optional[Dict[str, List[str]]] = None): if extra_args is None: extra_args = [] if no_save: From cb4b3e75e808c6527e85d31a64a8ded421d85279 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 12:58:24 +0200 Subject: [PATCH 0354/1062] smbserver: check impacket presence differently To silence ruff and GitHub CodeQL warnings. Closes #21929 --- tests/smbserver.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/smbserver.py b/tests/smbserver.py index 9b38c4695a73..c3eeb1105c7d 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -25,6 +25,7 @@ import argparse import configparser +import importlib.util import logging import os import signal @@ -36,9 +37,7 @@ from util import ClosingFileHandler, TestData # impacket needs to be installed in the Python environment -try: - import impacket # noqa: F401 -except ImportError: +if importlib.util.find_spec('impacket') is None: sys.stderr.write( 'Warning: Python package impacket is required for smb testing; ' 'use pip or your package manager to install it\n') From c7cba2fd2dfe7826484d99ead7c5fd925aa3d56f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 11:40:41 +0200 Subject: [PATCH 0355/1062] sigv4: URL encode the user name in the header - split into sub functions - add 'aws-sigv4' as keyword for many tests Verify with test 3222 Reported-by: Trail of Bits Closes #21923 --- lib/http_aws_sigv4.c | 431 ++++++++++++++++++++++++++--------------- tests/data/Makefile.am | 2 +- tests/data/test1933 | 1 + tests/data/test1934 | 1 + tests/data/test1935 | 1 + tests/data/test1936 | 1 + tests/data/test1937 | 1 + tests/data/test1938 | 1 + tests/data/test1955 | 1 + tests/data/test1956 | 1 + tests/data/test1957 | 1 + tests/data/test1959 | 1 + tests/data/test1970 | 1 + tests/data/test1971 | 1 + tests/data/test1972 | 1 + tests/data/test1973 | 1 + tests/data/test1974 | 1 + tests/data/test1975 | 1 + tests/data/test1976 | 1 + tests/data/test1978 | 1 + tests/data/test3222 | 57 ++++++ 21 files changed, 349 insertions(+), 159 deletions(-) create mode 100644 tests/data/test3222 diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 308970bb89d2..7a61bfb729c0 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -817,67 +817,14 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) return result; } -CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +static CURLcode parse_sigv4_params(struct Curl_easy *data, + const char *hostname, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *region, + struct Curl_str *service) { - CURLcode result = CURLE_OUT_OF_MEMORY; - struct connectdata *conn = data->conn; - const char *line; - struct Curl_str provider0; - struct Curl_str provider1; - struct Curl_str region = { NULL, 0 }; - struct Curl_str service = { NULL, 0 }; - const char *hostname = conn->origin->hostname; - time_t clock; - struct tm tm; - char timestamp[TIMESTAMP_SIZE]; - char date[9]; - struct dynbuf canonical_headers; - struct dynbuf signed_headers; - struct dynbuf canonical_query; - struct dynbuf canonical_path; - char *date_header = NULL; - Curl_HttpReq httpreq; - const char *method = NULL; - const char *payload_hash = NULL; - size_t payload_hash_len = 0; - unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; - char sha_hex[SHA256_HEX_LENGTH]; - char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ - char *canonical_request = NULL; - char *request_type = NULL; - char *credential_scope = NULL; - char *str_to_sign = NULL; - const char *user = Curl_creds_user(data->state.creds); - const char *passwd = Curl_creds_passwd(data->state.creds); - char *secret = NULL; - unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - char *auth_headers = NULL; - - if(data->set.path_as_is) { - failf(data, "Cannot use sigv4 authentication with path-as-is flag"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - if(Curl_checkheaders(data, STRCONST("Authorization"))) { - /* Authorization already present, Bailing out */ - return CURLE_OK; - } - - /* we init those buffers here, so goto fail will free initialized dynbuf */ - curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); - - /* - * Parameters parsing - * Google and Outscale use the same OSC or GOOG, - * but Amazon uses AWS and AMZ for header arguments. - * AWS is the default because most of non-amazon providers - * are still using aws:amz as a prefix. - */ - line = data->set.str[STRING_AWS_SIGV4]; + const char *line = data->set.str[STRING_AWS_SIGV4]; if(!line || !*line) line = "aws:amz"; @@ -885,71 +832,89 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) No string can be longer than N bytes of non-whitespace */ - if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) { + if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) { failf(data, "first aws-sigv4 provider cannot be empty"); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto fail; + return CURLE_BAD_FUNCTION_ARGUMENT; } if(curlx_str_single(&line, ':') || - curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) { - provider1 = provider0; + curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) { + *provider1 = *provider0; } else if(curlx_str_single(&line, ':') || - curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') || + curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') || curlx_str_single(&line, ':') || - curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) { + curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) { /* nothing to do */ } - if(!curlx_strlen(&service)) { + if(!curlx_strlen(service)) { const char *p = hostname; - if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') || + if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: service missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked service %.*s from host", - (int)curlx_strlen(&service), curlx_str(&service)); + (int)curlx_strlen(service), curlx_str(service)); - if(!curlx_strlen(®ion)) { - if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') || + if(!curlx_strlen(region)) { + if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: region missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked region %.*s from host", - (int)curlx_strlen(®ion), curlx_str(®ion)); + (int)curlx_strlen(region), curlx_str(region)); } } - Curl_http_method(data, &method, &httpreq); + return CURLE_OK; +} - payload_hash = - parse_content_sha_hdr(data, curlx_str(&provider1), - curlx_strlen(&provider1), &payload_hash_len); +static CURLcode get_payload_hash(struct Curl_easy *data, + Curl_HttpReq httpreq, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *service, + unsigned char *sha_hash, + char *sha_hex, + char *content_sha256_hdr, + const char **payload_hash_out, + size_t *payload_hash_len_out) +{ + *payload_hash_out = + parse_content_sha_hdr(data, curlx_str(provider1), + curlx_strlen(provider1), payload_hash_len_out); - if(!payload_hash) { + if(!*payload_hash_out) { + CURLcode result; /* AWS S3 requires a x-amz-content-sha256 header, and supports special * values like UNSIGNED-PAYLOAD */ - bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") && - curlx_str_casecompare(&service, "s3"); + bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") && + curlx_str_casecompare(service, "s3"); if(sign_as_s3) - result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1), - curlx_strlen(&provider1), sha_hash, + result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1), + curlx_strlen(provider1), sha_hash, sha_hex, content_sha256_hdr); else result = calc_payload_hash(data, sha_hash, sha_hex); if(result) - goto fail; + return result; - payload_hash = sha_hex; + *payload_hash_out = sha_hex; /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ - payload_hash_len = strlen(sha_hex); + *payload_hash_len_out = strlen(sha_hex); } + return CURLE_OK; +} + +static CURLcode get_timestamp(char *timestamp, size_t stampsize) +{ + time_t clock; + struct tm tm; + CURLcode result; #ifdef DEBUGBUILD { @@ -963,43 +928,54 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) clock = time(NULL); #endif result = curlx_gmtime(clock, &tm); - if(result) { - goto fail; - } - if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + if(result) + return result; + + if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static CURLcode make_canonical_request(struct Curl_easy *data, + const char *hostname, + char *timestamp, + struct Curl_str *provider1, + struct Curl_str *service, + const char *method, + const char *payload_hash, + size_t payload_hash_len, + char **date_header_out, + char *content_sha256_hdr, + struct dynbuf *canonical_headers, + struct dynbuf *signed_headers, + char **canonical_request_out) +{ + struct dynbuf canonical_query; + struct dynbuf canonical_path; + CURLcode result; + + curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); result = make_headers(data, hostname, timestamp, - curlx_str(&provider1), curlx_strlen(&provider1), - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); + curlx_str(provider1), curlx_strlen(provider1), + date_header_out, content_sha256_hdr, + canonical_headers, signed_headers); if(result) goto fail; - if(*content_sha256_hdr) { - /* make_headers() needed this without the \r\n for canonicalization */ - size_t hdrlen = strlen(content_sha256_hdr); - DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); - memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); - } - - memcpy(date, timestamp, sizeof(date)); - date[sizeof(date) - 1] = 0; - result = canon_query(data->state.up.query, &canonical_query); if(result) goto fail; result = canon_path(data->state.up.path, strlen(data->state.up.path), &canonical_path, - should_urlencode(&service)); + should_urlencode(service)); if(result) goto fail; - result = CURLE_OUT_OF_MEMORY; - canonical_request = + *canonical_request_out = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ "%s\n" /* CanonicalQueryString */ @@ -1010,37 +986,65 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) curlx_dyn_ptr(&canonical_path), curlx_dyn_ptr(&canonical_query) ? curlx_dyn_ptr(&canonical_query) : "", - curlx_dyn_ptr(&canonical_headers), - curlx_dyn_ptr(&signed_headers), + curlx_dyn_ptr(canonical_headers), + curlx_dyn_ptr(signed_headers), (int)payload_hash_len, payload_hash); - if(!canonical_request) + if(!*canonical_request_out) { + result = CURLE_OUT_OF_MEMORY; goto fail; + } + + result = CURLE_OK; +fail: + curlx_dyn_free(&canonical_query); + curlx_dyn_free(&canonical_path); + return result; +} - infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]", - canonical_request); +static CURLcode make_string_to_sign(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *date, + const char *timestamp, + const char *canonical_request, + char **request_type_out, + char **credential_scope_out, + char **str_to_sign_out) +{ + char *request_type; + char *credential_scope; + char *str_to_sign; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; request_type = curl_maprintf("%.*s4_request", - (int)curlx_strlen(&provider0), - curlx_str(&provider0)); + (int)curlx_strlen(provider0), + curlx_str(provider0)); if(!request_type) - goto fail; + return CURLE_OUT_OF_MEMORY; /* provider0 is lowercased *after* curl_maprintf() so that the buffer can be written to */ - Curl_strntolower(request_type, request_type, curlx_strlen(&provider0)); + Curl_strntolower(request_type, request_type, curlx_strlen(provider0)); credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date, - (int)curlx_strlen(®ion), - curlx_str(®ion), - (int)curlx_strlen(&service), - curlx_str(&service), + (int)curlx_strlen(region), + curlx_str(region), + (int)curlx_strlen(service), + curlx_str(service), request_type); - if(!credential_scope) - goto fail; + if(!credential_scope) { + curlx_free(request_type); + return CURLE_OUT_OF_MEMORY; + } - if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request, - strlen(canonical_request))) - goto fail; + if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request, + strlen(canonical_request))) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } sha256_to_hex(sha_hex, sha_hash); @@ -1052,35 +1056,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "%s\n" /* RequestDateTime */ "%s\n" /* CredentialScope */ "%s", /* HashedCanonicalRequest in hex */ - (int)curlx_strlen(&provider0), - curlx_str(&provider0), + (int)curlx_strlen(provider0), + curlx_str(provider0), timestamp, credential_scope, sha_hex); - if(!str_to_sign) - goto fail; + if(!str_to_sign) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } /* make provider0 part done uppercase */ - Curl_strntoupper(str_to_sign, curlx_str(&provider0), - curlx_strlen(&provider0)); + Curl_strntoupper(str_to_sign, curlx_str(provider0), + curlx_strlen(provider0)); infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]", str_to_sign); - secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), passwd); + *request_type_out = request_type; + *credential_scope_out = credential_scope; + *str_to_sign_out = str_to_sign; + return CURLE_OK; +} + +static CURLcode sign_and_set_auth_headers(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *request_type, + const char *credential_scope, + const char *date, + const char *str_to_sign, + const char *date_header, + const char *content_sha256_hdr, + struct dynbuf *signed_headers) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + const char *passwd = Curl_creds_passwd(data->state.creds); + char *secret = NULL; + unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + char sha_hex[SHA256_HEX_LENGTH]; + char *auth_headers = NULL; + char *user = curl_escape(Curl_creds_user(data->state.creds), 0); + if(!user) + return CURLE_OUT_OF_MEMORY; + + secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0), + curlx_str(provider0), passwd); if(!secret) goto fail; /* make provider0 part done uppercase */ - Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0)); + Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0)); HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); HMAC_SHA256(sign0, sizeof(sign0), - curlx_str(®ion), curlx_strlen(®ion), sign1); + curlx_str(region), curlx_strlen(region), sign1); + HMAC_SHA256(sign1, sizeof(sign1), + curlx_str(service), curlx_strlen(service), sign0); + HMAC_SHA256(sign0, sizeof(sign0), + request_type, strlen(request_type), sign1); HMAC_SHA256(sign1, sizeof(sign1), - curlx_str(&service), curlx_strlen(&service), sign0); - HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); - HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); + str_to_sign, strlen(str_to_sign), sign0); sha256_to_hex(sha_hex, sign0); @@ -1090,27 +1128,28 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" + "%s" + "%s%s", + (int)curlx_strlen(provider0), + curlx_str(provider0), + user, + credential_scope, + curlx_dyn_ptr(signed_headers), + sha_hex, /* * date_header is added here, only if it was not * user-specified (using CURLOPT_HTTPHEADER). * date_header includes \r\n */ - "%s" - "%s", /* optional sha256 header includes \r\n */ - (int)curlx_strlen(&provider0), - curlx_str(&provider0), - user, - credential_scope, - curlx_dyn_ptr(&signed_headers), - sha_hex, date_header ? date_header : "", - content_sha256_hdr); - if(!auth_headers) { + content_sha256_hdr, + content_sha256_hdr[0] ? "\r\n": ""); + if(!auth_headers) goto fail; - } + /* provider 0 uppercase */ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], - curlx_str(&provider0), curlx_strlen(&provider0)); + curlx_str(provider0), curlx_strlen(provider0)); curlx_free(data->req.hd_auth); data->req.hd_auth = auth_headers; @@ -1118,15 +1157,91 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) result = CURLE_OK; fail: - curlx_dyn_free(&canonical_query); - curlx_dyn_free(&canonical_path); + curlx_free(user); + curlx_free(secret); + return result; +} + +CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + struct connectdata *conn = data->conn; + struct Curl_str provider0 = { NULL, 0 }; + struct Curl_str provider1 = { NULL, 0 }; + struct Curl_str region = { NULL, 0 }; + struct Curl_str service = { NULL, 0 }; + const char *hostname = conn->origin->hostname; + char timestamp[TIMESTAMP_SIZE]; + char date[9]; + struct dynbuf canonical_headers; + struct dynbuf signed_headers; + char *date_header = NULL; + Curl_HttpReq httpreq; + const char *method = NULL; + const char *payload_hash = NULL; + size_t payload_hash_len = 0; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; + char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ + char *canonical_request = NULL; + char *request_type = NULL; + char *credential_scope = NULL; + char *str_to_sign = NULL; + + if(data->set.path_as_is) { + failf(data, "Cannot use sigv4 authentication with path-as-is flag"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(Curl_checkheaders(data, STRCONST("Authorization"))) + /* Authorization already present, Bailing out */ + return CURLE_OK; + + /* we init those buffers here, so goto fail will free initialized dynbuf */ + curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); + + result = parse_sigv4_params(data, hostname, &provider0, &provider1, + ®ion, &service); + if(!result) { + Curl_http_method(data, &method, &httpreq); + result = get_payload_hash(data, httpreq, &provider0, &provider1, &service, + sha_hash, sha_hex, content_sha256_hdr, + &payload_hash, &payload_hash_len); + } + + if(!result) + result = get_timestamp(timestamp, sizeof(timestamp)); + + if(!result) + result = make_canonical_request(data, hostname, timestamp, + &provider1, &service, + method, payload_hash, payload_hash_len, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers, + &canonical_request); + if(!result) { + /* the timestamp might have been updated in make_canonical_request */ + memcpy(date, timestamp, sizeof(date) - 1); + date[sizeof(date) - 1] = 0; + + result = make_string_to_sign(data, &provider0, ®ion, &service, + date, timestamp, canonical_request, + &request_type, &credential_scope, + &str_to_sign); + } + if(!result) + result = sign_and_set_auth_headers(data, &provider0, ®ion, &service, + request_type, credential_scope, + date, str_to_sign, date_header, + content_sha256_hdr, &signed_headers); + curlx_dyn_free(&canonical_headers); curlx_dyn_free(&signed_headers); curlx_free(canonical_request); curlx_free(request_type); curlx_free(credential_scope); curlx_free(str_to_sign); - curlx_free(secret); curlx_free(date_header); return result; } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 393f7531bdb2..413f7a1143cf 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -281,7 +281,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ -test3216 test3217 test3218 test3219 test3220 test3221 \ +test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ \ test3300 test3301 test3302 test3303 test3304 test3305 \ \ diff --git a/tests/data/test1933 b/tests/data/test1933 index e1ef8b95ba13..cda12d19a121 100644 --- a/tests/data/test1933 +++ b/tests/data/test1933 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1934 b/tests/data/test1934 index 2128b299c2eb..9c52a0d0bddd 100644 --- a/tests/data/test1934 +++ b/tests/data/test1934 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1935 b/tests/data/test1935 index 158620a3bd29..f92c7e8ea2d3 100644 --- a/tests/data/test1935 +++ b/tests/data/test1935 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1936 b/tests/data/test1936 index 330fccc75650..e43eb0be6916 100644 --- a/tests/data/test1936 +++ b/tests/data/test1936 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1937 b/tests/data/test1937 index 19049410be98..2043053e59db 100644 --- a/tests/data/test1937 +++ b/tests/data/test1937 @@ -5,6 +5,7 @@ HTTP HTTP POST CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1938 b/tests/data/test1938 index 91e27f876833..a2ea20fc9662 100644 --- a/tests/data/test1938 +++ b/tests/data/test1938 @@ -5,6 +5,7 @@ HTTP HTTP POST CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1955 b/tests/data/test1955 index 7b84f746bd04..82349c25395b 100644 --- a/tests/data/test1955 +++ b/tests/data/test1955 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1956 b/tests/data/test1956 index 3e8a9f9ce449..f3ad8b859a14 100644 --- a/tests/data/test1956 +++ b/tests/data/test1956 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1957 b/tests/data/test1957 index 181eabb9f5af..77b48b238897 100644 --- a/tests/data/test1957 +++ b/tests/data/test1957 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1959 b/tests/data/test1959 index 7f0357da8f4b..27a778c305ac 100644 --- a/tests/data/test1959 +++ b/tests/data/test1959 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1970 b/tests/data/test1970 index e697cabfeec6..8dae28c75339 100644 --- a/tests/data/test1970 +++ b/tests/data/test1970 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1971 b/tests/data/test1971 index bc44cd3eaa82..c303018c72df 100644 --- a/tests/data/test1971 +++ b/tests/data/test1971 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1972 b/tests/data/test1972 index 7de801da7e97..d128dccf452c 100644 --- a/tests/data/test1972 +++ b/tests/data/test1972 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1973 b/tests/data/test1973 index 896631f1514d..c2527df686f4 100644 --- a/tests/data/test1973 +++ b/tests/data/test1973 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1974 b/tests/data/test1974 index 6a99684d8855..20a10a87056d 100644 --- a/tests/data/test1974 +++ b/tests/data/test1974 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1975 b/tests/data/test1975 index a4d1a7f0f750..e5d6272eda0b 100644 --- a/tests/data/test1975 +++ b/tests/data/test1975 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1976 b/tests/data/test1976 index 5a0ff39dc086..3a09cc2ca31e 100644 --- a/tests/data/test1976 +++ b/tests/data/test1976 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1978 b/tests/data/test1978 index 3ad4c670e2be..b2a4285b96ae 100644 --- a/tests/data/test1978 +++ b/tests/data/test1978 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test3222 b/tests/data/test3222 new file mode 100644 index 000000000000..cf6caa157f80 --- /dev/null +++ b/tests/data/test3222 @@ -0,0 +1,57 @@ + + + + +HTTP +aws-sigv4 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +Debug +aws + + +aws-sigv4 with CRLF in username + + +"http://user%0d%0a:secret@fake.fake.fake:8000/" --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: fake.fake.fake:8000 +Authorization: AWS4-HMAC-SHA256 Credential=user%0D%0A/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=e5747e9555c0e96f1067cc4bf9f6055e72a185178e5dd0c2909279ec1d66360b +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* + + + + From 62b118cf22d88d8d3962d41fd6757e635c70b166 Mon Sep 17 00:00:00 2001 From: alhudz Date: Tue, 9 Jun 2026 16:26:14 +0530 Subject: [PATCH 0356/1062] http-proxy: verify CONNECT response headers Verifed by test 2107 Closes #21927 --- lib/cf-h1-proxy.c | 6 ++++++ tests/data/Makefile.am | 2 +- tests/data/test2107 | 49 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2107 diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index f7974a99319d..21ac6da29369 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -476,6 +476,12 @@ static CURLcode single_header(struct Curl_cfilter *cf, /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); + /* a CONNECT response line is handed to the client as a header, so it must + pass the same checks as a regular response header before delivery */ + result = Curl_verify_header(data, linep, line_len); + if(result) + return result; + /* send the header to the callback */ writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 413f7a1143cf..a7c293cb20c1 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -253,7 +253,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ -test2100 test2101 test2102 test2103 test2104 test2105 test2106 \ +test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ \ diff --git a/tests/data/test2107 b/tests/data/test2107 new file mode 100644 index 000000000000..8c4404410997 --- /dev/null +++ b/tests/data/test2107 @@ -0,0 +1,49 @@ + + + + +HTTP +HTTP CONNECT +HTTP proxy +proxytunnel + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 0 + + + +HTTP/1.1 200 OK%CR +X-Evil: he%hex[%00]hex%llo%CR +%CR + + + +# Client-side + + +http +http-proxy + + +proxy + + +HTTP CONNECT response with a nul byte in a header + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -p -x http://%HOSTIP:%PROXYPORT + + + +# Verify data after the test has been "shot" + + +8 + + + From e37417e0213c4a5a226c20859720b8aba8dac03f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 14:02:32 +0200 Subject: [PATCH 0357/1062] psl: require libpsl 0.16.0 (2016-12-10) or greater Debian Stretch offers 0.17.0. Ref: https://github.com/rockdaboot/libpsl/releases/tag/libpsl-0.16.0 Ref: https://sources.debian.org/src/libpsl/ Closes #21933 --- docs/INTERNALS.md | 1 + lib/psl.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index 77f2e4373576..ee32a2dbfd13 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -31,6 +31,7 @@ We aim to support these or later versions. - GnuTLS 3.6.5 (2018-12-01) - libidn2 2.0.0 (2017-03-29) - libgsasl 1.6.0 (2010-12-14) +- libpsl 0.16.0 (2016-12-10) - LibreSSL 2.9.1 (2019-04-22) - libssh 0.9.0 (2019-06-28) - libssh2 1.9.0 (2019-06-20) diff --git a/lib/psl.c b/lib/psl.c index e2488aea2215..b9ce47d0a7e0 100644 --- a/lib/psl.c +++ b/lib/psl.c @@ -29,6 +29,10 @@ #include "progress.h" #include "curl_share.h" +#if !defined(PSL_VERSION_NUMBER) || PSL_VERSION_NUMBER < 0x001000 +#error "libpsl 0.16.0 or greater required" +#endif + void Curl_psl_destroy(struct PslCache *pslcache) { if(pslcache->psl) { @@ -65,7 +69,6 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) bool dynamic = FALSE; time_t expires = TIME_T_MAX; -#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000 psl = psl_latest(NULL); dynamic = psl != NULL; /* Take care of possible time computation overflow. */ @@ -74,8 +77,6 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) /* Only get the built-in PSL if we do not already have the "latest". */ if(!psl && !pslcache->dynamic) -#endif - psl = psl_builtin(); if(psl) { From d8c97b021b8b23be4dd5baf0ebade5823de01c21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:06:35 +0000 Subject: [PATCH 0358/1062] GHA: update dependency openssl/openssl to v4.0.1 Closes #21934 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 7a79fab4baf3..1d404b9b1424 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -47,7 +47,7 @@ env: # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.0 + OPENSSL_VERSION: 4.0.1 # manually bumped OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c8e5fe05ac81..df5c20196d88 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -52,7 +52,7 @@ env: # handled in renovate.json OPENLDAP_VERSION: 2.6.10 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.0 + OPENSSL_VERSION: 4.0.1 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com RUSTLS_VERSION: 0.15.3 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com From 59213abfb2fdac9936595330c37e722dd952b01b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 13:38:17 +0200 Subject: [PATCH 0359/1062] tidy-up: drop redundant `!= NULL` syntax Where missed by checksrc. Closes #21932 --- docs/examples/crawler.c | 2 +- lib/asyn-ares.c | 2 +- lib/cfilters.c | 2 +- lib/curl_addrinfo.c | 6 +++--- lib/curl_sha512_256.c | 4 ++-- lib/fake_addrinfo.c | 2 +- lib/formdata.c | 4 ++-- lib/http2.c | 6 +++--- lib/if2ip.c | 2 +- lib/ldap.c | 2 +- lib/memdebug.c | 6 +++--- lib/openldap.c | 4 ++-- lib/psl.c | 2 +- lib/transfer.c | 2 +- lib/urlapi.c | 3 +-- lib/vauth/vauth.c | 2 +- lib/version.c | 2 +- lib/vquic/cf-ngtcp2.c | 2 +- lib/vssh/libssh2.c | 14 +++++++------- lib/vtls/keylog.c | 2 +- lib/vtls/schannel.c | 6 +++--- lib/vtls/schannel_verify.c | 2 +- lib/vtls/wolfssl.c | 4 ++-- src/tool_doswin.c | 2 +- src/tool_vms.c | 2 +- tests/libtest/lib2302.c | 2 +- tests/libtest/lib2700.c | 4 ++-- tests/unit/unit1396.c | 6 +++--- tests/unit/unit1602.c | 4 ++-- tests/unit/unit1616.c | 4 ++-- tests/unit/unit1676.c | 6 +++--- tests/unit/unit3200.c | 4 ++-- 32 files changed, 58 insertions(+), 59 deletions(-) diff --git a/docs/examples/crawler.c b/docs/examples/crawler.c index 04d816320435..21cdb5fcff4c 100644 --- a/docs/examples/crawler.c +++ b/docs/examples/crawler.c @@ -173,7 +173,7 @@ static size_t follow_links(CURLM *multi, struct memory *mem, const char *url) static int is_html(const char *ctype) { - return ctype != NULL && strlen(ctype) > 10 && strstr(ctype, "text/html"); + return ctype && strlen(ctype) > 10 && strstr(ctype, "text/html"); } int main(void) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 4b6dd02182cc..4685cdd3b758 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -489,7 +489,7 @@ static struct Curl_addrinfo *async_ares_node2addr( struct Curl_addrinfo *calast = NULL; int error = 0; - for(ai = node; ai != NULL; ai = ai->ai_next) { + for(ai = node; ai; ai = ai->ai_next) { size_t ss_size; struct Curl_addrinfo *ca; /* ignore elements with unsupported address family, diff --git a/lib/cfilters.c b/lib/cfilters.c index b2ad4a03cb94..3c97a12a4d59 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -625,7 +625,7 @@ bool Curl_conn_is_setup(struct connectdata *conn, int sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; - return (conn->cfilter[sockindex] != NULL); + return !!conn->cfilter[sockindex]; } bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 901727541f15..a927d44c9923 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -114,7 +114,7 @@ int Curl_getaddrinfo_ex(const char *nodename, /* traverse the addrinfo list */ - for(ai = aihead; ai != NULL; ai = ai->ai_next) { + for(ai = aihead; ai; ai = ai->ai_next) { size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; /* ignore elements with unsupported address family, settle family-specific sockaddr structure size. */ @@ -257,7 +257,7 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) /* no input == no output! */ return NULL; - DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); + DEBUGASSERT(he->h_name && he->h_addr_list); for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; @@ -613,7 +613,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) #ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif - for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { + for(ca = addrinfo; ca; ca = ca->ai_next) { switch(ca->ai_family) { case AF_INET: addr = (void *)ca->ai_addr; /* storage area for this info */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index eb8bc66fc8e0..c429bba8f268 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -262,7 +262,7 @@ static CURLcode Curl_sha512_256_update(void *context, { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); sha512_256_update(ctx, length, (const uint8_t *)data); @@ -645,7 +645,7 @@ static CURLcode Curl_sha512_256_update(void *context, /* the void pointer here is required to mute Intel compiler warning */ void * const ctx_buf = ctx->buffer; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); if(length == 0) return CURLE_OK; /* Shortcut, do nothing */ diff --git a/lib/fake_addrinfo.c b/lib/fake_addrinfo.c index 5a89202064de..6b04b5d2f560 100644 --- a/lib/fake_addrinfo.c +++ b/lib/fake_addrinfo.c @@ -64,7 +64,7 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) const char *name = aihead->name; /* traverse the addrinfo list */ - for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) { + for(ai = aihead->nodes; ai; ai = ai->ai_next) { size_t ss_size; size_t namelen = name ? strlen(name) + 1 : 0; /* ignore elements with unsupported address family, diff --git a/lib/formdata.c b/lib/formdata.c index 3619c15bb121..ccfe1aa09d42 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -154,7 +154,7 @@ static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent) static void free_formlist(struct FormInfo *ptr) { - for(; ptr != NULL; ptr = ptr->more) { + for(; ptr; ptr = ptr->more) { Curl_bufref_free(&ptr->name); Curl_bufref_free(&ptr->value); Curl_bufref_free(&ptr->contenttype); @@ -223,7 +223,7 @@ static CURLFORMcode FormAddCheck(struct FormInfo *first_form, /* go through the list, check for completeness and if everything is * alright add the HttpPost item otherwise set retval accordingly */ - for(form = first_form; form != NULL; form = form->more) { + for(form = first_form; form; form = form->more) { const char *name = Curl_bufref_ptr(&form->name); if(((!name || !Curl_bufref_ptr(&form->value)) && !post) || diff --git a/lib/http2.c b/lib/http2.c index 736c04e10095..846222216ac6 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -215,8 +215,8 @@ static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) } static size_t populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data, - struct cf_h2_ctx *ctx) + struct Curl_easy *data, + struct cf_h2_ctx *ctx) { iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = Curl_multi_max_concurrent_streams(data->multi); @@ -226,7 +226,7 @@ static size_t populate_settings(nghttp2_settings_entry *iv, if(ctx) ctx->initial_win_size = iv[1].value; iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; + iv[2].value = !!data->multi->push_cb; return 3; } diff --git a/lib/if2ip.c b/lib/if2ip.c index b71254ada00e..fd34f204b5be 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -107,7 +107,7 @@ if2ip_result_t Curl_if2ip(int af, #endif if(getifaddrs(&head) >= 0) { - for(iface = head; iface != NULL; iface = iface->ifa_next) { + for(iface = head; iface; iface = iface->ifa_next) { if(iface->ifa_addr) { if(iface->ifa_addr->sa_family == af) { if(curl_strequal(iface->ifa_name, interf)) { diff --git a/lib/ldap.c b/lib/ldap.c index d8ec859126c5..d61ba8b5d1ce 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -488,7 +488,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) vals = ldap_get_values_len(server, entryIterator, attribute); if(vals) { - for(i = 0; (vals[i] != NULL); i++) { + for(i = 0; vals[i]; i++) { result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1); if(result) { ldap_value_free_len(vals); diff --git a/lib/memdebug.c b/lib/memdebug.c index 6eda7a8236ba..b6af2c6d3528 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -269,7 +269,7 @@ char *curl_dbg_strdup(const char *str, int line, const char *source) char *mem; size_t len; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("strdup", line, source)) return NULL; @@ -294,7 +294,7 @@ wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) wchar_t *mem; size_t wsiz, bsiz; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("wcsdup", line, source)) return NULL; @@ -510,7 +510,7 @@ int curl_dbg_fclose(FILE *file, int line, const char *source) { int res; - DEBUGASSERT(file != NULL); + DEBUGASSERT(file); if(source) curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); diff --git a/lib/openldap.c b/lib/openldap.c index 2696fcdc52e8..58b31b32af78 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -499,7 +499,7 @@ static Sockbuf_IO ldapsb_tls = { static bool ssl_installed(struct connectdata *conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - return li && li->recv != NULL; + return li && li->recv; } static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) @@ -1177,7 +1177,7 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, binary = bv.bv_len > 7 && curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7); - for(i = 0; bvals[i].bv_val != NULL; i++) { + for(i = 0; bvals[i].bv_val; i++) { bool binval = FALSE; result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, diff --git a/lib/psl.c b/lib/psl.c index b9ce47d0a7e0..195841f3a3f4 100644 --- a/lib/psl.c +++ b/lib/psl.c @@ -70,7 +70,7 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) time_t expires = TIME_T_MAX; psl = psl_latest(NULL); - dynamic = psl != NULL; + dynamic = !!psl; /* Take care of possible time computation overflow. */ expires = (now_sec < TIME_T_MAX - PSL_TTL) ? (now_sec + PSL_TTL) : TIME_T_MAX; diff --git a/lib/transfer.c b/lib/transfer.c index 49930518eeac..a903f6438f86 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -678,7 +678,7 @@ static void xfer_setup( struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; - DEBUGASSERT(conn != NULL); + DEBUGASSERT(conn); /* indexes are in range */ DEBUGASSERT((send_idx <= 1) && (send_idx >= -1)); DEBUGASSERT((recv_idx <= 1) && (recv_idx >= -1)); diff --git a/lib/urlapi.c b/lib/urlapi.c index b96f8ba19bde..7a926debb6d5 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1171,8 +1171,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* this pathlen also contains the query and the fragment */ pathlen = urllen - (path - url); if(hostlen) { - ures = parse_authority(u, hostp, hostlen, flags, &host, - u->scheme != NULL); + ures = parse_authority(u, hostp, hostlen, flags, &host, !!u->scheme); if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) ures = guess_scheme(u, &host); } diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index 1bd3575af9b1..e258b44c55d2 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -120,7 +120,7 @@ bool Curl_auth_user_contains_domain(struct Curl_creds *creds) /* Check we have a domain name or UPN present */ const char *p = strpbrk(creds->user, "\\/@"); - valid = (p != NULL) && (p > creds->user) && + valid = p && (p > creds->user) && (p < (creds->user + strlen(creds->user) - 1)); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) diff --git a/lib/version.c b/lib/version.c index 6522f0951e18..299caee9fb7d 100644 --- a/lib/version.c +++ b/lib/version.c @@ -388,7 +388,7 @@ static int idn_present(curl_version_info_data *info) (void)info; return TRUE; #else - return info->libidn != NULL; + return !!info->libidn; #endif } #endif diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index ad0c9e582f68..3d1c8a15f82e 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -2528,7 +2528,7 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - DEBUGASSERT(cf != NULL); + DEBUGASSERT(cf); if(cf && session) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index bc313b122765..027ef71b9919 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -479,9 +479,9 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; infof(data, "SSH MD5 public key: %s", - pubkey_md5 != NULL ? pubkey_md5 : "NULL"); + pubkey_md5 ? pubkey_md5 : "NULL"); infof(data, "SSH SHA256 public key: %s", - pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); + pubkey_sha256 ? pubkey_sha256 : "NULL"); if(pubkey_sha256) { const char *fingerprint = NULL; @@ -1098,7 +1098,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, sshc->authed = FALSE; if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { bool out_of_memory = FALSE; sshc->rsa_pub = sshc->rsa = NULL; @@ -1587,7 +1587,7 @@ static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && - (strstr(sshc->authlist, "password") != NULL)) { + strstr(sshc->authlist, "password")) { myssh_to(data, sshc, SSH_AUTH_PASS); } else { @@ -1626,7 +1626,7 @@ static CURLcode ssh_state_auth_host_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && - (strstr(sshc->authlist, "hostbased") != NULL)) { + strstr(sshc->authlist, "hostbased")) { myssh_to(data, sshc, SSH_AUTH_HOST); } else { @@ -1640,7 +1640,7 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, { int rc = 0; if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { /* Connect to the ssh-agent */ /* The agent could be shared by a curl thread i believe @@ -1736,7 +1736,7 @@ static CURLcode ssh_state_auth_key_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) && - (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { + strstr(sshc->authlist, "keyboard-interactive")) { myssh_to(data, sshc, SSH_AUTH_KEY); } else { diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 23c74de04f5f..094bf69db728 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -66,7 +66,7 @@ void Curl_tls_keylog_close(void) bool Curl_tls_keylog_enabled(void) { - return keylog_file_fp != NULL; + return !!keylog_file_fp; } const char *Curl_tls_keylog_file_name(void) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 3782593c8ba7..c0b46c58c772 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -379,7 +379,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, FILE *fInCert = NULL; void *certdata = NULL; size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; + bool blob = !!data->set.ssl.primary.cert_blob; if(blob) { certdata = data->set.ssl.primary.cert_blob->data; @@ -1491,9 +1491,9 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, static bool valid_cert_encoding(const CERT_CONTEXT *cert_context) { - return (cert_context != NULL) && + return cert_context && ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (cert_context->pbCertEncoded != NULL) && + cert_context->pbCertEncoded && (cert_context->cbCertEncoded > 0); } diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 1aa75fd84cfb..38be1dcdc01c 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -353,7 +353,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, if(!alt_name_info) return 0; - compute_content = host_names != NULL && length != 0; + compute_content = host_names && length != 0; /* Initialize default return values. */ actual_length = 1; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 836cd2688aa7..c7d86a810161 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -480,7 +480,7 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) struct Curl_cfilter *cf; cf = (struct Curl_cfilter *)wolfSSL_get_app_data(ssl); - DEBUGASSERT(cf != NULL); + DEBUGASSERT(cf); if(cf && session) { struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); @@ -1153,7 +1153,7 @@ static CURLcode wssl_init_curves(struct Curl_easy *data, if(curves) { #ifdef WOLFSSL_HAVE_KYBER size_t idx; - for(idx = 0; gnm[idx].name != NULL; idx++) { + for(idx = 0; gnm[idx].name; idx++) { if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) { *out_pqkem = gnm[idx].group; break; diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 577270cdb078..2864c47deaa3 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -770,7 +770,7 @@ curl_socket_t win32_stdin_read_thread(void) static curl_socket_t socket_r = CURL_SOCKET_BAD; if(socket_r != CURL_SOCKET_BAD) { - assert(stdin_thread != NULL); + assert(stdin_thread); return socket_r; } assert(stdin_thread == NULL); diff --git a/src/tool_vms.c b/src/tool_vms.c index 74eb210ab546..5a006f39abf3 100644 --- a/src/tool_vms.c +++ b/src/tool_vms.c @@ -154,7 +154,7 @@ static void decc_init(void) decc_init_done = 1; /* Loop through all items in the decc_feat_array[]. */ - for(i = 0; decc_feat_array[i].name != NULL; i++) { + for(i = 0; decc_feat_array[i].name; i++) { /* Get the feature index. */ feat_index = decc$feature_get_index(decc_feat_array[i].name); diff --git a/tests/libtest/lib2302.c b/tests/libtest/lib2302.c index 01185bf0c329..4c96755d8fcf 100644 --- a/tests/libtest/lib2302.c +++ b/tests/libtest/lib2302.c @@ -63,7 +63,7 @@ static size_t add_data(struct ws_data *wd, const char *buf, size_t blen, (meta && meta->flags != wd->meta_flags)) { if(wd->nwrites > 0) flush_data(wd); - wd->has_meta = (meta != NULL); + wd->has_meta = !!meta; wd->meta_flags = meta ? meta->flags : 0; } diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index b3782972ea2f..04c39c1e3535 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -92,7 +92,7 @@ static CURLcode recv_header(CURL *curl, int *flags, curl_off_t *offset, } assert(nread == 0); - assert(meta != NULL); + assert(meta); assert(meta->flags); assert(meta->offset == 0); @@ -163,7 +163,7 @@ static CURLcode recv_chunk(CURL *curl, int flags, curl_off_t *offset, } assert(nread <= sizeof(buffer)); - assert(meta != NULL); + assert(meta); assert(meta->flags == flags); assert(meta->offset == *offset); assert(meta->bytesleft == (*bytesleft - (curl_off_t)nread)); diff --git a/tests/unit/unit1396.c b/tests/unit/unit1396.c index 33317c73e816..5b03e092bb82 100644 --- a/tests/unit/unit1396.c +++ b/tests/unit/unit1396.c @@ -83,12 +83,12 @@ static CURLcode test_unit1396(const char *arg) int i; easy = curl_easy_init(); - abort_unless(easy != NULL, "returned NULL!"); + abort_unless(easy, "returned NULL!"); for(i = 0; list1[i].in; i++) { int outlen; char *out = curl_easy_unescape(easy, list1[i].in, list1[i].inlen, &outlen); - abort_unless(out != NULL, "returned NULL!"); + abort_unless(out, "returned NULL!"); fail_unless(outlen == list1[i].outlen, "wrong output length returned"); fail_unless(!memcmp(out, list1[i].out, list1[i].outlen), "bad output data returned"); @@ -101,7 +101,7 @@ static CURLcode test_unit1396(const char *arg) for(i = 0; list2[i].in; i++) { int outlen; char *out = curl_easy_escape(easy, list2[i].in, list2[i].inlen); - abort_unless(out != NULL, "returned NULL!"); + abort_unless(out, "returned NULL!"); outlen = (int)strlen(out); fail_unless(outlen == list2[i].outlen, "wrong output length returned"); diff --git a/tests/unit/unit1602.c b/tests/unit/unit1602.c index 057ead6dd79a..cca3667a0dbe 100644 --- a/tests/unit/unit1602.c +++ b/tests/unit/unit1602.c @@ -57,7 +57,7 @@ static CURLcode test_unit1602(const char *arg) int key2 = 25; value = curlx_malloc(sizeof(int)); - abort_unless(value != NULL, "Out of memory"); + abort_unless(value, "Out of memory"); *value = 199; nodep = Curl_hash_add(&hash, &key, klen, value); if(!nodep) @@ -67,7 +67,7 @@ static CURLcode test_unit1602(const char *arg) /* Attempt to add another key/value pair */ value2 = curlx_malloc(sizeof(int)); - abort_unless(value2 != NULL, "Out of memory"); + abort_unless(value2, "Out of memory"); *value2 = 204; nodep = Curl_hash_add(&hash, &key2, klen, value2); if(!nodep) diff --git a/tests/unit/unit1616.c b/tests/unit/unit1616.c index 5043355d57ee..cd7d0cca54ea 100644 --- a/tests/unit/unit1616.c +++ b/tests/unit/unit1616.c @@ -56,7 +56,7 @@ static CURLcode test_unit1616(const char *arg) uint32_t key2 = 25; value = curlx_malloc(sizeof(int)); - abort_unless(value != NULL, "Out of memory"); + abort_unless(value, "Out of memory"); *value = 199; ok = Curl_uint32_hash_set(&hash, key, value); if(!ok) @@ -70,7 +70,7 @@ static CURLcode test_unit1616(const char *arg) /* Attempt to add another key/value pair */ value2 = curlx_malloc(sizeof(int)); - abort_unless(value2 != NULL, "Out of memory"); + abort_unless(value2, "Out of memory"); *value2 = 204; ok = Curl_uint32_hash_set(&hash, key2, value2); if(!ok) diff --git a/tests/unit/unit1676.c b/tests/unit/unit1676.c index 3cc80b9cb6c8..85f74e2bb80a 100644 --- a/tests/unit/unit1676.c +++ b/tests/unit/unit1676.c @@ -100,9 +100,9 @@ static CURLcode test_unit1676(const char *arg) dhpk_value = slist->data + 12; } - abort_unless(dhp_value != NULL, "dh(p) not found in certinfo"); - abort_unless(dhg_value != NULL, "dh(g) not found in certinfo"); - abort_unless(dhpk_value != NULL, "dh(pub_key) not found in certinfo"); + abort_unless(dhp_value, "dh(p) not found in certinfo"); + abort_unless(dhg_value, "dh(g) not found in certinfo"); + abort_unless(dhpk_value, "dh(pub_key) not found in certinfo"); fail_if(strcmp(dhp_value, dhg_value) == 0, "dh(p) and dh(g) have the same value (bug: g re-reads p)"); fail_unless(strcmp(dhp_value, "17") == 0, "dh(p) expected 17 (0x11)"); diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c index 3e1b06a0aafe..3a7a37597c42 100644 --- a/tests/unit/unit3200.c +++ b/tests/unit/unit3200.c @@ -85,12 +85,12 @@ static CURLcode test_unit3200(const char *arg) curlx_dyn_init(&buf, len); fp = curlx_fopen(arg, "wb"); - abort_unless(fp != NULL, "Cannot open testfile"); + abort_unless(fp, "Cannot open testfile"); fwrite(filecontents[i], 1, strlen(filecontents[i]), fp); curlx_fclose(fp); fp = curlx_fopen(arg, "rb"); - abort_unless(fp != NULL, "Cannot open testfile"); + abort_unless(fp, "Cannot open testfile"); curl_mfprintf(stderr, "Test %zu...", i); switch(i) { From 014be82a66f62972a68c4c9ae5300edc84e9b0df Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 14:18:02 +0200 Subject: [PATCH 0360/1062] tidy-up: drop redundant `== NULL` syntax Where missed by checksrc. Closes #21935 --- docs/examples/htmltitle.cpp | 2 +- lib/cfilters.h | 4 ++-- lib/easy.c | 2 +- lib/ftplistparser.c | 2 +- lib/multi.c | 2 +- lib/pingpong.c | 2 +- lib/url.c | 4 ++-- lib/vssh/libssh.c | 4 ++-- lib/vssh/libssh2.c | 15 +++++++-------- lib/vtls/rustls.c | 2 +- src/tool_doswin.c | 2 +- tests/libtest/lib1536.c | 3 +-- tests/unit/README.md | 8 ++++---- tests/unit/unit1300.c | 10 +++++----- tests/unit/unit1304.c | 2 +- tests/unit/unit1309.c | 4 ++-- tests/unit/unit1605.c | 4 ++-- tests/unit/unit1620.c | 2 +- tests/unit/unit2601.c | 6 +++--- 19 files changed, 39 insertions(+), 41 deletions(-) diff --git a/docs/examples/htmltitle.cpp b/docs/examples/htmltitle.cpp index 7986b94640a9..cce6d16e11a1 100644 --- a/docs/examples/htmltitle.cpp +++ b/docs/examples/htmltitle.cpp @@ -73,7 +73,7 @@ static std::string buffer; static size_t writer(char *data, size_t size, size_t nmemb, std::string *writerData) { - if(writerData == NULL) + if(!writerData) return 0; writerData->append(data, size * nmemb); diff --git a/lib/cfilters.h b/lib/cfilters.h index 17cc634b37f6..13bb428b5566 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -657,7 +657,7 @@ struct cf_call_data { #define CF_DATA_SAVE(save, cf, data) \ do { \ (save) = CF_CTX_CALL_DATA(cf); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf).depth++; \ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \ } while(0) @@ -665,7 +665,7 @@ struct cf_call_data { #define CF_DATA_RESTORE(cf, save) \ do { \ DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf) = (save); \ } while(0) diff --git a/lib/easy.c b/lib/easy.c index ce3d00200a26..8d6124195242 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -942,7 +942,7 @@ static void dupeasy_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /* diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index a4fffc86dc84..f205848b6d0c 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -196,7 +196,7 @@ void Curl_wildcard_dtor(struct WildcardData **wcp) wc->dtor = ZERO_NULL; wc->ftpwc = NULL; } - DEBUGASSERT(wc->ftpwc == NULL); + DEBUGASSERT(!wc->ftpwc); Curl_llist_destroy(&wc->filelist, NULL); curlx_safefree(wc->path); diff --git a/lib/multi.c b/lib/multi.c index fe9bcfaeaf9d..dd29328a1713 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -220,7 +220,7 @@ static void ph_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /* diff --git a/lib/pingpong.c b/lib/pingpong.c index 6952d659a908..ae3f7faa30e0 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -152,7 +152,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); - DEBUGASSERT(pp->sendthis == NULL); + DEBUGASSERT(!pp->sendthis); if(!conn) /* cannot send without a connection! */ diff --git a/lib/url.c b/lib/url.c index 99463551e8c4..bc9308438a76 100644 --- a/lib/url.c +++ b/lib/url.c @@ -442,7 +442,7 @@ static void easy_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /** @@ -2591,7 +2591,7 @@ static void conn_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } static CURLcode url_create_needle(struct Curl_easy *data, diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 4842ff3a916a..8bad2ec4ae94 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -1888,8 +1888,8 @@ static void sshc_cleanup(struct ssh_conn *sshc) } /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->scp_session); if(sshc->readdir_tmp) { ssh_string_free_char(sshc->readdir_tmp); diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 027ef71b9919..e0dccc7ddebf 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -152,8 +152,7 @@ static void kbd_callback(const char *name, int name_len, /* this function must allocate memory that can be freed by libssh2, which uses the LIBSSH2_FREE_FUNC callback */ responses[0].text = Curl_cstrdup(passwd); - responses[0].length = - responses[0].text == NULL ? 0 : curlx_uztoui(strlen(passwd)); + responses[0].length = responses[0].text ? curlx_uztoui(strlen(passwd)) : 0; } (void)prompts; } /* kbd_callback */ @@ -2607,12 +2606,12 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, } /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); - DEBUGASSERT(sshc->kh == NULL); - DEBUGASSERT(sshc->ssh_agent == NULL); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->ssh_channel); + DEBUGASSERT(!sshc->sftp_session); + DEBUGASSERT(!sshc->sftp_handle); + DEBUGASSERT(!sshc->kh); + DEBUGASSERT(!sshc->ssh_agent); curlx_safefree(sshc->rsa_pub); curlx_safefree(sshc->rsa); diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 90a37cbedaea..d69ec03f8ffd 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1100,7 +1100,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - DEBUGASSERT(rconn == NULL); + DEBUGASSERT(!rconn); rr = rustls_client_connection_new(backend->config, connssl->peer.dest->hostname, &rconn); diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 2864c47deaa3..f836af95063c 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -773,7 +773,7 @@ curl_socket_t win32_stdin_read_thread(void) assert(stdin_thread); return socket_r; } - assert(stdin_thread == NULL); + assert(!stdin_thread); do { curl_socklen_t socksize = 0; diff --git a/tests/libtest/lib1536.c b/tests/libtest/lib1536.c index 9debd78807f7..cd3111619494 100644 --- a/tests/libtest/lib1536.c +++ b/tests/libtest/lib1536.c @@ -76,8 +76,7 @@ static CURLcode test_lib1536(const char *URL) if(!scheme || memcmp(scheme, "http", 5) != 0) { curl_mfprintf(stderr, "%s:%d scheme of http resource is incorrect; " "expected 'http' but is %s\n", - __FILE__, __LINE__, - (scheme == NULL ? "NULL" : "invalid")); + __FILE__, __LINE__, scheme ? "invalid" : "NULL"); result = CURLE_HTTP_RETURNED_ERROR; goto test_cleanup; } diff --git a/tests/unit/README.md b/tests/unit/README.md index 292e3bfa2f49..28908a7a15df 100644 --- a/tests/unit/README.md +++ b/tests/unit/README.md @@ -55,8 +55,8 @@ For the actual C file, here's a simple example: /* here you start doing things and checking that the results are good */ - fail_unless( size == 0 , "initial size should be zero" ); - fail_if( head == NULL , "head should not be initiated to NULL" ); + fail_unless(size == 0, "initial size should be zero"); + fail_if(!head, "head should not be initiated to NULL"); /* you end the test code like this: */ @@ -87,8 +87,8 @@ Here's an example using optional initialization and cleanup: /* here you start doing things and checking that the results are good */ - fail_unless( size == 0 , "initial size should be zero" ); - fail_if( head == NULL , "head should not be initiated to NULL" ); + fail_unless(size == 0, "initial size should be zero"); + fail_if(!head, "head should not be initiated to NULL"); /* you end the test code like this: */ diff --git a/tests/unit/unit1300.c b/tests/unit/unit1300.c index 86112ee08818..5db667cdf26e 100644 --- a/tests/unit/unit1300.c +++ b/tests/unit/unit1300.c @@ -67,9 +67,9 @@ static CURLcode test_unit1300(const char *arg) fail_unless(Curl_llist_count(&llist) == 0, "list initial size should be zero"); - fail_unless(Curl_llist_head(&llist) == NULL, + fail_unless(!Curl_llist_head(&llist), "list head should initiate to NULL"); - fail_unless(llist_tail(&llist) == NULL, + fail_unless(!llist_tail(&llist), "list tail should initiate to NULL"); /** @@ -152,7 +152,7 @@ static CURLcode test_unit1300(const char *arg) fail_unless(Curl_llist_head(&llist) == element_next, "llist new head not modified properly"); abort_unless(Curl_llist_head(&llist), "llist.head is NULL"); - fail_unless(llist_node_prev(Curl_llist_head(&llist)) == NULL, + fail_unless(!llist_node_prev(Curl_llist_head(&llist)), "new head previous not set to null"); /** @@ -208,9 +208,9 @@ static CURLcode test_unit1300(const char *arg) to_remove = Curl_llist_head(&llist); Curl_node_remove(to_remove); - fail_unless(Curl_llist_head(&llist) == NULL, + fail_unless(!Curl_llist_head(&llist), "llist head is not NULL while the llist is empty"); - fail_unless(llist_tail(&llist) == NULL, + fail_unless(!llist_tail(&llist), "llist tail is not NULL while the llist is empty"); /** diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index a65a3021b851..3c51cceaa7e6 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -71,7 +71,7 @@ static CURLcode test_unit1304(const char *arg) Curl_netrc_init(&store); res = Curl_netrc_scan(data, &store, "test.example.com", NULL, arg, &cr_out); fail_unless(res == NETRC_NO_MATCH, "expected no match"); - fail_unless(cr_out == NULL, "creds did not return NULL!"); + fail_unless(!cr_out, "creds did not return NULL!"); Curl_netrc_cleanup(&store); /* diff --git a/tests/unit/unit1309.c b/tests/unit/unit1309.c index b49a5de9948f..607e49aa88be 100644 --- a/tests/unit/unit1309.c +++ b/tests/unit/unit1309.c @@ -97,7 +97,7 @@ static CURLcode test_unit1309(const char *arg) } } - fail_unless(root == NULL, "tree not empty after removing all nodes"); + fail_unless(!root, "tree not empty after removing all nodes"); /* rebuild tree */ for(i = 0; i < NUM_NODES; i++) { @@ -127,7 +127,7 @@ static CURLcode test_unit1309(const char *arg) } } - fail_unless(root == NULL, "tree not empty when it should be"); + fail_unless(!root, "tree not empty when it should be"); UNITTEST_END_SIMPLE } diff --git a/tests/unit/unit1605.c b/tests/unit/unit1605.c index 8c63dc3b194c..baeb121085ef 100644 --- a/tests/unit/unit1605.c +++ b/tests/unit/unit1605.c @@ -53,10 +53,10 @@ static CURLcode test_unit1605(const char *arg) char *esc; esc = curl_easy_escape(easy, "", -1); - fail_unless(esc == NULL, "negative string length cannot work"); + fail_unless(!esc, "negative string length cannot work"); esc = curl_easy_unescape(easy, "%41%41%41%41", -1, &len); - fail_unless(esc == NULL, "negative string length cannot work"); + fail_unless(!esc, "negative string length cannot work"); UNITTEST_END(t1605_stop(easy)) } diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c index 2ce26b1ed458..57a90e85cdc7 100644 --- a/tests/unit/unit1620.c +++ b/tests/unit/unit1620.c @@ -127,7 +127,7 @@ static CURLcode test_unit1620(const char *arg) Curl_freeset(empty); for(i = (enum dupstring)0; i < STRING_LAST; i++) { - fail_unless(empty->set.str[i] == NULL, "Curl_free() did not set to NULL"); + fail_unless(!empty->set.str[i], "Curl_free() did not set to NULL"); } result = Curl_close(&dupe); diff --git a/tests/unit/unit2601.c b/tests/unit/unit2601.c index 01ea112ce6d2..21cdb1383604 100644 --- a/tests/unit/unit2601.c +++ b/tests/unit/unit2601.c @@ -99,9 +99,9 @@ static void check_bufq(size_t pool_spares, fail_unless(q.chunk_size == chunk_size, "chunk_size init wrong"); fail_unless(q.max_chunks == max_chunks, "max_chunks init wrong"); - fail_unless(q.head == NULL, "init: head not NULL"); - fail_unless(q.tail == NULL, "init: tail not NULL"); - fail_unless(q.spare == NULL, "init: spare not NULL"); + fail_unless(!q.head, "init: head not NULL"); + fail_unless(!q.tail, "init: tail not NULL"); + fail_unless(!q.spare, "init: spare not NULL"); fail_unless(Curl_bufq_len(&q) == 0, "init: bufq length != 0"); result = Curl_bufq_write(&q, test_data, wsize, &n2); From 4ead4285a6af5d5645d4ad6e17a4df40ab53e297 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 14:25:51 +0200 Subject: [PATCH 0361/1062] tests: enhance names, remove duplicates - test 1030: remove, duplicate of 154 - test 1105: make name unique - test 161: make name reflect what it tests - test 2074: correct the name - test 310: improve name - test 358: correct the name - test 409: removed, duplicate of 401 - test 472: clarify the test name (how it differs from 439) - test 1509: update name - test 527: duplicate of 526 - test 758: separate the name from 530 - test 611: duplicate of 608, remove - test 639: adjust the name - test 688: minor name tweak to clarify - test 708: enhance name - test 800/847: clarify the names - test 1520: dedupe the name - test 962: enhance name - test 1196/2203: enhanced names - test 1211: name tweak - test 1256/1257: enhance the names - test 1483: fix name - test 1541: fix name - test 1553: fix name - test 1609: removed, exact duplicate of 1607 - test 2200: fix name - test 3031: corret the name - test 3016/3203: fix names and keywords - test 3201/3220: enhance names - test 3212: fix name - add missing FILE keywords - drop FAILURE as keyword Closes #21936 --- tests/data/Makefile.am | 10 ++-- tests/data/test1007 | 1 - tests/data/test1030 | 108 ----------------------------------------- tests/data/test1034 | 1 - tests/data/test1035 | 1 - tests/data/test1042 | 1 - tests/data/test1059 | 1 - tests/data/test1063 | 1 - tests/data/test1084 | 1 - tests/data/test1085 | 1 - tests/data/test1086 | 1 - tests/data/test1096 | 1 - tests/data/test1099 | 1 - tests/data/test1105 | 2 +- tests/data/test111 | 1 - tests/data/test1112 | 1 - tests/data/test1120 | 1 - tests/data/test113 | 1 - tests/data/test114 | 1 - tests/data/test115 | 1 - tests/data/test1152 | 1 - tests/data/test116 | 1 - tests/data/test117 | 1 - tests/data/test118 | 1 - tests/data/test119 | 1 - tests/data/test1196 | 2 +- tests/data/test1208 | 1 - tests/data/test1209 | 1 - tests/data/test1211 | 3 +- tests/data/test1234 | 1 - tests/data/test1236 | 1 - tests/data/test1238 | 1 - tests/data/test125 | 1 - tests/data/test1256 | 2 +- tests/data/test1257 | 2 +- tests/data/test1409 | 1 - tests/data/test1410 | 1 - tests/data/test1422 | 1 + tests/data/test1423 | 1 + tests/data/test1447 | 1 - tests/data/test1453 | 1 - tests/data/test1469 | 1 - tests/data/test1471 | 1 - tests/data/test1472 | 1 - tests/data/test1483 | 2 +- tests/data/test1509 | 2 +- tests/data/test1515 | 1 - tests/data/test1516 | 1 - tests/data/test1520 | 2 +- tests/data/test1541 | 2 +- tests/data/test1553 | 2 +- tests/data/test1609 | 19 -------- tests/data/test161 | 2 +- tests/data/test162 | 1 - tests/data/test19 | 1 - tests/data/test190 | 1 - tests/data/test20 | 1 - tests/data/test201 | 1 - tests/data/test205 | 1 - tests/data/test207 | 1 - tests/data/test2074 | 2 +- tests/data/test21 | 1 - tests/data/test2200 | 2 +- tests/data/test2203 | 2 +- tests/data/test221 | 1 - tests/data/test223 | 1 - tests/data/test225 | 1 - tests/data/test226 | 1 - tests/data/test229 | 1 - tests/data/test23 | 1 - tests/data/test256 | 1 - tests/data/test283 | 1 - tests/data/test289 | 1 - tests/data/test29 | 1 - tests/data/test293 | 1 - tests/data/test295 | 1 - tests/data/test30 | 1 - tests/data/test3016 | 4 +- tests/data/test302 | 1 - tests/data/test303 | 1 - tests/data/test3031 | 2 +- tests/data/test305 | 1 - tests/data/test308 | 1 - tests/data/test310 | 2 +- tests/data/test315 | 1 - tests/data/test3201 | 2 +- tests/data/test3203 | 4 +- tests/data/test321 | 1 - tests/data/test3212 | 4 +- tests/data/test3217 | 1 - tests/data/test3218 | 1 - tests/data/test322 | 1 - tests/data/test3220 | 2 +- tests/data/test323 | 1 - tests/data/test324 | 1 - tests/data/test332 | 1 - tests/data/test358 | 2 +- tests/data/test36 | 1 - tests/data/test37 | 1 - tests/data/test38 | 1 - tests/data/test390 | 1 + tests/data/test393 | 1 - tests/data/test394 | 1 - tests/data/test402 | 1 - tests/data/test403 | 1 - tests/data/test404 | 1 - tests/data/test405 | 1 - tests/data/test409 | 58 ---------------------- tests/data/test41 | 1 - tests/data/test419 | 1 - tests/data/test472 | 2 +- tests/data/test504 | 1 - tests/data/test507 | 1 - tests/data/test527 | 64 ------------------------ tests/data/test538 | 1 - tests/data/test594 | 1 - tests/data/test604 | 1 - tests/data/test605 | 1 - tests/data/test606 | 1 - tests/data/test607 | 1 - tests/data/test609 | 1 - tests/data/test611 | 42 ---------------- tests/data/test615 | 1 - tests/data/test620 | 1 - tests/data/test621 | 1 - tests/data/test622 | 1 - tests/data/test623 | 1 - tests/data/test626 | 1 - tests/data/test628 | 1 - tests/data/test629 | 1 - tests/data/test630 | 1 - tests/data/test631 | 1 - tests/data/test632 | 1 - tests/data/test639 | 2 +- tests/data/test656 | 1 - tests/data/test688 | 2 +- tests/data/test702 | 1 - tests/data/test703 | 1 - tests/data/test704 | 1 - tests/data/test705 | 1 - tests/data/test708 | 2 +- tests/data/test75 | 1 - tests/data/test758 | 2 +- tests/data/test800 | 2 +- tests/data/test803 | 1 - tests/data/test847 | 2 +- tests/data/test852 | 1 - tests/data/test855 | 1 - tests/data/test856 | 1 - tests/data/test87 | 1 - tests/data/test94 | 1 - tests/data/test962 | 2 +- tests/data/test99 | 1 - 153 files changed, 39 insertions(+), 446 deletions(-) delete mode 100644 tests/data/test1030 delete mode 100644 tests/data/test1609 delete mode 100644 tests/data/test409 delete mode 100644 tests/data/test527 delete mode 100644 tests/data/test611 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a7c293cb20c1..8c693f4e7f89 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -70,7 +70,7 @@ test370 test371 test372 test373 test374 test375 test376 test378 test379 \ test380 test381 test383 test384 test385 test386 test387 test388 test389 \ test390 test391 test392 test393 test394 test395 test396 test397 test398 \ test399 test400 test401 test402 test403 test404 test405 test406 test407 \ -test408 test409 test410 test411 test412 test413 test414 test415 test416 \ +test408 test410 test411 test412 test413 test414 test415 test416 \ test417 test418 test419 test420 test421 test422 test423 test424 test425 \ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ test435 test436 test437 test438 test439 test440 test441 test442 test443 \ @@ -82,7 +82,7 @@ test483 test484 test485 test486 test487 test488 test489 test490 test491 \ test492 test493 test494 test495 test496 test497 test498 test499 test500 \ test501 test502 test503 test504 test505 test506 test507 test508 test509 \ test510 test511 test512 test513 test514 test515 test516 test517 test518 \ -test519 test520 test521 test522 test523 test524 test525 test526 test527 \ +test519 test520 test521 test522 test523 test524 test525 test526 \ test528 test529 test530 test531 test532 test533 test534 test535 test536 \ test537 test538 test539 test540 test541 test542 test543 test544 test545 \ test546 test547 test548 test549 test550 test551 test552 test553 test554 \ @@ -92,7 +92,7 @@ test573 test574 test575 test576 test577 test578 test579 test580 test581 \ test582 test583 test584 test585 test586 test587 test588 test589 test590 \ test591 test592 test593 test594 test595 test596 test597 test598 test599 \ test600 test601 test602 test603 test604 test605 test606 test607 test608 \ -test609 test610 test611 test612 test613 test614 test615 test616 test617 \ +test609 test610 test612 test613 test614 test615 test616 test617 \ test618 test619 test620 test621 test622 test623 test624 test625 test626 \ test627 test628 test629 test630 test631 test632 test633 test634 test635 \ test636 test637 test638 test639 test640 test641 test642 test643 test644 \ @@ -139,7 +139,7 @@ test997 test998 test999 test1000 test1001 test1002 test1003 test1004 \ test1005 test1006 test1007 test1008 test1009 test1010 test1011 test1012 \ test1013 test1014 test1015 test1016 test1017 test1018 test1019 test1020 \ test1021 test1022 test1023 test1024 test1025 test1026 test1027 test1028 \ -test1029 test1030 test1031 test1032 test1033 test1034 test1035 test1036 \ +test1029 test1031 test1032 test1033 test1034 test1035 test1036 \ test1037 test1038 test1039 test1040 test1041 test1042 test1043 test1044 \ test1045 test1046 test1047 test1048 test1049 test1050 test1051 test1052 \ test1053 test1054 test1055 test1056 test1057 test1058 test1059 test1060 \ @@ -211,7 +211,7 @@ test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 \ test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 \ test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 \ -test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 \ +test1605 test1606 test1607 test1608 test1610 test1611 test1612 \ test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 \ test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 \ test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ diff --git a/tests/data/test1007 b/tests/data/test1007 index fdd6af2c9a3c..2706c049a55a 100644 --- a/tests/data/test1007 +++ b/tests/data/test1007 @@ -4,7 +4,6 @@ TFTP TFTP WRQ -FAILURE diff --git a/tests/data/test1030 b/tests/data/test1030 deleted file mode 100644 index ddf7370eaaf5..000000000000 --- a/tests/data/test1030 +++ /dev/null @@ -1,108 +0,0 @@ - - - - -HTTP -HTTP PUT -HTTP Digest auth ---anyauth - - - -# Server-side - - -HTTP/1.1 401 Authorization Required -Server: Apache/1.3.27 (Darwin) PHP/4.1.2 -WWW-Authenticate: Blackmagic realm="gimme all yer s3cr3ts" -WWW-Authenticate: Basic realm="gimme all yer s3cr3ts" -WWW-Authenticate: Digest realm="gimme all yer s3cr3ts", nonce="11223344" -Content-Length: 26 -Content-Type: text/html; charset=iso-8859-1 - -This is not the real page - - -# This is supposed to be returned when the server gets a -# Authorization: Digest line passed-in from the client - -HTTP/1.1 200 OK -Server: Apache/1.3.27 (Darwin) PHP/4.1.2 -Content-Type: text/html; charset=iso-8859-1 -Content-Length: 23 -Connection: close - -This IS the real page! - - - -HTTP/1.1 401 Authorization Required -Server: Apache/1.3.27 (Darwin) PHP/4.1.2 -WWW-Authenticate: Blackmagic realm="gimme all yer s3cr3ts" -WWW-Authenticate: Basic realm="gimme all yer s3cr3ts" -WWW-Authenticate: Digest realm="gimme all yer s3cr3ts", nonce="11223344" -Content-Length: 26 -Content-Type: text/html; charset=iso-8859-1 - -HTTP/1.1 200 OK -Server: Apache/1.3.27 (Darwin) PHP/4.1.2 -Content-Type: text/html; charset=iso-8859-1 -Content-Length: 23 -Connection: close - -This IS the real page! - - - - -# Client-side - - -http - - -!SSPI -crypto -digest - - -HTTP PUT with --anyauth authorization (picking Digest) - - -http://%HOSTIP:%HTTPPORT/%TESTNUMBER -T %LOGDIR/put%TESTNUMBER -u testuser:testpass --anyauth - - -This is data we upload with PUT -a second line -line three -four is the number of lines - - - -# Verify data after the test has been "shot" - - -PUT /%TESTNUMBER HTTP/1.1 -Host: %HOSTIP:%HTTPPORT -User-Agent: curl/%VERSION -Accept: */* -Content-Length: 85 - -This is data we upload with PUT -a second line -line three -four is the number of lines -PUT /%TESTNUMBER HTTP/1.1 -Host: %HOSTIP:%HTTPPORT -Authorization: Digest username="testuser", realm="gimme all yer s3cr3ts", nonce="11223344", uri="/%TESTNUMBER", response="01cb59db1ddaac246b072d5f5f0716d9" -User-Agent: curl/%VERSION -Accept: */* -Content-Length: 85 - -This is data we upload with PUT -a second line -line three -four is the number of lines - - - diff --git a/tests/data/test1034 b/tests/data/test1034 index 8d0c470e51a3..be48039b54ca 100644 --- a/tests/data/test1034 +++ b/tests/data/test1034 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy IDN -FAILURE config file diff --git a/tests/data/test1035 b/tests/data/test1035 index fe8ae115b234..9139a41aa348 100644 --- a/tests/data/test1035 +++ b/tests/data/test1035 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy IDN -FAILURE diff --git a/tests/data/test1042 b/tests/data/test1042 index 66ac19f4fd74..22b1acc03e04 100644 --- a/tests/data/test1042 +++ b/tests/data/test1042 @@ -5,7 +5,6 @@ HTTP HTTP GET Resume -FAILURE diff --git a/tests/data/test1059 b/tests/data/test1059 index c982ec3ac1a7..1cb2ff836a81 100644 --- a/tests/data/test1059 +++ b/tests/data/test1059 @@ -6,7 +6,6 @@ HTTP HTTP CONNECT proxytunnel FTP -FAILURE diff --git a/tests/data/test1063 b/tests/data/test1063 index 0e95511d2748..28b97be29f7f 100644 --- a/tests/data/test1063 +++ b/tests/data/test1063 @@ -4,7 +4,6 @@ FILE Range -FAILURE diff --git a/tests/data/test1084 b/tests/data/test1084 index 975c751b160a..66335111f899 100644 --- a/tests/data/test1084 +++ b/tests/data/test1084 @@ -5,7 +5,6 @@ HTTP HTTP GET --interface -FAILURE non-existing host diff --git a/tests/data/test1085 b/tests/data/test1085 index 8a2bbd8c9072..2b692ffee3a7 100644 --- a/tests/data/test1085 +++ b/tests/data/test1085 @@ -6,7 +6,6 @@ HTTP HTTP GET --interface IPv6 -FAILURE non-existing host diff --git a/tests/data/test1086 b/tests/data/test1086 index efb51c413bc5..440aadfa0e75 100644 --- a/tests/data/test1086 +++ b/tests/data/test1086 @@ -6,7 +6,6 @@ FTP EPSV RETR timeout -FAILURE SLOWDOWNDATA flaky timing-dependent diff --git a/tests/data/test1096 b/tests/data/test1096 index 0485f7c2e359..971aef1cb177 100644 --- a/tests/data/test1096 +++ b/tests/data/test1096 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test1099 b/tests/data/test1099 index 7539832ca4f9..a38038713f56 100644 --- a/tests/data/test1099 +++ b/tests/data/test1099 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test1105 b/tests/data/test1105 index 7d4df9556cde..45251d3e7ca0 100644 --- a/tests/data/test1105 +++ b/tests/data/test1105 @@ -33,7 +33,7 @@ Set-Cookie: bar=foo%TABbar http -HTTP with cookie parser and header recording +HTTP POST with cookies "http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER?parm1=this*that/other/thing%AMPparm2=foobar/%TESTNUMBER" -c %LOGDIR/cookie%TESTNUMBER.txt -d "userid=myname%AMPpassword=mypassword" diff --git a/tests/data/test111 b/tests/data/test111 index 655cc0c275af..10fbfa91cffa 100644 --- a/tests/data/test111 +++ b/tests/data/test111 @@ -5,7 +5,6 @@ FTP EPSV Resume -FAILURE # Server-side diff --git a/tests/data/test1112 b/tests/data/test1112 index 0d9e19ef80ed..45ce8e0740b7 100644 --- a/tests/data/test1112 +++ b/tests/data/test1112 @@ -7,7 +7,6 @@ FTPS EPSV RETR timeout -FAILURE SLOWDOWNDATA timing-dependent diff --git a/tests/data/test1120 b/tests/data/test1120 index 14500563344e..e5c223aa0679 100644 --- a/tests/data/test1120 +++ b/tests/data/test1120 @@ -7,7 +7,6 @@ PORT RETR 421 timeout -FAILURE # Server-side diff --git a/tests/data/test113 b/tests/data/test113 index 6a7266031d56..68637a12b816 100644 --- a/tests/data/test113 +++ b/tests/data/test113 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test114 b/tests/data/test114 index 850584479da4..6972e6a8352d 100644 --- a/tests/data/test114 +++ b/tests/data/test114 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test115 b/tests/data/test115 index 00b300a58ef7..5fe1a74a61bf 100644 --- a/tests/data/test115 +++ b/tests/data/test115 @@ -4,7 +4,6 @@ FTP PASV -FAILURE # Server-side diff --git a/tests/data/test1152 b/tests/data/test1152 index 692f51faa06a..dfbde558d0af 100644 --- a/tests/data/test1152 +++ b/tests/data/test1152 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test116 b/tests/data/test116 index fe519c5bbffc..3ce674e401f2 100644 --- a/tests/data/test116 +++ b/tests/data/test116 @@ -5,7 +5,6 @@ FTP EPRT PORT -FAILURE EPRT refused diff --git a/tests/data/test117 b/tests/data/test117 index 9142e15c2e9f..6fdbb33a582d 100644 --- a/tests/data/test117 +++ b/tests/data/test117 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test118 b/tests/data/test118 index 148e7d6e4d9e..67c17065d511 100644 --- a/tests/data/test118 +++ b/tests/data/test118 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test119 b/tests/data/test119 index 8b6ef10339f1..5f9140ff2c35 100644 --- a/tests/data/test119 +++ b/tests/data/test119 @@ -5,7 +5,6 @@ FTP PORT RETR -FAILURE # Server-side diff --git a/tests/data/test1196 b/tests/data/test1196 index dc79141cb3a5..118c52a3367f 100644 --- a/tests/data/test1196 +++ b/tests/data/test1196 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT with error in CONNACK +MQTT with "unaccaptable protocol version" CONNACK mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER diff --git a/tests/data/test1208 b/tests/data/test1208 index 124419b6dc1b..8ae1e3acbe8d 100644 --- a/tests/data/test1208 +++ b/tests/data/test1208 @@ -7,7 +7,6 @@ PORT RETR NODATACONN150 timeout -FAILURE flaky timing-dependent diff --git a/tests/data/test1209 b/tests/data/test1209 index 8eb540a22c81..449e8f5fa920 100644 --- a/tests/data/test1209 +++ b/tests/data/test1209 @@ -7,7 +7,6 @@ PORT RETR NODATACONN timeout -FAILURE # Server-side diff --git a/tests/data/test1211 b/tests/data/test1211 index cb66fae8c5be..eb267d9dcbec 100644 --- a/tests/data/test1211 +++ b/tests/data/test1211 @@ -7,7 +7,6 @@ PORT RETR NODATACONN425 timeout -FAILURE # Server-side @@ -26,7 +25,7 @@ NODATACONN425 ftp -FTP PORT and 425 on download +FTP PORT and 425 on download and timeout ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -P - diff --git a/tests/data/test1234 b/tests/data/test1234 index e7ffbf2eec26..bb3cde5974e4 100644 --- a/tests/data/test1234 +++ b/tests/data/test1234 @@ -4,7 +4,6 @@ globbing {} list -FAILURE # Server-side diff --git a/tests/data/test1236 b/tests/data/test1236 index 0d31ac1f4afd..2b7318516d4a 100644 --- a/tests/data/test1236 +++ b/tests/data/test1236 @@ -3,7 +3,6 @@ globbing -FAILURE # Server-side diff --git a/tests/data/test1238 b/tests/data/test1238 index a8fbc8a3f027..9c2e22091ba8 100644 --- a/tests/data/test1238 +++ b/tests/data/test1238 @@ -5,7 +5,6 @@ TFTP TFTP RRQ timeout -FAILURE DELAY diff --git a/tests/data/test125 b/tests/data/test125 index ef2591d4a71e..1f28daaf0fcc 100644 --- a/tests/data/test125 +++ b/tests/data/test125 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test1256 b/tests/data/test1256 index d35ee627e187..7db26428ec76 100644 --- a/tests/data/test1256 +++ b/tests/data/test1256 @@ -29,7 +29,7 @@ foo http -http_proxy, override NO_PROXY by --noproxy and target URL through proxy +http_proxy, override NO_PROXY by --noproxy host http_proxy=http://%HOSTIP:%HTTPPORT diff --git a/tests/data/test1257 b/tests/data/test1257 index 40f1028c5c29..fc6f56e33cbd 100644 --- a/tests/data/test1257 +++ b/tests/data/test1257 @@ -29,7 +29,7 @@ foo http -http_proxy, override NO_PROXY by --noproxy and target URL through proxy +http_proxy, override NO_PROXY by --noproxy "" http_proxy=http://%HOSTIP:%HTTPPORT diff --git a/tests/data/test1409 b/tests/data/test1409 index 153d4a064453..50d00e44d739 100644 --- a/tests/data/test1409 +++ b/tests/data/test1409 @@ -3,7 +3,6 @@ cmdline -FAILURE diff --git a/tests/data/test1410 b/tests/data/test1410 index 4f1b39113e4c..e5d8660e7881 100644 --- a/tests/data/test1410 +++ b/tests/data/test1410 @@ -3,7 +3,6 @@ cmdline -FAILURE diff --git a/tests/data/test1422 b/tests/data/test1422 index d51cd6e7f529..b7f856c13ea3 100644 --- a/tests/data/test1422 +++ b/tests/data/test1422 @@ -3,6 +3,7 @@ HTTP +FILE HTTP GET -J diff --git a/tests/data/test1423 b/tests/data/test1423 index 4acc0757b27f..b42d369d7c12 100644 --- a/tests/data/test1423 +++ b/tests/data/test1423 @@ -3,6 +3,7 @@ HTTP +FILE HTTP GET diff --git a/tests/data/test1447 b/tests/data/test1447 index 4b0ed1b584af..ea68ef92300b 100644 --- a/tests/data/test1447 +++ b/tests/data/test1447 @@ -3,7 +3,6 @@ HTTP proxy -FAILURE # Server-side diff --git a/tests/data/test1453 b/tests/data/test1453 index 3b1729633505..5d2166332c46 100644 --- a/tests/data/test1453 +++ b/tests/data/test1453 @@ -3,7 +3,6 @@ Too long tftp filename -FAILURE # Server-side diff --git a/tests/data/test1469 b/tests/data/test1469 index 4ca24418f99b..3b742c1b2c32 100644 --- a/tests/data/test1469 +++ b/tests/data/test1469 @@ -4,7 +4,6 @@ FTP URL -FAILURE diff --git a/tests/data/test1471 b/tests/data/test1471 index ffd3ef59abab..70c454b0a45e 100644 --- a/tests/data/test1471 +++ b/tests/data/test1471 @@ -4,7 +4,6 @@ Onion Tor -FAILURE # Server-side diff --git a/tests/data/test1472 b/tests/data/test1472 index 26fb456808b6..b9e2d6dd67c0 100644 --- a/tests/data/test1472 +++ b/tests/data/test1472 @@ -4,7 +4,6 @@ Onion Tor -FAILURE # Server-side diff --git a/tests/data/test1483 b/tests/data/test1483 index eb1931770644..c7373b2a4615 100644 --- a/tests/data/test1483 +++ b/tests/data/test1483 @@ -51,7 +51,7 @@ writedelay: 10 http -HTTP GET with double chunked in TE header +HTTP GET with two Transfer-Encoding: chunked headers http://%HOSTIP:%HTTPPORT/%TESTNUMBER -D %LOGDIR/heads%TESTNUMBER diff --git a/tests/data/test1509 b/tests/data/test1509 index afb1a6613955..7e57cd0fad9b 100644 --- a/tests/data/test1509 +++ b/tests/data/test1509 @@ -58,7 +58,7 @@ lib%TESTNUMBER proxy -simple multi http:// through proxytunnel with authentication info +http:// through proxytunnel http://the.old.moo.%TESTNUMBER:%HTTPPORT/%TESTNUMBER %HOSTIP:%PROXYPORT diff --git a/tests/data/test1515 b/tests/data/test1515 index ac3ad254b85a..2fca5beb70fe 100644 --- a/tests/data/test1515 +++ b/tests/data/test1515 @@ -5,7 +5,6 @@ HTTP multi -FAILURE resolve diff --git a/tests/data/test1516 b/tests/data/test1516 index 704a79164648..02f2c6d84f17 100644 --- a/tests/data/test1516 +++ b/tests/data/test1516 @@ -5,7 +5,6 @@ HTTP multi -FAILURE resolve diff --git a/tests/data/test1520 b/tests/data/test1520 index 07ccdf281c95..c26c08866fb2 100644 --- a/tests/data/test1520 +++ b/tests/data/test1520 @@ -17,7 +17,7 @@ lib%TESTNUMBER -SMTP with CRLF-dot-CRLF in data +SMTP with CRLF-dot-CRLF in upload payload From: different diff --git a/tests/data/test1541 b/tests/data/test1541 index ecba40ff9319..2f030cc904b1 100644 --- a/tests/data/test1541 +++ b/tests/data/test1541 @@ -56,7 +56,7 @@ http lib%TESTNUMBER -chunked with trailers and pausing the receive +CURLINFO timer check http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test1553 b/tests/data/test1553 index fbf5390b156b..84f383085225 100644 --- a/tests/data/test1553 +++ b/tests/data/test1553 @@ -34,7 +34,7 @@ Mime imap -IMAP cleanup before a connection was created +IMAP cleanup with non-existing hostname # tool is what to use instead of 'curl' diff --git a/tests/data/test1609 b/tests/data/test1609 deleted file mode 100644 index 5153174a3afe..000000000000 --- a/tests/data/test1609 +++ /dev/null @@ -1,19 +0,0 @@ - - - - -unittest -CURLOPT_RESOLVE - - - -# Client-side - - -unittest - - -CURLOPT_RESOLVE parsing - - - diff --git a/tests/data/test161 b/tests/data/test161 index 16fdaf41862e..23db19005e2e 100644 --- a/tests/data/test161 +++ b/tests/data/test161 @@ -23,7 +23,7 @@ PASV ftp -FTP RETR PASV +FTP RETR partial file ftp://%HOSTIP:%FTPPORT/%TESTNUMBER diff --git a/tests/data/test162 b/tests/data/test162 index cef8fc2f08e7..d03df40bb497 100644 --- a/tests/data/test162 +++ b/tests/data/test162 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy HTTP proxy NTLM auth -FAILURE diff --git a/tests/data/test19 b/tests/data/test19 index 0c1b08b4c0b1..814163859c69 100644 --- a/tests/data/test19 +++ b/tests/data/test19 @@ -4,7 +4,6 @@ HTTP connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test190 b/tests/data/test190 index 4b4da41b62e1..f59f871d6a36 100644 --- a/tests/data/test190 +++ b/tests/data/test190 @@ -4,7 +4,6 @@ FTP timeout -FAILURE DELAY diff --git a/tests/data/test20 b/tests/data/test20 index a851ba30d0a3..5dfe22dcd716 100644 --- a/tests/data/test20 +++ b/tests/data/test20 @@ -3,7 +3,6 @@ HTTP -FAILURE non-existing host diff --git a/tests/data/test201 b/tests/data/test201 index 7a1699d3e47f..ac1593c8e2c5 100644 --- a/tests/data/test201 +++ b/tests/data/test201 @@ -3,7 +3,6 @@ FILE -FAILURE diff --git a/tests/data/test205 b/tests/data/test205 index 278562532357..3324d7d64f4f 100644 --- a/tests/data/test205 +++ b/tests/data/test205 @@ -3,7 +3,6 @@ FILE -FAILURE diff --git a/tests/data/test207 b/tests/data/test207 index f1c0cf81fb63..8ac782f067bd 100644 --- a/tests/data/test207 +++ b/tests/data/test207 @@ -5,7 +5,6 @@ HTTP HTTP GET CURLE_PARTIAL_FILE -FAILURE chunked Transfer-Encoding diff --git a/tests/data/test2074 b/tests/data/test2074 index c1571231a2df..0967031906e0 100644 --- a/tests/data/test2074 +++ b/tests/data/test2074 @@ -32,7 +32,7 @@ Funny-head: yesyes http -HTTP GET +HTTP with oauth2-bearer http://%HOSTIP:%HTTPPORT/%TESTNUMBER --oauth2-bearer mF_9.B5f-4.1JqM diff --git a/tests/data/test21 b/tests/data/test21 index af47cf914e03..8f3da43b8162 100644 --- a/tests/data/test21 +++ b/tests/data/test21 @@ -2,7 +2,6 @@ -FAILURE multiple HTTP requests diff --git a/tests/data/test2200 b/tests/data/test2200 index 95baca6bc098..bc1652eb2e98 100644 --- a/tests/data/test2200 +++ b/tests/data/test2200 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT SUBSCRIBE with user and password +MQTT SUBSCRIBE with user and password, not authorized mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER -u fakeuser:fakepasswd diff --git a/tests/data/test2203 b/tests/data/test2203 index 9d8c1da6793e..9dcfedadaf7d 100644 --- a/tests/data/test2203 +++ b/tests/data/test2203 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT with error in CONNACK +MQTT with "no user or password" CONNACK mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER diff --git a/tests/data/test221 b/tests/data/test221 index 5a7d082e7954..03f6d0505537 100644 --- a/tests/data/test221 +++ b/tests/data/test221 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test223 b/tests/data/test223 index 9912d3d47185..660c85da129c 100644 --- a/tests/data/test223 +++ b/tests/data/test223 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test225 b/tests/data/test225 index 1ce22088ee0d..403bd2fec2f1 100644 --- a/tests/data/test225 +++ b/tests/data/test225 @@ -3,7 +3,6 @@ FTP -FAILURE # Client-side diff --git a/tests/data/test226 b/tests/data/test226 index 1872981d644f..dbf6d1af38f0 100644 --- a/tests/data/test226 +++ b/tests/data/test226 @@ -3,7 +3,6 @@ FTP -FAILURE diff --git a/tests/data/test229 b/tests/data/test229 index b90214ff5107..bd911db9ee95 100644 --- a/tests/data/test229 +++ b/tests/data/test229 @@ -4,7 +4,6 @@ FTP ACCT -FAILURE # Server-side diff --git a/tests/data/test23 b/tests/data/test23 index 1771fe9e1583..8126025b9063 100644 --- a/tests/data/test23 +++ b/tests/data/test23 @@ -3,7 +3,6 @@ unsupported scheme -FAILURE # Server-side diff --git a/tests/data/test256 b/tests/data/test256 index 3bf44c5770f9..cc4d7e209dc4 100644 --- a/tests/data/test256 +++ b/tests/data/test256 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy Resume -FAILURE # Server-side diff --git a/tests/data/test283 b/tests/data/test283 index 3ca5bd1e0d62..051e58c87c8b 100644 --- a/tests/data/test283 +++ b/tests/data/test283 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test289 b/tests/data/test289 index cf8464b1b629..4253c1b77604 100644 --- a/tests/data/test289 +++ b/tests/data/test289 @@ -5,7 +5,6 @@ FTP STOR Resume -FAILURE diff --git a/tests/data/test29 b/tests/data/test29 index 122cfd23b73f..df67f850197f 100644 --- a/tests/data/test29 +++ b/tests/data/test29 @@ -5,7 +5,6 @@ HTTP HTTP GET timeout -FAILURE # Server-side diff --git a/tests/data/test293 b/tests/data/test293 index cc6cccb41ee1..c6f69f2e0105 100644 --- a/tests/data/test293 +++ b/tests/data/test293 @@ -5,7 +5,6 @@ HTTP HTTP GET --max-filesize -FAILURE diff --git a/tests/data/test295 b/tests/data/test295 index 1eb14475fb98..e7fbf6745c4b 100644 --- a/tests/data/test295 +++ b/tests/data/test295 @@ -6,7 +6,6 @@ FTP PASV LIST ACCT -FAILURE # Server-side diff --git a/tests/data/test30 b/tests/data/test30 index c8984105f83f..c0b5d60d431d 100644 --- a/tests/data/test30 +++ b/tests/data/test30 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE # Server-side diff --git a/tests/data/test3016 b/tests/data/test3016 index 2d530f55c026..c710e3976283 100644 --- a/tests/data/test3016 +++ b/tests/data/test3016 @@ -2,8 +2,6 @@ -HTTP -HTTP GET FILE @@ -14,7 +12,7 @@ FILE file -GET a directory using file:// +Get current directory using file:// diff --git a/tests/data/test302 b/tests/data/test302 index 1bae0c76410f..3c6eb09851eb 100644 --- a/tests/data/test302 +++ b/tests/data/test302 @@ -6,7 +6,6 @@ HTTPS HTTP GET HTTP CONNECT HTTP proxy -FAILURE diff --git a/tests/data/test303 b/tests/data/test303 index 44060b496e79..51dc471e61a1 100644 --- a/tests/data/test303 +++ b/tests/data/test303 @@ -5,7 +5,6 @@ HTTPS HTTP GET timeout -FAILURE diff --git a/tests/data/test3031 b/tests/data/test3031 index bf41ac78dbf7..68ffb466919d 100644 --- a/tests/data/test3031 +++ b/tests/data/test3031 @@ -32,7 +32,7 @@ http http ---output-dir with --create-dirs +--dump-header with --create-dirs http://%HOSTIP:%HTTPPORT/this/is/the/%TESTNUMBER --dump-header %PWD/%LOGDIR/tmp/out.txt --create-dirs diff --git a/tests/data/test305 b/tests/data/test305 index 57d33938e118..1acefe29be4c 100644 --- a/tests/data/test305 +++ b/tests/data/test305 @@ -4,7 +4,6 @@ HTTPS HTTP GET -FAILURE diff --git a/tests/data/test308 b/tests/data/test308 index 60274f489d75..593976b4c239 100644 --- a/tests/data/test308 +++ b/tests/data/test308 @@ -4,7 +4,6 @@ HTTPS HTTP GET -FAILURE diff --git a/tests/data/test310 b/tests/data/test310 index 558f137d8ac4..12a12a4f1503 100644 --- a/tests/data/test310 +++ b/tests/data/test310 @@ -31,7 +31,7 @@ local-http https test-localhost.pem -simple HTTPS GET +HTTPS GET with specified CA cert bundle -4 --cacert %CERTDIR/certs/test-ca.crt https://localhost:%HTTPSPORT/%TESTNUMBER diff --git a/tests/data/test315 b/tests/data/test315 index 62499204a2ef..31d812cd05f9 100644 --- a/tests/data/test315 +++ b/tests/data/test315 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test3201 b/tests/data/test3201 index 7e051a216729..cf98f4f0a394 100644 --- a/tests/data/test3201 +++ b/tests/data/test3201 @@ -33,7 +33,7 @@ Funny-head: barkbark http -HTTP GET when PROXY Protocol enabled and spoofed client IP +HTTP GET when PROXY Protocol enabled and spoofed client IPv4 http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "192.168.1.1" -H "Testno: %TESTNUMBER" diff --git a/tests/data/test3203 b/tests/data/test3203 index 6a548140604a..401f69741976 100644 --- a/tests/data/test3203 +++ b/tests/data/test3203 @@ -2,8 +2,6 @@ -HTTP -HTTP GET FILE @@ -14,7 +12,7 @@ FILE file -GET a directory using file:// +Get a directory using file:// diff --git a/tests/data/test321 b/tests/data/test321 index cdc18028b9a0..ca258c078d79 100644 --- a/tests/data/test321 +++ b/tests/data/test321 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test3212 b/tests/data/test3212 index 826c961a4d63..3c5cd37e7f51 100644 --- a/tests/data/test3212 +++ b/tests/data/test3212 @@ -3,7 +3,7 @@ unittest -uint_bset +uint32_tbl @@ -13,7 +13,7 @@ uint_bset unittest -uint_bset unit tests +uint32_tbl unit tests diff --git a/tests/data/test3217 b/tests/data/test3217 index df7c890eba72..4749779b6753 100644 --- a/tests/data/test3217 +++ b/tests/data/test3217 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test3218 b/tests/data/test3218 index 0c98b202eca5..a56b9a2b98e6 100644 --- a/tests/data/test3218 +++ b/tests/data/test3218 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test322 b/tests/data/test322 index c0581088ae6c..e0aca6b28eda 100644 --- a/tests/data/test322 +++ b/tests/data/test322 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test3220 b/tests/data/test3220 index ba8dec8f923f..ca8bfaa134b0 100644 --- a/tests/data/test3220 +++ b/tests/data/test3220 @@ -33,7 +33,7 @@ Funny-head: barkbark http -HTTP GET when PROXY Protocol enabled and spoofed client IP +HTTP GET when PROXY Protocol enabled and spoofed client IPv6 http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "2a04:4e42::347" -H "Testno: %TESTNUMBER" diff --git a/tests/data/test323 b/tests/data/test323 index e8f45c5ef8fe..ba09253c0f17 100644 --- a/tests/data/test323 +++ b/tests/data/test323 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test324 b/tests/data/test324 index a91301fe1d8a..d85fecb9b33d 100644 --- a/tests/data/test324 +++ b/tests/data/test324 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test332 b/tests/data/test332 index b7a7e6000df3..e968c26fe4f8 100644 --- a/tests/data/test332 +++ b/tests/data/test332 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test358 b/tests/data/test358 index ac7f3d57e421..99035cc049f8 100644 --- a/tests/data/test358 +++ b/tests/data/test358 @@ -36,7 +36,7 @@ http http/2 -HTTPS GET translated by alt-svc lookup to HTTP/2 GET +HTTP GET translated by alt-svc lookup to HTTP/2 GET # make Debug-curl accept Alt-Svc over plain HTTP diff --git a/tests/data/test36 b/tests/data/test36 index ea97f6160b99..a99e560bdcde 100644 --- a/tests/data/test36 +++ b/tests/data/test36 @@ -5,7 +5,6 @@ HTTP HTTP GET chunked Transfer-Encoding -FAILURE # Server-side diff --git a/tests/data/test37 b/tests/data/test37 index cf455095475e..46f994f1e787 100644 --- a/tests/data/test37 +++ b/tests/data/test37 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE # Server-side diff --git a/tests/data/test38 b/tests/data/test38 index 842b0e535e7d..05afab2829f8 100644 --- a/tests/data/test38 +++ b/tests/data/test38 @@ -5,7 +5,6 @@ HTTP HTTP GET Resume -FAILURE # Server-side diff --git a/tests/data/test390 b/tests/data/test390 index 7a5faaf8361b..0489d9be8662 100644 --- a/tests/data/test390 +++ b/tests/data/test390 @@ -3,6 +3,7 @@ HTTP +FILE FTP parallel diff --git a/tests/data/test393 b/tests/data/test393 index fbf07d7c601d..3353da688a37 100644 --- a/tests/data/test393 +++ b/tests/data/test393 @@ -5,7 +5,6 @@ HTTP HTTP GET --max-filesize -FAILURE diff --git a/tests/data/test394 b/tests/data/test394 index 3a51d5a9c594..c3cbc691eb3c 100644 --- a/tests/data/test394 +++ b/tests/data/test394 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE diff --git a/tests/data/test402 b/tests/data/test402 index 5226d6c731b2..ab036b27d645 100644 --- a/tests/data/test402 +++ b/tests/data/test402 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test403 b/tests/data/test403 index bbf162d22c71..6bf16a62305d 100644 --- a/tests/data/test403 +++ b/tests/data/test403 @@ -7,7 +7,6 @@ FTPS PASV LIST CCC -FAILURE # Server-side diff --git a/tests/data/test404 b/tests/data/test404 index 96f3eaa9c09e..1e69b881f7ea 100644 --- a/tests/data/test404 +++ b/tests/data/test404 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test405 b/tests/data/test405 index 73a6dcf430ad..e67dcf16d4b0 100644 --- a/tests/data/test405 +++ b/tests/data/test405 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test409 b/tests/data/test409 deleted file mode 100644 index 5660b5d6ca84..000000000000 --- a/tests/data/test409 +++ /dev/null @@ -1,58 +0,0 @@ - - - - -FTP -FTPS -EPSV -STOR - - - -# Client-side - - -SSL - - -ftps - - -FTPS PASV upload file - - -data - to - see -that FTP -works - so does it? - - ---insecure --ftp-ssl-control ftps://%HOSTIP:%FTPSPORT/%TESTNUMBER -T %LOGDIR/test%TESTNUMBER.txt - - - -# Verify data after the test has been "shot" - - -data - to - see -that FTP -works - so does it? - - -USER anonymous -PASS ftp@example.com -PBSZ 0 -PROT C -PWD -EPSV -TYPE I -STOR %TESTNUMBER -QUIT - - - diff --git a/tests/data/test41 b/tests/data/test41 index efeb58ec9b65..0259f25bb94f 100644 --- a/tests/data/test41 +++ b/tests/data/test41 @@ -4,7 +4,6 @@ HTTP HTTP FORMPOST -FAILURE # Server-side diff --git a/tests/data/test419 b/tests/data/test419 index 51c5a16a0810..04a6a63d9f89 100644 --- a/tests/data/test419 +++ b/tests/data/test419 @@ -3,7 +3,6 @@ --dump-header -FAILURE diff --git a/tests/data/test472 b/tests/data/test472 index dd426b9cecf1..f2e96717f78d 100644 --- a/tests/data/test472 +++ b/tests/data/test472 @@ -36,7 +36,7 @@ Unicode aws -aws-sigv4 with query +aws-sigv4 with query using unicode "http://fake.fake.fake:8000/%TESTNUMBER/a=%hex[%e3%81%82]hex%" -u user:secret --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test504 b/tests/data/test504 index 457dfb6d3848..c4670ee1b174 100644 --- a/tests/data/test504 +++ b/tests/data/test504 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy multi -FAILURE connect to non-listen diff --git a/tests/data/test507 b/tests/data/test507 index c06743f30c9d..5915ef70422b 100644 --- a/tests/data/test507 +++ b/tests/data/test507 @@ -4,7 +4,6 @@ HTTP multi -FAILURE non-existing host diff --git a/tests/data/test527 b/tests/data/test527 deleted file mode 100644 index bcc0d8781844..000000000000 --- a/tests/data/test527 +++ /dev/null @@ -1,64 +0,0 @@ - - - - -FTP -PASV -RETR -multi - - - -# Server-side - - -file contents should appear once for each file - - -file contents should appear once for each file -file contents should appear once for each file -file contents should appear once for each file -file contents should appear once for each file - - - -# Client-side - - -ftp - - -lib526 - - -FTP RETR same file using different handles but same connection - - -ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER - - - -# Verify data after the test has been "shot" - - -USER anonymous -PASS ftp@example.com -PWD -CWD path -EPSV -TYPE I -SIZE %TESTNUMBER -RETR %TESTNUMBER -EPSV -SIZE %TESTNUMBER -RETR %TESTNUMBER -EPSV -SIZE %TESTNUMBER -RETR %TESTNUMBER -EPSV -SIZE %TESTNUMBER -RETR %TESTNUMBER -QUIT - - - diff --git a/tests/data/test538 b/tests/data/test538 index 2ee171b5b1a0..69e6fa931c35 100644 --- a/tests/data/test538 +++ b/tests/data/test538 @@ -3,7 +3,6 @@ FTP -FAILURE multi diff --git a/tests/data/test594 b/tests/data/test594 index 98db071f2649..aaa007cbde8a 100644 --- a/tests/data/test594 +++ b/tests/data/test594 @@ -12,7 +12,6 @@ multi EPRT refused NODATACONN timeout -FAILURE diff --git a/tests/data/test604 b/tests/data/test604 index e6d6371945d5..c901cb6fbf2e 100644 --- a/tests/data/test604 +++ b/tests/data/test604 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test605 b/tests/data/test605 index ea7af94af466..ba708afeffbb 100644 --- a/tests/data/test605 +++ b/tests/data/test605 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test606 b/tests/data/test606 index 48286e63e6c9..95e9f3d6b3cf 100644 --- a/tests/data/test606 +++ b/tests/data/test606 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test607 b/tests/data/test607 index 92ffe48e5596..960cb0b2aaaa 100644 --- a/tests/data/test607 +++ b/tests/data/test607 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test609 b/tests/data/test609 index efd8496f3264..c5c7335f735d 100644 --- a/tests/data/test609 +++ b/tests/data/test609 @@ -4,7 +4,6 @@ SFTP post-quote -FAILURE diff --git a/tests/data/test611 b/tests/data/test611 deleted file mode 100644 index c64d5ba94de1..000000000000 --- a/tests/data/test611 +++ /dev/null @@ -1,42 +0,0 @@ - - - - -SFTP -post-quote - - - -# Server-side - - -Dummy test file for rename test - - - -# Client-side - - -sftp - - -%PERL %SRCDIR/libtest/test610.pl mkdir %PWD/%LOGDIR/test%TESTNUMBER.dir - - -SFTP post-quote rename - - ---key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-rename %SFTP_PWD/%LOGDIR/test%TESTNUMBER.dir %SFTP_PWD/%LOGDIR/test%TESTNUMBER.new" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure - - -Dummy test file for rename test - - - -# Verify data after the test has been "shot" - - -%PERL %SRCDIR/libtest/test610.pl rmdir %PWD/%LOGDIR/test%TESTNUMBER.new - - - diff --git a/tests/data/test615 b/tests/data/test615 index 82307392ba65..8e2a6760b736 100644 --- a/tests/data/test615 +++ b/tests/data/test615 @@ -4,7 +4,6 @@ SFTP SFTP put -FAILURE diff --git a/tests/data/test620 b/tests/data/test620 index 0136b6e2bcc3..8ed5784e2d9d 100644 --- a/tests/data/test620 +++ b/tests/data/test620 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test621 b/tests/data/test621 index 33290e9953de..d15974cbd626 100644 --- a/tests/data/test621 +++ b/tests/data/test621 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test622 b/tests/data/test622 index 97d0075b971d..33a73dee0315 100644 --- a/tests/data/test622 +++ b/tests/data/test622 @@ -4,7 +4,6 @@ SFTP SFTP put -FAILURE diff --git a/tests/data/test623 b/tests/data/test623 index 94f0ddc55b61..cbafe3811e51 100644 --- a/tests/data/test623 +++ b/tests/data/test623 @@ -4,7 +4,6 @@ SCP SCP upload -FAILURE diff --git a/tests/data/test626 b/tests/data/test626 index 3ded7bf87467..2ec6f5d48b14 100644 --- a/tests/data/test626 +++ b/tests/data/test626 @@ -4,7 +4,6 @@ SFTP pre-quote -FAILURE diff --git a/tests/data/test628 b/tests/data/test628 index 5394d00c6df7..c343108e2dbc 100644 --- a/tests/data/test628 +++ b/tests/data/test628 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test629 b/tests/data/test629 index 0c9e32ae946f..2869517e8f9e 100644 --- a/tests/data/test629 +++ b/tests/data/test629 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test630 b/tests/data/test630 index 8d5c1f8e2971..e5e2fd8fa4d3 100644 --- a/tests/data/test630 +++ b/tests/data/test630 @@ -3,7 +3,6 @@ SFTP -FAILURE server key check diff --git a/tests/data/test631 b/tests/data/test631 index 33e7fff9b5d5..813c008e5537 100644 --- a/tests/data/test631 +++ b/tests/data/test631 @@ -3,7 +3,6 @@ SCP -FAILURE server key check diff --git a/tests/data/test632 b/tests/data/test632 index 0520bcea0bb6..1c67dff5d801 100644 --- a/tests/data/test632 +++ b/tests/data/test632 @@ -3,7 +3,6 @@ SFTP -FAILURE server key check diff --git a/tests/data/test639 b/tests/data/test639 index e87b2c931d24..529e70515a78 100644 --- a/tests/data/test639 +++ b/tests/data/test639 @@ -25,7 +25,7 @@ sftp %PERL %SRCDIR/libtest/test610.pl mkdir %PWD/%LOGDIR/test%TESTNUMBER.dir -SFTP post-quote rename * asterisk accept-fail +SFTP post-quote rename error with accept-fail --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-*rename %SFTP_PWD/%LOGDIR/test%TESTNUMBER-not-exists-dir %SFTP_PWD/%LOGDIR/test%TESTNUMBER.new" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure diff --git a/tests/data/test656 b/tests/data/test656 index 8f586b739d13..4e1599baccf9 100644 --- a/tests/data/test656 +++ b/tests/data/test656 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test688 b/tests/data/test688 index 9de1bab933dc..44f621e33c49 100644 --- a/tests/data/test688 +++ b/tests/data/test688 @@ -36,7 +36,7 @@ xattr CURL_FAKE_XATTR=1 -basic --xattr with -O +basic --xattr with (uppercase) -O --xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test702 b/tests/data/test702 index 7b899884c5c0..ea565d4be54f 100644 --- a/tests/data/test702 +++ b/tests/data/test702 @@ -6,7 +6,6 @@ HTTP SOCKS4 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test703 b/tests/data/test703 index e3ce17e142f2..197b0da2b4ab 100644 --- a/tests/data/test703 +++ b/tests/data/test703 @@ -6,7 +6,6 @@ HTTP SOCKS5 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test704 b/tests/data/test704 index 0decb7cddff4..5ef11ea6f098 100644 --- a/tests/data/test704 +++ b/tests/data/test704 @@ -6,7 +6,6 @@ HTTP SOCKS4 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test705 b/tests/data/test705 index b2d1642ba9c0..c86289be6756 100644 --- a/tests/data/test705 +++ b/tests/data/test705 @@ -6,7 +6,6 @@ HTTP SOCKS5 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test708 b/tests/data/test708 index 8c52751423f7..e96900b52bd4 100644 --- a/tests/data/test708 +++ b/tests/data/test708 @@ -39,7 +39,7 @@ socks4 all_proxy=socks4://%HOSTIP:%SOCKSPORT -HTTP GET via SOCKS4 proxy +HTTP GET via SOCKS4 all_proxy http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test75 b/tests/data/test75 index 0ba5e782c55e..b76cefbf77d9 100644 --- a/tests/data/test75 +++ b/tests/data/test75 @@ -5,7 +5,6 @@ HTTP HTTP GET globbing -FAILURE # Server-side diff --git a/tests/data/test758 b/tests/data/test758 index df39300fe2ae..08f024fecd61 100644 --- a/tests/data/test758 +++ b/tests/data/test758 @@ -39,7 +39,7 @@ https lib%TESTNUMBER -multi_socket interface transfer with callbacks returning error +HTTPS multi_socket interface transfer with callbacks returning error https://localhost:%HTTPSPORT/file%TESTNUMBER diff --git a/tests/data/test800 b/tests/data/test800 index 512f9bbcae2a..2c4d97088efa 100644 --- a/tests/data/test800 +++ b/tests/data/test800 @@ -27,7 +27,7 @@ body imap -IMAP FETCH message +IMAP FETCH message with MAILINDEX 'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER/;MAILINDEX=1' -u '"user:sec"ret{' diff --git a/tests/data/test803 b/tests/data/test803 index 20f84f83d7a8..6c4c67697cf1 100644 --- a/tests/data/test803 +++ b/tests/data/test803 @@ -6,7 +6,6 @@ IMAP Clear Text SELECT UIDVALIDITY -FAILURE diff --git a/tests/data/test847 b/tests/data/test847 index e175ba106218..8ee2a22b38c3 100644 --- a/tests/data/test847 +++ b/tests/data/test847 @@ -27,7 +27,7 @@ body imap -IMAP FETCH message +IMAP FETCH message with UID 'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER/;UID=1' -u '"user:sec"ret{' diff --git a/tests/data/test852 b/tests/data/test852 index 43779dcdc898..64e2ad33ada2 100644 --- a/tests/data/test852 +++ b/tests/data/test852 @@ -5,7 +5,6 @@ POP3 Clear Text LIST -FAILURE diff --git a/tests/data/test855 b/tests/data/test855 index 966eeaa8226a..3e01b3221670 100644 --- a/tests/data/test855 +++ b/tests/data/test855 @@ -5,7 +5,6 @@ POP3 Clear Text RETR -FAILURE diff --git a/tests/data/test856 b/tests/data/test856 index 1bd80741937c..cf8602b5ae16 100644 --- a/tests/data/test856 +++ b/tests/data/test856 @@ -4,7 +4,6 @@ POP3 Clear Text -FAILURE diff --git a/tests/data/test87 b/tests/data/test87 index 620ae384886c..8477513f0ad5 100644 --- a/tests/data/test87 +++ b/tests/data/test87 @@ -6,7 +6,6 @@ HTTP HTTP GET globbing [] range -FAILURE # Server-side diff --git a/tests/data/test94 b/tests/data/test94 index bca0a8e1cc71..30c370ef9fe5 100644 --- a/tests/data/test94 +++ b/tests/data/test94 @@ -6,7 +6,6 @@ HTTPS HTTP GET HTTP CONNECT HTTP proxy -FAILURE # Server-side diff --git a/tests/data/test962 b/tests/data/test962 index 07bb95905c70..a3fb2c99a8e6 100644 --- a/tests/data/test962 +++ b/tests/data/test962 @@ -25,7 +25,7 @@ codeset-utf8 LC_ALL=C.UTF-8 -SMTP without SMTPUTF8 support - UTF-8 based sender (host part only) +SMTP without SMTPUTF8 support - UTF-8 based sender, with IDN From: different diff --git a/tests/data/test99 b/tests/data/test99 index 6a4f27677496..4582eba4065d 100644 --- a/tests/data/test99 +++ b/tests/data/test99 @@ -6,7 +6,6 @@ HTTP HTTP GET Resume Largefile -FAILURE # Server-side From 084ceb66018e514eb33233fb42b3d62cae77384f Mon Sep 17 00:00:00 2001 From: A Johnston Date: Mon, 1 Jun 2026 14:52:23 -0700 Subject: [PATCH 0362/1062] hsts: duplicate live HSTS data in curl_easy_duphandle Verified by test 1922 Closes #21809 --- docs/libcurl/curl_easy_duphandle.md | 4 +- lib/easy.c | 5 ++ lib/hsts.c | 19 +++++ lib/hsts.h | 1 + tests/data/Makefile.am | 2 +- tests/data/test1922 | 91 ++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib1922.c | 123 ++++++++++++++++++++++++++++ 8 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 tests/data/test1922 create mode 100644 tests/libtest/lib1922.c diff --git a/docs/libcurl/curl_easy_duphandle.md b/docs/libcurl/curl_easy_duphandle.md index 5ea9c4fba018..0de33496cacd 100644 --- a/docs/libcurl/curl_easy_duphandle.md +++ b/docs/libcurl/curl_easy_duphandle.md @@ -42,7 +42,9 @@ SSL sessions and no cookies. It also does not inherit any share object states or options (created as if CURLOPT_SHARE(3) was set to NULL). If the source handle has HSTS or alt-svc enabled, the duplicate gets data read -data from the main filename to populate the cache. +from the main filename to populate the cache. For HSTS, any entries learned at +runtime (E.g. `Strict-Transport-Security` response headers) are also copied to +the duplicate handle. In multi-threaded programs, this function must be called in a synchronous way, the input handle may not be in use when cloned. diff --git a/lib/easy.c b/lib/easy.c index 8d6124195242..d60bdaed7ba3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1052,6 +1052,11 @@ CURL *curl_easy_duphandle(CURL *curl) (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, outcurl->set.str[STRING_HSTS]); (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); + + /* Copy entries learned at runtime. (E.g. Strict-Transport-Security + headers.) */ + if(Curl_hsts_copy(outcurl->hsts, data->hsts)) + goto fail; } #endif diff --git a/lib/hsts.c b/lib/hsts.c index a8e6bcae43bc..0884ef589324 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -130,6 +130,25 @@ static CURLcode hsts_create(struct hsts *h, return CURLE_OK; } +/* Copy all live entries from src into dst. Used by curl_easy_duphandle so the + * clone inherits entries learned at runtime. E.g. Strict-Transport-Security. + */ +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src) +{ + struct Curl_llist_node *e; + time_t now = time(NULL); + for(e = Curl_llist_head(&src->list); e; e = Curl_node_next(e)) { + struct stsentry *sts = Curl_node_elem(e); + if(sts->expires > now) { + CURLcode result = hsts_create(dst, sts->host, strlen(sts->host), + sts->includeSubDomains != 0, sts->expires); + if(result) + return result; + } + } + return CURLE_OK; +} + /* * Return the matching HSTS entry, or NULL if the given hostname is not * currently an HSTS one. diff --git a/lib/hsts.h b/lib/hsts.h index d4c7fe826b13..08215f5eaab8 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -54,6 +54,7 @@ struct hsts { struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 8c693f4e7f89..dfb238345dd9 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -231,7 +231,7 @@ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \ test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \ -test1916 test1917 test1918 test1919 test1920 test1921 \ +test1916 test1917 test1918 test1919 test1920 test1921 test1922 \ \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ diff --git a/tests/data/test1922 b/tests/data/test1922 new file mode 100644 index 000000000000..dcf30557ffe1 --- /dev/null +++ b/tests/data/test1922 @@ -0,0 +1,91 @@ + + + + +HTTP +HTTP proxy +HSTS +curl_easy_duphandle + + + + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/plain +Content-Length: 5 +Strict-Transport-Security: max-age=31536000 + +Hello + + + + +HTTP/1.1 403 Forbidden +Content-Length: 0 +Connection: close + + + + + + +HSTS +https +Debug +proxy + + +http +http-proxy + + +CURL_HSTS_HTTP=yes + + +curl_easy_duphandle copies HSTS cache, auto upgrading HTTP to HTTPS. + + +lib%TESTNUMBER + + +- %HOSTIP %HTTPPORT %PROXYPORT + + + + +# First request: original handle GETs from the http server; the response +# carries Strict-Transport-Security, populating the live HSTS cache that +# the dup inherits. + +GET /%TESTNUMBER HTTP/1.1 +Host: hsts.example.com:%HTTPPORT +Accept: */* + + +# Second request: dup handle upgraded HTTP to HTTPS by copied HSTS cache, +# proxy receives CONNECT to port 443 proving the upgrade happened + +CONNECT hsts.example.com:443 HTTP/1.1 +Host: hsts.example.com:443 +Proxy-Connection: Keep-Alive + + + +First request: HTTPS cache populated +Dup effective URL: https://hsts.example.com/%TESTNUMBER + +# CURLE_COULDNT_CONNECT (7) is intentional: The proxy rejects the CONNECT +# to port 443, collapsing the tunnel. All that is being validated is the +# CONNECT to port 443 itself. + +7 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index d9a94a1e715b..22fff2272f60 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -104,6 +104,7 @@ TESTS_C = \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ + lib1922.c \ lib1933.c lib1934.c lib1935.c lib1936.c lib1937.c lib1938.c lib1939.c \ lib1940.c lib1945.c \ lib1947.c lib1948.c \ diff --git a/tests/libtest/lib1922.c b/tests/libtest/lib1922.c new file mode 100644 index 000000000000..fffb6d4fcc6d --- /dev/null +++ b/tests/libtest/lib1922.c @@ -0,0 +1,123 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static size_t test_lib1922_discard_write(char *ptr, size_t size, size_t nmemb, + void *ud) +{ + (void)ptr; (void)ud; + return size * nmemb; +} + +static CURLcode test_lib1922(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + CURL *dup = NULL; + struct curl_slist *resolve = NULL; + char resolve_entry[256]; + char direct_url[256]; + char http_url[256]; + char proxy_url[256]; + const char *effective = NULL; + const char *host = libtest_arg2; /* %HOSTIP */ + const char *httpport = libtest_arg3; /* %HTTPPORT */ + const char *proxyport = libtest_arg4;/* %PROXYPORT */ + + (void)URL; + + if(!host || !httpport || !proxyport) { + curl_mfprintf(stderr, + "Usage: lib1922 - \n"); + return TEST_ERR_MAJOR_BAD; + } + + /* Synthetic DNS so hsts.example.com resolves to the test server. */ + curl_msnprintf(resolve_entry, sizeof(resolve_entry), + "hsts.example.com:%s:%s", httpport, host); + resolve = curl_slist_append(NULL, resolve_entry); + if(!resolve) { + return CURLE_OUT_OF_MEMORY; + } + + curl_msnprintf(direct_url, sizeof(direct_url), + "http://hsts.example.com:%s/%d", httpport, 1922); + curl_msnprintf(http_url, sizeof(http_url), + "http://hsts.example.com/%d", 1922); + curl_msnprintf(proxy_url, sizeof(proxy_url), + "http://%s:%s", host, proxyport); + + global_init(CURL_GLOBAL_ALL); + easy_init(curl); + + easy_setopt(curl, CURLOPT_WRITEFUNCTION, test_lib1922_discard_write); + easy_setopt(curl, CURLOPT_RESOLVE, resolve); + easy_setopt(curl, CURLOPT_URL, direct_url); + easy_setopt(curl, CURLOPT_HSTS_CTRL, CURLHSTS_ENABLE); + + /* Direct HTTP request: Server returns Strict-Transport-Security. + * CURL_HSTS_HTTP env var (set in the test) allows processing it over + * HTTP in debug builds, populating the live HSTS cache. */ + result = curl_easy_perform(curl); + if(result) { + curl_mfprintf(stderr, "First perform failed: %d (%s)\n", + result, curl_easy_strerror(result)); + goto test_cleanup; + } + curl_mprintf("First request: HTTPS cache populated\n"); + + dup = curl_easy_duphandle(curl); + if(!dup) { + result = CURLE_FAILED_INIT; + goto test_cleanup; + } + + /* Point the dup at the plain HTTP URL for the same hostname, via a proxy. + * The copied HSTS cache upgrades the URL to HTTPS, causing a CONNECT to + * port 443. The test proxy rejects CONNECT with 403, so curl returns + * CURLE_COULDNT_CONNECT (7). The CONNECT to port 443 is itself the proof + * of the upgrade. */ + easy_setopt(dup, CURLOPT_URL, http_url); + easy_setopt(dup, CURLOPT_PROXY, proxy_url); + + result = curl_easy_perform(dup); + if(result != CURLE_COULDNT_CONNECT) { + curl_mfprintf(stderr, "Dup perform unexpected result: %d (%s)\n", + result, curl_easy_strerror(result)); + goto test_cleanup; + } + + /* Confirm the dup's URL was upgraded to HTTPS by the copied HSTS cache. */ + curl_easy_getinfo(dup, CURLINFO_EFFECTIVE_URL, &effective); + if(effective) { + curl_mprintf("Dup effective URL: %s\n", effective); + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_easy_cleanup(dup); + curl_slist_free_all(resolve); + curl_global_cleanup(); + return result; +} From ce53f90f2046e63b89c53473e654793d0a705a19 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 16:58:21 +0200 Subject: [PATCH 0363/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 75 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8e8be87d454b..5aa1693c98d8 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1483 - Contributors: 3710 + Authors: 1485 + Contributors: 3716 This release includes the following changes: @@ -18,6 +18,7 @@ This release includes the following changes: This release includes the following bugfixes: + o _ENVIRONMENT.md. Windows does case insensitive env variables [214] o asyn-thrdd: fix result processing without wakeup socketpair [2] o autotools: mbedtls detection fixes [163] o BINDINGS: Update Hollywood link [181] @@ -26,7 +27,9 @@ This release includes the following bugfixes: o cf-h2-prox: fix peer leak [132] o cf-h2-proxy: drop interim responses [47] o cf-socket: set scope_id for IPv6 link-local addresses [150] + o cf-socket: store errno from do_connect in ctx->error [199] o cfilters: fix busy loop on blocked transfers [72] + o chunked: reject invalid bytes in trailer [210] o CIPHERS.md: fix the example that uses only TLS 1.3 [137] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] @@ -39,6 +42,7 @@ This release includes the following bugfixes: o content_encoding: timeout during slow decoding [170] o cookie: compare path case sensitively [52] o cookie: simplify strstore(), remove outdated comment [12] + o cookie: tailmatch the domains for secure override [200] o cookie: trim trailing dots when checking PSL [39] o creds: add sasl service name [75] o creds: mask OAuth bearer token in trace logs [117] @@ -50,6 +54,7 @@ This release includes the following bugfixes: o curl_sha512_256: fix result code on error [166] o CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only [156] o CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only [61] + o CURLOPT_DOH_URL.md: does not inherit proxy options [213] o CURLOPT_ECH.md: simplify the description language [18] o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] @@ -58,6 +63,7 @@ This release includes the following bugfixes: o CURLOPT_SHARE: warn about early remove [51] o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] o delta: harden external command invocations [98] + o digest: escape control codes too [206] o dnscache: remove Curl_dns_entry_link [160] o docs/libcurl: fix the version for curl_multi_socket_action o docs: end "...can be used several times..." sentences with period [34] @@ -66,6 +72,7 @@ This release includes the following bugfixes: o docs: fix grammar and wording in FAQ [66] o docs: fix odd wording in CONTRIBUTE.md [107] o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] + o docs: returned header size reflects HTTP/1-style format [203] o ECH: cleanups [20] o event: fix wakeup consumption [93] o ftp: avoid accessing EPSV response one byte past the NULL [9] @@ -84,6 +91,8 @@ This release includes the following bugfixes: o h3-proxy: fix callback return values, and a typo in tests [139] o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] o hsts.md: mention multiple curl invokes effect [189] + o hsts: duplicate live HSTS data in curl_easy_duphandle [183] + o http-proxy: verify CONNECT response headers [192] o http: don't pass on set cookies to new origins [140] o http: prefer chunked encoding over Content-Length: 0 [146] o http: reject spurious CR bytes in headers [157] @@ -99,6 +108,7 @@ This release includes the following bugfixes: o libcurl-easy.md: minor clarifications [19] o libssh2: do not use deprecated macros when unavailable [177] o libssh2: replace macro names with non-misspelled alternatives [169] + o libssh2: save non-standard port to `known_hosts` [217] o libssh2: sync version check with INTERNALS.md [176] o libssh2: use non-deprecated `libssh2_knownhost_addc()` [178] o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] @@ -110,14 +120,18 @@ This release includes the following bugfixes: o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] o multi: handle pause in multi socket callback [109] o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] + o netrc: remember and check filename loaded [212] o netrc: scanner refactor [121] o ngtcp2: fail handshake directly [138] o os400sys: fix theoretical length overflows [141] + o peer.h: fix typo in comment [202] o progress: fix CURLINFO time reporting [145] + o psl: require libpsl 0.16.0 (2016-12-10) or greater [188] o pytest: pass `--disable` to curl [175] o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o quic: count zero length packets against max [179] + o resolve: mention in error that IP address is expected [205] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] @@ -132,15 +146,18 @@ This release includes the following bugfixes: o scripts: catch Credits-to contributors [127] o setopt: changing the proxy port is also a proxy change [23] o setopt: clear proxy auth properly on NULL [81] + o setopt: clear the "custom" CA booleans when set to NULL [218] o setopt: CURLOPT_MAXCONNECTS set to 0 restores default value [161] o setopt: defref the old referer when setting a new [168] o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] o show-headers.md: mention bold headers and --no-styled-output [17] + o sigv4: URL encode the user name in the header [193] o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o src: fix comment typos [83] + o ssl native_ca_store: always reinit [211] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] @@ -166,6 +183,7 @@ This release includes the following bugfixes: o tool_urlglob: better 'Duplicate glob name' position [82] o tool_urlglob: make globbing error reported for correct position [91] o transfer: clear referer when set to NULL [112] + o unit1675: fix potential memory leak on dynbuf fail path [197] o unix-sockets: ignore proxy settings [6] o URL-SYNTAX: document more URL parsing details [134] o url: compare full origin when setting credentials [42] @@ -173,7 +191,6 @@ This release includes the following bugfixes: o url: detect proxy changes read from environment [110] o url: fix connection reuse for starttls protocols [27] o url: keep the question mark for empty queries [73] - o url: remove ssh_config_matches [31] o url: remove superfluous check [131] o url: url_match_destination fix [43] o urlapi: accept 0X prefix in IPv4 address as well [63] @@ -183,10 +200,13 @@ This release includes the following bugfixes: o urlapi: deny hostnames with more than one trailing dot [58] o urlapi: drop base fragment on empty redirect [64] o urlapi: fix an issue parsing file URLs [149] + o urlapi: fix memleaks on error in `parse_hostname_login()` [221] o urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set [46] o urlapi: forbid '|' in host [172] o urlapi: handle redirect without set scheme with default-scheme [38] + o urlapi: URL decode hostname before IP address normalization [207] o user-agent.md: mention double quotes too [3] + o var: use a dedicated pointer for the alloc [219] o vquic: drop stray casts for `iovec.iov_len` [162] o vtls: more large buffer support and error checks for SHA-256 [164] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] @@ -197,6 +217,7 @@ This release includes the following bugfixes: o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] o websockets: auto-tunnel through http proxy [102] o windows: update MS SDK versions in comments [60] + o ws: make pong sending lazy [201] o x509asn1: fix DH public key parameter extraction [44] o x509asn1: fix operator order in do_pubkey [21] @@ -220,24 +241,25 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 0xN3R3K3, 11soda11, Ady Elouej, Alan De Smet, ambikeesshh, amitbidlan, - Andreas Falkenhahn, Andrei Rybak, Andrew Nesbitt, Aritra Basu, - azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, - BazaarAcc32 on github, Bill Mill, chrizilla on github, co-authors in libssh2, - Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, - dependabot[bot], Earnestly on github, Elise Vance, Emanuel Krollmann, - Eunsoo Kim, Fabian Keil, Gao Liyou, Guancheng Li, Guannan Wang, - Harry Sintonen, htasta, jeffhuang, Jeremy Nicoll, Jiashuo Liang, + 0xN3R3K3, 11soda11, Ady Elouej, A Johnston, Alan De Smet, alhudz, + ambikeesshh, amitbidlan, Andreas Falkenhahn, Andrei Rybak, Andrew Nesbitt, + Aritra Basu, azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, + BazaarAcc32 on github, Bill Mill, ByteRay on hackerone, chrizilla on github, + co-authors in libssh2, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, + Dario Vinella, dependabot[bot], dyingc on github, Earnestly on github, + Elise Vance, Emanuel Krollmann, Eunsoo Kim, evergarden1123 on hackerone, + Fabian Keil, Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, + Hem Parekh, htasta, jeffhuang, Jeremy Nicoll, Jiashuo Liang, Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, Mark Esler, Max Dymond, mik, Mike-menny on github, Muhamad Arga Reksapati, mulan_dh on hackerone, parasol-aser, penpal, Peter Krefting, Randall S. Becker, Raymond Steen, Ray Satiro, renjian on hackerone, renovate[bot], Ross Burton, Sergio Correia, sfan5 on github, Shintomon Mathew, Sollace on github, Song X. Gao, Stefan Eissing, Tim Martin, - tiymat, Vasiliy-Kkk, vectorqueue on hackerone, vegagent on hackerone, - Viktor Szakats, Will Cosgrove, Xi Ruoyao, x-xiang on github, - zhanhb on github, Zhanpeng Liu - (72 contributors) + tiymat, Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, + vegagent on hackerone, Viktor Szakats, Will Cosgrove, Xi Ruoyao, + x-xiang on github, Yedaya Katsman, zhanhb on github, Zhanpeng Liu + (80 contributors) References to bug reports and discussions on issues: @@ -271,7 +293,6 @@ References to bug reports and discussions on issues: [28] = https://curl.se/bug/?i=21521 [29] = https://curl.se/bug/?i=21629 [30] = https://curl.se/bug/?i=21512 - [31] = https://curl.se/bug/?i=21519 [32] = https://curl.se/bug/?i=21517 [33] = https://curl.se/bug/?i=21518 [34] = https://curl.se/bug/?i=21644 @@ -423,6 +444,28 @@ References to bug reports and discussions on issues: [180] = https://curl.se/bug/?i=21870 [181] = https://curl.se/bug/?i=21862 [182] = https://curl.se/bug/?i=21858 + [183] = https://curl.se/bug/?i=21809 + [188] = https://curl.se/bug/?i=21933 [189] = https://curl.se/bug/?i=21851 [190] = https://curl.se/bug/?i=21850 [191] = https://curl.se/bug/?i=21773 + [192] = https://curl.se/bug/?i=21927 + [193] = https://curl.se/bug/?i=21923 + [197] = https://curl.se/bug/?i=21922 + [199] = https://curl.se/bug/?i=21914 + [200] = https://curl.se/bug/?i=21910 + [201] = https://curl.se/bug/?i=21911 + [202] = https://curl.se/bug/?i=21920 + [203] = https://curl.se/bug/?i=21912 + [205] = https://curl.se/bug/?i=21913 + [206] = https://curl.se/bug/?i=21915 + [207] = https://curl.se/bug/?i=21918 + [210] = https://curl.se/bug/?i=21896 + [211] = https://curl.se/bug/?i=21902 + [212] = https://curl.se/bug/?i=21903 + [213] = https://curl.se/bug/?i=21904 + [214] = https://curl.se/bug/?i=21907 + [217] = https://curl.se/bug/?i=21863 + [218] = https://curl.se/bug/?i=21901 + [219] = https://curl.se/bug/?i=21898 + [221] = https://curl.se/bug/?i=21879 From 2864e995435e71a152a9ae090a72df6833025a01 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 17:44:05 +0200 Subject: [PATCH 0364/1062] smbserver: update internal id generation for Python 3 Also: - make next id based on highest in list + 1. (was: last id in list + 1) - unfold a line. Spotted by GitHub Code Quality Ref: https://portingguide.readthedocs.io/en/latest/dicts.html?highlight=keys Closes #21937 --- tests/smbserver.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/smbserver.py b/tests/smbserver.py index c3eeb1105c7d..49c6162463b5 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -212,8 +212,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): flags2 = recv_packet["Flags2"] ncax_data = imp_smb.SMBNtCreateAndX_Data(flags=flags2, - data=smb_command[ - "Data"]) + data=smb_command["Data"]) requested_file = imp_smbserver.decodeSMBString( flags2, ncax_data["FileName"]) @@ -234,7 +233,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): if len(conn_data["OpenedFiles"]) == 0: fakefid = 1 else: - fakefid = conn_data["OpenedFiles"].keys()[-1] + 1 + fakefid = max(conn_data["OpenedFiles"].keys()) + 1 resp_params["Fid"] = fakefid resp_params["CreateAction"] = disposition From 81cdf4d8e5be60bf3fe498c823c34a42f2b08294 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 19:57:38 +0200 Subject: [PATCH 0365/1062] appveyor: bump 3 VS2022 jobs to VS2026 Also: - install CMake 4.2.1 manually for VS2026 jobs, because the preinstalled version (4.1.2) does not yet support the compatible generator. - VisualStudioSolution VS2010 job to VS2015 worker image (from VS2013). VS2013 is no longer listed on the AppVeyor support page. - downgrade OpenSSL to 3.5 (from 3.6) for the VS2022 job, to add variation. Note: the jobs run much slower after bumping to VS2026. This seems to be due slower configure and build steps. Refs: https://github.com/appveyor/website/commit/9ef4152eda92d0f8a486ab67824a6d420e1151e4 https://github.com/appveyor/website/pull/912 https://github.com/appveyor/build-images/commit/fa7f7b928ebee4e2cfc6eccc953d2dec95374114 https://github.com/appveyor/build-images/pull/175 https://www.appveyor.com/docs/windows-images-software/ https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2018%202026.html Closes #21939 --- appveyor.sh | 7 ++++--- appveyor.yml | 28 +++++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/appveyor.sh b/appveyor.sh index 56bfc8848d6c..eed6295349f8 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -39,13 +39,14 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then *) openssl_suffix='-Win64';; esac - if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then + if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2026' ]; then openssl_root_win="C:/OpenSSL-v36${openssl_suffix}" - openssl_root="$(cygpath "${openssl_root_win}")" + elif [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then + openssl_root_win="C:/OpenSSL-v35${openssl_suffix}" elif [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2019' ]; then openssl_root_win="C:/OpenSSL-v30${openssl_suffix}" - openssl_root="$(cygpath "${openssl_root_win}")" fi + [ -n "${openssl_root_win:-}" ] && openssl_root="$(cygpath "${openssl_root_win}")" # Install custom cmake version if [ -n "${CMAKE_VERSION:-}" ]; then diff --git a/appveyor.yml b/appveyor.yml index 899bb497c4c7..9335da3892c0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,14 +36,16 @@ environment: matrix: # CMake Visual Studio builds - - job_name: 'CM VS2022, Release, x64, OpenSSL 3.6, Shared, Build-tests' + - job_name: 'CM VS2022, Release, x64, OpenSSL 3.5, Shared, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' CMAKE_GENERATOR: 'Visual Studio 17 2022' CMAKE_GENERATE: '-A x64 -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON' - - job_name: 'CM VS2022, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A ARM64 -DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=OFF' - job_name: 'CM VS2010, Debug, x64, Schannel, Shared, Build-tests & examples' @@ -86,9 +88,11 @@ environment: CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATE: '-A x64 -DCURL_USE_OPENSSL=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON' - - job_name: 'CM VS2022, Debug, x64, OpenSSL 3.6 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Debug, x64, OpenSSL 3.5 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A x64 -T ClangCl -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=ON' - job_name: 'CM VS2022, Release, x64, Schannel, Shared, Unicode, !DEBUGBUILD, Build-tests' @@ -97,9 +101,11 @@ environment: ENABLE_UNICODE: 'ON' CMAKE_GENERATE: '-A x64 -DENABLE_UNICODE=ON -DENABLE_DEBUG=OFF' - - job_name: 'CM VS2022, Debug, x64, !ssl, Static, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Debug, x64, !ssl, Static, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A x64 -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=OFF' - job_name: 'CM VS2022, Debug, x64, !ssl, Static, HTTP-only, Build-tests' @@ -110,7 +116,7 @@ environment: # VisualStudioSolution builds - job_name: 'VisualStudioSolution VS2010, Release, x86, Schannel' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2013' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' PRJ_CFG: 'DLL Release - DLL Windows SSPI - DLL WinIDN' PLAT: 'Win32' VC_VERSION: VC10 From 3f1055303e57eb22c01aae638588b96e9426c9db Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Jun 2026 18:44:21 +0200 Subject: [PATCH 0366/1062] tests: restore 1030 and 611 They were not exact duplicates. Tweaked their names to clarify. Also renamed 608 accordingly. Follow-up to 4ead4285a6af5d5645d4a Spotted-by: Dan Fandrich Closes #21938 --- tests/data/Makefile.am | 4 +- tests/data/test1030 | 108 +++++++++++++++++++++++++++++++++++++++++ tests/data/test608 | 2 +- tests/data/test611 | 42 ++++++++++++++++ 4 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 tests/data/test1030 create mode 100644 tests/data/test611 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index dfb238345dd9..10d72fd7c18d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -92,7 +92,7 @@ test573 test574 test575 test576 test577 test578 test579 test580 test581 \ test582 test583 test584 test585 test586 test587 test588 test589 test590 \ test591 test592 test593 test594 test595 test596 test597 test598 test599 \ test600 test601 test602 test603 test604 test605 test606 test607 test608 \ -test609 test610 test612 test613 test614 test615 test616 test617 \ +test609 test610 test611 test612 test613 test614 test615 test616 test617 \ test618 test619 test620 test621 test622 test623 test624 test625 test626 \ test627 test628 test629 test630 test631 test632 test633 test634 test635 \ test636 test637 test638 test639 test640 test641 test642 test643 test644 \ @@ -139,7 +139,7 @@ test997 test998 test999 test1000 test1001 test1002 test1003 test1004 \ test1005 test1006 test1007 test1008 test1009 test1010 test1011 test1012 \ test1013 test1014 test1015 test1016 test1017 test1018 test1019 test1020 \ test1021 test1022 test1023 test1024 test1025 test1026 test1027 test1028 \ -test1029 test1031 test1032 test1033 test1034 test1035 test1036 \ +test1029 test1030 test1031 test1032 test1033 test1034 test1035 test1036 \ test1037 test1038 test1039 test1040 test1041 test1042 test1043 test1044 \ test1045 test1046 test1047 test1048 test1049 test1050 test1051 test1052 \ test1053 test1054 test1055 test1056 test1057 test1058 test1059 test1060 \ diff --git a/tests/data/test1030 b/tests/data/test1030 new file mode 100644 index 000000000000..cb35a0f085cb --- /dev/null +++ b/tests/data/test1030 @@ -0,0 +1,108 @@ + + + + +HTTP +HTTP PUT +HTTP Digest auth +--anyauth + + + +# Server-side + + +HTTP/1.1 401 Authorization Required +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Blackmagic realm="gimme all yer s3cr3ts" +WWW-Authenticate: Basic realm="gimme all yer s3cr3ts" +WWW-Authenticate: Digest realm="gimme all yer s3cr3ts", nonce="11223344" +Content-Length: 26 +Content-Type: text/html; charset=iso-8859-1 + +This is not the real page + + +# This is supposed to be returned when the server gets a +# Authorization: Digest line passed-in from the client + +HTTP/1.1 200 OK +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 +Connection: close + +This IS the real page! + + + +HTTP/1.1 401 Authorization Required +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Blackmagic realm="gimme all yer s3cr3ts" +WWW-Authenticate: Basic realm="gimme all yer s3cr3ts" +WWW-Authenticate: Digest realm="gimme all yer s3cr3ts", nonce="11223344" +Content-Length: 26 +Content-Type: text/html; charset=iso-8859-1 + +HTTP/1.1 200 OK +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 +Connection: close + +This IS the real page! + + + + +# Client-side + + +http + + +!SSPI +crypto +digest + + +HTTP PUT with --anyauth, picking Digest. Persistent connection. + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -T %LOGDIR/put%TESTNUMBER -u testuser:testpass --anyauth + + +This is data we upload with PUT +a second line +line three +four is the number of lines + + + +# Verify data after the test has been "shot" + + +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 85 + +This is data we upload with PUT +a second line +line three +four is the number of lines +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Digest username="testuser", realm="gimme all yer s3cr3ts", nonce="11223344", uri="/%TESTNUMBER", response="01cb59db1ddaac246b072d5f5f0716d9" +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 85 + +This is data we upload with PUT +a second line +line three +four is the number of lines + + + diff --git a/tests/data/test608 b/tests/data/test608 index 5ac7a67d96c0..9e35595e763f 100644 --- a/tests/data/test608 +++ b/tests/data/test608 @@ -20,7 +20,7 @@ Test file for rename test sftp -SFTP post-quote rename +SFTP post-quote rename a file --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-rename %SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt %SFTP_PWD/%LOGDIR/file%TESTNUMBER-renamed.txt" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure diff --git a/tests/data/test611 b/tests/data/test611 new file mode 100644 index 000000000000..3de6009e5230 --- /dev/null +++ b/tests/data/test611 @@ -0,0 +1,42 @@ + + + + +SFTP +post-quote + + + +# Server-side + + +Dummy test file for rename test + + + +# Client-side + + +sftp + + +%PERL %SRCDIR/libtest/test610.pl mkdir %PWD/%LOGDIR/test%TESTNUMBER.dir + + +SFTP post-quote rename a directory + + +--key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-rename %SFTP_PWD/%LOGDIR/test%TESTNUMBER.dir %SFTP_PWD/%LOGDIR/test%TESTNUMBER.new" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure + + +Dummy test file for rename test + + + +# Verify data after the test has been "shot" + + +%PERL %SRCDIR/libtest/test610.pl rmdir %PWD/%LOGDIR/test%TESTNUMBER.new + + + From 5c6b4880357ab3e72967c1c45cae0f96ffabc535 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 10:27:50 +0200 Subject: [PATCH 0367/1062] digest: flush state on origin or credential change Verified by test 1686 Closes #21944 --- lib/http_digest.c | 16 +++++++ lib/urldata.h | 5 +- lib/vauth/digest.c | 2 + lib/vauth/digest_sspi.c | 1 + tests/data/Makefile.am | 2 +- tests/data/test1686 | 84 +++++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib1686.c | 96 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 tests/data/test1686 create mode 100644 tests/libtest/lib1686.c diff --git a/lib/http_digest.c b/lib/http_digest.c index d20e16562354..6949b3bd0d02 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -94,6 +94,22 @@ CURLcode Curl_output_digest(struct Curl_easy *data, #endif } else { + bool flush = FALSE; + DEBUGASSERT(data->conn->origin); + if(data->state.digest.origin && + !Curl_peer_same_destination(data->conn->origin, + data->state.digest.origin)) + flush = TRUE; + else if(data->state.digest.creds && + !Curl_creds_same(data->state.creds, data->state.digest.creds)) + flush = TRUE; + + if(flush) + /* flush host Digest state */ + Curl_auth_digest_cleanup(&data->state.digest); + + Curl_peer_link(&data->state.digest.origin, data->conn->origin); + Curl_creds_link(&data->state.digest.creds, data->state.creds); digest = &data->state.digest; allocuserpwd = &data->req.hd_auth; creds = data->state.creds; diff --git a/lib/urldata.h b/lib/urldata.h index e5363cf56964..f746b5c3011c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -148,13 +148,12 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { + struct Curl_creds *creds; + struct Curl_peer *origin; #ifdef USE_WINDOWS_SSPI BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* linked credentials used to make the identity for http_context. - may be NULL. */ - struct Curl_creds *creds; #else char *nonce; char *cnonce; diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 6cc4edbee126..9c57affaf9b4 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -1039,6 +1039,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, */ void Curl_auth_digest_cleanup(struct digestdata *digest) { + Curl_peer_unlink(&digest->origin); + Curl_creds_unlink(&digest->creds); curlx_safefree(digest->nonce); curlx_safefree(digest->cnonce); curlx_safefree(digest->realm); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 74d654fc46e5..aca6237735a2 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -630,6 +630,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Free the copy of user/passwd used to make the identity for http_context */ Curl_creds_unlink(&digest->creds); + Curl_peer_unlink(&digest->origin); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 10d72fd7c18d..96cbda0b8b7c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -221,7 +221,7 @@ test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ test1677 test1680 test1681 test1682 test1683 test1684 \ -test1685 \ +test1685 test1686 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ diff --git a/tests/data/test1686 b/tests/data/test1686 new file mode 100644 index 000000000000..2d419ad60834 --- /dev/null +++ b/tests/data/test1686 @@ -0,0 +1,84 @@ + + + + +HTTP +Digest + + + + + +HTTP/1.1 401 Authorization Required +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Digest realm="my-backyard", nonce="314156295" +Content-Length: 26 + +This is not the real page + + +# This is supposed to be returned when the server gets a +# Authorization: Digest line passed-in from the client + +HTTP/1.1 200 OK +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 + +This IS the real page! + + + + + + +!SSPI +crypto +digest + + +http + + +HTTP Digest to different origins and switching credentials + + +lib%TESTNUMBER + + +%HOSTIP %HTTPPORT + + + + + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Accept: */* + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/api", response="4ecc00e567c37a9d537727890c2e5b32" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/hook", response="d3a7738fb6a23f5543fb8dacc0f0f253" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="bob", realm="my-backyard", nonce="314156295", uri="/hook", response="777e68eddb77294d9cbd6134973cbbab" +Accept: */* + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 22fff2272f60..98c99399944e 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -101,6 +101,7 @@ TESTS_C = \ lib1598.c lib1599.c \ lib1647.c lib1648.c lib1649.c \ lib1662.c \ + lib1686.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ diff --git a/tests/libtest/lib1686.c b/tests/libtest/lib1686.c new file mode 100644 index 000000000000..e457012bb97e --- /dev/null +++ b/tests/libtest/lib1686.c @@ -0,0 +1,96 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static size_t devnull_1686(char *p, size_t s, size_t n, void *u) +{ + (void)p; + (void)u; + return s * n; +} + +#define FIRSTHOST "first.test" +#define SECONDHOST "second.test" + +static CURLcode test_lib1686(const char *hostip) +{ + CURL *curl = NULL; + CURLcode result = CURLE_OK; + const char *httpport = libtest_arg2; + char firsturl[100]; + char secondurl[100]; + char firstres[100]; + char secondres[100]; + struct curl_slist *host = NULL; + struct curl_slist *host2 = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + /* create strings for CURLOPT_RESOLVE */ + curl_msnprintf(firstres, sizeof(firstres), "%s:%s:%s", + FIRSTHOST, httpport, hostip); + curl_msnprintf(secondres, sizeof(secondres), "%s:%s:%s", + SECONDHOST, httpport, hostip); + + /* create URLs */ + curl_msnprintf(firsturl, sizeof(firsturl), "http://%s:%s/api", + FIRSTHOST, httpport); + curl_msnprintf(secondurl, sizeof(secondurl), "http://%s:%s/hook", + SECONDHOST, httpport); + + host = curl_slist_append(NULL, firstres); + if(!host) + goto test_cleanup; + host2 = curl_slist_append(host, secondres); + if(!host2) + goto test_cleanup; + host = host2; + + curl = curl_easy_init(); + if(curl) { + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "alice:bond"); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, devnull_1686); + + easy_setopt(curl, CURLOPT_URL, firsturl); + result = curl_easy_perform(curl); + + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + + easy_setopt(curl, CURLOPT_USERPWD, "bob:secret"); + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(host); + return result; +} From 4fcf9c8f592349042374c29d3fb9611e830d26a9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 08:14:10 +0200 Subject: [PATCH 0368/1062] test 527: bring back, not a dupe Fixed the name to clarify the difference to 526. Follow-up to 4ead4285a6af5d5645d4ad Closes #21942 --- tests/data/Makefile.am | 2 +- tests/data/test527 | 64 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/data/test527 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 96cbda0b8b7c..50a5e5629e93 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -82,7 +82,7 @@ test483 test484 test485 test486 test487 test488 test489 test490 test491 \ test492 test493 test494 test495 test496 test497 test498 test499 test500 \ test501 test502 test503 test504 test505 test506 test507 test508 test509 \ test510 test511 test512 test513 test514 test515 test516 test517 test518 \ -test519 test520 test521 test522 test523 test524 test525 test526 \ +test519 test520 test521 test522 test523 test524 test525 test526 test527 \ test528 test529 test530 test531 test532 test533 test534 test535 test536 \ test537 test538 test539 test540 test541 test542 test543 test544 test545 \ test546 test547 test548 test549 test550 test551 test552 test553 test554 \ diff --git a/tests/data/test527 b/tests/data/test527 new file mode 100644 index 000000000000..735bd20e8528 --- /dev/null +++ b/tests/data/test527 @@ -0,0 +1,64 @@ + + + + +FTP +PASV +RETR +multi + + + +# Server-side + + +file contents should appear once for each file + + +file contents should appear once for each file +file contents should appear once for each file +file contents should appear once for each file +file contents should appear once for each file + + + +# Client-side + + +ftp + + +lib526 + + +FTP RETR same file using different handles and closed connections + + +ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com +PWD +CWD path +EPSV +TYPE I +SIZE %TESTNUMBER +RETR %TESTNUMBER +EPSV +SIZE %TESTNUMBER +RETR %TESTNUMBER +EPSV +SIZE %TESTNUMBER +RETR %TESTNUMBER +EPSV +SIZE %TESTNUMBER +RETR %TESTNUMBER +QUIT + + + From f924489b25034c87f3d3acde9de0f09d660a1df7 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 5 Jun 2026 12:55:50 +0200 Subject: [PATCH 0369/1062] ngtcp2: share common functionality Share common functions/structs between ngtcp2 HTTP/3 and the proxy version. Fix bugs in proxy implementation when it comes to stream and pollset handling and transfer lifetimes. Curl_multi_xfer_sockbuf_borrow: work without multi When a connection gets shutdown by a share, the easy handle used is share->admin and it does not have a multi handle. In that case let Curl_multi_xfer_sockbuf_borrow() allocate a buffer to be freed on release. This happens when a TLS filter sends its last notify through a HTTP/3 proxy tunnel. Closes #21871 --- lib/Makefile.inc | 2 + lib/cf-ip-happy.c | 182 +- lib/cf-ip-happy.h | 37 +- lib/cf-socket.c | 72 +- lib/cf-socket.h | 21 +- lib/connect.c | 69 +- lib/connect.h | 9 + lib/ftp.c | 10 +- lib/http_proxy.c | 46 +- lib/http_proxy.h | 5 +- lib/imap.c | 3 +- lib/multi.c | 20 +- lib/openldap.c | 3 +- lib/pop3.c | 3 +- lib/smtp.c | 3 +- lib/urldata.h | 2 + lib/vquic/cf-ngtcp2-cmn.c | 1965 +++++++++++++++++++++ lib/vquic/cf-ngtcp2-cmn.h | 239 +++ lib/vquic/cf-ngtcp2-proxy.c | 2970 +++++--------------------------- lib/vquic/cf-ngtcp2-proxy.h | 19 +- lib/vquic/cf-ngtcp2.c | 2176 +---------------------- lib/vquic/cf-ngtcp2.h | 14 +- lib/vquic/cf-quiche.c | 58 +- lib/vquic/cf-quiche.h | 11 +- lib/vquic/vquic-tls.c | 42 +- lib/vquic/vquic-tls.h | 9 +- lib/vquic/vquic.c | 67 +- lib/vquic/vquic.h | 24 +- lib/vtls/apple.c | 2 +- lib/vtls/gtls.c | 6 +- lib/vtls/mbedtls.c | 4 +- lib/vtls/openssl.c | 37 +- lib/vtls/rustls.c | 2 +- lib/vtls/schannel.c | 10 +- lib/vtls/schannel_verify.c | 2 +- lib/vtls/vtls.c | 110 +- lib/vtls/vtls.h | 19 +- lib/vtls/vtls_scache.c | 105 +- lib/vtls/vtls_scache.h | 22 +- lib/vtls/wolfssl.c | 6 +- tests/http/test_06_eyeballs.py | 13 + tests/unit/unit2600.c | 17 +- tests/unit/unit3304.c | 38 +- 43 files changed, 3379 insertions(+), 5095 deletions(-) create mode 100644 lib/vquic/cf-ngtcp2-cmn.c create mode 100644 lib/vquic/cf-ngtcp2-cmn.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 88ca0a1ef20a..1699fc5653d1 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -125,6 +125,7 @@ LIB_VQUIC_CFILES = \ vquic/capsule.c \ vquic/cf-capsule.c \ vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c \ vquic/cf-ngtcp2-proxy.c \ vquic/cf-quiche.c \ vquic/vquic.c \ @@ -134,6 +135,7 @@ LIB_VQUIC_HFILES = \ vquic/capsule.h \ vquic/cf-capsule.h \ vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h \ vquic/cf-ngtcp2-proxy.h \ vquic/cf-quiche.h \ vquic/vquic.h \ diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 4f1787e78458..08140b643a1c 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -62,7 +62,7 @@ struct transport_provider { cf_ip_connect_create *cf_create; uint8_t transport; - bool tunnel_proxy; + bool tunnel; }; static @@ -88,12 +88,12 @@ struct transport_provider transport_providers[] = { }; static cf_ip_connect_create *get_cf_create(uint8_t transport, - bool tunnel_proxy) + bool tunnel) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { if((transport == transport_providers[i].transport) && - (tunnel_proxy == transport_providers[i].tunnel_proxy)) + (tunnel == transport_providers[i].tunnel)) return transport_providers[i].cf_create; } return NULL; @@ -155,14 +155,17 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, struct cf_ip_attempt { struct cf_ip_attempt *next; + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; 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 */ CURLcode result; int ai_family; - uint8_t transport_in; - uint8_t transport_out; + uint8_t transport_peer; + uint8_t tunnel_transport; int error; BIT(connected); /* cf has connected */ BIT(shutdown); /* cf has shutdown */ @@ -176,17 +179,23 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a, if(a) { if(a->cf) Curl_conn_cf_discard_chain(&a->cf, data); + Curl_peer_unlink(&a->origin); + Curl_peer_unlink(&a->peer); + Curl_peer_unlink(&a->tunnel_peer); curlx_free(a); } } static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, - struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct Curl_sockaddr_ex *addr, int ai_family, - uint8_t transport_in, - uint8_t transport_out, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, cf_ip_connect_create *cf_create) { struct Curl_cfilter *wcf; @@ -198,16 +207,20 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, if(!a) return CURLE_OUT_OF_MEMORY; + Curl_peer_link(&a->origin, origin); + Curl_peer_link(&a->peer, peer); + a->transport_peer = transport_peer; + Curl_peer_link(&a->tunnel_peer, tunnel_peer); + a->tunnel_transport = tunnel_transport; a->addr = *addr; a->ai_family = ai_family; - a->transport_in = transport_in; - a->transport_out = transport_out; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - result = a->cf_create(&a->cf, data, cf->conn, &a->addr, - a->transport_in, a->transport_out); + result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, a->tunnel_peer, + a->tunnel_transport); if(result) goto out; @@ -256,14 +269,17 @@ struct cf_ip_ballers { #ifdef USE_IPV6 struct cf_ai_iter ipv6_iter; #endif + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; cf_ip_connect_create *cf_create; /* for creating cf */ struct curltime started; struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; uint32_t max_concurrent; - uint8_t transport_in; - uint8_t transport_out; + uint8_t transport_peer; + uint8_t tunnel_transport; }; static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, @@ -281,8 +297,9 @@ 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_in, - a->transport_out); + result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, + a->tunnel_peer, a->tunnel_transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -295,11 +312,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, return result; } -static void cf_ip_ballers_clear(struct Curl_cfilter *cf, - struct Curl_easy *data, +static void cf_ip_ballers_clear(struct Curl_easy *data, struct cf_ip_ballers *bs) { - (void)cf; while(bs->running) { struct cf_ip_attempt *a = bs->running; bs->running = a->next; @@ -307,37 +322,36 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf, } cf_ip_attempt_free(bs->winner, data); bs->winner = NULL; + Curl_peer_unlink(&bs->origin); + Curl_peer_unlink(&bs->peer); + Curl_peer_unlink(&bs->tunnel_peer); } static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, - struct Curl_cfilter *cf, - cf_ip_connect_create *cf_create, - uint8_t transport_in, - uint8_t transport_out, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, timediff_t attempt_delay_ms, uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); - bs->cf_create = cf_create; - bs->transport_in = transport_in; - bs->transport_out = transport_out; + bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer); + if(!bs->cf_create) { + failf(data, "unsupported transport type %u%s", + transport_peer, tunnel_peer ? "to proxy" : ""); + return CURLE_UNSUPPORTED_PROTOCOL; + } + Curl_peer_link(&bs->origin, origin); + Curl_peer_link(&bs->peer, peer); + bs->transport_peer = transport_peer; + Curl_peer_link(&bs->tunnel_peer, tunnel_peer); + bs->tunnel_transport = tunnel_transport; bs->attempt_delay_ms = attempt_delay_ms; bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ - - if(transport_in == TRNSPRT_UNIX) { -#ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); -#else - return CURLE_UNSUPPORTED_PROTOCOL; -#endif - } - else { /* TCP/UDP/QUIC */ -#ifdef USE_IPV6 - cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); -#endif - cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); - } return CURLE_OK; } @@ -473,12 +487,13 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(bs->max_concurrent) cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); - result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_out); + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_peer); if(result) goto out; - result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, - bs->transport_in, bs->transport_out, + result = cf_ip_attempt_new(&a, data, cf, bs->origin, bs->peer, + bs->transport_peer, &addr, ai_family, + bs->tunnel_peer, bs->tunnel_transport, bs->cf_create); CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d", bs->running ? "next" : "first", @@ -668,13 +683,10 @@ typedef enum { } cf_connect_state; struct cf_ip_happy_ctx { - struct Curl_peer *peer; cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; struct curltime started; - uint8_t transport_in; - uint8_t transport_out; BIT(dns_resolved); }; @@ -750,29 +762,39 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } + if(ctx->ballers.transport_peer == TRNSPRT_UNIX) { +#ifdef USE_UNIX_SOCKETS + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_UNIX); +#else + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + } + else { /* TCP/UDP/QUIC */ +#ifdef USE_IPV6 + cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, AF_INET6); +#endif + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_INET); + } + CURL_TRC_CF(data, cf, "init ip ballers for transport %u", - ctx->transport_out); + ctx->ballers.transport_peer); ctx->started = *Curl_pgrs_now(data); - return cf_ip_ballers_init(&ctx->ballers, cf, ctx->cf_create, - ctx->transport_in, ctx->transport_out, - data->set.happy_eyeballs_timeout, - IP_HE_MAX_CONCURRENT_ATTEMPTS); + return CURLE_OK; } -static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, +static void cf_ip_happy_ctx_clear(struct cf_ip_happy_ctx *ctx, struct Curl_easy *data) { - struct cf_ip_happy_ctx *ctx = cf->ctx; - DEBUGASSERT(ctx); - DEBUGASSERT(data); - cf_ip_ballers_clear(cf, data, &ctx->ballers); + if(ctx) + cf_ip_ballers_clear(data, &ctx->ballers); } -static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx, + struct Curl_easy *data) { if(ctx) { - Curl_peer_unlink(&ctx->peer); + cf_ip_happy_ctx_clear(ctx, data); curlx_free(ctx); } } @@ -860,7 +882,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, cf->connected = TRUE; cf->next = ctx->ballers.winner->cf; ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_clear(ctx, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); /* whatever errors where reported by ballers, clear our errorbuf */ Curl_reset_fail(data); @@ -943,8 +965,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); + cf_ip_happy_ctx_clear(ctx, data); + cf_ip_happy_ctx_destroy(ctx, data); } } @@ -977,11 +999,12 @@ struct Curl_cftype Curl_cft_ip_happy = { */ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, - cf_ip_connect_create *cf_create, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_ip_happy_ctx *ctx = NULL; CURLcode result; @@ -994,42 +1017,39 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport_in = transport_in; - ctx->transport_out = transport_out; - ctx->cf_create = cf_create; - Curl_peer_link(&ctx->peer, peer); + result = cf_ip_ballers_init(&ctx->ballers, data, + origin, peer, transport_peer, + tunnel_peer, tunnel_transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); + if(result) + goto out; result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx); out: if(result) { curlx_safefree(*pcf); - cf_ip_happy_ctx_destroy(ctx); + cf_ip_happy_ctx_destroy(ctx, data); } return result; } CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, + struct Curl_peer *origin, struct Curl_peer *peer, - uint8_t transport_in, - uint8_t transport_out, - bool tunnel_proxy) + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - cf_ip_connect_create *cf_create; struct Curl_cfilter *cf; CURLcode result; /* Need to be first */ DEBUGASSERT(cf_at); - cf_create = get_cf_create(transport_out, tunnel_proxy); - if(!cf_create) { - CURL_TRC_CF(data, cf_at, "unsupported transport type %u%s", - transport_out, tunnel_proxy ? "to proxy" : ""); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = cf_ip_happy_create(&cf, data, peer, cf_at->conn, cf_create, - transport_in, transport_out); + result = cf_ip_happy_create(&cf, data, origin, peer, transport_peer, + cf_at->conn, tunnel_peer, tunnel_transport); if(result) return result; diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 90cecae8894a..d2994aad43fb 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -33,11 +33,15 @@ struct Curl_peer; struct Curl_sockaddr_ex; /** - * Create a cfilter for making an "ip" connection to the - * given address, using parameters from `conn`. The "ip" connection - * can be a TCP socket, a UDP socket or even a QUIC connection. - * - * It MUST use only the supplied `ai` for its connection attempt. + * Create a cfilter for making an "ip" connect to a peer. + * `pcf`: the filter created on success + * `data`: the transfer initiating the connect + * `peer`: the peer to connect to + * `transport_peer': the transport used for the peer connect + * `conn`: the connection that gets connected + * `addr`: the socket address to connect to + * `tunnel_peer`: NULL or the peer to tunnel through + * `tunnel_transport`: the transport that goes through the tunnel * * Such a filter may be used in "happy eyeball" scenarios, and its * `connect` implementation needs to support non-blocking. Once connected, @@ -45,26 +49,21 @@ struct Curl_sockaddr_ex; */ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, + struct Curl_peer *origin, struct Curl_peer *peer, - uint8_t transport_in, - uint8_t transport_out, - bool tunnel_proxy); - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) -/* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw UDP - sockets with TRNSPRT_QUIC transport so the socket is connected to the - proxy peer. H3-PROXY manages its own ngtcp2 QUIC stack on top. */ -CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); -#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_ip_happy; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 354f43e7eaa4..a556b8f8da13 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -906,7 +906,7 @@ static CURLcode socket_connect_result(struct Curl_easy *data, } struct cf_socket_ctx { - uint8_t transport; + struct Curl_peer *peer; struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ @@ -924,6 +924,7 @@ struct cf_socket_ctx { int rblock_percent; /* percent of reads doing EAGAIN */ size_t recv_max; /* max enforced read size */ #endif + uint8_t transport; BIT(got_first_byte); /* if first byte was received */ BIT(listening); /* socket is listening */ BIT(accepted); /* socket was accepted, not connected */ @@ -932,10 +933,12 @@ struct cf_socket_ctx { }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, + struct Curl_peer *peer, struct Curl_sockaddr_ex *addr, uint8_t transport) { memset(ctx, 0, sizeof(*ctx)); + Curl_peer_link(&ctx->peer, peer); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; ctx->addr = *addr; @@ -972,6 +975,14 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, return CURLE_OK; } +static void cf_socket_ctx_free(struct cf_socket_ctx *ctx) +{ + if(ctx) { + Curl_peer_unlink(&ctx->peer); + curlx_free(ctx); + } +} + static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1006,7 +1017,7 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; socket_close(data, cf->conn, !ctx->accepted, ctx->sock); } - curlx_free(ctx); + cf_socket_ctx_free(ctx); } } @@ -1754,19 +1765,24 @@ struct Curl_cftype Curl_cft_tcp = { CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - (void)transport_in; - DEBUGASSERT(transport_out == TRNSPRT_TCP); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_TCP); if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; @@ -1778,7 +1794,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, goto out; } - result = cf_socket_ctx_init(ctx, addr, transport_out); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1788,7 +1804,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1921,26 +1937,31 @@ struct Curl_cftype Curl_cft_udp = { CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - (void)transport_in; - DEBUGASSERT(transport_out == TRNSPRT_UDP || transport_out == TRNSPRT_QUIC); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport_out); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1950,7 +1971,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1975,27 +1996,32 @@ struct Curl_cftype Curl_cft_unix = { }; CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - (void)transport_in; - DEBUGASSERT(transport_out == TRNSPRT_UNIX); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UNIX); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport_out); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -2005,7 +2031,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 9c1f3bf4b4c0..767fd30e15ab 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -94,10 +94,13 @@ 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 Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UDP socket to the given address @@ -108,10 +111,13 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UNIX socket to the given address @@ -122,10 +128,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that keeps a listening socket. diff --git a/lib/connect.c b/lib/connect.c index 8578aed73db2..b47e55e48ea5 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -413,7 +413,8 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, #ifdef USE_SSL if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); + result = Curl_cf_ssl_proxy_insert_after( + cf, data, cf->conn->http_proxy.peer); if(result) { CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", result); @@ -424,10 +425,12 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, #endif /* USE_SSL */ if(cf->conn->bits.tunnel_proxy) { - struct Curl_peer *dest; /* where HTTP should tunnel to */ - dest = Curl_conn_get_destination(cf->conn, cf->sockindex); + struct Curl_peer *peer = cf->conn->http_proxy.peer; + struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */ + tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); result = Curl_cf_http_proxy_insert_after( - cf, data, dest, ctx->transport, cf->conn->http_proxy.proxytype); + cf, data, peer, tunnel_peer, + ctx->transport, cf->conn->http_proxy.proxytype); if(result) { CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", result); @@ -449,41 +452,47 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - /* What is the fist hop we directly connect to and what transport - * do we use for it? Only on the first hop we can do Happy Eyeballs. */ + /* What is the first hop we directly connect to and what transport + * do we use for it? Only on the first hop we can do Happy Eyeballs. + * first_origin and first_peer differ on --connect-to. */ + struct Curl_peer *first_origin = + Curl_conn_get_first_origin(cf->conn, cf->sockindex); struct Curl_peer *first_peer = Curl_conn_get_first_peer(cf->conn, cf->sockindex); + struct Curl_peer *tunnel_peer = NULL; uint8_t first_transport = ctx->transport; - bool tunnel_proxy = FALSE; + + if(!first_peer) + return CURLE_FAILED_INIT; #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { first_transport = Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); + tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { failf(data, "HTTP/3 proxy not possible via SOCKS"); return CURLE_UNSUPPORTED_PROTOCOL; } - tunnel_proxy = TRUE; } #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ - result = cf_ip_happy_insert_after(cf, data, first_peer, - ctx->transport, first_transport, - tunnel_proxy); + result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer, + first_transport, + tunnel_peer, ctx->transport); if(result) { CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", result); return result; } - if(tunnel_proxy && (first_transport == TRNSPRT_QUIC)) { + if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) { CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u", first_peer->hostname, first_peer->port); ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; } else { CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u", - tunnel_proxy ? "proxy" : "origin", + tunnel_peer ? "proxy" : "origin", first_peer->hostname, first_peer->port); ctx->state = CF_SETUP_CNNCT_EYEBALLS; } @@ -501,17 +510,22 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, if(ctx->state < CF_SETUP_CNNCT_SSL) { #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ !defined(CURL_DISABLE_PROXY) + /* Wanting QUIC with a HTTP tunneling filter, we now need to add * the QUIC filter on top. Without tunneling, this has already * happened in the Happy Eyeball filter. */ if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + result = Curl_cf_capsule_insert_after(cf, data); if(result) { CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", result); return result; } - result = Curl_cf_quic_insert_after(cf); + result = Curl_cf_quic_insert_after(cf, origin, peer); if(result) { CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", result); return result; @@ -525,7 +539,13 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, (ctx->ssl_mode != CURL_CF_SSL_DISABLE && cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ - result = Curl_cf_ssl_insert_after(cf, data); + /* Another FTP quirk: when adding SSL verification, to a DATA + * connection, always verify against the control's origin */ + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + + result = Curl_cf_ssl_insert_after(cf, data, origin, peer); if(result) { CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", result); @@ -777,6 +797,13 @@ void Curl_conn_set_multiplex(struct connectdata *conn) } } +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex) +{ + return (sockindex == SECONDARYSOCKET) ? + conn->origin2 : conn->origin; +} + struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, int sockindex) { @@ -789,6 +816,18 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, (conn->via_peer ? conn->via_peer : conn->origin); } +struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; +} + struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, int sockindex) { diff --git a/lib/connect.h b/lib/connect.h index 65e1ab1ea76c..968314ea8e74 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -126,12 +126,21 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, /* Set conn to allow multiplexing. */ void Curl_conn_set_multiplex(struct connectdata *conn); +/* Get the origin peer at sockindex. */ +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex); + /* Get the peer the connection actually connects to at sockindex. * Often the same as "origin", but can be redirected via "connect-to" * or "alt-svc". May tunnel through proxies. */ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, int sockindex); +/* Get the origin curl connects its socket to. + * Can be origin or the first proxy. */ +struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn, + int sockindex); + /* Get the peer curl connects its socket to. * Can be origin, "connect-to" or the first proxy. */ struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, diff --git a/lib/ftp.c b/lib/ftp.c index 864fb1509c3b..9908d50afb9b 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1390,10 +1390,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); } else { - /* successfully set up the listen socket filter. SSL needed? */ + /* successfully set up the listen socket filter. SSL needed? + * Use the control connections origin for cert verification. */ if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), + conn, SECONDARYSOCKET); } conn->bits.do_more = FALSE; Curl_pgrsTime(data, TIMER_STARTACCEPT); @@ -3196,7 +3199,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* this was BLOCKING, keep it so for now */ bool done; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) { /* we failed and bail out */ return CURLE_USE_SSL_FAILED; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 8c3be63b12aa..b01affeeb931 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -172,10 +172,11 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, } struct cf_proxy_ctx { - struct Curl_peer *dest; /* tunnel destination */ + struct Curl_peer *peer; /* proxy */ + struct Curl_peer *tunnel_peer; /* tunnel destination */ uint8_t proxytype; + uint8_t tunnel_transport; BIT(sub_filter_installed); - BIT(udp_tunnel); }; static int proxy_http_ver_major(proxy_http_ver ver) @@ -556,9 +557,8 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; - const char *tunnel_type; /* Determine tunnel type once and reuse */ - - tunnel_type = ctx->udp_tunnel ? "CONNECT-UDP" : "CONNECT"; + bool udp_tunnel = TRNSPRT_IS_DGRAM(ctx->tunnel_transport); + const char *tunnel_type = udp_tunnel ? "CONNECT-UDP" : "CONNECT"; if(cf->connected) { *done = TRUE; @@ -606,8 +606,8 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); - result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10, - (bool)ctx->udp_tunnel); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, 10, + udp_tunnel); if(result) goto out; } @@ -615,16 +615,16 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", httpversion % 10); - result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion, - (bool)ctx->udp_tunnel); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, + httpversion, udp_tunnel); if(result) goto out; } #ifdef USE_NGHTTP2 else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); - result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest, - (bool)ctx->udp_tunnel); + result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->tunnel_peer, + udp_tunnel); if(result) goto out; } @@ -633,8 +633,9 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, defined(USE_NGTCP2) && defined(USE_OPENSSL) else if(!strcmp(alpn, "h3")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3"); - result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->dest, - (bool)ctx->udp_tunnel); + result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->peer, ctx->peer, + ctx->tunnel_peer, + ctx->tunnel_transport); if(result) goto out; } @@ -673,8 +674,8 @@ static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, struct cf_proxy_ctx *ctx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)ctx->dest->port; - *((const char **)pres2) = ctx->dest->hostname; + *pres1 = (int)ctx->tunnel_peer->port; + *((const char **)pres2) = ctx->tunnel_peer->hostname; return CURLE_OK; case CF_QUERY_ALPN_NEGOTIATED: { const char **palpn = pres2; @@ -693,7 +694,8 @@ static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx) { if(ctx) { - Curl_peer_unlink(&ctx->dest); + Curl_peer_unlink(&ctx->peer); + Curl_peer_unlink(&ctx->tunnel_peer); curlx_free(ctx); } } @@ -727,8 +729,9 @@ struct Curl_cftype Curl_cft_http_proxy = { CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - uint8_t transport, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, uint8_t proxytype) { struct Curl_cfilter *cf; @@ -736,7 +739,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, CURLcode result; (void)data; - if(!dest) + if(!peer || !tunnel_peer) return CURLE_FAILED_INIT; ctx = curlx_calloc(1, sizeof(*ctx)); @@ -744,9 +747,10 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, result = CURLE_OUT_OF_MEMORY; goto out; } - Curl_peer_link(&ctx->dest, dest); + Curl_peer_link(&ctx->peer, peer); + Curl_peer_link(&ctx->tunnel_peer, tunnel_peer); ctx->proxytype = proxytype; - ctx->udp_tunnel = (transport == TRNSPRT_QUIC); + ctx->tunnel_transport = tunnel_transport; result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) diff --git a/lib/http_proxy.h b/lib/http_proxy.h index ef4becdacf98..b60bad96f6fc 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -68,8 +68,9 @@ CURLcode Curl_http_proxy_inspect_tunnel_response( CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - uint8_t transport, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, uint8_t proxytype); extern struct Curl_cftype Curl_cft_http_proxy; diff --git a/lib/imap.c b/lib/imap.c index 87d33c9bce84..abb43ea2d8d7 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -555,7 +555,8 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, bool ssldone = FALSE; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ diff --git a/lib/multi.c b/lib/multi.c index dd29328a1713..14fbc3125915 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -4074,11 +4074,12 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, size_t blen, char **pbuf) { DEBUGASSERT(data); - DEBUGASSERT(data->multi); *pbuf = NULL; if(!data->multi) { - failf(data, "transfer has no multi handle"); - return CURLE_FAILED_INIT; + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + *pbuf = curlx_malloc(blen); + return *pbuf ? CURLE_OK : CURLE_OUT_OF_MEMORY; } if(data->multi->xfer_sockbuf_borrowed) { failf(data, "attempt to borrow xfer_sockbuf when already borrowed"); @@ -4107,11 +4108,16 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf) { - (void)buf; DEBUGASSERT(data); - DEBUGASSERT(data->multi); - DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); - data->multi->xfer_sockbuf_borrowed = FALSE; + if(!data->multi) { + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + curlx_free(buf); + } + else { + DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); + data->multi->xfer_sockbuf_borrowed = FALSE; + } } static void multi_xfer_bufs_free(struct Curl_multi *multi) diff --git a/lib/openldap.c b/lib/openldap.c index 58b31b32af78..8ec6bb27cd84 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -900,7 +900,8 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) result = oldap_perform_bind(data, OLDAP_BIND); break; } - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) break; FALLTHROUGH(); diff --git a/lib/pop3.c b/lib/pop3.c index 3036ce717c81..56157af291a0 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -485,7 +485,8 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, return CURLE_FAILED_INIT; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ diff --git a/lib/smtp.c b/lib/smtp.c index dee7a329ca1b..6bda7ae81441 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -689,7 +689,8 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS); if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler and SMTP state */ diff --git a/lib/urldata.h b/lib/urldata.h index f746b5c3011c..97cfc5efc466 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -253,6 +253,8 @@ struct hostname { #define TRNSPRT_QUIC 5 #define TRNSPRT_UNIX 6 +#define TRNSPRT_IS_DGRAM(x) (((x) == TRNSPRT_UDP) || ((x) == TRNSPRT_QUIC)) + struct ip_quadruple { char remote_ip[MAX_IPADR_LEN]; char local_ip[MAX_IPADR_LEN]; diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c new file mode 100644 index 000000000000..52422c22e4d7 --- /dev/null +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -0,0 +1,1965 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#include + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) +#include +#elif defined(OPENSSL_QUIC_API2) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#include "vtls/wolfssl.h" +#endif + +#include + +#include "urldata.h" +#include "url.h" +#include "uint-hash.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "cfilters.h" +#include "cf-dns.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curlx/dynbuf.h" +#include "http1.h" +#include "select.h" +#include "transfer.h" +#include "bufref.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/cf-ngtcp2-cmn.h" + +/* + * Store ngtcp2 version info in this buffer. + */ +void Curl_ngtcp2_ver(char *p, size_t len) +{ + const ngtcp2_info *ng2 = ngtcp2_version(0); + const nghttp3_info *ht3 = nghttp3_version(0); + (void)curl_msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); +} + +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + curlx_free(stream); +} + +static void h3_stream_hash_free(unsigned int id, void *stream) +{ + (void)id; + DEBUGASSERT(stream); + Curl_cf_ngtcp2_h3_stream_ctx_free((struct h3_stream_ctx *)stream); +} + +static bool cf_ngtcp2_h3_err_is_fatal(int code) +{ + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); +} + +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); + } + if(cf_ngtcp2_h3_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); +} + +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb) +{ + DEBUGASSERT(!ctx->initialized); + ctx->qlogfd = -1; + ctx->tunnel_inbuf = NULL; + ctx->tunnel_inbuf_len = 0; + ctx->version = NGTCP2_PROTO_VER_MAX; + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->init_h3_conn_cb = init_h3_conn_cb; + ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); +} + +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + curlx_dyn_free(&ctx->scratch); + Curl_uint32_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); + curlx_safefree(ctx->tunnel_inbuf); + ctx->tunnel_inbuf_len = 0; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } +} + +static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) +{ + struct Curl_cfilter *cf = conn_ref->user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + return ctx->qconn; +} + +#ifdef DEBUG_NGTCP2 +static void quic_printf(void *user_data, const char *fmt, ...) +{ + va_list ap; + (void)user_data; + va_start(ap, fmt); + curl_mvfprintf(stderr, fmt, ap); + va_end(ap); + curl_mfprintf(stderr, "\n"); +} +#endif + +static void qlog_callback(void *user_data, uint32_t flags, + const void *data, size_t datalen) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + (void)flags; + if(ctx->qlogfd != -1) { + ssize_t rc = write(ctx->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + curlx_close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } +} + +static void quic_settings(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) +{ + ngtcp2_settings *s = &ctx->settings; + ngtcp2_transport_params *t = &ctx->transport_params; + + ngtcp2_settings_default(s); + ngtcp2_transport_params_default(t); +#ifdef DEBUG_NGTCP2 + s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif + + s->initial_ts = pktx->ts; + s->handshake_timeout = (data->set.connecttimeout > 0) ? + data->set.connecttimeout * NGTCP2_MILLISECONDS : QUIC_HANDSHAKE_TIMEOUT; + s->max_window = H3_CONN_WINDOW_SIZE_MAX; + s->max_stream_window = 0; /* disable ngtcp2 auto-tuning of window */ + s->no_pmtud = FALSE; +#ifdef NGTCP2_SETTINGS_V3 + /* try ten times the ngtcp2 defaults here for problems with Caddy */ + s->glitch_ratelim_burst = 1000 * 10; + s->glitch_ratelim_rate = 33 * 10; +#endif + t->initial_max_data = s->max_window; + t->initial_max_stream_data_bidi_local = H3_STREAM_WINDOW_SIZE_INITIAL; + t->initial_max_stream_data_bidi_remote = H3_STREAM_WINDOW_SIZE_INITIAL; + t->initial_max_stream_data_uni = t->initial_max_data; + t->initial_max_streams_bidi = QUIC_MAX_STREAMS; + t->initial_max_streams_uni = QUIC_MAX_STREAMS; + t->max_idle_timeout = 0; /* no idle timeout from our side */ + if(ctx->qlogfd != -1) { + s->qlog_write = qlog_callback; + } +} + +#if defined(_MSC_VER) && defined(_DLL) +#pragma warning(push) +#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + +static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; + struct Curl_easy *data; + + (void)tconn; + DEBUGASSERT(ctx); + data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!ctx || !data) + return NGTCP2_ERR_CALLBACK_FAILURE; + + ctx->handshake_at = *Curl_pgrs_now(data); + ctx->tls_handshake_complete = TRUE; + Curl_vquic_report_handshake(&ctx->tls, cf, data); + + ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, + data, &ctx->ssl_peer); + if(ctx->tls_vrfy_result) + return NGTCP2_ERR_CALLBACK_FAILURE; + +#ifdef CURLVERBOSE + if(Curl_trc_is_verbose(data)) { + const ngtcp2_transport_params *rp; + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T + "ms, remote transport[max_udp_payload=%" PRIu64 + ", initial_max_data=%" PRIu64 "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); + } +#endif + + /* In case of earlydata, where we simulate being connected, update + * the handshake time when we really did connect */ + if(ctx->use_earlydata) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + if(ctx->use_earlydata) { +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_accepted = + (SSL_get_early_data_status(ctx->tls.ossl.ssl) != + SSL_EARLY_DATA_REJECTED); +#endif +#ifdef USE_GNUTLS + int flags = gnutls_session_get_flags(ctx->tls.gtls.session); + ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); +#endif +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_accepted = + (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) != + WOLFSSL_EARLY_DATA_REJECTED); +#else + DEBUGASSERT(0); /* should not come here if ED is disabled. */ + ctx->earlydata_accepted = FALSE; +#endif /* WOLFSSL_EARLY_DATA */ +#endif + CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", + ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); + Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? + (curl_off_t)ctx->earlydata_skip : + -(curl_off_t)ctx->earlydata_skip); + } + return 0; +} + +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + nghttp3_ssize rc; + uint64_t nconsumed; + int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)offset; + + rc = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + if(rc < 0) { + if(data && stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " + "reset=%d, closed=%d", + stream_id, stream->reset, stream->closed); + } + return NGTCP2_ERR_CALLBACK_FAILURE; + } + nconsumed = (uint64_t)rc; + if(nconsumed) { + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + if(stream) { + stream->rx_offset += nconsumed; + stream->rx_offset_max += nconsumed; + } + } + return 0; +} + +static int cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, uint64_t datalen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + int rv; + + (void)tconn; + /* stream is closed... */ + if(!data) + data = CF_DATA_CURRENT(cf); + if(!data) + return NGTCP2_ERR_CALLBACK_FAILURE; + + if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { + app_error_code = NGHTTP3_H3_NO_ERROR; + } + + rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" + PRIu64 ") -> %d", stream_id, app_error_code, rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + Curl_cf_ngtcp2_h3_err_set(cf, data, rv); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)tconn; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)tconn; + ctx->max_bidi_streams = max_streams; + if(data) + CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 ", used %" PRIu64, + ctx->max_bidi_streams, ctx->used_bidi_streams); + return 0; +} + +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *s_data = stream_user_data; + struct h3_stream_ctx *stream; + int rv; + (void)tconn; + (void)max_data; + + rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + stream = H3_STREAM_CTX(ctx, s_data); + if(stream && stream->quic_flow_blocked) { + CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", stream_id); + stream->quic_flow_blocked = FALSE; + Curl_multi_mark_dirty(s_data); + } + return 0; +} + +static void cb_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + +/* for ngtcp2 data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ +static int cb_get_new_connection_id2( + ngtcp2_conn *tconn, ngtcp2_cid *cid, + struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token->data, sizeof(token->data)); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} +#endif + +static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + (void)tconn; + + if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) + return 0; + + DEBUGASSERT(ctx); + DEBUGASSERT(data); + if(ctx && data && !ctx->h3conn && ctx->init_h3_conn_cb) { + if(ctx->init_h3_conn_cb(cf, data, ctx)) + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static ngtcp2_callbacks ng_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + ngtcp2_crypto_recv_crypto_data_cb, + cb_ngtcp2_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_recv_stream_data, + cb_acked_stream_data_offset, + NULL, /* stream_open */ + cb_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + cb_rand, + cb_get_new_connection_id, /* for ngtcp2 user_data : NULL; + ctx = cf ? cf->ctx : NULL; + data = cf ? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; +#ifdef HAVE_OPENSSL_EARLYDATA + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } +#endif + Curl_ossl_add_session(cf, data, ctx->ssl_peer.scache_key, ssl_sessionid, + SSL_version(ssl), "h3", quic_tp, quic_tp_len); + } + return 0; +} +#endif /* USE_OPENSSL */ + +#ifdef USE_GNUTLS + +#ifdef CURLVERBOSE +static const char *gtls_hs_msg_name(int mtype) +{ + switch(mtype) { + case 1: + return "ClientHello"; + case 2: + return "ServerHello"; + case 4: + return "SessionTicket"; + case 8: + return "EncryptedExtensions"; + case 11: + return "Certificate"; + case 13: + return "CertificateRequest"; + case 15: + return "CertificateVerify"; + case 20: + return "Finished"; + case 24: + return "KeyUpdate"; + case 254: + return "MessageHash"; + } + return "Unknown"; +} +#endif + +static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, + const gnutls_datum_t *msg) +{ + ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; + + (void)msg; + (void)incoming; + if(when && cf && ctx) { /* after message has been processed */ + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!data) + return 0; + CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", + incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; + + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_gtls_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, 0, "h3", quic_tp, quic_tp_len); + break; + } + default: + break; + } + } + return 0; +} +#endif /* USE_GNUTLS */ + +#ifdef USE_WOLFSSL +static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) +{ + ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; + + DEBUGASSERT(cf); + if(cf && session) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(data && ctx) { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; + + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_wssl_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, wolfSSL_version(ssl), + "h3", quic_tp, quic_tp_len); + } + } + return 0; +} +#endif /* USE_WOLFSSL */ + +static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) +{ + struct curl_tls_ctx *ctx = user_data; + +#ifdef USE_OPENSSL +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#elif defined(OPENSSL_QUIC_API2) + /* nothing to do */ +#else + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ + if(Curl_ssl_scache_use(cf, data)) { + /* Enable the session cache because it is a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); + } + +#elif defined(USE_GNUTLS) + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { + failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); + return CURLE_FAILED_INIT; + } + if(Curl_ssl_scache_use(cf, data)) { + gnutls_handshake_set_hook_function(ctx->gtls.session, + GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, + quic_gtls_handshake_cb); + } + +#elif defined(USE_WOLFSSL) + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } + if(Curl_ssl_scache_use(cf, data)) { + /* Register to get notified when a new session is received */ + wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb); + } +#endif + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_max = scs->earlydata_max; +#endif +#ifdef USE_GNUTLS + ctx->earlydata_max = + gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); +#endif +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_max = scs->earlydata_max; +#else + ctx->earlydata_max = 0; +#endif /* WOLFSSL_EARLY_DATA */ +#endif +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if(!ctx->earlydata_max) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); + } + else if(!scs->quic_tp || !scs->quic_tp_len) { + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); + } + else { + int rv; + rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( + ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); + if(rv) + CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " + "parameters: %s", ngtcp2_strerror(rv)); + else if(ctx->init_h3_conn_cb) { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); + result = ctx->init_h3_conn_cb(cf, data, ctx); + if(!result) { + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } + else { /* h3_conn_init set, assume done */ + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } +#else /* not supported in the TLS backend */ + (void)data; + (void)ctx; + (void)scs; + (void)alpns; +#endif + return result; +} + +/* + * Might be called twice for happy eyeballs. + */ +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rc; + int rv; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr = NULL; + int qfd; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; + + DEBUGASSERT(ctx->initialized); + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + quic_settings(ctx, data, pktx); + + result = vquic_ctx_init(data, &ctx->q); + if(result) + return result; + + /* Query socket and remote address from sub-chain */ + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) { + /* No direct socket - must be tunneled QUIC (CONNECT-UDP through proxy) */ + ctx->q.sockfd = CURL_SOCKET_BAD; + } + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get local address for ngtcp2 */ + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + } + else { + /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address + from the connected filter below */ + const struct Curl_sockaddr_ex *remote = NULL; + if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&remote))) + return CURLE_QUIC_CONNECT_ERROR; + if(!remote) + return CURLE_QUIC_CONNECT_ERROR; + + memset(&ctx->q.local_addr, 0, sizeof(ctx->q.local_addr)); + switch(remote->family) { + case AF_INET: + ((struct sockaddr_in *)&ctx->q.local_addr)->sin_family = AF_INET; + ctx->q.local_addrlen = sizeof(struct sockaddr_in); + break; +#ifdef USE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)&ctx->q.local_addr)->sin6_family = AF_INET6; + ctx->q.local_addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return CURLE_QUIC_CONNECT_ERROR; + } + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &remote->curl_sa_addr, + (socklen_t)remote->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + } + + result = Curl_vquic_tls_init(&ctx->tls, cf, data, + &ctx->ssl_peer, &ALPN_SPEC_H3, + cf_ngtcp2_tls_ctx_setup, &ctx->tls, + &ctx->conn_ref, + cf_ngtcp2_on_session_reuse); + if(result) + return result; + +#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) + if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); + return CURLE_FAILED_INIT; + } + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); + if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); + return CURLE_FAILED_INIT; + } +#elif defined(USE_OPENSSL) + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); +#elif defined(USE_WOLFSSL) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); +#else +#error "ngtcp2 TLS backend not defined" +#endif + + ngtcp2_ccerr_default(&ctx->last_error); + + return CURLE_OK; +} + +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the sub-chain */ + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + *done = FALSE; + + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + CF_DATA_SAVE(save, cf, data); + + if(!ctx->qconn) { + ctx->started_at = *Curl_pgrs_now(data); + result = cf_connect_start(cf, data, &pktx); + if(result) + goto out; + if(cf->connected) { + *done = TRUE; + goto out; + } + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(result) + goto out; + + if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { + result = ctx->tls_vrfy_result; + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + cf->connected = TRUE; + *done = TRUE; + } + } + +out: + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; + if(ctx->qconn && + ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && + ngtcp2_conn_in_draining_period(ctx->qconn)) { + const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); + + result = CURLE_COULDNT_CONNECT; + if(cerr) { + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, + cerr->type, cerr->error_code); + switch(cerr->type) { + case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: + CURL_TRC_CF(data, cf, "error in version negotiation"); + break; + default: + if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { + CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", + (unsigned int)(cerr->error_code & 0xffU)); + } + else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { + CURL_TRC_CF(data, cf, "connection refused by server"); + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE with this code right away. We want + * to keep on trying in this case. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + } + } + } + +#ifdef CURLVERBOSE + if(result) { + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get IP info for error reporting */ + struct ip_quadruple ip; + + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } + } +#endif + if(!result && ctx->qconn) { + result = Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx); + } + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CF_DATA_RESTORE(cf, save); + return result; +} + +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } + + if(!cf->next) { + Curl_bufq_reset(&ctx->q.sendbuf); + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + + if(!ctx->shutdown_started) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_ssize nwritten; + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + PRIu64 ") -> %zd", ctx->last_error.type, + ctx->last_error.error_code, (ssize_t)nwritten); + /* there are cases listed in ngtcp2 documentation where this call + * may fail. Since we are doing a connection shutdown as graceful + * as we can, such an error is ignored here. */ + if(nwritten > 0) { + /* Ignore amount written. sendbuf was empty and has always room for + * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which + * case `result` is set non zero. */ + size_t n; + result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &n); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", result); + goto out; + } + + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. We are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; + } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + bool done; + Curl_cf_ngtcp2_cmn_shutdown(cf, data, &done); +} + +static bool cf_ngtcp2_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} + +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); + } + else { + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); + } + } + if(cf_ngtcp2_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); +} + +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); + + io_ctx->cf = cf; + io_ctx->data = data; + ngtcp2_path_storage_zero(&io_ctx->ps); + vquic_ctx_set_time(&ctx->q, pnow); + io_ctx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} + +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx, + struct Curl_cfilter *cf) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); + + vquic_ctx_update_time(&ctx->q, pnow); + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} + +#if NGTCP2_VERSION_NUM < 0x011100 +struct cf_ngtcp2_sfind_ctx { + int64_t stream_id; + struct h3_stream_ctx *stream; + uint32_t mid; +}; + +static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) +{ + struct cf_ngtcp2_sfind_ctx *fctx = user_data; + struct h3_stream_ctx *stream = value; + + if(fctx->stream_id == stream->id) { + fctx->mid = mid; + fctx->stream = stream; + return FALSE; + } + return TRUE; /* continue */ +} + +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) +{ + struct cf_ngtcp2_sfind_ctx fctx; + fctx.stream_id = stream_id; + fctx.stream = NULL; + Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx); + return fctx.stream; +} +#else +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) +{ + struct Curl_easy *data = + ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); + + if(!data) { + return NULL; + } + + return H3_STREAM_CTX(ctx, data); +} +#endif + +/** + * Read a network packet to send from ngtcp2 into `buf`. + * Return number of bytes written or -1 with *err set. + */ +static CURLcode read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + size_t *pnread) +{ + struct cf_ngtcp2_io_ctx *x = userp; + struct cf_ngtcp2_ctx *ctx = x->cf->ctx; + nghttp3_vec vec[16]; + nghttp3_ssize veccnt; + ngtcp2_ssize ndatalen; + uint32_t flags; + int64_t stream_id; + int fin; + ssize_t n; + + *pnread = 0; + veccnt = 0; + stream_id = -1; + fin = 0; + + /* ngtcp2 may want to put several frames from different streams into + * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. + * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make + * another iteration. + * When ngtcp2 is happy (because it has no other frame that would fit + * or it has nothing more to send), it returns the total length + * of the assembled packet. This may be 0 if there was nothing to send. */ + for(;;) { + + if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { + veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, + CURL_ARRAYSIZE(vec)); + if(veccnt < 0) { + failf(x->data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)veccnt)); + Curl_cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); + return CURLE_SEND_ERROR; + } + } + + flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, + NULL, buf, buflen, + &ndatalen, flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, x->ts); + if(n == 0) { + /* nothing to send */ + return CURLE_AGAIN; + } + else if(n < 0) { + switch(n) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: { + struct h3_stream_ctx *stream; + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_block_stream(ctx->h3conn, stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", + stream_id); + stream = cf_ngtcp2_get_stream(ctx, stream_id); + if(stream) /* it might be not one of our h3 streams? */ + stream->quic_flow_blocked = TRUE; + n = 0; + break; + } + case NGTCP2_ERR_STREAM_SHUT_WR: + DEBUGASSERT(ndatalen == -1); + nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); + n = 0; + break; + case NGTCP2_ERR_WRITE_MORE: + /* ngtcp2 wants to send more. update the flow of the stream whose data + * is in the buffer and continue */ + DEBUGASSERT(ndatalen >= 0); + n = 0; + break; + default: + DEBUGASSERT(ndatalen == -1); + failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", + ngtcp2_strerror((int)n)); + Curl_cf_ngtcp2_cmn_err_set(x->cf, x->data, (int)n); + return CURLE_SEND_ERROR; + } + } + + if(ndatalen >= 0) { + /* we add the amount of data bytes to the flow windows */ + int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); + if(rv) { + failf(x->data, "nghttp3_conn_add_write_offset returned error: %s", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + } + + if(n > 0) { + /* packet assembled, leave */ + *pnread = (size_t)n; + return CURLE_OK; + } + } +} + +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + size_t nread; + size_t max_payload_size, path_max_payload_size; + size_t pktcnt = 0; + size_t gsolen = 0; /* this disables gso until we have a clue */ + size_t send_quantum; + CURLcode result; + struct cf_ngtcp2_io_ctx local_pktx; + + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + + /* In UDP, there is a maximum theoretical packet payload length and + * a minimum payload length that is "guaranteed" to work. + * To detect if this minimum payload can be increased, ngtcp2 sends + * now and then a packet payload larger than the minimum. It that + * is ACKed by the peer, both parties know that it works and + * the subsequent packets can use a larger one. + * This is called PMTUD (Path Maximum Transmission Unit Discovery). + * Since a PMTUD might be rejected right on send, we do not want it + * be followed by other packets of lesser size. Because those would + * also fail then. If we detect a PMTUD while buffering, we flush. + */ + max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); + path_max_payload_size = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); + CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", + send_quantum); + for(;;) { + /* add the next packet to send, if any, to our buffer */ + result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &nread); + if(result == CURLE_AGAIN) + break; + else if(result) + return result; + else { + size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); + if((buflen >= send_quantum) || + ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) + break; + DEBUGASSERT(nread > 0); + ++pktcnt; + if(pktcnt == 1) { + /* first packet in buffer. This is either of a known, "good" + * payload size or it is a PMTUD. We shall see. */ + gsolen = nread; + } + else if(nread > gsolen || + (gsolen > path_max_payload_size && nread != gsolen)) { + /* The added packet is a PMTUD *or* the one(s) before the + * added were PMTUD and the last one is smaller. + * Flush the buffer before the last add. */ + result = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + pktcnt = 0; + } + else if(nread < gsolen) { + /* Reached capacity of our buffer *or* + * last add was shorter than the previous ones, flush */ + break; + } + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* time to send */ + CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", + pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return result; + } + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); + ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); + } + return CURLE_OK; +} + +struct cf_ngtcp2_recv_ctx { + struct cf_ngtcp2_io_ctx *pktx; + size_t pkt_count; +}; + +static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) +{ + struct cf_ngtcp2_recv_ctx *rctx = userp; + struct cf_ngtcp2_io_ctx *pktx = rctx->pktx; + struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; + ngtcp2_pkt_info pi; + ngtcp2_path path; + size_t offset, pktlen; + int rv; + + if(!rctx->pkt_count) { + Curl_cf_ngtcp2_io_ctx_update_time(pktx->data, pktx, pktx->cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + if(ecn) + CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", + buflen, gso_size, ecn); + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, + remote_addrlen); + pi.ecn = (uint8_t)ecn; + + for(offset = 0; offset < buflen; offset += gso_size) { + rctx->pkt_count++; + pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, + buf + offset, pktlen, pktx->ts); + if(rv) { + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); + Curl_cf_ngtcp2_cmn_err_set(pktx->cf, pktx->data, rv); + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } + } + return CURLE_OK; +} + +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_ngtcp2_io_ctx local_pktx; + struct cf_ngtcp2_recv_ctx rctx; + CURLcode result = CURLE_OK; + + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; + + rctx.pktx = pktx; + rctx.pkt_count = 0; + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + CURL_TRC_CF(data, cf, "progress_ingress(socket)"); + return vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts, &rctx); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + unsigned char *buf; + size_t max_udp_payload = QUIC_TUNNEL_INBUF_SIZE; + size_t pkt_limit = QUIC_TUNNEL_INGRESS_PKT_LIMIT; + size_t nread; + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + + CURL_TRC_CF(data, cf, "progress_ingress(sub-filters)"); + if(ctx->qconn) { + size_t max_path_payload; + max_path_payload = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + if(max_path_payload > max_udp_payload) + max_udp_payload = max_path_payload; + } + + if(ctx->tunnel_inbuf_len < max_udp_payload) { + unsigned char *newbuf = curlx_realloc(ctx->tunnel_inbuf, + max_udp_payload); + if(!newbuf) + return CURLE_OUT_OF_MEMORY; + ctx->tunnel_inbuf = newbuf; + ctx->tunnel_inbuf_len = max_udp_payload; + } + buf = ctx->tunnel_inbuf; + + while(pkt_limit--) { + result = Curl_conn_cf_recv(cf->next, data, (char *)buf, + ctx->tunnel_inbuf_len, &nread); + if(result == CURLE_AGAIN) { + /* no more data available at the moment */ + return CURLE_OK; + } + if(result) { + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", result); + return result; + } + if(nread == 0) { + /* tunnel closed */ + return CURLE_OK; + } + + memcpy(&remote_addr, ctx->connected_path.remote.addr, + ctx->connected_path.remote.addrlen); + remote_addrlen = (socklen_t)ctx->connected_path.remote.addrlen; + result = cf_ngtcp2_recv_pkts(buf, nread, nread, &remote_addr, + remote_addrlen, 0, &rctx); + if(result) + return result; + + if(!ctx->q.got_first_byte) { + ctx->q.got_first_byte = TRUE; + ctx->q.first_byte_at = ctx->q.last_op; + } + ctx->q.last_io = ctx->q.last_op; + } + return CURLE_OK; + } +} + +/** + * Connection maintenance like timeouts on packet ACKs etc. are done by us, not + * the OS like for TCP. POLL events on the socket therefore are not + * sufficient. + * ngtcp2 tells us when it wants to be invoked again. We handle that via + * the `Curl_expire()` mechanisms. + */ +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_ngtcp2_io_ctx local_pktx; + ngtcp2_tstamp expiry; + + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); + } + + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= pktx->ts) { + CURLcode result; + int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + Curl_cf_ngtcp2_cmn_err_set(cf, data, rv); + return CURLE_SEND_ERROR; + } + result = Curl_cf_ngtcp2_progress_ingress(cf, data, pktx); + if(result) + return result; + result = Curl_cf_ngtcp2_progress_egress(cf, data, pktx); + if(result) + return result; + /* ask again, things might have changed */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + } + + if(expiry > pktx->ts) { + ngtcp2_duration timeout = expiry - pktx->ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); + } + } + return CURLE_OK; +} + +static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const ngtcp2_transport_params *rp; + /* Peer should have sent us its transport parameters. If it + * announces a positive `max_idle_timeout` it closes the + * connection when it does not hear from us for that time. + * + * Some servers use this as a keep-alive timer at a rather low + * value. We are doing HTTP/3 here and waiting for the response + * to a request may take a considerable amount of time. We need + * to prevent the peer's QUIC stack from closing in this case. + */ + if(!ctx->qconn) + return; + + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(!rp || !rp->max_idle_timeout) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); + } + else if(!Curl_uint32_hash_count(&ctx->streams)) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); + } + else { + ngtcp2_duration keep_ns; + keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); + CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " + "set keep-alive to %" PRIu64 " ms.", + (rp->max_idle_timeout / NGTCP2_MILLISECONDS), + (keep_ns / NGTCP2_MILLISECONDS)); + } +} + +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + if(!data) + return CURLE_FAILED_INIT; + + if(stream) + return CURLE_OK; + + stream = curlx_calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; + + stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; + + /* on send, we control how much we put into the buffer */ + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + stream->sendbuf_len_in_flight = 0; + stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { + Curl_cf_ngtcp2_h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + + if(Curl_uint32_hash_count(&ctx->streams) == 1) + cf_ngtcp2_setup_keep_alive(cf, data); + + return CURLE_OK; +} + +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + DEBUGASSERT(data); + DEBUGASSERT(stream); + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; + + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", + stream->id, result); + } +} + +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + Curl_uint32_hash_remove(&ctx->streams, data->mid); + if(!Curl_uint32_hash_count(&ctx->streams)) + cf_ngtcp2_setup_keep_alive(cf, data); + } +} + +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool alive = FALSE; + const ngtcp2_transport_params *rp; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + *input_pending = FALSE; + if(!ctx->qconn || ctx->shutdown_started) + goto out; + + /* We do not announce a max idle timeout, but when the peer does + * it closes the connection when it expires. */ + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(rp && rp->max_idle_timeout) { + timediff_t idletime_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); + if(idletime_ms > 0) { + uint64_t max_idle_ms = + (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS); + if((uint64_t)idletime_ms > max_idle_ms) + goto out; + } + } + + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + goto out; + + alive = TRUE; + if(*input_pending) { + CURLcode result; + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, + only "protocol frames" */ + *input_pending = FALSE; + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result ? FALSE : TRUE; + } + +out: + CF_DATA_RESTORE(cf, save); + return alive; +} + +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data) +{ + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + int rc; + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack encoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack decoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + return CURLE_OK; +} + +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ diff --git a/lib/vquic/cf-ngtcp2-cmn.h b/lib/vquic/cf-ngtcp2-cmn.h new file mode 100644 index 000000000000..88554edfb6d3 --- /dev/null +++ b/lib/vquic/cf-ngtcp2-cmn.h @@ -0,0 +1,239 @@ +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#include +#include + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) +#include +#elif defined(OPENSSL_QUIC_API2) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#include +#include +#include +#include "vtls/wolfssl.h" +#endif + +#ifdef HAVE_NETINET_UDP_H +#include +#endif + +#include + +#include "http1.h" +#include "uint-hash.h" +#include "vtls/vtls.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" + +struct Curl_cfilter; +struct Curl_easy; +struct cf_ngtcp2_ctx; +struct cf_quic_ctx; + +#define QUIC_MAX_STREAMS (256 * 1024) +#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) +#define QUIC_TUNNEL_INBUF_SIZE (64 * 1024) + +/* We announce a small window size in transport param to the server, + * and grow that immediately to max when no rate limit is in place. + * We need to start small as we are not able to decrease it. */ +#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024) +#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) +#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX) + +#define H3_STREAM_CHUNK_SIZE (64 * 1024) +#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE +#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE +#endif +/* The pool keeps spares around and half of a full stream window + * seems good. More does not seem to improve performance. + * The benefit of the pool is that stream buffers do not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#define H3_STREAM_POOL_SPARES 2 +/* The max amount of un-acked upload data we keep around per stream */ +#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) +#define H3_STREAM_SEND_CHUNKS \ + (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 + + +void Curl_ngtcp2_ver(char *p, size_t len); + +typedef CURLcode cf_ngtcp2_init_h3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); + +struct cf_ngtcp2_ctx { + struct cf_quic_ctx q; + struct ssl_peer ssl_peer; + struct curl_tls_ctx tls; +#ifdef OPENSSL_QUIC_API2 + ngtcp2_crypto_ossl_ctx *ossl_ctx; +#endif + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_ccerr last_error; + ngtcp2_crypto_conn_ref conn_ref; + struct cf_call_data call_data; + cf_ngtcp2_init_h3_conn *init_h3_conn_cb; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; /* hash data->mid to h3_stream_ctx */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ + int qlogfd; + unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ + size_t tunnel_inbuf_len; + BIT(initialized); + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ +}; + +/* How to access `call_data` from a cf_ngtcp2 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data + +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb); +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx); +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +/** + * All about the H3 internals of a stream + */ +struct h3_stream_ctx { + int64_t id; /* HTTP/3 stream identifier */ + struct bufq sendbuf; /* h3 request body */ + struct h1_req_parser h1; /* h1 request parsing */ + size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t rx_total; /* total number of bytes received */ + uint64_t rx_offset; /* current receive offset */ + uint64_t rx_offset_max; /* allowed receive offset */ + uint64_t window_size_max; /* max flow control window set for stream */ + int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data); + +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +struct cf_ngtcp2_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + ngtcp2_path_storage ps; +}; + +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx, + struct Curl_cfilter *cf); + +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data); + +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending); + +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ + +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H */ diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index f449484292a9..2f4792e8a9f5 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -27,46 +27,18 @@ defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ defined(USE_NGTCP2) && defined(USE_OPENSSL) -#include -#include - -#ifdef USE_OPENSSL -#include -#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) -#include -#elif defined(OPENSSL_QUIC_API2) -#include -#else -#include -#endif -#include "vtls/openssl.h" -#endif - -#include - #include "urldata.h" #include "url.h" -#include "uint-hash.h" #include "curl_trc.h" -#include "rand.h" -#include "hash.h" #include "sendf.h" #include "multiif.h" #include "cfilters.h" -#include "cf-capsule.h" -#include "cf-socket.h" #include "connect.h" #include "progress.h" -#include "curlx/fopen.h" #include "curlx/dynbuf.h" -#include "dynhds.h" #include "http_proxy.h" -#include "select.h" #include "vquic/vquic.h" -#include "vquic/vquic_int.h" -#include "vquic/vquic-tls.h" -#include "vtls/vtls.h" -#include "vtls/vtls_scache.h" +#include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2-proxy.h" #include "capsule.h" @@ -74,25 +46,7 @@ * each active transfer. We use HTTP/3 flow control and only ACK * when we take things out of the buffer. * Chunk size is large enough to take a full DATA frame */ -#define PROXY_H3_STREAM_WINDOW_SIZE (128 * 1024) -#define PROXY_H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) -#define PROXY_H3_STREAM_CHUNK_SIZE (16 * 1024) - -/* The pool keeps spares around and half of a full stream window - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffers do not keep - * spares. Memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define PROXY_H3_STREAM_POOL_SPARES \ - ((PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) / 2) - -#define PROXY_H3_STREAM_RECV_CHUNKS \ - (PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) -#define PROXY_H3_STREAM_SEND_CHUNKS \ - (PROXY_H3_STREAM_WINDOW_SIZE / PROXY_H3_STREAM_CHUNK_SIZE) - -#define PROXY_QUIC_MAX_STREAMS (256 * 1024) -#define PROXY_QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) +#define PROXY_H3_STREAM_RECV_CHUNKS ((512 * 1024) / H3_STREAM_CHUNK_SIZE) typedef enum { H3_TUNNEL_INIT, /* init/default/no tunnel state */ @@ -102,30 +56,32 @@ typedef enum { H3_TUNNEL_FAILED } h3_tunnel_state; -struct h3_proxy_stream_ctx; - struct h3_tunnel_stream { + struct Curl_peer *peer; /* where the tunnel goes to */ struct http_resp *resp; + struct bufq recvbuf; char *authority; - struct h3_proxy_stream_ctx *stream; - int64_t stream_id; + struct h3_stream_ctx *stream; h3_tunnel_state state; + BIT(udp); BIT(has_final_response); BIT(closed); }; static CURLcode h3_tunnel_stream_init(struct h3_tunnel_stream *ts, - struct Curl_peer *dest) + struct Curl_peer *peer, + bool udp) { ts->state = H3_TUNNEL_INIT; - ts->stream_id = -1; - ts->has_final_response = FALSE; - + Curl_peer_link(&ts->peer, peer); + Curl_bufq_init2(&ts->recvbuf, H3_STREAM_CHUNK_SIZE, + PROXY_H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + ts->udp = udp; /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", - dest->hostname, - dest->ipv6 ? "]" : "", - dest->port); + ts->authority = curl_maprintf("%s%s%s:%u", peer->ipv6 ? "[" : "", + peer->hostname, + peer->ipv6 ? "]" : "", + peer->port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; @@ -134,34 +90,35 @@ static CURLcode h3_tunnel_stream_init(struct h3_tunnel_stream *ts, static void h3_tunnel_stream_reset(struct h3_tunnel_stream *ts) { + Curl_bufq_reset(&ts->recvbuf); Curl_http_resp_free(ts->resp); ts->resp = NULL; ts->stream = NULL; - ts->stream_id = -1; ts->has_final_response = FALSE; ts->closed = FALSE; ts->state = H3_TUNNEL_INIT; } -static void h3_tunnel_stream_clear(struct h3_tunnel_stream *ts) +static void h3_tunnel_stream_cleanup(struct h3_tunnel_stream *ts) { + Curl_peer_unlink(&ts->peer); + Curl_bufq_free(&ts->recvbuf); Curl_http_resp_free(ts->resp); curlx_safefree(ts->authority); - memset(ts, 0, sizeof(*ts)); ts->state = H3_TUNNEL_INIT; } static void h3_tunnel_go_state(struct Curl_cfilter *cf, struct h3_tunnel_stream *ts, h3_tunnel_state new_state, - struct Curl_easy *data, - bool udp_tunnel) + struct Curl_easy *data) { + VERBOSE(int64_t stream_id = ts->stream ? ts->stream->id : -1); (void)cf; - (void)udp_tunnel; if(ts->state == new_state) return; + /* leaving this one */ switch(ts->state) { case H3_TUNNEL_CONNECT: @@ -170,38 +127,32 @@ static void h3_tunnel_go_state(struct Curl_cfilter *cf, default: break; } + /* entering this one */ switch(new_state) { case H3_TUNNEL_INIT: - CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'init'", - ts->stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [init]", stream_id); h3_tunnel_stream_reset(ts); break; - case H3_TUNNEL_CONNECT: - CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'connect'", - ts->stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [connect]", stream_id); ts->state = H3_TUNNEL_CONNECT; break; - case H3_TUNNEL_RESPONSE: - CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'response'", - ts->stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [response]", stream_id); ts->state = H3_TUNNEL_RESPONSE; break; - case H3_TUNNEL_ESTABLISHED: - CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'established'", - ts->stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [established]", stream_id); infof(data, "CONNECT%s phase completed for HTTP/3 proxy", - udp_tunnel ? "-UDP" : ""); + ts->udp ? "-UDP" : ""); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; - FALLTHROUGH(); + ts->state = new_state; + curlx_safefree(data->req.hd_proxy_auth); + break; case H3_TUNNEL_FAILED: - if(new_state == H3_TUNNEL_FAILED) - CURL_TRC_CF(data, cf, "[%" PRId64 "] new tunnel state 'failed'", - ts->stream_id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [failed]", stream_id); ts->state = new_state; /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request @@ -211,437 +162,55 @@ static void h3_tunnel_go_state(struct Curl_cfilter *cf, } } -struct cf_ngtcp2_proxy_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; -#ifdef OPENSSL_QUIC_API2 - ngtcp2_crypto_ossl_ctx *ossl_ctx; -#endif - ngtcp2_path connected_path; - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_ccerr last_error; - ngtcp2_crypto_conn_ref conn_ref; - struct cf_call_data call_data; - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; /* hash data->mid to h3_proxy_stream_ctx */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ - int qlogfd; - BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ -}; - struct cf_h3_proxy_ctx { - struct cf_ngtcp2_proxy_ctx *ngtcp2_ctx; - struct cf_call_data call_data; /* fallback before backend ctx exists */ - struct bufq inbufq; /* network receive buffer */ - struct Curl_peer *dest; /* where to tunnel to */ + struct cf_ngtcp2_ctx ngtcp2_ctx; struct h3_tunnel_stream tunnel; /* our tunnel CONNECT stream */ BIT(connected); - BIT(udp_tunnel); -}; - -/** - * All about the H3 internals of a stream - */ -struct h3_proxy_stream_ctx { - int64_t id; /* HTTP/3 stream identifier */ - struct bufq sendbuf; /* h3 request body */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - curl_off_t tun_data_recvd; /* number of bytes received over tunnel */ - uint64_t rx_offset; /* current receive offset */ - uint64_t rx_offset_max; /* allowed receive offset */ - uint64_t window_size_max; /* max flow control window set for stream */ - int status_code; /* HTTP status code */ - CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is local closed */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ -}; - -#define H3_PROXY_STREAM_CTX(ctx, data) \ - ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) - -#define H3_STREAM_ID(stream) ((stream)->id) - -static void h3_proxy_stream_ctx_free(struct h3_proxy_stream_ctx *stream) -{ - Curl_bufq_free(&stream->sendbuf); - curlx_free(stream); -} - -static void h3_proxy_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h3_proxy_stream_ctx_free((struct h3_proxy_stream_ctx *)stream); -} - -static void cf_ngtcp2_proxy_ctx_init(struct cf_ngtcp2_proxy_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - ctx->q.sockfd = CURL_SOCKET_BAD; - ctx->qlogfd = -1; - ctx->version = NGTCP2_PROTO_VER_MAX; - Curl_bufcp_init(&ctx->stream_bufcp, PROXY_H3_STREAM_CHUNK_SIZE, - PROXY_H3_STREAM_POOL_SPARES); - curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint32_hash_init(&ctx->streams, 63, h3_proxy_stream_hash_free); - ctx->initialized = TRUE; -} - -static void cf_ngtcp2_proxy_ctx_free(struct cf_ngtcp2_proxy_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - curlx_dyn_free(&ctx->scratch); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - } - curlx_free(ctx); -} - -static void cf_ngtcp2_proxy_ctx_close(struct cf_ngtcp2_proxy_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; - - if(!ctx->initialized) - return; - if(ctx->qlogfd != -1) { - curlx_close(ctx->qlogfd); - } - ctx->qlogfd = -1; - Curl_vquic_tls_cleanup(&ctx->tls); - Curl_ssl_peer_cleanup(&ctx->peer); - vquic_ctx_free(&ctx->q); - if(ctx->h3conn) { - nghttp3_conn_del(ctx->h3conn); - ctx->h3conn = NULL; - } - if(ctx->qconn) { - ngtcp2_conn_del(ctx->qconn); - ctx->qconn = NULL; - } -#ifdef OPENSSL_QUIC_API2 - if(ctx->ossl_ctx) { - ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); - ctx->ossl_ctx = NULL; - } -#endif - ctx->call_data = save; -} - -static void cf_ngtcp2_proxy_setup_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - const ngtcp2_transport_params *rp; - /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it will close the - * connection when it does not hear from us for that time. - * - * Some servers use this as a keep-alive timer at a rather low - * value. We are doing HTTP/3 here and waiting for the response - * to a request may take a considerable amount of time. We need - * to prevent the peer's QUIC stack from closing in this case. - */ - if(!ctx->qconn) - return; - - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(!rp || !rp->max_idle_timeout) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); - } - else if(!Curl_uint32_hash_count(&ctx->streams)) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); - } - else { - ngtcp2_duration keep_ns; - keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); - CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " - "set keep-alive to %" PRIu64 " ms.", - rp->max_idle_timeout / NGTCP2_MILLISECONDS, - keep_ns / NGTCP2_MILLISECONDS); - } -} - -struct proxy_pkt_io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - ngtcp2_path_storage ps; }; -static void proxy_pktx_update_time(struct proxy_pkt_io_ctx *pktx, - struct Curl_cfilter *cf) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - const struct curltime *pnow = Curl_pgrs_now(pktx->data); - - vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); -} - -static void proxy_pktx_init(struct proxy_pkt_io_ctx *pktx, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - pktx->cf = cf; - pktx->data = data; - ngtcp2_path_storage_zero(&pktx->ps); - vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); -} - -static ngtcp2_conn *proxy_get_conn(ngtcp2_crypto_conn_ref *conn_ref) -{ - struct Curl_cfilter *cf = conn_ref->user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - return ctx->qconn; -} - -#ifdef DEBUG_NGTCP2 -static void proxy_quic_printf(void *user_data, const char *fmt, ...) -{ - va_list ap; - (void)user_data; - va_start(ap, fmt); - curl_mvfprintf(stderr, fmt, ap); - va_end(ap); - curl_mfprintf(stderr, "\n"); -} -#endif - -static void proxy_qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - (void)flags; - if(ctx->qlogfd != -1) { - ssize_t rc = write(ctx->qlogfd, data, datalen); - if(rc == -1) { - /* on write error, stop further write attempts */ - curlx_close(ctx->qlogfd); - ctx->qlogfd = -1; - } - } -} - -static void quic_settings_proxy(struct cf_ngtcp2_proxy_ctx *ctx, - struct Curl_easy *data, - struct proxy_pkt_io_ctx *pktx) -{ - ngtcp2_settings *s = &ctx->settings; - ngtcp2_transport_params *t = &ctx->transport_params; - - ngtcp2_settings_default(s); - ngtcp2_transport_params_default(t); -#ifdef DEBUG_NGTCP2 - s->log_printf = proxy_quic_printf; -#else - s->log_printf = NULL; -#endif - - s->initial_ts = pktx->ts; - s->handshake_timeout = (data->set.connecttimeout > 0) ? - data->set.connecttimeout * NGTCP2_MILLISECONDS : - PROXY_QUIC_HANDSHAKE_TIMEOUT; - s->max_window = 100 * PROXY_H3_STREAM_WINDOW_SIZE; - s->max_stream_window = 10 * PROXY_H3_STREAM_WINDOW_SIZE; - s->no_pmtud = FALSE; -#ifdef NGTCP2_SETTINGS_V3 - /* try ten times the ngtcp2 defaults here for problems with Caddy */ - s->glitch_ratelim_burst = 1000 * 10; - s->glitch_ratelim_rate = 33 * 10; -#endif - t->initial_max_data = 10 * PROXY_H3_STREAM_WINDOW_SIZE; - t->initial_max_stream_data_bidi_local = PROXY_H3_STREAM_WINDOW_SIZE; - t->initial_max_stream_data_bidi_remote = PROXY_H3_STREAM_WINDOW_SIZE; - t->initial_max_stream_data_uni = PROXY_H3_STREAM_WINDOW_SIZE; - t->initial_max_streams_bidi = PROXY_QUIC_MAX_STREAMS; - t->initial_max_streams_uni = PROXY_QUIC_MAX_STREAMS; - t->max_idle_timeout = 0; /* no idle timeout from our side */ - if(ctx->qlogfd != -1) { - s->qlog_write = proxy_qlog_callback; - } -} - -static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static bool cf_ngtcp2_proxy_err_is_fatal(int code) -{ - return (NGTCP2_ERR_FATAL >= code) || - (NGTCP2_ERR_DROP_CONN == code) || - (NGTCP2_ERR_IDLE_CLOSE == code); -} - -static void cf_ngtcp2_proxy_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - if(!ctx->last_error.error_code) { - if(NGTCP2_ERR_CRYPTO == code) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); - } - } - if(cf_ngtcp2_proxy_err_is_fatal(code)) - cf_ngtcp2_proxy_conn_close(cf, data); -} - -static bool cf_ngtcp2_proxy_h3_err_is_fatal(int code) -{ - return (NGHTTP3_ERR_FATAL >= code) || - (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); -} - -static void cf_ngtcp2_proxy_h3_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - if(!ctx->last_error.error_code) { - ngtcp2_ccerr_set_application_error(&ctx->last_error, - nghttp3_err_infer_quic_app_error_code(code), NULL, 0); - } - if(cf_ngtcp2_proxy_h3_err_is_fatal(code)) - cf_ngtcp2_proxy_conn_close(cf, data); -} - -/* How to access `call_data` from a cf_h3_proxy filter */ -static struct cf_call_data *cf_h3_proxy_call_data(struct Curl_cfilter *cf) -{ - struct cf_h3_proxy_ctx *ctx = cf ? cf->ctx : NULL; - static struct cf_call_data no_ctx; - - if(!ctx) - return &no_ctx; - if(ctx->ngtcp2_ctx) - return &ctx->ngtcp2_ctx->call_data; - return &ctx->call_data; -} - -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) (*cf_h3_proxy_call_data(cf)) +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); -static void cf_h3_proxy_ctx_clear(struct cf_h3_proxy_ctx *ctx) +static CURLcode cf_h3_proxy_ctx_init(struct cf_h3_proxy_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - Curl_bufq_free(&ctx->inbufq); - Curl_peer_unlink(&ctx->dest); - h3_tunnel_stream_clear(&ctx->tunnel); - memset(ctx, 0, sizeof(*ctx)); + CURLcode result; + result = Curl_cf_ngtcp2_ctx_init(&ctx->ngtcp2_ctx, origin, peer, + sslc, cf_ngtcp2_proxy_h3_init); + if(!result) + result = h3_tunnel_stream_init(&ctx->tunnel, tunnel_peer, + TRNSPRT_IS_DGRAM(tunnel_transport)); + return result; } static void cf_h3_proxy_ctx_free(struct cf_h3_proxy_ctx *ctx) { if(ctx) { - cf_h3_proxy_ctx_clear(ctx); + Curl_cf_ngtcp2_ctx_cleanup(&ctx->ngtcp2_ctx); + h3_tunnel_stream_cleanup(&ctx->tunnel); curlx_free(ctx); } } -static CURLcode h3_proxy_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream = NULL; - - if(!data) - return CURLE_FAILED_INIT; - - if(!ctx) - return CURLE_FAILED_INIT; - - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->id = -1; - stream->rx_offset = 0; - stream->rx_offset_max = PROXY_H3_STREAM_WINDOW_SIZE; - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - PROXY_H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - stream->window_size_max = PROXY_H3_STREAM_WINDOW_SIZE; - - if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_proxy_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - if(Curl_uint32_hash_count(&ctx->streams) == 1) - cf_ngtcp2_proxy_setup_keep_alive(cf, data); - - return CURLE_OK; -} - static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, uint64_t datalen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct h3_stream_ctx *stream; size_t skiplen; + (void)stream_user_data; - if(!ctx) - return 0; - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(!stream) + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) return 0; + /* The server acknowledged `datalen` of bytes from our request body. * This is a delta. We have kept this data in `sendbuf` for * re-transmissions and can free it now. */ @@ -667,25 +236,18 @@ static int cb_h3_proxy_stream_close(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; - bool tunnel_stream = FALSE; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; - if(!ctx) + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) return 0; - stream = H3_PROXY_STREAM_CTX(ctx, data); - tunnel_stream = (stream_id == proxy_ctx->tunnel.stream_id); - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) { - if(tunnel_stream) { - proxy_ctx->tunnel.stream = NULL; - proxy_ctx->tunnel.closed = TRUE; - } - return 0; - } stream->closed = TRUE; stream->error3 = app_error_code; @@ -693,26 +255,23 @@ static int cb_h3_proxy_stream_close(nghttp3_conn *conn, int64_t stream_id, stream->reset = TRUE; stream->send_closed = TRUE; CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, - H3_STREAM_ID(stream), stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", H3_STREAM_ID(stream)); - } - if(tunnel_stream) { - proxy_ctx->tunnel.stream = NULL; - proxy_ctx->tunnel.closed = TRUE; + stream->id, stream->error3); } + else + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; Curl_multi_mark_dirty(data); return 0; } static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream) + struct h3_stream_ctx *stream) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - uint64_t cur_win, wanted_win = PROXY_H3_STREAM_WINDOW_SIZE_MAX; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; /* how much does rate limiting allow us to acknowledge? */ if(Curl_rlimit_active(&data->progress.dl.rlimit)) { @@ -729,7 +288,7 @@ static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, " tokens)", stream->id, avail); return; } - wanted_win = CURLMIN((uint64_t)avail, PROXY_H3_STREAM_WINDOW_SIZE_MAX); + wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); } if(stream->rx_offset_max < stream->rx_offset) { @@ -761,28 +320,26 @@ static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; size_t nwritten; CURLcode result = CURLE_OK; (void)conn; (void)stream3_id; + (void)stream_user_data; - if(!ctx) - return NGHTTP3_ERR_CALLBACK_FAILURE; - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(!stream) { + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream3_id)) { return NGHTTP3_ERR_CALLBACK_FAILURE; } - stream->tun_data_recvd += (curl_off_t)buflen; + stream->rx_total += (curl_off_t)buflen; CURL_TRC_CF(data, cf, "[cb_h3_proxy_recv_data] " "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, - H3_STREAM_ID(stream), buflen, stream->tun_data_recvd); + stream->id, buflen, stream->rx_total); - result = Curl_bufq_write(&proxy_ctx->inbufq, buf, buflen, &nwritten); + result = Curl_bufq_write(&pctx->tunnel.recvbuf, buf, buflen, &nwritten); if(result || (nwritten < buflen)) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -807,8 +364,8 @@ static int cb_h3_proxy_deferred_consume(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; (void)conn; (void)stream_user_data; @@ -829,12 +386,11 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_h3_proxy_ctx *pctx = cf->ctx; nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; CURLcode result = CURLE_OK; int http_status; struct http_resp *resp; @@ -842,27 +398,21 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, (void)stream_id; (void)token; (void)flags; + (void)stream_user_data; /* stream_user_data might be NULL for control streams */ - if(!data) { - /* Silently ignore headers on streams without user data (control, etc) */ - return 0; - } + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; - if(!ctx) - return 0; - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(!stream) { + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { CURL_TRC_CF(data, cf, "[%" PRId64 "] recv_header: stream lookup " "failed for data=%p mid=%u", stream_id, (void *)data, data ? data->mid : 0); - } - - /* we might have cleaned up this transfer already */ - if(!stream) return 0; + } - if(proxy_ctx->tunnel.has_final_response) { + if(pctx->tunnel.has_final_response) { /* we do not do anything with trailers for tunnel streams */ return 0; } @@ -876,15 +426,15 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, result = Curl_http_resp_make(&resp, http_status, NULL); if(result) return NGHTTP3_ERR_CALLBACK_FAILURE; - if(proxy_ctx->tunnel.resp) - Curl_http_resp_free(proxy_ctx->tunnel.resp); - proxy_ctx->tunnel.resp = resp; + if(pctx->tunnel.resp) + Curl_http_resp_free(pctx->tunnel.resp); + pctx->tunnel.resp = resp; } else { /* store as an HTTP1-style header */ CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); - result = Curl_dynhds_add(&proxy_ctx->tunnel.resp->headers, + result = Curl_dynhds_add(&pctx->tunnel.resp->headers, (const char *)h3name.base, h3name.len, (const char *)h3val.base, h3val.len); if(result) { @@ -899,38 +449,31 @@ static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; (void)conn; (void)stream_id; (void)fin; + (void)stream_user_data; - /* stream_user_data might be NULL for control streams */ - if(!data) { - /* Silently ignore for streams without user data */ - return 0; - } + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; - if(!ctx) - return 0; - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(!stream) { + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers: stream lookup " "failed for data=%p mid=%u", stream_id, (void *)data, data ? data->mid : 0); - } - - if(!stream) return 0; + } CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, stream->status_code); - if(!proxy_ctx->tunnel.has_final_response) { + if(!pctx->tunnel.has_final_response) { if(stream->status_code / 100 != 1) { - proxy_ctx->tunnel.has_final_response = TRUE; + pctx->tunnel.has_final_response = TRUE; } } @@ -947,10 +490,9 @@ static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; (void)conn; - (void)stream_user_data; if(ctx) { @@ -970,55 +512,57 @@ static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); int rv; + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; - if(!ctx) + if(!pctx->tunnel.stream || + (stream_id != pctx->tunnel.stream->id)) return 0; rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(stream_id == proxy_ctx->tunnel.stream_id) { - proxy_ctx->tunnel.stream = NULL; - proxy_ctx->tunnel.closed = TRUE; - } + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGHTTP3_ERR_CALLBACK_FAILURE; } - return 0; } -static nghttp3_ssize cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, - int64_t stream_id, - nghttp3_vec *vec, - size_t veccnt, - uint32_t *pflags, - void *user_data, - void *stream_user_data) +static nghttp3_ssize cb_h3_tunnel_read_data(nghttp3_conn *conn, + int64_t stream_id, + nghttp3_vec *vec, + size_t veccnt, + uint32_t *pflags, + void *user_data, + void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - struct h3_proxy_stream_ctx *stream; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; size_t nwritten = 0; size_t nvecs = 0; const unsigned char *buf_base; + (void)conn; (void)stream_id; (void)veccnt; + (void)stream_user_data; + (void)pflags; - if(!ctx) + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream_id)) return NGHTTP3_ERR_CALLBACK_FAILURE; - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; /* nghttp3 keeps references to the sendbuf data until it is ACKed * by the server (see `cb_h3_proxy_acked_req_body()` for updates). * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` @@ -1042,33 +586,18 @@ static nghttp3_ssize cb_h3_read_data_for_tunnel_stream(nghttp3_conn *conn, DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ } - if(nwritten > 0 && - stream->upload_left != -1 && - (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) - stream->upload_left -= nwritten; - - /* When we stopped sending and everything in `sendbuf` is "in flight", - * we are at the end of the request body. */ - /* We should NOT set send_closed = TRUE for tunnel stream */ - if(stream->upload_left == 0 && - (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) { - *pflags = NGHTTP3_DATA_FLAG_EOF; - stream->send_closed = TRUE; - } - - else if(!nwritten) { + if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", - H3_STREAM_ID(stream)); + stream->id); return NGHTTP3_ERR_WOULDBLOCK; } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%zu vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", - H3_STREAM_ID(stream), nvecs, + "%zu vecs%s with %zu (buffered=%zu)", + stream->id, nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left); + nwritten, Curl_bufq_len(&stream->sendbuf)); return (nghttp3_ssize)nvecs; } @@ -1098,55 +627,10 @@ static nghttp3_callbacks ngh3_proxy_callbacks = { #endif }; -#if NGTCP2_VERSION_NUM < 0x011100 -struct cf_ngtcp2_proxy_sfind_ctx { - int64_t stream_id; - struct h3_proxy_stream_ctx *stream; - uint32_t mid; -}; - -static bool cf_ngtcp2_proxy_sfind(uint32_t mid, void *value, void *user_data) -{ - struct cf_ngtcp2_proxy_sfind_ctx *fctx = user_data; - struct h3_proxy_stream_ctx *stream = value; - - if(fctx->stream_id == H3_STREAM_ID(stream)) { - fctx->mid = mid; - fctx->stream = stream; - return FALSE; - } - return TRUE; /* continue */ -} - -static struct h3_proxy_stream_ctx *cf_ngtcp2_proxy_get_stream( - struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) -{ - struct cf_ngtcp2_proxy_sfind_ctx fctx; - fctx.stream_id = stream_id; - fctx.stream = NULL; - Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_proxy_sfind, &fctx); - return fctx.stream; -} -#else -static struct h3_proxy_stream_ctx *cf_ngtcp2_proxy_get_stream( - struct cf_ngtcp2_proxy_ctx *ctx, int64_t stream_id) -{ - struct Curl_easy *data = - ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); - - if(!data) { - return NULL; - } - return H3_PROXY_STREAM_CTX(ctx, data); -} -#endif /* NGTCP2_VERSION_NUM < 0x011100 */ - -static CURLcode cf_ngtcp2_h3conn_init(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; int rc; if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { @@ -1166,975 +650,25 @@ static CURLcode cf_ngtcp2_h3conn_init(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack encoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack decoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - CURL_TRC_CF(data, cf, "HTTP/3 connection initialized"); - return CURLE_OK; + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); } -static int cb_ngtcp2_proxy_handshake_completed(ngtcp2_conn *tconn, - void *user_data) +static ssize_t cf_h3_proxy_recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + CURLcode *err) { - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data; - - (void)tconn; - DEBUGASSERT(ctx); - data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(!ctx || !data) - return NGTCP2_ERR_CALLBACK_FAILURE; - - ctx->handshake_at = *Curl_pgrs_now(data); - ctx->tls_handshake_complete = TRUE; - Curl_vquic_report_handshake(&ctx->tls, cf, data); - - ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, - data, &ctx->peer); -#ifdef CURLVERBOSE - if(Curl_trc_is_verbose(data)) { - const ngtcp2_transport_params *rp; - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T - "ms, remote transport[max_udp_payload=%" PRIu64 - ", initial_max_data=%" PRIu64 "]", - curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), - rp->max_udp_payload_size, rp->initial_max_data); - } -#endif - - /* In case of earlydata, where we simulate being connected, update - * the handshake time when we really did connect */ - if(ctx->use_earlydata) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); - if(ctx->use_earlydata) { -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_accepted = - (SSL_get_early_data_status(ctx->tls.ossl.ssl) != - SSL_EARLY_DATA_REJECTED); -#endif -#ifdef USE_GNUTLS - int flags = gnutls_session_get_flags(ctx->tls.gtls.session); - ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_accepted = - (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) != - WOLFSSL_EARLY_DATA_REJECTED); -#else - DEBUGASSERT(0); /* should not come here if ED is disabled. */ - ctx->earlydata_accepted = FALSE; -#endif /* WOLFSSL_EARLY_DATA */ -#endif - CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", - ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); - Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? - (curl_off_t)ctx->earlydata_skip : - -(curl_off_t)ctx->earlydata_skip); - } + ssize_t nread = -1; + *err = CURLE_OK; - /* Initialize HTTP/3 connection after successful handshake */ - if(!ctx->h3conn) { - CURLcode result = cf_ngtcp2_h3conn_init(cf, data); - if(result) { - CURL_TRC_CF(data, cf, "HTTP/3 initialization failed: %d", result); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; -} - -static int cb_ngtcp2_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - nghttp3_ssize nconsumed; - int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; - struct Curl_easy *data = stream_user_data; - (void)offset; - (void)data; - - nconsumed = - nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - if(!data) - data = CF_DATA_CURRENT(cf); - if(data) - CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed); - if(nconsumed < 0) { - struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); - if(data && stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " - "reset=%d, closed=%d", - stream_id, stream->reset, stream->closed); - } - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - if(nconsumed) { - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); - } - - return 0; -} - -static int cb_ngtcp2_acked_stream_data_offset(ngtcp2_conn *tconn, - int64_t stream_id, - uint64_t offset, - uint64_t datalen, - void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - int rv; - (void)stream_id; - (void)tconn; - (void)offset; - (void)datalen; - (void)stream_user_data; - - rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -static int cb_ngtcp2_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - int rv; - - (void)tconn; - /* stream is closed... */ - if(!data) - data = CF_DATA_CURRENT(cf); - if(!data) - return NGTCP2_ERR_CALLBACK_FAILURE; - - if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { - app_error_code = NGHTTP3_H3_NO_ERROR; - } - - rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" - PRIu64 ") -> %d", stream_id, app_error_code, rv); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - cf_ngtcp2_proxy_h3_err_set(cf, data, rv); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -static int cb_ngtcp2_extend_max_local_streams_bidi(ngtcp2_conn *tconn, - uint64_t max_streams, - void *user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - (void)tconn; - ctx->max_bidi_streams = max_streams; - if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 ", used %" PRIu64, - ctx->max_bidi_streams, ctx->used_bidi_streams); - return 0; -} - -static void cb_ngtcp2_rand(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx) -{ - CURLcode result; - (void)rand_ctx; - - result = Curl_rand(NULL, dest, destlen); - if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, fill 0 and call it *random*. */ - memset(dest, 0, destlen); - } -} - -/* for ngtcp2 data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} - -#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ -static int cb_ngtcp2_get_new_connection_id2(ngtcp2_conn *tconn, - ngtcp2_cid *cid, struct ngtcp2_stateless_reset_token *token, - size_t cidlen, void *user_data) -{ - CURLcode result; - (void)tconn; - (void)user_data; - - result = Curl_rand(NULL, cid->data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token->data, sizeof(token->data)); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} -#endif - -static int cb_ngtcp2_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = stream_user_data; - int rv; - (void)tconn; - (void)final_size; - (void)app_error_code; - (void)data; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(stream_id == proxy_ctx->tunnel.stream_id) { - proxy_ctx->tunnel.stream = NULL; - proxy_ctx->tunnel.closed = TRUE; - } - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -static int cb_ngtcp2_extend_max_stream_data(ngtcp2_conn *tconn, - int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *s_data = stream_user_data; - struct h3_proxy_stream_ctx *stream = NULL; - int rv; - (void)tconn; - (void)max_data; - - rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - stream = H3_PROXY_STREAM_CTX(ctx, s_data); - if(stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", stream_id); - stream->quic_flow_blocked = FALSE; - Curl_multi_mark_dirty(s_data); - } - return 0; -} - -static int cb_ngtcp2_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t app_error_code, - void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - int rv; - (void)tconn; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -static int cb_ngtcp2_recv_rx_key(ngtcp2_conn *tconn, - ngtcp2_encryption_level level, - void *user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - (void)tconn; - - if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) - return 0; - - DEBUGASSERT(ctx); - DEBUGASSERT(data); - if(ctx && data && !ctx->h3conn) { - if(cf_ngtcp2_h3conn_init(cf, data)) - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -#if defined(_MSC_VER) && defined(_DLL) -#pragma warning(push) -#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ -#endif - -static ngtcp2_callbacks ngtcp2_proxy_callbacks = { - ngtcp2_crypto_client_initial_cb, - NULL, /* recv_client_initial */ - ngtcp2_crypto_recv_crypto_data_cb, - cb_ngtcp2_proxy_handshake_completed, - NULL, /* recv_version_negotiation */ - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - cb_ngtcp2_recv_stream_data, - cb_ngtcp2_acked_stream_data_offset, - NULL, /* stream_open */ - cb_ngtcp2_stream_close, - NULL, /* recv_stateless_reset */ - ngtcp2_crypto_recv_retry_cb, - cb_ngtcp2_extend_max_local_streams_bidi, - NULL, /* extend_max_local_streams_uni */ - cb_ngtcp2_rand, - cb_ngtcp2_get_new_connection_id, /* for ngtcp2 cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - ngtcp2_pkt_info pi; - ngtcp2_path path; - size_t offset, pktlen; - int rv; - - if(ecn) - CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", - buflen, gso_size, ecn); - ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, - remote_addrlen); - pi.ecn = (uint8_t)ecn; - - for(offset = 0; offset < buflen; offset += gso_size) { - pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, - buf + offset, pktlen, pktx->ts); - if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", - ngtcp2_strerror(rv), rv); - cf_ngtcp2_proxy_err_set(pktx->cf, pktx->data, rv); - - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } - } - return CURLE_OK; -} - -static CURLcode proxy_h3_progress_ingress_ngtcp2(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct proxy_pkt_io_ctx *pktx) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct proxy_pkt_io_ctx local_pktx; - CURLcode result = CURLE_OK; - - if(!ctx) - return CURLE_RECV_ERROR; - if(!data || !data->multi) - return CURLE_RECV_ERROR; - - if(!pktx) { - proxy_pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - proxy_pktx_update_time(pktx, cf); - ngtcp2_path_storage_zero(&pktx->ps); - } - - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - return result; - - if(ctx->q.sockfd == CURL_SOCKET_BAD) - return CURLE_RECV_ERROR; - - return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts_proxy, pktx); -} - -/** - * Read a network packet to send from ngtcp2 into `buf`. - * Return number of bytes written or -1 with *err set. - */ -static CURLcode proxy_read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - size_t *pnread) -{ - struct proxy_pkt_io_ctx *x = userp; - struct cf_h3_proxy_ctx *proxy_ctx = x->cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - nghttp3_vec vec[16]; - nghttp3_ssize veccnt; - ngtcp2_ssize ndatalen; - uint32_t flags; - int64_t stream_id; - int fin; - ssize_t n; - - *pnread = 0; - veccnt = 0; - stream_id = -1; - fin = 0; - - /* ngtcp2 may want to put several frames from different streams into - * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. - * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make - * another iteration. - * When ngtcp2 is happy (because it has no other frame that would fit - * or it has nothing more to send), it returns the total length - * of the assembled packet. This may be 0 if there was nothing to send. */ - for(;;) { - - if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { - veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, - CURL_ARRAYSIZE(vec)); - if(veccnt < 0) { - failf(x->data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)veccnt)); - cf_ngtcp2_proxy_h3_err_set(x->cf, x->data, (int)veccnt); - return CURLE_SEND_ERROR; - } - } - - flags = NGTCP2_WRITE_STREAM_FLAG_MORE | - (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, - NULL, buf, buflen, - &ndatalen, flags, stream_id, - (const ngtcp2_vec *)vec, veccnt, x->ts); - if(n == 0) { - /* nothing to send */ - return CURLE_AGAIN; - } - else if(n < 0) { - switch(n) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: { - struct h3_proxy_stream_ctx *stream; - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", - stream_id); - stream = cf_ngtcp2_proxy_get_stream(ctx, stream_id); - if(stream) /* it might be not one of our h3 streams? */ - stream->quic_flow_blocked = TRUE; - n = 0; - break; - } - case NGTCP2_ERR_STREAM_SHUT_WR: - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); - n = 0; - break; - case NGTCP2_ERR_WRITE_MORE: - /* ngtcp2 wants to send more. update the flow of the stream whose data - * is in the buffer and continue */ - DEBUGASSERT(ndatalen >= 0); - n = 0; - break; - default: - DEBUGASSERT(ndatalen == -1); - failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", - ngtcp2_strerror((int)n)); - cf_ngtcp2_proxy_err_set(x->cf, x->data, (int)n); - return CURLE_SEND_ERROR; - } - } - - if(ndatalen >= 0) { - /* we add the amount of data bytes to the flow windows */ - int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); - if(rv) { - failf(x->data, "nghttp3_conn_add_write_offset returned error: %s", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - } - - if(n > 0) { - /* packet assembled, leave */ - *pnread = (size_t)n; - return CURLE_OK; - } - } -} - -static CURLcode proxy_h3_progress_egress_ngtcp2(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct proxy_pkt_io_ctx *pktx) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - size_t nread; - size_t max_payload_size, path_max_payload_size; - size_t pktcnt = 0; - size_t gsolen = 0; /* this disables gso until we have a clue */ - size_t send_quantum; - CURLcode result; - struct proxy_pkt_io_ctx local_pktx; - - if(!pktx) { - proxy_pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - proxy_pktx_update_time(pktx, cf); - ngtcp2_path_storage_zero(&pktx->ps); - } - - result = vquic_flush(cf, data, &ctx->q); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - - /* In UDP, there is a maximum theoretical packet payload length and - * a minimum payload length that is "guaranteed" to work. - * To detect if this minimum payload can be increased, ngtcp2 sends - * now and then a packet payload larger than the minimum. It that - * is ACKed by the peer, both parties know that it works and - * the subsequent packets can use a larger one. - * This is called PMTUD (Path Maximum Transmission Unit Discovery). - * Since a PMTUD might be rejected right on send, we do not want it - * be followed by other packets of lesser size. Because those would - * also fail then. If we detect a PMTUD while buffering, we flush. - */ - max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); - path_max_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); - send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); - CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", - send_quantum); - for(;;) { - /* add the next packet to send, if any, to our buffer */ - result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - proxy_read_pkt_to_send, pktx, &nread); - if(result == CURLE_AGAIN) - break; - else if(result) - return result; - else { - size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); - if((buflen >= send_quantum) || - ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) - break; - DEBUGASSERT(nread > 0); - ++pktcnt; - if(pktcnt == 1) { - /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We shall see. */ - gsolen = nread; - } - else if(nread > gsolen || - (gsolen > path_max_payload_size && nread != gsolen)) { - /* The added packet is a PMTUD *or* the one(s) before the - * added were PMTUD and the last one is smaller. - * Flush the buffer before the last add. */ - result = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - pktcnt = 0; - } - else if(nread < gsolen) { - /* Reached capacity of our buffer *or* - * last add was shorter than the previous ones, flush */ - break; - } - } - } - - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* time to send */ - CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", - pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); - result = vquic_send(cf, data, &ctx->q, gsolen); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - proxy_pktx_update_time(pktx, cf); - ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); - } - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_proxy_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct cf_call_data save; - struct proxy_pkt_io_ctx pktx; - CURLcode result = CURLE_OK; - - if(cf->shutdown || !ctx->qconn) { - *done = TRUE; - return CURLE_OK; - } - - if(!cf->next) { - Curl_bufq_reset(&ctx->q.sendbuf); - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - proxy_pktx_init(&pktx, cf, data); - - if(!ctx->shutdown_started) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_ssize nwritten; - - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); - result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } - } - - DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); - ctx->shutdown_started = TRUE; - nwritten = ngtcp2_conn_write_connection_close( - ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %zd", ctx->last_error.type, - ctx->last_error.error_code, (ssize_t)nwritten); - /* there are cases listed in ngtcp2 documentation where this call - * may fail. Since we are doing a connection shutdown as graceful - * as we can, such an error is ignored here. */ - if(nwritten > 0) { - /* Ignore amount written. sendbuf was empty and has always room for - * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which - * case `result` is set non zero. */ - size_t n; - result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, - (size_t)nwritten, &n); - if(result) { - CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " - "aborting shutdown", result); - goto out; - } - - ctx->q.no_gso = TRUE; - ctx->q.gsolen = (size_t)nwritten; - ctx->q.split_len = 0; - } - } - - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing egress"); - result = vquic_flush(cf, data, &ctx->q); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } - } - - if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. We are done. */ - CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); - *done = TRUE; - result = CURLE_OK; - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_ngtcp2_proxy_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - bool done; - cf_ngtcp2_proxy_shutdown(cf, data, &done); -} - -static void cf_ngtcp2_proxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->qconn) { - cf_ngtcp2_proxy_conn_close(cf, data); - cf_ngtcp2_proxy_ctx_close(ctx); - CURL_TRC_CF(data, cf, "close"); - } - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} - -static void cf_ngtcp2_proxy_stream_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - DEBUGASSERT(data); - DEBUGASSERT(stream); - - if(stream->id == proxy_ctx->tunnel.stream_id) { - proxy_ctx->tunnel.stream = NULL; - proxy_ctx->tunnel.closed = TRUE; - } - - if(ctx->h3conn) - nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); - if(ctx->qconn) - ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); - - if(!stream->closed && ctx->qconn && ctx->h3conn) { - CURLcode result; - - stream->closed = TRUE; - (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, - NGHTTP3_H3_REQUEST_CANCELLED); - result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", - stream->id, result); - } -} - -/** - * Connection maintenance like timeouts on packet ACKs etc. are done by us, not - * the OS like for TCP. POLL events on the socket therefore are not - * sufficient. - * ngtcp2 tells us when it wants to be invoked again. We handle that via - * the `Curl_expire()` mechanisms. - */ -static CURLcode check_and_set_expiry_ngtcp2(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct proxy_pkt_io_ctx *pktx) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct proxy_pkt_io_ctx local_pktx; - ngtcp2_tstamp expiry; - - if(!ctx) - return CURLE_OK; - - if(!pktx) { - proxy_pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - proxy_pktx_update_time(pktx, cf); - } - - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= pktx->ts) { - CURLcode result; - int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - cf_ngtcp2_proxy_err_set(cf, data, rv); - return CURLE_SEND_ERROR; - } - result = proxy_h3_progress_ingress_ngtcp2(cf, data, pktx); - if(result) - return result; - result = proxy_h3_progress_egress_ngtcp2(cf, data, pktx); - if(result) - return result; - /* ask again, things might have changed */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - } - - if(expiry > pktx->ts) { - ngtcp2_duration timeout = expiry - pktx->ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), - EXPIRE_QUIC); - } - } - return CURLE_OK; -} - -static ssize_t proxy_recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream, - CURLcode *err) -{ - ssize_t nread = -1; - *err = CURLE_OK; - - if(stream->reset) { - if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { - infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " - "on a new connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); - data->state.refused_stream = TRUE; - *err = CURLE_RECV_ERROR; - goto out; + if(stream->reset) { + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); + data->state.refused_stream = TRUE; + *err = CURLE_RECV_ERROR; + goto out; } else if(stream->resp_hds_complete && data->req.no_body) { CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " @@ -2164,27 +698,10 @@ static ssize_t proxy_recv_closed_stream(struct Curl_cfilter *cf, return nread; } -static struct h3_proxy_stream_ctx *h3_proxy_resolve_send_stream( - struct cf_h3_proxy_ctx *proxy_ctx, - struct cf_ngtcp2_proxy_ctx *ctx, - struct Curl_easy *data) -{ - struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); - - if(stream) - return stream; - - /* send can be driven by a different easy handle during shutdown */ - if(proxy_ctx->tunnel.stream && !proxy_ctx->tunnel.closed) { - return proxy_ctx->tunnel.stream; - } - return NULL; -} - -static CURLcode h3_proxy_sendbuf_add(struct Curl_easy *data, - struct h3_proxy_stream_ctx *stream, - const uint8_t *buf, size_t len, - size_t *pnwritten) +static CURLcode cf_h3_proxy_sendbuf_add(struct Curl_easy *data, + struct h3_stream_ctx *stream, + const uint8_t *buf, size_t len, + size_t *pnwritten) { CURLcode result; *pnwritten = 0; @@ -2199,18 +716,18 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream = NULL; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; struct cf_call_data save; - struct proxy_pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx pktx; CURLcode result = CURLE_OK; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->qconn); DEBUGASSERT(ctx->h3conn); - proxy_pktx_init(&pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); *pnwritten = 0; /* handshake verification failed in callback, do not send anything */ @@ -2220,17 +737,17 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, } (void)eos; /* use for stream EOF and block handling */ - result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); if(result) goto out; - stream = h3_proxy_resolve_send_stream(proxy_ctx, ctx, data); - if(!stream) { + if(pctx->tunnel.closed) { result = CURLE_SEND_ERROR; goto denied; } - if(proxy_ctx->tunnel.closed) { + stream = pctx->tunnel.stream; + if(!stream) { result = CURLE_SEND_ERROR; goto denied; } @@ -2254,7 +771,7 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, goto out; } else { - result = h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); + result = cf_h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %d, %zu", stream->id, len, result, *pnwritten); @@ -2267,11 +784,11 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, ctx->earlydata_skip += *pnwritten; DEBUGASSERT(!result); - result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); out: result = Curl_1st_fatal(result, - check_and_set_expiry_ngtcp2(cf, data, &pktx)); + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : -1, len, result, *pnwritten); @@ -2284,11 +801,11 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, size_t *pnread) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = pctx->tunnel.stream; struct cf_call_data save; - struct proxy_pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx pktx; CURLcode result = CURLE_OK; CF_DATA_SAVE(save, cf, data); @@ -2304,698 +821,229 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, goto denied; } - proxy_pktx_init(&pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); if(!stream || ctx->shutdown_started) { result = CURLE_RECV_ERROR; goto out; } - if(!Curl_bufq_is_empty(&proxy_ctx->inbufq)) { - result = Curl_bufq_cread(&proxy_ctx->inbufq, buf, len, pnread); + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); if(result) goto out; } - result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); if(result) goto out; /* inbufq had nothing before, maybe after progressing ingress? */ - if(!*pnread && !Curl_bufq_is_empty(&proxy_ctx->inbufq)) { - result = Curl_bufq_cread(&proxy_ctx->inbufq, buf, len, pnread); + if(!*pnread && !Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); if(result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) -> %zu, %d", stream->id, len, *pnread, result); goto out; - } - } - - if(*pnread) { - Curl_multi_mark_dirty(data); - } - else { - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_proxy_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - ssize_t nread = proxy_recv_closed_stream(cf, data, stream, &result); - if(nread > 0) - *pnread = (size_t)nread; - goto out; - } - result = CURLE_AGAIN; - } - -out: - result = Curl_1st_fatal(result, - proxy_h3_progress_egress_ngtcp2(cf, data, &pktx)); - result = Curl_1st_fatal(result, - check_and_set_expiry_ngtcp2(cf, data, &pktx)); -denied: - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnread); - CF_DATA_RESTORE(cf, save); - return result; -} - -static void proxy_h3_submit(int64_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct httpreq *req, - CURLcode *err) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream = NULL; - - struct dynhds h2_headers; - nghttp3_nv *nva = NULL; - size_t nheader; - - int rc = 0; - unsigned int i; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - *err = Curl_http_req_to_h2(&h2_headers, req, data); - if(*err) - goto out; - - *err = h3_proxy_data_setup(cf, data); - if(*err) - goto out; - - if(!ctx) { - *err = CURLE_FAILED_INIT; - goto out; - } - - stream = H3_PROXY_STREAM_CTX(ctx, data); - - DEBUGASSERT(stream); - if(!stream) { - *err = CURLE_FAILED_INIT; - goto out; - } - - nheader = Curl_dynhds_count(&h2_headers); - nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - - /* Open a bidirectional stream */ - { - int64_t sid; - int rv; - - DEBUGASSERT(stream->id == -1); - rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); - if(rv) { - failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); - *err = CURLE_SEND_ERROR; - goto out; - } - stream->id = sid; - ++ctx->used_bidi_streams; - - /* Set stream user data in ngtcp2 connection for callbacks */ - rv = ngtcp2_conn_set_stream_user_data(ctx->qconn, sid, data); - if(rv) { - failf(data, "cannot set stream user data: %s", ngtcp2_strerror(rv)); - *err = CURLE_SEND_ERROR; - goto out; - } - proxy_ctx->tunnel.stream = stream; - CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); - } - - /* CONNECT-UDP request stream remains open for capsules, no fixed EOF. */ - stream->upload_left = -1; - stream->send_closed = 0; - reader.read_data = cb_h3_read_data_for_tunnel_stream; - preader = &reader; - - rc = nghttp3_conn_submit_request(ctx->h3conn, H3_STREAM_ID(stream), - nva, nheader, preader, data); - - if(rc) { - switch(rc) { - case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " - "connection is closing", H3_STREAM_ID(stream)); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", - H3_STREAM_ID(stream), rc, nghttp3_strerror(rc)); - break; - } - *err = CURLE_SEND_ERROR; - goto out; - } - - if(Curl_trc_is_verbose(data)) { - CURL_TRC_CF(data, cf, "[H3-PROXY] [%" PRId64 "] OPENED stream " - "for %s", H3_STREAM_ID(stream), - Curl_bufref_ptr(&data->state.url)); - } - -out: - curlx_free(nva); - Curl_dynhds_free(&h2_headers); - if(*err == CURLE_OK) { - *pstream_id = H3_STREAM_ID(stream); - } -} - -static bool cf_h3_proxy_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - bool alive = FALSE; - const ngtcp2_transport_params *rp; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - *input_pending = FALSE; - - if(!ctx || !ctx->qconn || ctx->shutdown_started) - goto out; - if(proxy_ctx->tunnel.closed) - goto out; - - /* We do not announce a max idle timeout, but when the peer does - * it closes the connection when it expires. */ - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(rp && rp->max_idle_timeout) { - timediff_t idletime_ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); - if(idletime_ms > 0) { - uint64_t max_idle_ms = - (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS); - if((uint64_t)idletime_ms > max_idle_ms) - goto out; - } - } - - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - goto out; - - alive = TRUE; - if(*input_pending) { - CURLcode result; - /* This happens before we have sent off a request and the connection is - not in use by any other transfer, there should not be any data here, - only "protocol frames" */ - *input_pending = FALSE; - if(!data || !data->multi) { - alive = FALSE; - goto out; - } - result = proxy_h3_progress_ingress_ngtcp2(cf, data, NULL); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); - alive = result ? FALSE : TRUE; - } - -out: - CF_DATA_RESTORE(cf, save); - return alive; -} - -static CURLcode cf_ngtcp2_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct cf_call_data save; - - if(!ctx) - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - DEBUGASSERT(pres1); - CF_DATA_SAVE(save, cf, data); - /* Set after transport params arrived and continually updated - * by callback. QUIC counts the number over the lifetime of the - * connection, ever increasing. - * We count the *open* transfers plus the budget for new ones. */ - if(!ctx->qconn || ctx->shutdown_started) { - *pres1 = 0; - } - else if(ctx->max_bidi_streams) { - uint64_t avail_bidi_streams = 0; - uint64_t max_streams = cf->conn->attached_xfers; - if(ctx->max_bidi_streams > ctx->used_bidi_streams) - avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; - max_streams += avail_bidi_streams; - *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; - } - else /* transport params not arrived yet? take our default. */ - *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); - CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " - "MAX_CONCURRENT -> %d (%u in use)", - cf->conn->connection_id, *pres1, cf->conn->attached_xfers); - CF_DATA_RESTORE(cf, save); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->q.got_first_byte) { - timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, - &ctx->started_at); - *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->q.got_first_byte) - *when = ctx->q.first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - case CF_QUERY_HTTP_VERSION: - *pres1 = 30; - return CURLE_OK; - case CF_QUERY_SSL_INFO: - case CF_QUERY_SSL_CTX_INFO: { - struct curl_tlssessioninfo *info = pres2; - if(Curl_vquic_tls_get_ssl_info(&ctx->tls, - (query == CF_QUERY_SSL_CTX_INFO), info)) - return CURLE_OK; - break; - } - case CF_QUERY_ALPN_NEGOTIATED: { - const char **palpn = pres2; - DEBUGASSERT(palpn); - *palpn = cf->connected ? "h3" : NULL; - return CURLE_OK; - } - default: - break; - } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static CURLcode cf_ngtcp2_proxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - bool want_recv, want_send; - CURLcode result = CURLE_OK; - - if(!ctx->qconn) - return CURLE_OK; - - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); - if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) - want_send = TRUE; - - if(want_recv || want_send) { - struct h3_proxy_stream_ctx *stream = H3_PROXY_STREAM_CTX(ctx, data); - struct cf_call_data save; - bool c_exhaust, s_exhaust; - - CF_DATA_SAVE(save, cf, data); - c_exhaust = want_send && - (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || - !ngtcp2_conn_get_max_data_left(ctx->qconn)); - s_exhaust = want_send && stream && H3_STREAM_ID(stream) >= 0 && - stream->quic_flow_blocked; - want_recv = (want_recv || c_exhaust || s_exhaust); - want_send = (!s_exhaust && want_send) || - !Curl_bufq_is_empty(&ctx->q.sendbuf); - - result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); - CF_DATA_RESTORE(cf, save); - } - return result; -} - -static CURLcode cf_h3_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - - if(!proxy_ctx) - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; - return cf_ngtcp2_proxy_query(cf, data, query, pres1, pres2); -} - -static CURLcode cf_h3_proxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - - if(!proxy_ctx) - return cf->next ? - cf->next->cft->adjust_pollset(cf->next, data, ps) : - CURLE_OK; - return cf_ngtcp2_proxy_adjust_pollset(cf, data, ps); -} - -static bool cf_h3_proxy_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - if(!proxy_ctx) - return cf->next ? - cf->next->cft->has_data_pending(cf->next, data) : FALSE; - if(!Curl_bufq_is_empty(&proxy_ctx->inbufq)) - return TRUE; - return cf->next ? - cf->next->cft->has_data_pending(cf->next, data) : FALSE; -} - -#ifdef USE_OPENSSL -static int proxy_quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) -{ - ngtcp2_crypto_conn_ref *cref; - struct Curl_cfilter *cf; - struct cf_ngtcp2_proxy_ctx *ctx; - struct cf_h3_proxy_ctx *proxy_ctx; - struct Curl_easy *data; - - cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); - cf = cref ? cref->user_data : NULL; - proxy_ctx = cf ? cf->ctx : NULL; - ctx = proxy_ctx ? proxy_ctx->ngtcp2_ctx : NULL; - data = cf ? CF_DATA_CURRENT(cf) : NULL; - if(cf && data && ctx) { - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; -#ifdef HAVE_OPENSSL_EARLYDATA - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } -#endif - Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, - SSL_version(ssl), "h3", quic_tp, quic_tp_len); - } - return 0; -} -#endif /* USE_OPENSSL */ - -static CURLcode cf_ngtcp2_proxy_tls_ctx_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *user_data) -{ - struct curl_tls_ctx *ctx = user_data; - -#ifdef USE_OPENSSL -#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) - != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#elif defined(OPENSSL_QUIC_API2) - /* nothing to do */ -#else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ - if(Curl_ssl_scache_use(cf, data)) { - SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, proxy_quic_ossl_new_session_cb); - } - -#else -#error "ngtcp2 TLS backend not configured" -#endif /* USE_OPENSSL */ - - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_proxy_on_session_reuse(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct alpn_spec *alpns, - struct Curl_ssl_session *scs, - bool *do_early_data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_max = scs->earlydata_max; -#endif -#ifdef USE_GNUTLS - ctx->earlydata_max = - gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_max = scs->earlydata_max; -#else - ctx->earlydata_max = 0; -#endif /* WOLFSSL_EARLY_DATA */ -#endif -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if(!ctx->earlydata_max) { - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); + } } - else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); + + if(*pnread) { + Curl_multi_mark_dirty(data); } else { - int rv; - rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( - ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); - if(rv) - CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " - "parameters: %s", ngtcp2_strerror(rv)); - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); - result = cf_ngtcp2_h3conn_init(cf, data); - if(!result) { - ctx->use_earlydata = TRUE; - proxy_ctx->connected = TRUE; - *do_early_data = TRUE; - } + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + ssize_t nread = + cf_h3_proxy_recv_closed_stream(cf, data, stream, &result); + if(nread > 0) + *pnread = (size_t)nread; + goto out; } + result = CURLE_AGAIN; } -#else /* not supported in the TLS backend */ - (void)data; - (void)ctx; - (void)scs; - (void)alpns; -#endif + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, result, *pnread); + CF_DATA_RESTORE(cf, save); return result; } -static CURLcode cf_h3_proxy_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_h3_proxy_submit(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + struct httpreq *req) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = NULL; - int rc; - int rv; - CURLcode result = CURLE_OK; - const struct Curl_sockaddr_ex *sockaddr = NULL; - int qfd; - static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - struct proxy_pkt_io_ctx pktx; - - ctx = curlx_calloc(1, sizeof(struct cf_ngtcp2_proxy_ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_ngtcp2_proxy_ctx_init(ctx); - - memset(&proxy_ctx->tunnel, 0, sizeof(proxy_ctx->tunnel)); - - Curl_bufq_init2(&proxy_ctx->inbufq, PROXY_H3_STREAM_CHUNK_SIZE, - PROXY_H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - - result = h3_tunnel_stream_init(&proxy_ctx->tunnel, proxy_ctx->dest); - if(result) - goto out; - - DEBUGASSERT(ctx->initialized); - ctx->started_at = *Curl_pgrs_now(data); + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; + struct dynhds h2_headers; + nghttp3_nv *nva = NULL; + size_t nheader; + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + CURLcode result; - /* Initialize connection IDs BEFORE creating the connection */ - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + result = Curl_http_req_to_h2(&h2_headers, req, data); if(result) goto out; - ctx->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); if(result) goto out; - - (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - ctx->qlogfd = qfd; /* -1 if failure above */ - - result = CURLE_QUIC_CONNECT_ERROR; - if(!cf->next) { - CURL_TRC_CF(data, cf, "h3_proxy_ctx_init: no lower filter"); + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + result = CURLE_FAILED_INIT; goto out; } - ctx->q.sockfd = Curl_conn_cf_get_socket(cf->next, data); - if(ctx->q.sockfd == CURL_SOCKET_BAD) - goto out; - /* Get remote address from the socket filter below */ - if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, - CURL_UNCONST(&sockaddr))) - goto out; - if(!sockaddr) - goto out; - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - goto out; - /* Initialize vquic context BEFORE proxy_pktx_init which needs it */ - result = vquic_ctx_init(data, &ctx->q); - if(result) + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; goto out; + } - /* Set ngtcp2_ctx in proxy_ctx BEFORE proxy_pktx_init which accesses it */ - proxy_ctx->ngtcp2_ctx = ctx; + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } - /* Now we can safely initialize pktx and settings */ - proxy_pktx_init(&pktx, cf, data); - quic_settings_proxy(ctx, data, &pktx); + /* Open a bidirectional stream */ + { + int64_t sid; + int rv; - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + DEBUGASSERT(stream->id == -1); + rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + if(rv) { + failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); + result = CURLE_SEND_ERROR; + goto out; + } + stream->id = sid; + ++ctx->used_bidi_streams; - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ngtcp2_proxy_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; + /* Do NOT set `data` as stream user data. The transfer `data` may + * get cleaned up long before the tunnel goes down. */ + ts->stream = stream; + CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); } - ctx->conn_ref.get_conn = proxy_get_conn; - ctx->conn_ref.user_data = cf; + /* CONNECT-UDP request stream remains open for capsules, no fixed EOF. */ + stream->send_closed = 0; + reader.read_data = cb_h3_tunnel_read_data; + preader = &reader; - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, - cf_ngtcp2_proxy_tls_ctx_setup, &ctx->tls, - &ctx->conn_ref, - cf_ngtcp2_proxy_on_session_reuse); - if(result) - goto out; + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, + nva, nheader, preader, data); -#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) - if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); - result = CURLE_FAILED_INIT; + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " + "connection is closing", stream->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", + stream->id, rc, nghttp3_strerror(rc)); + break; + } + result = CURLE_SEND_ERROR; goto out; } - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); - if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); - result = CURLE_FAILED_INIT; - goto out; + + if(Curl_trc_is_verbose(data)) { + CURL_TRC_CF(data, cf, "[H3-PROXY] [%" PRId64 "] OPENED stream " + "for %s", stream->id, + Curl_bufref_ptr(&data->state.url)); } -#elif defined(USE_OPENSSL) - SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); -#else -#error "ngtcp2 TLS backend not defined" -#endif /* USE_OPENSSL */ - ngtcp2_ccerr_default(&ctx->last_error); +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + return result; +} + +static CURLcode cf_h3_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); - proxy_ctx->connected = FALSE; + if(!ctx->qconn || !pctx->tunnel.stream || (sock == CURL_SOCKET_BAD)) + return CURLE_OK; -out: - if(result) { - if(ctx) { - proxy_ctx->ngtcp2_ctx = NULL; /* Clear before freeing on error */ - cf_ngtcp2_proxy_ctx_free(ctx); - } + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + + if(want_recv || want_send || !Curl_bufq_is_empty(&ctx->q.sendbuf)) { + struct h3_stream_ctx *stream = pctx->tunnel.stream; + bool c_exhaust, s_exhaust; + + c_exhaust = want_send && + (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); + s_exhaust = want_send && stream && stream->id >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); } - CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); return result; } -static CURLcode h3_submit_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_tunnel_stream *ts) +static bool cf_h3_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) + return TRUE; + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static CURLcode cf_h3_proxy_submit_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; CURLcode result; struct httpreq *req = NULL; result = Curl_http_proxy_create_tunnel_request(&req, cf, data, - proxy_ctx->dest, + ts->peer, PROXY_HTTP_V3, - (bool)proxy_ctx->udp_tunnel); - if(result) - goto out; - result = Curl_creader_set_null(data); - if(result) - goto out; + (bool)ts->udp); + if(!result) + result = Curl_creader_set_null(data); + if(!result) + result = cf_h3_proxy_submit(cf, data, ts, req); - proxy_h3_submit(&ts->stream_id, cf, data, req, &result); - -out: if(req) Curl_http_req_free(req); if(result) @@ -3003,173 +1051,54 @@ static CURLcode h3_submit_CONNECT(struct Curl_cfilter *cf, return result; } -static CURLcode h3_proxy_inspect_response(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_tunnel_stream *ts) +static CURLcode cf_h3_proxy_inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_h3_proxy_ctx *pctx = cf->ctx; proxy_inspect_result res; CURLcode result; result = Curl_http_proxy_inspect_tunnel_response( - cf, data, ts->resp, (bool)proxy_ctx->udp_tunnel, &res); + cf, data, ts->resp, (bool)pctx->tunnel.udp, &res); if(result) return result; switch(res) { case PROXY_INSPECT_OK: - h3_tunnel_go_state(cf, ts, H3_TUNNEL_ESTABLISHED, data, - (bool)proxy_ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_ESTABLISHED, data); break; case PROXY_INSPECT_FAILED: - h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, - (bool)proxy_ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); result = CURLE_COULDNT_CONNECT; break; case PROXY_INSPECT_AUTH_RETRY: - h3_tunnel_go_state(cf, ts, H3_TUNNEL_INIT, data, - (bool)proxy_ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_INIT, data); break; } return result; } -static CURLcode cf_h3_proxy_quic_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_call_data save; - CURLcode result = CURLE_OK; - struct proxy_pkt_io_ctx pktx; - - if(proxy_ctx->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the sub-chain (UDP via happy eyeballs) */ - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - *done = FALSE; - if(!proxy_ctx->dest) { - Curl_peer_link(&proxy_ctx->dest, - Curl_conn_get_destination(cf->conn, cf->sockindex)); - } - - if(!proxy_ctx->ngtcp2_ctx) { - result = cf_h3_proxy_ctx_init(cf, data); - if(result) - return result; - } - - /* Initialize pktx AFTER ensuring ngtcp2_ctx exists */ - proxy_pktx_init(&pktx, cf, data); - - CF_DATA_SAVE(save, cf, data); - - if(!proxy_ctx->ngtcp2_ctx->qconn) { - proxy_ctx->ngtcp2_ctx->started_at = *Curl_pgrs_now(data); - if(proxy_ctx->connected) { - *done = TRUE; - goto out; - } - result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); - /* we do not expect to be able to recv anything yet */ - goto out; - } - - result = proxy_h3_progress_ingress_ngtcp2(cf, data, &pktx); - if(result) - goto out; - - result = proxy_h3_progress_egress_ngtcp2(cf, data, &pktx); - if(result) - goto out; - - if(ngtcp2_conn_get_handshake_completed(proxy_ctx->ngtcp2_ctx->qconn)) { - result = proxy_ctx->ngtcp2_ctx->tls_vrfy_result; - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - proxy_ctx->connected = TRUE; - *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); - } - } - -out: - if(proxy_ctx->ngtcp2_ctx->qconn && - ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && - ngtcp2_conn_in_draining_period(proxy_ctx->ngtcp2_ctx->qconn)) { - const ngtcp2_ccerr *cerr = - ngtcp2_conn_get_ccerr(proxy_ctx->ngtcp2_ctx->qconn); - - result = CURLE_COULDNT_CONNECT; - if(cerr) { - CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, - cerr->type, cerr->error_code); - switch(cerr->type) { - case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: - CURL_TRC_CF(data, cf, "error in version negotiation"); - break; - default: - if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { - CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", - (unsigned int)(cerr->error_code & 0xffU)); - } - else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { - CURL_TRC_CF(data, cf, "connection refused by server"); - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE with this code right away. We want - * to keep on trying in this case. */ - result = CURLE_WEIRD_SERVER_REPLY; - } - } - } - } - -#ifdef CURLVERBOSE - if(result) { - bool is_ipv6; - struct ip_quadruple ip; - if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); - } -#endif - if(!result && proxy_ctx->ngtcp2_ctx->qconn) { - result = check_and_set_expiry_ngtcp2(cf, data, &pktx); - } - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode H3_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_tunnel_stream *ts) +static CURLcode cf_h3_proxy_tunnel(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + bool *pdone) { struct cf_h3_proxy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; DEBUGASSERT(ts); DEBUGASSERT(ts->authority); - + *pdone = FALSE; do { switch(ts->state) { case H3_TUNNEL_INIT: CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); - result = h3_submit_CONNECT(cf, data, ts); + result = cf_h3_proxy_submit_CONNECT(cf, data, ts); if(result) goto out; - h3_tunnel_go_state(cf, ts, H3_TUNNEL_CONNECT, data, - (bool)ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_CONNECT, data); - result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); if(result) goto out; FALLTHROUGH(); @@ -3177,19 +1106,17 @@ static CURLcode H3_CONNECT(struct Curl_cfilter *cf, case H3_TUNNEL_CONNECT: /* Non-blocking: call ingress/egress once and return. * The multi interface will call us again when ready. */ - result = proxy_h3_progress_ingress_ngtcp2(cf, data, NULL); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); if(result) goto out; - result = proxy_h3_progress_egress_ngtcp2(cf, data, NULL); + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); if(result && result != CURLE_AGAIN) { - h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, - (bool)ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); goto out; } if(ts->has_final_response) { - h3_tunnel_go_state(cf, ts, H3_TUNNEL_RESPONSE, data, - (bool)ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_RESPONSE, data); } else { /* Not done yet, return and let multi interface call us again */ @@ -3200,13 +1127,14 @@ static CURLcode H3_CONNECT(struct Curl_cfilter *cf, case H3_TUNNEL_RESPONSE: DEBUGASSERT(ts->has_final_response); - result = h3_proxy_inspect_response(cf, data, ts); + result = cf_h3_proxy_inspect_response(cf, data, ts); if(result) goto out; ctx->connected = TRUE; break; case H3_TUNNEL_ESTABLISHED: + *pdone = TRUE; return CURLE_OK; case H3_TUNNEL_FAILED: @@ -3220,49 +1148,30 @@ static CURLcode H3_CONNECT(struct Curl_cfilter *cf, out: if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) - h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data, (bool)ctx->udp_tunnel); + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); return result; } static CURLcode cf_h3_proxy_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; + struct cf_h3_proxy_ctx *pctx = cf->ctx; struct cf_call_data save = { 0 }; CURLcode result = CURLE_OK; - timediff_t check; - struct h3_tunnel_stream *ts = &proxy_ctx->tunnel; + struct h3_tunnel_stream *ts = &pctx->tunnel; bool data_saved = FALSE; - /* Curl_cft_http_proxy --> Curl_cft_h3_proxy --> HAPPY-EYEBALLS --> UDP */ - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - *done = FALSE; - - check = Curl_timeleft_ms(data); - if(check <= 0) { - failf(data, "Proxy CONNECT aborted due to timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; - } - - result = cf_h3_proxy_quic_connect(cf, data, done); - if(*done != TRUE) + result = Curl_cf_ngtcp2_cmn_connect(cf, data, done); + if(result || !*done) goto out; CF_DATA_SAVE(save, cf, data); data_saved = TRUE; /* At this point the QUIC is connected, but the proxy isn't connected */ - *done = FALSE; - - result = H3_CONNECT(cf, data, ts); + result = cf_h3_proxy_tunnel(cf, data, ts, done); out: - *done = (result == CURLE_OK) && (ts->state == H3_TUNNEL_ESTABLISHED); if(*done) { cf->connected = TRUE; /* The real request will follow the CONNECT, reset request partially */ @@ -3275,100 +1184,14 @@ static CURLcode cf_h3_proxy_connect(struct Curl_cfilter *cf, return result; } -static CURLcode h3_proxy_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - (void)cf; - if(!pause) { - /* unpaused. make it run again right away */ - Curl_multi_mark_dirty(data); - } - return CURLE_OK; -} - -static void h3_proxy_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream; - - if(!ctx) - return; - - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); - cf_ngtcp2_proxy_stream_close(cf, data, stream); - Curl_uint32_hash_remove(&ctx->streams, data->mid); - if(!Curl_uint32_hash_count(&ctx->streams)) - cf_ngtcp2_proxy_setup_keep_alive(cf, data); - } -} - -static CURLcode cf_h3_proxy_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_h3_proxy_ctx *proxy_ctx = cf->ctx; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = h3_proxy_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: - h3_proxy_data_done(cf, data); - break; - case CF_CTRL_DATA_DONE_SEND: { - struct cf_ngtcp2_proxy_ctx *ctx = proxy_ctx->ngtcp2_ctx; - struct h3_proxy_stream_ctx *stream = NULL; - if(ctx) { - stream = H3_PROXY_STREAM_CTX(ctx, data); - if(stream && !stream->send_closed && - (H3_STREAM_ID(stream) != proxy_ctx->tunnel.stream_id)) { - stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf) - - stream->sendbuf_len_in_flight; - (void)nghttp3_conn_resume_stream(ctx->h3conn, H3_STREAM_ID(stream)); - } - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) { - cf->conn->httpversion_seen = 30; - Curl_conn_set_multiplex(cf->conn); - } - break; - default: - break; - } - - CF_DATA_RESTORE(cf, save); - return result; -} - static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h3_proxy_ctx *ctx = cf->ctx; + (void)data; if(ctx) { - /* Clean up the ngtcp2 context properly */ - if(ctx->ngtcp2_ctx) { - CURL_TRC_CF(data, cf, "cf_ngtcp2_proxy_ctx_close()"); - cf_ngtcp2_proxy_close(cf, data); - cf_ngtcp2_proxy_ctx_free(ctx->ngtcp2_ctx); - ctx->ngtcp2_ctx = NULL; - } + CURL_TRC_CF(data, cf, "cf_h3_proxy_destroy()"); cf_h3_proxy_ctx_free(ctx); cf->ctx = NULL; } @@ -3377,7 +1200,7 @@ static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - return cf_ngtcp2_proxy_shutdown(cf, data, done); + return Curl_cf_ngtcp2_cmn_shutdown(cf, data, done); } struct Curl_cftype Curl_cft_h3_proxy = { @@ -3391,24 +1214,29 @@ struct Curl_cftype Curl_cft_h3_proxy = { cf_h3_proxy_data_pending, cf_h3_proxy_send, cf_h3_proxy_recv, - cf_h3_proxy_cntrl, - cf_h3_proxy_is_alive, + Curl_cf_def_cntrl, + Curl_cf_ngtcp2_cmn_conn_is_alive, Curl_cf_def_conn_keep_alive, - cf_h3_proxy_query, + Curl_cf_def_query, }; CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct Curl_cfilter *cf = NULL; struct cf_h3_proxy_ctx *ctx; CURLcode result = CURLE_OUT_OF_MEMORY; - if((transport_out != TRNSPRT_QUIC) || (!conn->http_proxy.peer)) + if(!tunnel_peer) + return CURLE_FAILED_INIT; + if((transport_peer != TRNSPRT_QUIC) || (!conn->http_proxy.peer)) return CURLE_FAILED_INIT; ctx = curlx_calloc(1, sizeof(*ctx)); @@ -3416,15 +1244,18 @@ CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->udp_tunnel = (transport_in == TRNSPRT_QUIC); + result = cf_h3_proxy_ctx_init(ctx, origin, peer, &conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); if(result) goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, - TRNSPRT_QUIC, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; @@ -3439,14 +1270,16 @@ CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, cf_h3_proxy_ctx_free(ctx); } else - CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->udp_tunnel); + CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->tunnel.udp); return result; } CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel) + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct Curl_cfilter *cf = NULL; struct cf_h3_proxy_ctx *ctx; @@ -3456,8 +1289,11 @@ CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; - Curl_peer_link(&ctx->dest, dest); - ctx->udp_tunnel = udp_tunnel; + result = cf_h3_proxy_ctx_init(ctx, origin, peer, + &cf_at->conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); if(result) diff --git a/lib/vquic/cf-ngtcp2-proxy.h b/lib/vquic/cf-ngtcp2-proxy.h index fc176fbab409..acdee0e46338 100644 --- a/lib/vquic/cf-ngtcp2-proxy.h +++ b/lib/vquic/cf-ngtcp2-proxy.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_H3_PROXY_H -#define HEADER_CURL_H3_PROXY_H +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -32,16 +32,21 @@ CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel); + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); #endif -#endif /* HEADER_CURL_H3_PROXY_H */ +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H */ diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 3d1c8a15f82e..0a3679b7b6a6 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -25,28 +25,6 @@ #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) -#include - -#ifdef USE_OPENSSL -#include -#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) -#include -#elif defined(OPENSSL_QUIC_API2) -#include -#else -#include -#endif -#include "vtls/openssl.h" -#elif defined(USE_GNUTLS) -#include -#include "vtls/gtls.h" -#elif defined(USE_WOLFSSL) -#include -#include "vtls/wolfssl.h" -#endif - -#include - #include "urldata.h" #include "url.h" #include "uint-hash.h" @@ -66,918 +44,14 @@ #include "bufref.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" -#include "vquic/vquic-tls.h" -#include "vtls/vtls.h" -#include "vtls/vtls_scache.h" +#include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2.h" -#define QUIC_MAX_STREAMS (256 * 1024) -#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) -#define QUIC_TUNNEL_INBUF_SIZE (64 * 1024) - -/* We announce a small window size in transport param to the server, - * and grow that immediately to max when no rate limit is in place. - * We need to start small as we are not able to decrease it. */ -#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024) -#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) -#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX) - -#define H3_STREAM_CHUNK_SIZE (64 * 1024) -#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE -#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE -#endif - -/* The pool keeps spares around and half of a full stream window - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffers do not keep - * spares. Memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES 2 -/* The max amount of un-acked upload data we keep around per stream */ -#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) -#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_ngtcp2_ver(char *p, size_t len) -{ - const ngtcp2_info *ng2 = ngtcp2_version(0); - const nghttp3_info *ht3 = nghttp3_version(0); - (void)curl_msnprintf(p, len, "ngtcp2/%s nghttp3/%s", - ng2->version_str, ht3->version_str); -} - -struct cf_ngtcp2_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; -#ifdef OPENSSL_QUIC_API2 - ngtcp2_crypto_ossl_ctx *ossl_ctx; -#endif - ngtcp2_path connected_path; - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_ccerr last_error; - ngtcp2_crypto_conn_ref conn_ref; - struct cf_call_data call_data; - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; /* hash data->mid to h3_stream_ctx */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ - int qlogfd; - unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ - size_t tunnel_inbuf_len; - BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ -}; - -/* How to access `call_data` from a cf_ngtcp2 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data - -static void h3_stream_hash_free(unsigned int id, void *stream); - -static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - ctx->qlogfd = -1; - ctx->tunnel_inbuf = NULL; - ctx->tunnel_inbuf_len = 0; - ctx->version = NGTCP2_PROTO_VER_MAX; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->initialized = TRUE; -} - -static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - curlx_dyn_free(&ctx->scratch); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - curlx_safefree(ctx->tunnel_inbuf); - ctx->tunnel_inbuf_len = 0; - } - curlx_free(ctx); -} - -static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const ngtcp2_transport_params *rp; - /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it closes the - * connection when it does not hear from us for that time. - * - * Some servers use this as a keep-alive timer at a rather low - * value. We are doing HTTP/3 here and waiting for the response - * to a request may take a considerable amount of time. We need - * to prevent the peer's QUIC stack from closing in this case. - */ - if(!ctx->qconn) - return; - - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(!rp || !rp->max_idle_timeout) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); - } - else if(!Curl_uint32_hash_count(&ctx->streams)) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); - } - else { - ngtcp2_duration keep_ns; - keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); - CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " - "set keep-alive to %" PRIu64 " ms.", - (rp->max_idle_timeout / NGTCP2_MILLISECONDS), - (keep_ns / NGTCP2_MILLISECONDS)); - } -} - -struct pkt_io_ctx; -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - int64_t id; /* HTTP/3 protocol identifier */ - struct bufq sendbuf; /* h3 request body */ - struct h1_req_parser h1; /* h1 request parsing */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - uint64_t rx_offset; /* current receive offset */ - uint64_t rx_offset_max; /* allowed receive offset */ - uint64_t window_size_max; /* max flow control window set for stream */ - int status_code; /* HTTP status code */ - CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is local closed */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ -}; - -static void h3_stream_ctx_free(struct h3_stream_ctx *stream) -{ - Curl_bufq_free(&stream->sendbuf); - Curl_h1_req_parse_free(&stream->h1); - curlx_free(stream); -} - -static void h3_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h3_stream_ctx_free((struct h3_stream_ctx *)stream); -} - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(!data) - return CURLE_FAILED_INIT; - - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->id = -1; - stream->rx_offset = 0; - stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; - - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; - Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - - if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - if(Curl_uint32_hash_count(&ctx->streams) == 1) - cf_ngtcp2_setup_keep_alive(cf, data); - - return CURLE_OK; -} - -#if NGTCP2_VERSION_NUM < 0x011100 -struct cf_ngtcp2_sfind_ctx { - int64_t stream_id; - struct h3_stream_ctx *stream; - uint32_t mid; -}; - -static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) -{ - struct cf_ngtcp2_sfind_ctx *fctx = user_data; - struct h3_stream_ctx *stream = value; - - if(fctx->stream_id == stream->id) { - fctx->mid = mid; - fctx->stream = stream; - return FALSE; - } - return TRUE; /* continue */ -} - -static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, - int64_t stream_id) -{ - struct cf_ngtcp2_sfind_ctx fctx; - fctx.stream_id = stream_id; - fctx.stream = NULL; - Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx); - return fctx.stream; -} -#else -static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, - int64_t stream_id) -{ - struct Curl_easy *data = - ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); - - if(!data) { - return NULL; - } - - return H3_STREAM_CTX(ctx, data); -} -#endif - -static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - DEBUGASSERT(data); - DEBUGASSERT(stream); - if(!stream->closed && ctx->qconn && ctx->h3conn) { - CURLcode result; - - nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); - ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); - stream->closed = TRUE; - (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, - NGHTTP3_H3_REQUEST_CANCELLED); - result = cf_progress_egress(cf, data, NULL); - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", - stream->id, result); - } -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)cf; - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - Curl_uint32_hash_remove(&ctx->streams, data->mid); - if(!Curl_uint32_hash_count(&ctx->streams)) - cf_ngtcp2_setup_keep_alive(cf, data); - } -} - -struct pkt_io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - ngtcp2_path_storage ps; -}; - -static void pktx_update_time(struct Curl_easy *data, - struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); -} - -static void pktx_init(struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - pktx->cf = cf; - pktx->data = data; - ngtcp2_path_storage_zero(&pktx->ps); - vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); -} - static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, uint64_t datalen, void *user_data, void *stream_user_data); -static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) -{ - struct Curl_cfilter *cf = conn_ref->user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - return ctx->qconn; -} - -#ifdef DEBUG_NGTCP2 -static void quic_printf(void *user_data, const char *fmt, ...) -{ - va_list ap; - (void)user_data; - va_start(ap, fmt); - curl_mvfprintf(stderr, fmt, ap); - va_end(ap); - curl_mfprintf(stderr, "\n"); -} -#endif - -static void qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - (void)flags; - if(ctx->qlogfd != -1) { - ssize_t rc = write(ctx->qlogfd, data, datalen); - if(rc == -1) { - /* on write error, stop further write attempts */ - curlx_close(ctx->qlogfd); - ctx->qlogfd = -1; - } - } -} - -static void quic_settings(struct cf_ngtcp2_ctx *ctx, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - ngtcp2_settings *s = &ctx->settings; - ngtcp2_transport_params *t = &ctx->transport_params; - - ngtcp2_settings_default(s); - ngtcp2_transport_params_default(t); -#ifdef DEBUG_NGTCP2 - s->log_printf = quic_printf; -#else - s->log_printf = NULL; -#endif - - s->initial_ts = pktx->ts; - s->handshake_timeout = (data->set.connecttimeout > 0) ? - data->set.connecttimeout * NGTCP2_MILLISECONDS : QUIC_HANDSHAKE_TIMEOUT; - s->max_window = H3_CONN_WINDOW_SIZE_MAX; - s->max_stream_window = 0; /* disable ngtcp2 auto-tuning of window */ - s->no_pmtud = FALSE; -#ifdef NGTCP2_SETTINGS_V3 - /* try ten times the ngtcp2 defaults here for problems with Caddy */ - s->glitch_ratelim_burst = 1000 * 10; - s->glitch_ratelim_rate = 33 * 10; -#endif - t->initial_max_data = s->max_window; - t->initial_max_stream_data_bidi_local = H3_STREAM_WINDOW_SIZE_INITIAL; - t->initial_max_stream_data_bidi_remote = H3_STREAM_WINDOW_SIZE_INITIAL; - t->initial_max_stream_data_uni = t->initial_max_data; - t->initial_max_streams_bidi = QUIC_MAX_STREAMS; - t->initial_max_streams_uni = QUIC_MAX_STREAMS; - t->max_idle_timeout = 0; /* no idle timeout from our side */ - if(ctx->qlogfd != -1) { - s->qlog_write = qlog_callback; - } -} - -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - struct Curl_easy *data; - - (void)tconn; - DEBUGASSERT(ctx); - data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(!ctx || !data) - return NGTCP2_ERR_CALLBACK_FAILURE; - - ctx->handshake_at = *Curl_pgrs_now(data); - ctx->tls_handshake_complete = TRUE; - Curl_vquic_report_handshake(&ctx->tls, cf, data); - - ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, - data, &ctx->peer); - if(ctx->tls_vrfy_result) - return NGTCP2_ERR_CALLBACK_FAILURE; - -#ifdef CURLVERBOSE - if(Curl_trc_is_verbose(data)) { - const ngtcp2_transport_params *rp; - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T - "ms, remote transport[max_udp_payload=%" PRIu64 - ", initial_max_data=%" PRIu64 "]", - curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), - rp->max_udp_payload_size, rp->initial_max_data); - } -#endif - - /* In case of earlydata, where we simulate being connected, update - * the handshake time when we really did connect */ - if(ctx->use_earlydata) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); - if(ctx->use_earlydata) { -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_accepted = - (SSL_get_early_data_status(ctx->tls.ossl.ssl) != - SSL_EARLY_DATA_REJECTED); -#endif -#ifdef USE_GNUTLS - int flags = gnutls_session_get_flags(ctx->tls.gtls.session); - ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_accepted = - (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) != - WOLFSSL_EARLY_DATA_REJECTED); -#else - DEBUGASSERT(0); /* should not come here if ED is disabled. */ - ctx->earlydata_accepted = FALSE; -#endif /* WOLFSSL_EARLY_DATA */ -#endif - CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", - ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); - Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? - (curl_off_t)ctx->earlydata_skip : - -(curl_off_t)ctx->earlydata_skip); - } - return 0; -} - -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static bool cf_ngtcp2_err_is_fatal(int code) -{ - return (NGTCP2_ERR_FATAL >= code) || - (NGTCP2_ERR_DROP_CONN == code) || - (NGTCP2_ERR_IDLE_CLOSE == code); -} - -static void cf_ngtcp2_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - if(NGTCP2_ERR_CRYPTO == code) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); - } - } - if(cf_ngtcp2_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static bool cf_ngtcp2_h3_err_is_fatal(int code) -{ - return (NGHTTP3_ERR_FATAL >= code) || - (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); -} - -static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - ngtcp2_ccerr_set_application_error(&ctx->last_error, - nghttp3_err_infer_quic_app_error_code(code), NULL, 0); - } - if(cf_ngtcp2_h3_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - nghttp3_ssize rc; - uint64_t nconsumed; - int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)offset; - - rc = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - if(rc < 0) { - if(data && stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " - "reset=%d, closed=%d", - stream_id, stream->reset, stream->closed); - } - return NGTCP2_ERR_CALLBACK_FAILURE; - } - nconsumed = (uint64_t)rc; - if(nconsumed) { - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); - if(stream) { - stream->rx_offset += nconsumed; - stream->rx_offset_max += nconsumed; - } - } - return 0; -} - -static int cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t offset, uint64_t datalen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)stream_id; - (void)tconn; - (void)offset; - (void)datalen; - (void)stream_user_data; - - rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - int rv; - - (void)tconn; - /* stream is closed... */ - if(!data) - data = CF_DATA_CURRENT(cf); - if(!data) - return NGTCP2_ERR_CALLBACK_FAILURE; - - if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { - app_error_code = NGHTTP3_H3_NO_ERROR; - } - - rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" - PRIu64 ") -> %d", stream_id, app_error_code, rv); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - cf_ngtcp2_h3_err_set(cf, data, rv); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - int rv; - (void)tconn; - (void)final_size; - (void)app_error_code; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)tconn; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, - uint64_t max_streams, - void *user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - (void)tconn; - ctx->max_bidi_streams = max_streams; - if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 ", used %" PRIu64, - ctx->max_bidi_streams, ctx->used_bidi_streams); - return 0; -} - -static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *s_data = stream_user_data; - struct h3_stream_ctx *stream; - int rv; - (void)tconn; - (void)max_data; - - rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - stream = H3_STREAM_CTX(ctx, s_data); - if(stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", stream_id); - stream->quic_flow_blocked = FALSE; - Curl_multi_mark_dirty(s_data); - } - return 0; -} - -static void cb_rand(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx) -{ - CURLcode result; - (void)rand_ctx; - - result = Curl_rand(NULL, dest, destlen); - if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, fill 0 and call it *random*. */ - memset(dest, 0, destlen); - } -} - -/* for ngtcp2 data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} - -#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ -static int cb_get_new_connection_id2( - ngtcp2_conn *tconn, ngtcp2_cid *cid, - struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) -{ - CURLcode result; - (void)tconn; - (void)user_data; - - result = Curl_rand(NULL, cid->data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token->data, sizeof(token->data)); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} -#endif - -static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, - void *user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - (void)tconn; - - if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) - return 0; - - DEBUGASSERT(ctx); - DEBUGASSERT(data); - if(ctx && data && !ctx->h3conn) { - if(init_ngh3_conn(cf, data)) - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -#if defined(_MSC_VER) && defined(_DLL) -#pragma warning(push) -#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ -#endif - -static ngtcp2_callbacks ng_callbacks = { - ngtcp2_crypto_client_initial_cb, - NULL, /* recv_client_initial */ - ngtcp2_crypto_recv_crypto_data_cb, - cb_ngtcp2_handshake_completed, - NULL, /* recv_version_negotiation */ - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - cb_recv_stream_data, - cb_acked_stream_data_offset, - NULL, /* stream_open */ - cb_stream_close, - NULL, /* recv_stateless_reset */ - ngtcp2_crypto_recv_retry_cb, - cb_extend_max_local_streams_bidi, - NULL, /* extend_max_local_streams_uni */ - cb_rand, - cb_get_new_connection_id, /* for ngtcp2 ctx; - struct pkt_io_ctx local_pktx; - ngtcp2_tstamp expiry; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx_update_time(data, pktx, cf); - } - - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= pktx->ts) { - CURLcode result; - int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - cf_ngtcp2_err_set(cf, data, rv); - return CURLE_SEND_ERROR; - } - result = cf_progress_ingress(cf, data, pktx); - if(result) - return result; - result = cf_progress_egress(cf, data, pktx); - if(result) - return result; - /* ask again, things might have changed */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - } - - if(expiry > pktx->ts) { - ngtcp2_duration timeout = expiry - pktx->ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), - EXPIRE_QUIC); - } - } - return CURLE_OK; -} - static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -985,16 +59,13 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; bool want_recv, want_send; CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); - if(!ctx->qconn) + if(!ctx->qconn || (sock == CURL_SOCKET_BAD)) return CURLE_OK; - if(ctx->q.sockfd == CURL_SOCKET_BAD) { - /* Tunneled QUIC, no direct socket - delegate to next filter */ - return cf->next->cft->adjust_pollset(cf->next, data, ps); - } - - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) want_send = TRUE; @@ -1012,7 +83,7 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); - result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CF_DATA_RESTORE(cf, save); } return result; @@ -1341,10 +412,9 @@ static nghttp3_callbacks ngh3_callbacks = { }; static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; int rc; if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { @@ -1364,42 +434,7 @@ static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack encoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack decoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - return CURLE_OK; + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); } static CURLcode recv_closed_stream(struct Curl_cfilter *cf, @@ -1446,7 +481,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; - struct pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx pktx; CURLcode result = CURLE_OK; int i; @@ -1467,7 +502,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto denied; } - pktx_init(&pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); if(!stream || ctx->shutdown_started) { result = CURLE_RECV_ERROR; @@ -1484,7 +519,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, for(i = 0; i < 2; ++i) { if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); result = stream->xfer_result; goto out; } @@ -1493,7 +528,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } - if(!i && cf_progress_ingress(cf, data, &pktx)) { + if(!i && Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx)) { result = CURLE_RECV_ERROR; goto out; } @@ -1502,8 +537,10 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, result = CURLE_AGAIN; out: - result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); - result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); if(ctx->tls_vrfy_result) result = ctx->tls_vrfy_result; denied: @@ -1630,7 +667,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, *pnwritten = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - result = h3_data_setup(cf, data); + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); if(result) goto out; stream = H3_STREAM_CTX(ctx, data); @@ -1721,7 +758,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); break; } - cf_ngtcp2_stream_close(cf, data, stream); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); result = CURLE_SEND_ERROR; goto out; } @@ -1751,14 +788,14 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; - struct pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx pktx; CURLcode result = CURLE_OK; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->qconn); DEBUGASSERT(ctx->h3conn); - pktx_init(&pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); *pnwritten = 0; /* handshake verification failed in callback, do not send anything */ @@ -1768,7 +805,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } (void)eos; /* use for stream EOF and block handling */ - result = cf_progress_ingress(cf, data, &pktx); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); if(result) goto out; @@ -1787,7 +824,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); result = stream->xfer_result; goto out; } @@ -1795,405 +832,51 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream->resp_hds_complete) { /* Server decided to close the stream after having sent us a final * response. This is valid if it is not interested in the request - * body. This happens on 30x or 40x responses. - * We silently discard the data sent, since this is not a transport - * error situation. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" - "on closed stream with response", stream->id); - result = CURLE_OK; - *pnwritten = len; - goto out; - } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " - "-> stream closed", stream->id, len); - result = CURLE_HTTP3; - goto out; - } - else if(ctx->shutdown_started) { - CURL_TRC_CF(data, cf, "cannot send on closed connection"); - result = CURLE_SEND_ERROR; - goto out; - } - else { - result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %d, %zu", - stream->id, len, result, *pnwritten); - if(result) - goto out; - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); - } - - if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) - ctx->earlydata_skip += *pnwritten; - - DEBUGASSERT(!result); - result = cf_progress_egress(cf, data, &pktx); - -out: - result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); - if(ctx->tls_vrfy_result) - result = ctx->tls_vrfy_result; -denied: - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnwritten); - CF_DATA_RESTORE(cf, save); - return result; -} - -struct cf_ngtcp2_recv_ctx { - struct pkt_io_ctx *pktx; - size_t pkt_count; -}; - -static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, - size_t gso_size, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) -{ - struct cf_ngtcp2_recv_ctx *rctx = userp; - struct pkt_io_ctx *pktx = rctx->pktx; - struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; - ngtcp2_pkt_info pi; - ngtcp2_path path; - size_t offset, pktlen; - int rv; - - if(!rctx->pkt_count) { - pktx_update_time(pktx->data, pktx, pktx->cf); - ngtcp2_path_storage_zero(&pktx->ps); - } - - if(ecn) - CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", - buflen, gso_size, ecn); - ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, - remote_addrlen); - pi.ecn = (uint8_t)ecn; - - for(offset = 0; offset < buflen; offset += gso_size) { - rctx->pkt_count++; - pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, - buf + offset, pktlen, pktx->ts); - if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", - ngtcp2_strerror(rv), rv); - cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); - - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } - } - return CURLE_OK; -} - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - struct cf_ngtcp2_recv_ctx rctx; - CURLcode result = CURLE_OK; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - return result; - - rctx.pktx = pktx; - rctx.pkt_count = 0; - - if(ctx->q.sockfd != CURL_SOCKET_BAD) { - /* Direct UDP socket (via happy eyeballs) */ - return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts, &rctx); - } - else { - /* Tunneled QUIC (CONNECT-UDP through proxy) */ - unsigned char *buf; - size_t max_udp_payload = QUIC_TUNNEL_INBUF_SIZE; - size_t pkt_limit = QUIC_TUNNEL_INGRESS_PKT_LIMIT; - size_t nread; - struct sockaddr_storage remote_addr; - socklen_t remote_addrlen; - - if(ctx->qconn) { - size_t max_path_payload; - max_path_payload = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); - if(max_path_payload > max_udp_payload) - max_udp_payload = max_path_payload; - } - - if(ctx->tunnel_inbuf_len < max_udp_payload) { - unsigned char *newbuf = curlx_realloc(ctx->tunnel_inbuf, - max_udp_payload); - if(!newbuf) - return CURLE_OUT_OF_MEMORY; - ctx->tunnel_inbuf = newbuf; - ctx->tunnel_inbuf_len = max_udp_payload; - } - buf = ctx->tunnel_inbuf; - - while(pkt_limit--) { - result = Curl_conn_cf_recv(cf->next, data, (char *)buf, - ctx->tunnel_inbuf_len, &nread); - if(result == CURLE_AGAIN) { - /* no more data available at the moment */ - return CURLE_OK; - } - if(result) { - CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", result); - return result; - } - if(nread == 0) { - /* tunnel closed */ - return CURLE_OK; - } - - memcpy(&remote_addr, ctx->connected_path.remote.addr, - ctx->connected_path.remote.addrlen); - remote_addrlen = (socklen_t)ctx->connected_path.remote.addrlen; - result = cf_ngtcp2_recv_pkts(buf, nread, nread, &remote_addr, - remote_addrlen, 0, &rctx); - if(result) - return result; - - if(!ctx->q.got_first_byte) { - ctx->q.got_first_byte = TRUE; - ctx->q.first_byte_at = ctx->q.last_op; - } - ctx->q.last_io = ctx->q.last_op; - } - return CURLE_OK; - } -} - -/** - * Read a network packet to send from ngtcp2 into `buf`. - * Return number of bytes written or -1 with *err set. - */ -static CURLcode read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - size_t *pnread) -{ - struct pkt_io_ctx *x = userp; - struct cf_ngtcp2_ctx *ctx = x->cf->ctx; - nghttp3_vec vec[16]; - nghttp3_ssize veccnt; - ngtcp2_ssize ndatalen; - uint32_t flags; - int64_t stream_id; - int fin; - ssize_t n; - - *pnread = 0; - veccnt = 0; - stream_id = -1; - fin = 0; - - /* ngtcp2 may want to put several frames from different streams into - * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. - * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make - * another iteration. - * When ngtcp2 is happy (because it has no other frame that would fit - * or it has nothing more to send), it returns the total length - * of the assembled packet. This may be 0 if there was nothing to send. */ - for(;;) { - - if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { - veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, - CURL_ARRAYSIZE(vec)); - if(veccnt < 0) { - failf(x->data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)veccnt)); - cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); - return CURLE_SEND_ERROR; - } - } - - flags = NGTCP2_WRITE_STREAM_FLAG_MORE | - (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, - NULL, buf, buflen, - &ndatalen, flags, stream_id, - (const ngtcp2_vec *)vec, veccnt, x->ts); - if(n == 0) { - /* nothing to send */ - return CURLE_AGAIN; - } - else if(n < 0) { - switch(n) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: { - struct h3_stream_ctx *stream; - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", - stream_id); - stream = cf_ngtcp2_get_stream(ctx, stream_id); - if(stream) /* it might be not one of our h3 streams? */ - stream->quic_flow_blocked = TRUE; - n = 0; - break; - } - case NGTCP2_ERR_STREAM_SHUT_WR: - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); - n = 0; - break; - case NGTCP2_ERR_WRITE_MORE: - /* ngtcp2 wants to send more. update the flow of the stream whose data - * is in the buffer and continue */ - DEBUGASSERT(ndatalen >= 0); - n = 0; - break; - default: - DEBUGASSERT(ndatalen == -1); - failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", - ngtcp2_strerror((int)n)); - cf_ngtcp2_err_set(x->cf, x->data, (int)n); - return CURLE_SEND_ERROR; - } - } - - if(ndatalen >= 0) { - /* we add the amount of data bytes to the flow windows */ - int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); - if(rv) { - failf(x->data, "nghttp3_conn_add_write_offset returned error: %s", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - } - - if(n > 0) { - /* packet assembled, leave */ - *pnread = (size_t)n; - return CURLE_OK; + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; } -} - -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - size_t nread; - size_t max_payload_size, path_max_payload_size; - size_t pktcnt = 0; - size_t gsolen = 0; /* this disables gso until we have a clue */ - size_t send_quantum; - CURLcode result; - struct pkt_io_ctx local_pktx; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; + else if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot send on closed connection"); + result = CURLE_SEND_ERROR; + goto out; } else { - pktx_update_time(data, pktx, cf); - ngtcp2_path_storage_zero(&pktx->ps); + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } - result = vquic_flush(cf, data, &ctx->q); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; - /* In UDP, there is a maximum theoretical packet payload length and - * a minimum payload length that is "guaranteed" to work. - * To detect if this minimum payload can be increased, ngtcp2 sends - * now and then a packet payload larger than the minimum. It that - * is ACKed by the peer, both parties know that it works and - * the subsequent packets can use a larger one. - * This is called PMTUD (Path Maximum Transmission Unit Discovery). - * Since a PMTUD might be rejected right on send, we do not want it - * be followed by other packets of lesser size. Because those would - * also fail then. If we detect a PMTUD while buffering, we flush. - */ - max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); - path_max_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); - send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); - CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", - send_quantum); - for(;;) { - /* add the next packet to send, if any, to our buffer */ - result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, pktx, &nread); - if(result == CURLE_AGAIN) - break; - else if(result) - return result; - else { - size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); - if((buflen >= send_quantum) || - ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) - break; - DEBUGASSERT(nread > 0); - ++pktcnt; - if(pktcnt == 1) { - /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We shall see. */ - gsolen = nread; - } - else if(nread > gsolen || - (gsolen > path_max_payload_size && nread != gsolen)) { - /* The added packet is a PMTUD *or* the one(s) before the - * added were PMTUD and the last one is smaller. - * Flush the buffer before the last add. */ - result = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - pktcnt = 0; - } - else if(nread < gsolen) { - /* Reached capacity of our buffer *or* - * last add was shorter than the previous ones, flush */ - break; - } - } - } + DEBUGASSERT(!result); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* time to send */ - CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", - pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); - result = vquic_send(cf, data, &ctx->q, gsolen); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - pktx_update_time(data, pktx, cf); - ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); - } - return CURLE_OK; +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; } static CURLcode h3_data_pause(struct Curl_cfilter *cf, @@ -2226,7 +909,7 @@ static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, result = h3_data_pause(cf, data, (arg1 != 0)); break; case CF_CTRL_DATA_DONE: - h3_data_done(cf, data); + Curl_cf_ngtcp2_h3_stream_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: { struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -2262,7 +945,7 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) } ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); - Curl_ssl_peer_cleanup(&ctx->peer); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); vquic_ctx_free(&ctx->q); if(ctx->h3conn) { nghttp3_conn_del(ctx->h3conn); @@ -2281,114 +964,6 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) ctx->call_data = save; } -static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - struct pkt_io_ctx pktx; - CURLcode result = CURLE_OK; - - if(cf->shutdown || !ctx->qconn) { - *done = TRUE; - return CURLE_OK; - } - - if(!cf->next) { - Curl_bufq_reset(&ctx->q.sendbuf); - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - pktx_init(&pktx, cf, data); - - if(!ctx->shutdown_started) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_ssize nwritten; - - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); - result = cf_progress_egress(cf, data, &pktx); - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } - } - - DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); - ctx->shutdown_started = TRUE; - nwritten = ngtcp2_conn_write_connection_close( - ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %zd", ctx->last_error.type, - ctx->last_error.error_code, (ssize_t)nwritten); - /* there are cases listed in ngtcp2 documentation where this call - * may fail. Since we are doing a connection shutdown as graceful - * as we can, such an error is ignored here. */ - if(nwritten > 0) { - /* Ignore amount written. sendbuf was empty and has always room for - * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which - * case `result` is set non zero. */ - size_t n; - result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, - (size_t)nwritten, &n); - if(result) { - CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " - "aborting shutdown", result); - goto out; - } - - ctx->q.no_gso = TRUE; - ctx->q.gsolen = (size_t)nwritten; - ctx->q.split_len = 0; - } - } - - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing egress"); - result = vquic_flush(cf, data, &ctx->q); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } - } - - if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. We are done. */ - CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); - *done = TRUE; - result = CURLE_OK; - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - bool done; - cf_ngtcp2_shutdown(cf, data, &done); -} - static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; @@ -2398,552 +973,21 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) if(ctx->qconn) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - cf_ngtcp2_conn_close(cf, data); + Curl_cf_ngtcp2_cmn_conn_close(cf, data); cf_ngtcp2_ctx_close(ctx); CF_DATA_RESTORE(cf, save); } - cf_ngtcp2_ctx_free(cf->ctx); + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); cf->ctx = NULL; } } -#ifdef USE_OPENSSL -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) -{ - struct Curl_cfilter *cf; - struct cf_ngtcp2_ctx *ctx; - struct Curl_easy *data; - ngtcp2_crypto_conn_ref *cref; - - cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); - cf = cref ? cref->user_data : NULL; - ctx = cf ? cf->ctx : NULL; - data = cf ? CF_DATA_CURRENT(cf) : NULL; - if(cf && data && ctx) { - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; -#ifdef HAVE_OPENSSL_EARLYDATA - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } -#endif - Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, - SSL_version(ssl), "h3", quic_tp, quic_tp_len); - } - return 0; -} -#endif /* USE_OPENSSL */ - -#ifdef USE_GNUTLS - -#ifdef CURLVERBOSE -static const char *gtls_hs_msg_name(int mtype) -{ - switch(mtype) { - case 1: - return "ClientHello"; - case 2: - return "ServerHello"; - case 4: - return "SessionTicket"; - case 8: - return "EncryptedExtensions"; - case 11: - return "Certificate"; - case 13: - return "CertificateRequest"; - case 15: - return "CertificateVerify"; - case 20: - return "Finished"; - case 24: - return "KeyUpdate"; - case 254: - return "MessageHash"; - } - return "Unknown"; -} -#endif - -static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, - unsigned when, unsigned int incoming, - const gnutls_datum_t *msg) -{ - ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - - (void)msg; - (void)incoming; - if(when && cf && ctx) { /* after message has been processed */ - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(!data) - return 0; - CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", - incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); - switch(htype) { - case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key, - session, 0, "h3", quic_tp, quic_tp_len); - break; - } - default: - break; - } - } - return 0; -} -#endif /* USE_GNUTLS */ - -#ifdef USE_WOLFSSL -static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) -{ - ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - - DEBUGASSERT(cf); - if(cf && session) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(data && ctx) { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key, - session, wolfSSL_version(ssl), - "h3", quic_tp, quic_tp_len); - } - } - return 0; -} -#endif /* USE_WOLFSSL */ - -static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *user_data) -{ - struct curl_tls_ctx *ctx = user_data; - -#ifdef USE_OPENSSL -#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) - != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#elif defined(OPENSSL_QUIC_API2) - /* nothing to do */ -#else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ - if(Curl_ssl_scache_use(cf, data)) { - /* Enable the session cache because it is a prerequisite for the - * "new session" callback. Use the "external storage" mode to prevent - * OpenSSL from creating an internal session cache. - */ - SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); - } - -#elif defined(USE_GNUTLS) - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { - failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); - return CURLE_FAILED_INIT; - } - if(Curl_ssl_scache_use(cf, data)) { - gnutls_handshake_set_hook_function(ctx->gtls.session, - GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, - quic_gtls_handshake_cb); - } - -#elif defined(USE_WOLFSSL) - if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } - if(Curl_ssl_scache_use(cf, data)) { - /* Register to get notified when a new session is received */ - wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb); - } -#endif - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct alpn_spec *alpns, - struct Curl_ssl_session *scs, - bool *do_early_data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_max = scs->earlydata_max; -#endif -#ifdef USE_GNUTLS - ctx->earlydata_max = - gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_max = scs->earlydata_max; -#else - ctx->earlydata_max = 0; -#endif /* WOLFSSL_EARLY_DATA */ -#endif -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if(!ctx->earlydata_max) { - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); - } - else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); - } - else { - int rv; - rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( - ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); - if(rv) - CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " - "parameters: %s", ngtcp2_strerror(rv)); - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); - result = init_ngh3_conn(cf, data); - if(!result) { - ctx->use_earlydata = TRUE; - cf->connected = TRUE; - *do_early_data = TRUE; - } - } - } -#else /* not supported in the TLS backend */ - (void)data; - (void)ctx; - (void)scs; - (void)alpns; -#endif - return result; -} - -static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) -{ -#ifdef USE_OPENSSL - return Curl_ossl_need_httpsrr(data); -#elif defined(USE_WOLFSSL) - return Curl_wssl_need_httpsrr(data); -#else - (void)data; - return FALSE; -#endif -} - -/* - * Might be called twice for happy eyeballs. - */ -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc; - int rv; - CURLcode result; - const struct Curl_sockaddr_ex *sockaddr = NULL; - int qfd; - static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - - DEBUGASSERT(ctx->initialized); - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - ctx->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - ctx->qlogfd = qfd; /* -1 if failure above */ - quic_settings(ctx, data, pktx); - - result = vquic_ctx_init(data, &ctx->q); - if(result) - return result; - - /* Query socket and remote address from sub-chain */ - if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) { - /* No direct socket - must be tunneled QUIC (CONNECT-UDP through proxy) */ - ctx->q.sockfd = CURL_SOCKET_BAD; - } - - if(ctx->q.sockfd != CURL_SOCKET_BAD) { - /* Direct UDP socket - get local address for ngtcp2 */ - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - } - else { - /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address - from the connected filter below */ - const struct Curl_sockaddr_ex *remote = NULL; - if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, - CURL_UNCONST(&remote))) - return CURLE_QUIC_CONNECT_ERROR; - if(!remote) - return CURLE_QUIC_CONNECT_ERROR; - - memset(&ctx->q.local_addr, 0, sizeof(ctx->q.local_addr)); - switch(remote->family) { - case AF_INET: - ((struct sockaddr_in *)&ctx->q.local_addr)->sin_family = AF_INET; - ctx->q.local_addrlen = sizeof(struct sockaddr_in); - break; -#ifdef USE_IPV6 - case AF_INET6: - ((struct sockaddr_in6 *)&ctx->q.local_addr)->sin6_family = AF_INET6; - ctx->q.local_addrlen = sizeof(struct sockaddr_in6); - break; -#endif - default: - return CURLE_QUIC_CONNECT_ERROR; - } - - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &remote->curl_sa_addr, - (socklen_t)remote->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - } - - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, - cf_ngtcp2_tls_ctx_setup, &ctx->tls, - &ctx->conn_ref, - cf_ngtcp2_on_session_reuse); - if(result) - return result; - -#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) - if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); - return CURLE_FAILED_INIT; - } - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); - if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); - return CURLE_FAILED_INIT; - } -#elif defined(USE_OPENSSL) - SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); -#elif defined(USE_GNUTLS) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); -#elif defined(USE_WOLFSSL) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); -#else -#error "ngtcp2 TLS backend not defined" -#endif - - ngtcp2_ccerr_default(&ctx->last_error); - - return CURLE_OK; -} - static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - struct pkt_io_ctx pktx; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the sub-chain */ - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - *done = FALSE; - - if(cf_ngtcp2_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { - CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); - return CURLE_OK; - } - - pktx_init(&pktx, cf, data); - CF_DATA_SAVE(save, cf, data); - - if(!ctx->qconn) { - ctx->started_at = *Curl_pgrs_now(data); - result = cf_connect_start(cf, data, &pktx); - if(result) - goto out; - if(cf->connected) { - *done = TRUE; - goto out; - } - result = cf_progress_egress(cf, data, &pktx); - /* we do not expect to be able to recv anything yet */ - goto out; - } - - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; - - result = cf_progress_egress(cf, data, &pktx); - if(result) - goto out; - - if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { - result = ctx->tls_vrfy_result; - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - } - } - -out: - if(ctx->tls_vrfy_result) - result = ctx->tls_vrfy_result; - if(ctx->qconn && - ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && - ngtcp2_conn_in_draining_period(ctx->qconn)) { - const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); - - result = CURLE_COULDNT_CONNECT; - if(cerr) { - CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, - cerr->type, cerr->error_code); - switch(cerr->type) { - case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: - CURL_TRC_CF(data, cf, "error in version negotiation"); - break; - default: - if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { - CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", - (unsigned int)(cerr->error_code & 0xffU)); - } - else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { - CURL_TRC_CF(data, cf, "connection refused by server"); - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE with this code right away. We want - * to keep on trying in this case. */ - result = CURLE_WEIRD_SERVER_REPLY; - } - } - } - } - -#ifdef CURLVERBOSE - if(result) { - if(ctx->q.sockfd != CURL_SOCKET_BAD) { - /* Direct UDP socket - get IP info for error reporting */ - struct ip_quadruple ip; - - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); - } - } -#endif - if(!result && ctx->qconn) { - result = check_and_set_expiry(cf, data, &pktx); - } - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; + return Curl_cf_ngtcp2_cmn_connect(cf, data, done); } static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, @@ -3026,73 +1070,27 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } -static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - bool alive = FALSE; - const ngtcp2_transport_params *rp; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - *input_pending = FALSE; - if(!ctx->qconn || ctx->shutdown_started) - goto out; - - /* We do not announce a max idle timeout, but when the peer does - * it closes the connection when it expires. */ - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(rp && rp->max_idle_timeout) { - timediff_t idletime_ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); - if(idletime_ms > 0) { - uint64_t max_idle_ms = - (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS); - if((uint64_t)idletime_ms > max_idle_ms) - goto out; - } - } - - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - goto out; - - alive = TRUE; - if(*input_pending) { - CURLcode result; - /* This happens before we have sent off a request and the connection is - not in use by any other transfer, there should not be any data here, - only "protocol frames" */ - *input_pending = FALSE; - result = cf_progress_ingress(cf, data, NULL); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); - alive = result ? FALSE : TRUE; - } - -out: - CF_DATA_RESTORE(cf, save); - return alive; -} - struct Curl_cftype Curl_cft_http3 = { "HTTP/3", CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_ngtcp2_destroy, cf_ngtcp2_connect, - cf_ngtcp2_shutdown, + Curl_cf_ngtcp2_cmn_shutdown, cf_ngtcp2_adjust_pollset, Curl_cf_def_data_pending, cf_ngtcp2_send, cf_ngtcp2_recv, cf_ngtcp2_cntrl, - cf_ngtcp2_conn_is_alive, + Curl_cf_ngtcp2_cmn_conn_is_alive, Curl_cf_def_conn_keep_alive, cf_ngtcp2_query, }; CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr) { @@ -3105,15 +1103,16 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_ngtcp2_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, - TRNSPRT_QUIC, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; @@ -3124,13 +1123,17 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, if(result) { if(cf) Curl_conn_cf_discard_chain(&cf, data); - else if(ctx) - cf_ngtcp2_ctx_free(ctx); + else if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } } return result; } -CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at) +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) { struct cf_ngtcp2_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -3141,17 +1144,20 @@ CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at) result = CURLE_OUT_OF_MEMORY; goto out; } - cf_ngtcp2_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &cf_at->conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; Curl_conn_cf_insert_after(cf_at, cf); - cf->conn = cf_at->conn; out: if(result) { curlx_safefree(cf); - cf_ngtcp2_ctx_free(ctx); + if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } } return result; } diff --git a/lib/vquic/cf-ngtcp2.h b/lib/vquic/cf-ngtcp2.h index d69ae08eaec5..601efc82245a 100644 --- a/lib/vquic/cf-ngtcp2.h +++ b/lib/vquic/cf-ngtcp2.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H -#define HEADER_CURL_VQUIC_CURL_NGTCP2_H +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -48,14 +48,16 @@ struct Curl_cfilter; #include "urldata.h" -void Curl_ngtcp2_ver(char *p, size_t len); - CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); -CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at); +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_H */ diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 5736341e1a6a..3b568c196511 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -75,7 +75,7 @@ void Curl_quiche_ver(char *p, size_t len) struct cf_quiche_ctx { struct cf_quic_ctx q; - struct ssl_peer peer; + struct ssl_peer ssl_peer; struct curl_tls_ctx tls; quiche_conn *qconn; quiche_config *cfg; @@ -106,7 +106,10 @@ static void quiche_debug_log(const char *line, void *argp) static void h3_stream_hash_free(unsigned int id, void *stream); -static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) +static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) { DEBUGASSERT(!ctx->initialized); #ifdef DEBUG_QUICHE @@ -121,6 +124,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) BUFQ_OPT_SOFT_LIMIT); ctx->data_recvd = 0; ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); } static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) @@ -129,7 +133,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) /* quiche freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); - Curl_ssl_peer_cleanup(&ctx->peer); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); vquic_ctx_free(&ctx->q); Curl_uint32_hash_destroy(&ctx->streams); curlx_dyn_free(&ctx->h1hdr); @@ -156,7 +160,7 @@ static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) quiche_config_free(ctx->cfg); ctx->cfg = NULL; } - Curl_ssl_peer_cleanup(&ctx->peer); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, @@ -1291,7 +1295,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer, &ALPN_SPEC_H3, NULL, NULL, cf, NULL); if(result) return result; @@ -1357,7 +1361,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->ssl_peer); } static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, @@ -1629,6 +1633,8 @@ struct Curl_cftype Curl_cft_http3 = { CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr) { @@ -1641,15 +1647,15 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_quiche_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + result = cf_quiche_ctx_init(ctx, origin, peer, &conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, - TRNSPRT_QUIC, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; @@ -1667,4 +1673,34 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, return result; } +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ + struct cf_quiche_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = cf_quiche_ctx_init(ctx, origin, peer, &cf_at->conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + curlx_safefree(cf); + if(ctx) + cf_quiche_ctx_free(ctx); + } + + return result; +} + #endif diff --git a/lib/vquic/cf-quiche.h b/lib/vquic/cf-quiche.h index c2c88ddeafe3..88d9161dd768 100644 --- a/lib/vquic/cf-quiche.h +++ b/lib/vquic/cf-quiche.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H -#define HEADER_CURL_VQUIC_CURL_QUICHE_H +#ifndef HEADER_CURL_VQUIC_CF_QUICHE_H +#define HEADER_CURL_VQUIC_CF_QUICHE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -37,9 +37,14 @@ void Curl_quiche_ver(char *p, size_t len); CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ +#endif /* HEADER_CURL_VQUIC_CF_QUICHE_H */ diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 00366b7d309a..58f139306a3d 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -49,17 +49,12 @@ #include "vtls/vtls_scache.h" #include "vquic/vquic-tls.h" -CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns, - Curl_vquic_tls_ctx_setup *cb_setup, - void *cb_user_data, void *ssl_user_data, - Curl_vquic_session_reuse_cb *session_reuse_cb) +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer) { char tls_id[80]; - CURLcode result; #ifdef USE_OPENSSL Curl_ossl_version(tls_id, sizeof(tls_id)); @@ -71,24 +66,31 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; #endif - (void)session_reuse_cb; - if(peer->dest) - Curl_ssl_peer_cleanup(peer); - result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC); - if(result) - return result; + if(ssl_peer->origin || ssl_peer->peer) + Curl_ssl_peer_cleanup(ssl_peer); + return Curl_ssl_peer_init(ssl_peer, origin, peer, sslc, + tls_id, TRNSPRT_QUIC); +} +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *ssl_peer, + const struct alpn_spec *alpns, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data, + Curl_vquic_session_reuse_cb *session_reuse_cb) +{ #ifdef USE_OPENSSL - (void)result; - return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns, + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, NULL, ssl_user_data, session_reuse_cb); #elif defined(USE_GNUTLS) - return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns, + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #elif defined(USE_WOLFSSL) - return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns, + return Curl_wssl_ctx_init(&ctx->wssl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #else @@ -180,7 +182,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, NULL) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; else if(!peer->sni && - (wolfSSL_X509_check_ip_asc(cert, peer->dest->hostname, + (wolfSSL_X509_check_ip_asc(cert, peer->origin->hostname, 0) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; wolfSSL_X509_free(cert); diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index c461c1548b17..e8d2418b0611 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -66,13 +66,18 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, struct Curl_ssl_session *scs, bool *do_early_data); +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer); + /** * Initialize the QUIC TLS instances based of the SSL configurations * for the connection filter, transfer and peer. * @param ctx the TLS context to initialize * @param cf the connection filter involved * @param data the transfer involved - * @param peer the peer to be connected to + * @param ssl_peer the SSL peer to be connected to * @param alpns the ALPN specifications to negotiate, may be NULL * @param cb_setup optional callback for early TLS config * @param cb_user_data user_data param for callback @@ -82,7 +87,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, - struct ssl_peer *peer, + struct ssl_peer *ssl_peer, const struct alpn_spec *alpns, Curl_vquic_tls_ctx_setup *cb_setup, void *cb_user_data, diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index c0a0cbe3b7e1..2c076b2a7095 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -42,6 +42,7 @@ #include "curlx/fopen.h" #include "cfilters.h" #include "vquic/cf-ngtcp2.h" +#include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2-proxy.h" #include "vquic/cf-quiche.h" #include "multiif.h" @@ -760,35 +761,49 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } -CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at) +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) { #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_insert_after(cf_at); + return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); +#elif defined(USE_QUICHE) + return Curl_cf_quiche_insert_after(cf_at, origin, peer); #else (void)cf_at; + (void)origin; + (void)peer; return CURLE_NOT_BUILT_IN; #endif } CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - (void)transport_in; - (void)transport_out; - DEBUGASSERT(transport_out == TRNSPRT_QUIC); + (void)transport_peer; + (void)tunnel_transport; + (void)tunnel_peer; + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, addr); + return Curl_cf_ngtcp2_create(pcf, data, origin, peer, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, addr); + return Curl_cf_quiche_create(pcf, data, origin, peer, conn, addr); #else *pcf = NULL; (void)data; + (void)origin; + (void)peer; (void)conn; (void)addr; + (void)tunnel_peer; + (void)tunnel_transport; return CURLE_NOT_BUILT_IN; #endif } @@ -797,35 +812,49 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel) + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, dest, udp_tunnel); + return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, origin, peer, + tunnel_peer, tunnel_transport); #else (void)cf_at; + (void)data; + (void)origin; + (void)peer; + (void)tunnel_peer; + (void)tunnel_transport; return CURLE_NOT_BUILT_IN; #endif } CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - (void)transport_in; - (void)transport_out; - DEBUGASSERT(transport_out == TRNSPRT_QUIC); + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_proxy_create(pcf, data, conn, addr, - transport_in, transport_out); + return Curl_cf_ngtcp2_proxy_create(pcf, data, origin, peer, transport_peer, + conn, addr, + tunnel_peer, tunnel_transport); #else *pcf = NULL; (void)data; (void)conn; (void)addr; + (void)peer; + (void)transport_peer; + (void)tunnel_peer; + (void)tunnel_transport; return CURLE_NOT_BUILT_IN; #endif } diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index fe53803be3d1..5211a9b33a66 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -39,14 +39,19 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, size_t scidlen, int *qlogfdp); -CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at); +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_http3; @@ -54,15 +59,20 @@ extern struct Curl_cftype Curl_cft_http3; CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - struct Curl_peer *dest, - bool udp_tunnel); + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_h3_proxy; diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index 8ad77bd7fd53..2e132f29772b 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, if(conn_config->verifyhost) { host_str = CFStringCreateWithCString(NULL, - peer->sni ? peer->sni : peer->dest->hostname, kCFStringEncodingUTF8); + peer->sni ? peer->sni : peer->origin->hostname, kCFStringEncodingUTF8); if(!host_str) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 6fda7590ce98..1be2e381c3a9 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1366,11 +1366,11 @@ static void gtls_msg_verify_result(struct Curl_easy *data, if(needs_verified) { failf(data, "SSL: certificate subject name (%s) does not match " "target hostname '%s'", certname, - peer->dest->user_hostname); + peer->origin->user_hostname); } else infof(data, " common name: %s (does not match '%s')", - certname, peer->dest->user_hostname); + certname, peer->origin->user_hostname); } else infof(data, " common name: %s (matched)", certname); @@ -1848,7 +1848,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, IP addresses) */ rc = (int)gnutls_x509_crt_check_hostname(x509_cert, peer->sni ? peer->sni : - peer->dest->hostname); + peer->origin->hostname); result = (!rc && config->verifyhost) ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index b750313084cf..e4bd8074abe1 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -798,7 +798,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, char errorbuf[128]; infof(data, "mbedTLS: Connecting to %s:%d", - connssl->peer.dest->hostname, connssl->peer.dest->port); + connssl->peer.origin->hostname, connssl->peer.origin->port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -940,7 +940,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? connssl->peer.sni : - connssl->peer.dest->hostname)) { + connssl->peer.origin->hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. Thus even if curl connects to a host specified as an IP address, this function must be used. */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 520ba95fa9b5..54ea089ae1b7 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ - size_t hostlen = strlen(peer->dest->hostname); + size_t hostlen = strlen(peer->origin->hostname); (void)conn; switch(peer->type) { case CURL_SSL_PEER_IPV4: - if(!curlx_inet_pton(AF_INET, peer->dest->hostname, &addr)) + if(!curlx_inet_pton(AF_INET, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in_addr); break; #ifdef USE_IPV6 case CURL_SSL_PEER_IPV6: - if(!curlx_inet_pton(AF_INET6, peer->dest->hostname, &addr)) + if(!curlx_inet_pton(AF_INET6, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in6_addr); @@ -2116,10 +2116,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ Curl_cert_hostcheck(altptr, altlen, - peer->dest->hostname, hostlen)) { + peer->origin->hostname, hostlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", - peer->dest->user_hostname, (int)altlen, altptr); + peer->origin->user_hostname, (int)altlen, altptr); } break; @@ -2129,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's IP address!", - peer->dest->user_hostname); + peer->origin->user_hostname); } break; } @@ -2146,9 +2146,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, (peer->type == CURL_SSL_PEER_IPV4) ? "ipv4 address" : "ipv6 address"; infof(data, " subjectAltName does not match %s %s", tname, - peer->dest->user_hostname); + peer->origin->user_hostname); failf(data, "SSL: no alternative certificate subject name matches " - "target %s '%s'", tname, peer->dest->user_hostname); + "target %s '%s'", tname, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2208,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)cn, cnlen, - peer->dest->hostname, hostlen)) { + peer->origin->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target hostname '%s'", cn, peer->dest->user_hostname); + "target hostname '%s'", cn, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -3534,9 +3534,9 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, #else if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", - peer->dest->hostname ? peer->dest->hostname : "NULL", outername); + peer->origin->hostname ? peer->origin->hostname : "NULL", outername); result = SSL_ech_set1_server_names(octx->ssl, - peer->dest->hostname, outername, + peer->origin->hostname, outername, 0 /* do send outer */); if(result != 1) { infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); @@ -4010,19 +4010,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; - char tls_id[80]; BIO *bio; CURLcode result; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(octx); - - if(!connssl->peer.dest) { - Curl_ossl_version(tls_id, sizeof(tls_id)); - result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); - if(result) - return result; - } + DEBUGASSERT(connssl->peer.origin); result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, connssl->alpn, NULL, NULL, @@ -4277,7 +4270,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, curlx_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->peer.dest->hostname, connssl->peer.dest->port); + connssl->peer.origin->hostname, connssl->peer.origin->port); } return result; @@ -4324,7 +4317,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config->verifypeer && !conn_config->verifyhost && - inner && !strcmp(inner, connssl->peer.dest->hostname)) { + inner && !strcmp(inner, connssl->peer.origin->hostname)) { VERBOSE(status = "bad name (tolerated without peer verification)"); rv = SSL_ECH_STATUS_SUCCESS; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index d69ec03f8ffd..e1a3eacd1ca5 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1102,7 +1102,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, DEBUGASSERT(!rconn); rr = rustls_client_connection_new(backend->config, - connssl->peer.dest->hostname, + connssl->peer.origin->hostname, &rconn); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "rustls_client_connection_new"); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index c0b46c58c772..8c714faa5ebc 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -849,7 +849,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.dest->hostname, connssl->peer.dest->port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; @@ -902,7 +902,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ snihost = connssl->peer.sni ? - connssl->peer.sni : connssl->peer.dest->hostname; + connssl->peer.sni : connssl->peer.origin->hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; @@ -1245,7 +1245,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.dest->hostname, connssl->peer.dest->port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1597,7 +1597,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.dest->hostname, connssl->peer.dest->port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -2435,7 +2435,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, *done = FALSE; if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.dest->hostname, connssl->peer.dest->port); + connssl->peer.origin->hostname, connssl->peer.origin->port); } if(!backend->ctxt || cf->shutdown) { diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 38be1dcdc01c..cd00287ff232 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -480,7 +480,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) SECURITY_STATUS sspi_status; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->peer.dest->hostname; + const char *conn_hostname = connssl->peer.origin->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 78a956a16608..913c39083c93 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -869,7 +869,8 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - Curl_peer_unlink(&peer->dest); + Curl_peer_unlink(&peer->origin); + Curl_peer_unlink(&peer->peer); curlx_safefree(peer->sni); curlx_safefree(peer->scache_key); peer->transport = TRNSPRT_NONE; @@ -908,62 +909,49 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport) { - struct Curl_peer *dest = NULL; CURLcode result = CURLE_OUT_OF_MEMORY; /* We expect a clean struct, e.g. called only ONCE */ - DEBUGASSERT(peer); - DEBUGASSERT(!peer->dest); - DEBUGASSERT(!peer->sni); - /* We need the hostname for SNI negotiation. Once handshaked, this remains - * the SNI hostname for the TLS connection. When the connection is reused, - * the settings in cf->conn might change. We keep a copy of the hostname we - * use for SNI. - */ - peer->transport = transport; -#ifndef CURL_DISABLE_PROXY - if(Curl_ssl_cf_is_proxy(cf)) { - dest = cf->conn->http_proxy.peer; - } - else -#endif - { - dest = cf->conn->origin; - } - - /* hostname MUST exist and not be empty */ - if(!dest) { - result = CURLE_FAILED_INIT; - goto out; + if(!ssl_peer || !origin) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } - - Curl_peer_link(&peer->dest, dest); - peer->type = get_peer_type(dest->hostname); - if(peer->type == CURL_SSL_PEER_DNS) { + DEBUGASSERT(!ssl_peer->origin); + DEBUGASSERT(!ssl_peer->peer); + DEBUGASSERT(!ssl_peer->sni); + ssl_peer->transport = transport; + + Curl_peer_link(&ssl_peer->origin, origin); + Curl_peer_link(&ssl_peer->peer, peer); + ssl_peer->type = get_peer_type(origin->hostname); + if(ssl_peer->type == CURL_SSL_PEER_DNS) { /* not an IP address, normalize according to RCC 6066 ch. 3, * max len of SNI is 2^16-1, no trailing dot */ - size_t len = strlen(dest->hostname); - if(len && (dest->hostname[len - 1] == '.')) + size_t len = strlen(origin->hostname); + if(len && (origin->hostname[len - 1] == '.')) len--; if(len < USHRT_MAX) { - peer->sni = curlx_calloc(1, len + 1); - if(!peer->sni) + ssl_peer->sni = curlx_calloc(1, len + 1); + if(!ssl_peer->sni) goto out; - Curl_strntolower(peer->sni, dest->hostname, len); - peer->sni[len] = 0; + Curl_strntolower(ssl_peer->sni, origin->hostname, len); + ssl_peer->sni[len] = 0; } } - result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key); + result = Curl_ssl_peer_key_make(ssl_peer, sslc, tls_id, + &ssl_peer->scache_key); out: if(result) - Curl_ssl_peer_cleanup(peer); + Curl_ssl_peer_cleanup(ssl_peer); return result; } @@ -991,7 +979,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - if(!cf->next) { + if(!cf->next || !connssl->peer.origin) { *done = FALSE; return CURLE_FAILED_INIT; } @@ -1016,14 +1004,6 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->prefs_checked = TRUE; } - if(!connssl->peer.dest) { - char tls_id[80]; - connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); - result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); - if(result) - goto out; - } - result = connssl->ssl_impl->do_connect(cf, data, done); if(!result && *done) { @@ -1406,28 +1386,53 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, return result; } +static CURLcode cf_ssl_peer_init(struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) +{ + struct ssl_connect_data *connssl = cf->ctx; + char tls_id[80]; + connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); + return Curl_ssl_peer_init(&connssl->peer, origin, peer, sslc, + tls_id, TRNSPRT_TCP); +} + CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex) { struct Curl_cfilter *cf; + struct Curl_peer *peer = (sockindex == SECONDARYSOCKET) ? + conn->via_peer2 : conn->via_peer; CURLcode result; result = cf_ssl_create(&cf, data, conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &conn->ssl_config); if(!result) Curl_conn_cf_add(data, conn, sockindex, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } @@ -1467,14 +1472,19 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_proxy_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, peer, NULL, &cf_at->conn->proxy_ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index f0825c37ed9a..4bf99ad30c92 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -89,7 +89,8 @@ typedef enum { } ssl_peer_type; struct ssl_peer { - struct Curl_peer *dest; + struct Curl_peer *origin; /* the authority we talk to */ + struct Curl_peer *peer; /* the machine we are connected to */ char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ @@ -106,8 +107,10 @@ curl_sslbackend Curl_ssl_backend(void); /** * Init SSL peer information for filter. Can be called repeatedly. */ -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport); /** @@ -174,18 +177,22 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex); CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer); CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, int sockindex, bool send_shutdown); #ifndef CURL_DISABLE_PROXY CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *peer); #endif /* !CURL_DISABLE_PROXY */ /** @@ -225,7 +232,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_random(x, y, z) ((void)(x), CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE #define Curl_ssl_supports(a, b) FALSE -#define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN +#define Curl_ssl_cfilter_add(a, b, c, d) CURLE_NOT_BUILT_IN #define Curl_ssl_cfilter_remove(a, b, c) CURLE_OK #define Curl_ssl_cf_get_config(a, b) NULL #define Curl_ssl_cf_get_primary_config(a) NULL diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index fe30091bfb56..24a568fbf467 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -177,69 +177,78 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key) (peer_key[len - 2] == ':'); } -CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, - const struct ssl_peer *peer, - const struct Curl_peer *via_peer, - const char *tls_id, - char **ppeer_key) +static CURLcode ssl_peer_key_add_transport(struct dynbuf *buf, + uint8_t transport) { - struct dynbuf buf; - size_t key_len; - bool is_local = FALSE; - CURLcode result; - - *ppeer_key = NULL; - curlx_dyn_init(&buf, 10 * 1024); - - result = curlx_dyn_addf(&buf, "%s:%d", - peer->dest->hostname, peer->dest->port); - if(result) - goto out; - - switch(peer->transport) { + switch(transport) { case TRNSPRT_TCP: - break; + return CURLE_OK; case TRNSPRT_UDP: - result = curlx_dyn_add(&buf, ":UDP"); - break; + return curlx_dyn_add(buf, ":UDP"); case TRNSPRT_QUIC: - result = curlx_dyn_add(&buf, ":QUIC"); - break; + return curlx_dyn_add(buf, ":QUIC"); case TRNSPRT_UNIX: - result = curlx_dyn_add(&buf, ":UNIX"); - break; + return curlx_dyn_add(buf, ":UNIX"); default: - result = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); - break; + return curlx_dyn_addf(buf, ":TRNSPRT-%d", transport); } - if(result) - goto out; +} + +static CURLcode ssl_peer_key_add_vrfy(struct dynbuf *buf, + struct ssl_primary_config *ssl, + const struct ssl_peer *peer) +{ + CURLcode result; if(!ssl->verifypeer) { - result = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); + result = curlx_dyn_add(buf, ":NO-VRFY-PEER"); if(result) - goto out; + return result; } if(!ssl->verifyhost) { - result = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); + result = curlx_dyn_add(buf, ":NO-VRFY-HOST"); if(result) - goto out; + return result; } if(ssl->verifystatus) { - result = curlx_dyn_add(&buf, ":VRFY-STATUS"); + result = curlx_dyn_add(buf, ":VRFY-STATUS"); if(result) - goto out; + return result; } - if(!ssl->verifypeer || !ssl->verifyhost) { - if(via_peer) { - result = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", - via_peer->hostname, - via_peer->port); - if(result) - goto out; - } + if((!ssl->verifypeer || !ssl->verifyhost) && + peer->peer && !Curl_peer_equal(peer->origin, peer->peer)) { + result = curlx_dyn_addf(buf, ":CHOST-%s:CPORT-%u", + peer->peer->hostname, + peer->peer->port); + if(result) + return result; } + return CURLE_OK; +} + +static CURLcode ssl_peer_key_build(struct ssl_primary_config *ssl, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct dynbuf buf; + size_t key_len; + bool is_local = FALSE; + CURLcode result; + + *ppeer_key = NULL; + curlx_dyn_init(&buf, 10 * 1024); + result = curlx_dyn_addf(&buf, "%s:%d", + peer->origin->hostname, peer->origin->port); + if(result) + goto out; + result = ssl_peer_key_add_transport(&buf, peer->transport); + if(result) + goto out; + result = ssl_peer_key_add_vrfy(&buf, ssl, peer); + if(result) + goto out; if(ssl->version || ssl->version_max) { result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, (ssl->version_max >> 16)); @@ -342,14 +351,12 @@ CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, return result; } -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, char **ppeer_key) { - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); - return Curl_ssl_peer_key_build(ssl, peer, cf->conn->via_peer, tls_id, - ppeer_key); + return ssl_peer_key_build(sslc, peer, tls_id, ppeer_key); } struct Curl_ssl_scache { diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index bfb0677e8448..effb1d8f96af 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -54,34 +54,18 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache); * connection to the peer. * If the filter is a TLS proxy filter, it uses the proxy relevant * information. - * @param cf the connection filter wanting to use it * @param peer the peer the filter wants to talk to + * @param sslc the relevant ssl configuration * @param tls_id identifier of TLS implementation for sessions. Should * include full version if session data from other versions * is to be avoided. * @param ppeer_key on successful return, the key generated */ -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, char **ppeer_key); -/** - * Like Curl_ssl_peer_key_make() but takes the primary config and peer - * descriptors directly, without requiring a Curl_cfilter. Exposed for - * unit testing. - * @param ssl the primary SSL config to key on - * @param peer the peer the filter wants to talk to - * @param via_peer the connecting-through peer, or NULL - * @param tls_id identifier of TLS implementation for sessions - * @param ppeer_key on successful return, the key generated - */ -CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl, - const struct ssl_peer *peer, - const struct Curl_peer *via_peer, - const char *tls_id, - char **ppeer_key); - /* Return if there is a session cache shall be used. * An SSL session might not be configured or not available for * "connect-only" transfers. diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index c7d86a810161..bed18998b3bc 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1763,9 +1763,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "unable to get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } - ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.dest->hostname, 0); + ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.origin->hostname, 0); CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", - connssl->peer.dest->hostname, ret); + connssl->peer.origin->hostname, ret); if(ret != WOLFSSL_SUCCESS) detail = DOMAIN_NAME_MISMATCH; wolfSSL_X509_free(cert); @@ -1788,7 +1788,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->peer.dest->hostname); + connssl->peer.origin->hostname); return CURLE_PEER_FAILED_VERIFICATION; } else if(ASN_NO_SIGNER_E == detail) { diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index fb9df11d2f76..a423524fa5ef 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -214,3 +214,16 @@ def test_06_24_h3_altsvc_h2_used(self, env: Env, httpd, nghttpx): r.check_exit_code(0) r.check_response(count=1, http_status=200) assert r.stats[0]['http_version'] == '2' + + # h3 download using --connect-to IPv6 address + @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") + @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") + def test_06_25_h3_connect_to(self, env: Env, httpd, nghttpx): + curl = CurlClient(env=env, force_resolv=False) + urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json' + r = curl.http_download(urls=[urln], extra_args=[ + '--http3-only', '--connect-to', + f'{env.authority_for(env.domain1, "h3")}:[::1]:{env.https_port}' + ]) + r.check_response(count=1, http_status=200) + assert r.stats[0]['http_version'] == '3' diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index fc4d8b7d2c05..e0eecf5b1395 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -113,8 +113,7 @@ static int test_idx; struct cf_test_ctx { int idx; int ai_family; - uint8_t transport_in; - uint8_t transport_out; + uint8_t transport_peer; char id[16]; struct curltime started; timediff_t fail_delay_ms; @@ -166,10 +165,13 @@ static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf, static CURLcode cf_test_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport_in, - uint8_t transport_out) + struct Curl_peer *tunnel_peer, + uint8_t transport_above) { static const struct Curl_cftype cft_test = { "TEST", @@ -194,7 +196,11 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; + (void)origin; + (void)peer; (void)conn; + (void)tunnel_peer; + (void)transport_above; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -202,8 +208,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf, } ctx->idx = test_idx++; ctx->ai_family = addr->family; - ctx->transport_in = transport_in; - ctx->transport_out = transport_out; + ctx->transport_peer = transport_peer; ctx->started = curlx_now(); current_tr->ongoing++; if(current_tr->ongoing > current_tr->max_concurrent) diff --git a/tests/unit/unit3304.c b/tests/unit/unit3304.c index 5573be39cc6e..bb2bd77b8613 100644 --- a/tests/unit/unit3304.c +++ b/tests/unit/unit3304.c @@ -43,7 +43,7 @@ static CURLcode test_unit3304(const char *arg) UNITTEST_BEGIN_SIMPLE #ifdef USE_SSL - struct Curl_peer dest; + struct Curl_peer origin; struct ssl_peer peer; struct ssl_primary_config ssl; char *key1 = NULL; @@ -60,12 +60,12 @@ static CURLcode test_unit3304(const char *arg) static char lc_ctype[] = "pem"; static char lc_ktype[] = "pem"; - memset(&dest, 0, sizeof(dest)); - dest.hostname = base_hostname; - dest.port = 443; + memset(&origin, 0, sizeof(origin)); + origin.hostname = base_hostname; + origin.port = 443; memset(&peer, 0, sizeof(peer)); - peer.dest = &dest; + peer.origin = &origin; peer.transport = TRNSPRT_TCP; memset(&ssl, 0, sizeof(ssl)); @@ -78,9 +78,9 @@ static CURLcode test_unit3304(const char *arg) ssl.key_type = base_ktype; /* Baseline: same config produces same key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && !strcmp(key1, key2), "identical config should produce identical peer key"); @@ -89,10 +89,10 @@ static CURLcode test_unit3304(const char *arg) /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison * via cf_ssl_scache_match_auth(), same as SRP credentials. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.key_passwd = NULL; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && !strcmp(key1, key2), "key_passwd must not affect the peer key"); @@ -101,10 +101,10 @@ static CURLcode test_unit3304(const char *arg) ssl.key_passwd = base_passwd; /* Different key path must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.key = alt_key; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && strcmp(key1, key2), "different key must produce different peer key"); @@ -113,10 +113,10 @@ static CURLcode test_unit3304(const char *arg) ssl.key = base_key; /* Different key_type must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.key_type = alt_ktype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && strcmp(key1, key2), "different key_type must produce different peer key"); @@ -125,10 +125,10 @@ static CURLcode test_unit3304(const char *arg) ssl.key_type = base_ktype; /* Different cert_type must produce a different peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.cert_type = alt_ctype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && strcmp(key1, key2), "different cert_type must produce different peer key"); @@ -138,10 +138,10 @@ static CURLcode test_unit3304(const char *arg) /* cert_type is case-insensitive: "PEM" and "pem" must produce the * same peer key, consistent with the conn-reuse comparison. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.cert_type = lc_ctype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && !strcmp(key1, key2), "cert_type case must not affect peer key"); @@ -151,10 +151,10 @@ static CURLcode test_unit3304(const char *arg) /* key_type is case-insensitive: "PEM" and "pem" must produce the * same peer key. */ - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.key_type = lc_ktype; - fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2), + fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2), "peer key build failed"); fail_unless(key1 && key2 && !strcmp(key1, key2), "key_type case must not affect peer key"); From f5cf5088ef8448ca7bd0bd3d7f49dfca5d17cc5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:46:44 +0000 Subject: [PATCH 0370/1062] GHA: update ruff from v0.15.12 to v0.15.16 Closes #21940 --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index a23dce588260..5f50cb4b0259 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -6,4 +6,4 @@ cmakelang==0.6.13 codespell==2.4.2 pytype==2024.10.11 reuse==6.2.0 -ruff==0.15.12 +ruff==0.15.16 From a6cece52e4387c14b7b3d8d6e0d1fa0b2faec5f3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:46:49 +0000 Subject: [PATCH 0371/1062] GHA: update awslabs/aws-lc from v1.73.0 to v5.0.0 Closes #21941 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 1d404b9b1424..ee74d5c47a7b 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -37,7 +37,7 @@ env: CURL_TEST_MIN: 1850 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.73.0 + AWSLC_VERSION: 5.0.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260526.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index df5c20196d88..2b2f91f0b899 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -35,7 +35,7 @@ env: CURL_TEST_MIN: 1660 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.73.0 + AWSLC_VERSION: 5.0.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260526.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com From ae2986cdf0c3177f7ed77f491b8ed084420ad46a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 13:52:13 +0200 Subject: [PATCH 0372/1062] mqtt: return error on truncated Remaining Length Pointed out by: Zeropath Closes #21949 --- lib/mqtt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/mqtt.c b/lib/mqtt.c index 113790945416..adbb3ebc44f1 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -602,9 +602,9 @@ static CURLcode mqtt_publish(struct Curl_easy *data) return result; } -/* return 0 on success, non-zero on error */ -static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, - size_t buflen) +/* return FALSE on success, TRUE on error */ +static bool mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -613,14 +613,17 @@ static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, for(i = 0; (i < buflen) && (encoded & 128); i++) { if(i == 4) - return 1; /* bad size */ + return TRUE; /* bad size */ encoded = buf[i]; len += (encoded & 127) * mult; mult *= 128; } + if(encoded & 128) + /* truncated size */ + return TRUE; *lenp = len; - return 0; + return FALSE; } #if defined(DEBUGBUILD) && defined(CURLVERBOSE) From 2f3fa479dd1d2a59106136b8739f7b9e1f204b3e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 16 Apr 2026 10:52:57 +0200 Subject: [PATCH 0373/1062] build: enable `-Wformat-signedness`, fix issues found Adjust code to avoid `-Wformat-signedness` warnings, while making sure that enums are always cast to a known type when passing them to `printf` functions, to support compilers and compiler settings where enums are not default-size signed ints. - cast integers printed as hex to `unsigned`. (63 times, 20 of them in `mbedtls.c`) - cast misc enums to `int` for printing. (31 times) - cast `CURL_LOCK_DATA_*` enums to `int`. (4 times) - cast `CURL_FORMADD_*` enums to `int`. (13 times) - cast `CURLSHE_*` enums to `int`. (3 times) - cast `CURLUE_*` enums to `int`. (33 times) - cast `CURLMSG_*` enums to `int`. (6 times) - cast `CURLE_*` enums to `int`. (~380 times) - unit1675: fix mask. Follow-up to 7c34365ccea19949317878c7fcd5f7376e2e09f1 #21879 Ref: #18343 (initial attempt) Closes #20848 --- CMake/PickyWarnings.cmake | 4 +- docs/examples/10-at-a-time.c | 6 +-- docs/examples/externalsocket.c | 2 +- docs/examples/ftp-delete.c | 2 +- docs/examples/ftpget.c | 2 +- docs/examples/ftpgetinfo.c | 2 +- docs/examples/ftpsget.c | 2 +- docs/examples/log_failed_transfers.c | 2 +- docs/examples/multi-app.c | 6 ++- docs/examples/multi-legacy.c | 6 ++- docs/examples/sftpget.c | 2 +- docs/examples/sslbackend.c | 2 +- docs/examples/websocket-updown.c | 2 +- docs/examples/websocket.c | 2 +- lib/asyn-ares.c | 2 +- lib/asyn-thrdd.c | 4 +- lib/cf-dns.c | 2 +- lib/cf-h1-proxy.c | 3 +- lib/cf-h2-proxy.c | 21 ++++----- lib/cf-https-connect.c | 7 +-- lib/cf-ip-happy.c | 11 ++--- lib/cf-socket.c | 11 ++--- lib/cfilters.c | 7 +-- lib/connect.c | 18 ++++---- lib/content_encoding.c | 3 +- lib/curl_gssapi.c | 2 +- lib/curlx/strerr.c | 2 +- lib/cw-pause.c | 12 ++--- lib/doh.c | 2 +- lib/ftp.c | 7 +-- lib/headers.c | 2 +- lib/hostip.c | 4 +- lib/http2.c | 36 +++++++-------- lib/http_chunks.c | 7 +-- lib/imap.c | 2 +- lib/mime.c | 6 +-- lib/mqtt.c | 4 +- lib/multi.c | 7 +-- lib/peer.c | 6 +-- lib/pop3.c | 2 +- lib/request.c | 2 +- lib/rtsp.c | 2 +- lib/sendf.c | 22 +++++----- lib/setopt.c | 4 +- lib/smtp.c | 19 ++++---- lib/socks.c | 4 +- lib/strerror.c | 7 +-- lib/tftp.c | 2 +- lib/transfer.c | 8 ++-- lib/url.c | 2 +- lib/vauth/digest.c | 5 ++- lib/vauth/digest_sspi.c | 3 +- lib/vauth/ntlm_sspi.c | 2 +- lib/vquic/cf-ngtcp2-cmn.c | 23 +++++----- lib/vquic/cf-ngtcp2-proxy.c | 8 ++-- lib/vquic/cf-ngtcp2.c | 13 +++--- lib/vquic/cf-quiche.c | 30 ++++++++----- lib/vquic/vquic.c | 12 ++--- lib/vssh/libssh.c | 4 +- lib/vssh/libssh2.c | 6 +-- lib/vtls/gtls.c | 10 +++-- lib/vtls/mbedtls.c | 50 +++++++++++---------- lib/vtls/openssl.c | 21 ++++----- lib/vtls/rustls.c | 18 ++++---- lib/vtls/schannel.c | 6 +-- lib/vtls/vtls.c | 7 +-- lib/vtls/vtls_scache.c | 12 ++--- lib/vtls/vtls_spack.c | 4 +- lib/vtls/wolfssl.c | 11 ++--- lib/ws.c | 21 ++++----- m4/curl-compilers.m4 | 4 +- src/tool_main.c | 2 +- src/tool_operate.c | 6 +-- src/tool_ssls.c | 4 +- src/tool_urlglob.c | 2 +- tests/libtest/cli_ftp_upload.c | 2 +- tests/libtest/cli_h2_pausing.c | 2 +- tests/libtest/cli_h2_upgrade_extreme.c | 2 +- tests/libtest/cli_hx_download.c | 10 ++--- tests/libtest/cli_hx_upload.c | 4 +- tests/libtest/cli_tls_session_reuse.c | 2 +- tests/libtest/cli_ws_data.c | 14 +++--- tests/libtest/cli_ws_pingpong.c | 2 +- tests/libtest/first.c | 12 ++--- tests/libtest/first.h | 40 ++++++++--------- tests/libtest/lib1156.c | 4 +- tests/libtest/lib1485.c | 6 +-- tests/libtest/lib1509.c | 4 +- tests/libtest/lib1515.c | 3 +- tests/libtest/lib1518.c | 2 +- tests/libtest/lib1522.c | 2 +- tests/libtest/lib1523.c | 4 +- tests/libtest/lib1531.c | 2 +- tests/libtest/lib1532.c | 6 +-- tests/libtest/lib1533.c | 2 +- tests/libtest/lib1534.c | 10 ++--- tests/libtest/lib1535.c | 10 ++--- tests/libtest/lib1536.c | 10 ++--- tests/libtest/lib1538.c | 6 +-- tests/libtest/lib1541.c | 2 +- tests/libtest/lib1555.c | 4 +- tests/libtest/lib1556.c | 2 +- tests/libtest/lib1558.c | 6 +-- tests/libtest/lib1559.c | 10 ++--- tests/libtest/lib1560.c | 61 ++++++++++++++------------ tests/libtest/lib1565.c | 2 +- tests/libtest/lib1597.c | 3 +- tests/libtest/lib1906.c | 4 +- tests/libtest/lib1907.c | 2 +- tests/libtest/lib1911.c | 2 +- tests/libtest/lib1915.c | 4 +- tests/libtest/lib1916.c | 2 +- tests/libtest/lib1918.c | 4 +- tests/libtest/lib1922.c | 4 +- tests/libtest/lib1945.c | 2 +- tests/libtest/lib2032.c | 7 +-- tests/libtest/lib2082.c | 3 +- tests/libtest/lib2301.c | 2 +- tests/libtest/lib2302.c | 4 +- tests/libtest/lib2304.c | 4 +- tests/libtest/lib2308.c | 2 +- tests/libtest/lib2309.c | 3 +- tests/libtest/lib2405.c | 2 +- tests/libtest/lib2700.c | 10 ++--- tests/libtest/lib3010.c | 6 ++- tests/libtest/lib3026.c | 4 +- tests/libtest/lib3033.c | 3 +- tests/libtest/lib3034.c | 2 +- tests/libtest/lib3100.c | 2 +- tests/libtest/lib506.c | 9 ++-- tests/libtest/lib530.c | 12 ++--- tests/libtest/lib540.c | 4 +- tests/libtest/lib554.c | 10 ++--- tests/libtest/lib574.c | 4 +- tests/libtest/lib582.c | 2 +- tests/libtest/lib586.c | 9 ++-- tests/libtest/lib650.c | 12 ++--- tests/libtest/lib651.c | 2 +- tests/libtest/lib655.c | 2 +- tests/libtest/lib658.c | 4 +- tests/libtest/lib659.c | 2 +- tests/libtest/lib661.c | 2 +- tests/libtest/lib670.c | 4 +- tests/libtest/lib674.c | 2 +- tests/libtest/lib677.c | 4 +- tests/libtest/lib758.c | 4 +- tests/libtest/mk-lib1521.pl | 14 +++--- tests/server/dnsd.c | 8 ++-- tests/server/sws.c | 2 +- tests/tunit/tool1623.c | 4 +- tests/unit/unit1650.c | 11 ++--- tests/unit/unit1656.c | 2 +- tests/unit/unit1657.c | 4 +- tests/unit/unit1660.c | 2 +- tests/unit/unit1664.c | 2 +- tests/unit/unit1666.c | 2 +- tests/unit/unit1667.c | 2 +- tests/unit/unit1675.c | 6 +-- tests/unit/unit2600.c | 2 +- tests/unit/unit2603.c | 2 +- tests/unit/unit2604.c | 4 +- tests/unit/unit2605.c | 2 +- 162 files changed, 565 insertions(+), 509 deletions(-) diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index b326345c1a40..4815c4fc8373 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -250,7 +250,7 @@ if(PICKY_COMPILER) if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.1) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0)) list(APPEND _picky_enable - -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default + -Wformat-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.1) OR @@ -306,7 +306,7 @@ if(PICKY_COMPILER) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) list(APPEND _picky_enable -Warray-bounds=2 # clang 2.9 gcc 5.0 (clang default: -Warray-bounds) - -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 + -Wformat-signedness # clang 19.1 gcc 5.1 appleclang 17.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) diff --git a/docs/examples/10-at-a-time.c b/docs/examples/10-at-a-time.c index f04702f59b8b..d0982e0d4118 100644 --- a/docs/examples/10-at-a-time.c +++ b/docs/examples/10-at-a-time.c @@ -134,14 +134,14 @@ int main(void) const char *url; CURL *curl = msg->easy_handle; curl_easy_getinfo(curl, CURLINFO_PRIVATE, &url); - fprintf(stderr, "R: %d - %s <%s>\n", - msg->data.result, curl_easy_strerror(msg->data.result), url); + fprintf(stderr, "R: %d - %s <%s>\n", (int)msg->data.result, + curl_easy_strerror(msg->data.result), url); curl_multi_remove_handle(multi, curl); curl_easy_cleanup(curl); left--; } else { - fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); + fprintf(stderr, "E: CURLMsg (%d)\n", (int)msg->msg); } if(transfers < NUM_URLS) add_transfer(multi, transfers++, &left); diff --git a/docs/examples/externalsocket.c b/docs/examples/externalsocket.c index 1d74ade723b7..b07f48b44724 100644 --- a/docs/examples/externalsocket.c +++ b/docs/examples/externalsocket.c @@ -164,7 +164,7 @@ int main(void) close(sockfd); if(result != CURLE_OK) { - printf("libcurl error: %d\n", result); + printf("libcurl error: %d\n", (int)result); return 4; } } diff --git a/docs/examples/ftp-delete.c b/docs/examples/ftp-delete.c index 5e49e5f8f7b6..c05f2f7a5457 100644 --- a/docs/examples/ftp-delete.c +++ b/docs/examples/ftp-delete.c @@ -74,7 +74,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/ftpget.c b/docs/examples/ftpget.c index 973049e78ba3..eec6c86348fe 100644 --- a/docs/examples/ftpget.c +++ b/docs/examples/ftpget.c @@ -87,7 +87,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/ftpgetinfo.c b/docs/examples/ftpgetinfo.c index b943f48d0285..d35cc47b5667 100644 --- a/docs/examples/ftpgetinfo.c +++ b/docs/examples/ftpgetinfo.c @@ -90,7 +90,7 @@ int main(void) } else { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } /* always cleanup */ diff --git a/docs/examples/ftpsget.c b/docs/examples/ftpsget.c index c79d2672a15e..44d8665325d0 100644 --- a/docs/examples/ftpsget.c +++ b/docs/examples/ftpsget.c @@ -92,7 +92,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/log_failed_transfers.c b/docs/examples/log_failed_transfers.c index 3279703176db..564735e30dd8 100644 --- a/docs/examples/log_failed_transfers.c +++ b/docs/examples/log_failed_transfers.c @@ -289,7 +289,7 @@ int main(void) failed = 0; } else { - mem_addf(&t->log, "Transfer failed: (%d) %s\n", result, + mem_addf(&t->log, "Transfer failed: (%d) %s\n", (int)result, (errbuf[0] ? errbuf : curl_easy_strerror(result))); fprintf(stderr, "%s", t->log.recent); failed = 1; diff --git a/docs/examples/multi-app.c b/docs/examples/multi-app.c index 4a1f3b1f979a..56509736e878 100644 --- a/docs/examples/multi-app.c +++ b/docs/examples/multi-app.c @@ -99,10 +99,12 @@ int main(void) switch(idx) { case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); + printf("HTTP transfer completed with status %d\n", + (int)msg->data.result); break; case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); + printf("FTP transfer completed with status %d\n", + (int)msg->data.result); break; } } diff --git a/docs/examples/multi-legacy.c b/docs/examples/multi-legacy.c index a0580c6712e2..bd23866d6f8c 100644 --- a/docs/examples/multi-legacy.c +++ b/docs/examples/multi-legacy.c @@ -177,10 +177,12 @@ int main(void) switch(idx) { case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); + printf("HTTP transfer completed with status %d\n", + (int)msg->data.result); break; case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); + printf("FTP transfer completed with status %d\n", + (int)msg->data.result); break; } } diff --git a/docs/examples/sftpget.c b/docs/examples/sftpget.c index 53bc81ea6dad..a3da2d672191 100644 --- a/docs/examples/sftpget.c +++ b/docs/examples/sftpget.c @@ -103,7 +103,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index ec411c217577..20002c4f816a 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -54,7 +54,7 @@ int main(int argc, const char **argv) for(i = 0; list[i]; i++) printf("SSL backend #%d: '%s' (ID: %d)\n", - i, list[i]->name, list[i]->id); + i, list[i]->name, (int)list[i]->id); return 0; } diff --git a/docs/examples/websocket-updown.c b/docs/examples/websocket-updown.c index 3000be059497..85a7e74ea397 100644 --- a/docs/examples/websocket-updown.c +++ b/docs/examples/websocket-updown.c @@ -69,7 +69,7 @@ static size_t read_cb(char *buf, size_t nitems, size_t buflen, void *p) result = curl_ws_start_frame(ctx->curl, CURLWS_TEXT, (curl_off_t)ctx->blen); if(result != CURLE_OK) { - fprintf(stderr, "error starting frame: %d\n", result); + fprintf(stderr, "error starting frame: %d\n", (int)result); return CURL_READFUNC_ABORT; } } diff --git a/docs/examples/websocket.c b/docs/examples/websocket.c index ec445d88ed2d..953c2f358fcf 100644 --- a/docs/examples/websocket.c +++ b/docs/examples/websocket.c @@ -94,7 +94,7 @@ static CURLcode recv_pong(CURL *curl, const char *expected_payload) else { /* some other frame arrived. */ fprintf(stderr, "ws: received frame of %u bytes rflags %x\n", - (unsigned int)rlen, meta->flags); + (unsigned int)rlen, (unsigned int)meta->flags); goto retry; } } diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 4685cdd3b758..d17a6038e2b9 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -314,7 +314,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, } CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", - result, *pdns ? "" : "not "); + (int)result, *pdns ? "" : "not "); async_ares_cleanup(async); out: diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 477075590682..9ce41b20d01e 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -635,7 +635,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, out: if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", - async->hostname, async->port, result); + async->hostname, async->port, (int)result); return result; } @@ -759,7 +759,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, (result != CURLE_COULDNT_RESOLVE_HOST) && (result != CURLE_COULDNT_RESOLVE_PROXY)) { CURL_TRC_DNS(data, "Error %d resolving %s:%d", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 3453202eb11e..c737ce14f435 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -264,7 +264,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, } if(ctx->resolv_result) { - CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); return ctx->resolv_result; } diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 21ac6da29369..f1e5c7a42ab2 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -737,7 +737,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); if(result) - CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", + (int)result); if(!result) result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index f12c094df43b..91403e1c6ed5 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -245,7 +245,7 @@ static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, CURLcode result; result = Curl_conn_cf_send(cf->next, data, buf, buflen, FALSE, pnwritten); CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", - buflen, result, *pnwritten); + buflen, (int)result, *pnwritten); return result; } return CURLE_FAILED_INIT; @@ -371,7 +371,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu", - Curl_bufq_len(&ctx->inbufq), result, nread); + Curl_bufq_len(&ctx->inbufq), (int)result, nread); if(result) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 proxy data"); @@ -975,7 +975,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, out: if(cbs) nghttp2_session_callbacks_del(cbs); - CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", (int)result); return result; } @@ -1135,7 +1135,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } else if(ctx->sent_goaway && !cf->shutdown) { @@ -1147,7 +1147,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, want_recv = nghttp2_session_want_read(ctx->h2); result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } return result; @@ -1196,7 +1196,7 @@ static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); return result; } @@ -1242,7 +1242,7 @@ static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, drain_tunnel(cf, data, &ctx->tunnel); } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); CF_DATA_RESTORE(cf, save); return result; } @@ -1272,7 +1272,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", (int)result, + *pnwritten); if(result && (result != CURLE_AGAIN)) goto out; @@ -1310,7 +1311,7 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, len, result, *pnwritten, + ctx->tunnel.stream_id, len, (int)result, *pnwritten, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -1342,7 +1343,7 @@ static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, result, + ctx->tunnel.stream_id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index c42701a15e49..461797152fee 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -575,7 +575,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); return result; } @@ -615,7 +615,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, result = ctx->ballers[i].result; } } - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -634,7 +634,8 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 08140b643a1c..35b1a8d325ff 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -497,7 +497,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, 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); + (ai_family == AF_INET) ? "4" : "6", (int)result); if(result) goto out; DEBUGASSERT(a); @@ -526,7 +526,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(!a->inconclusive) continue; result = cf_ip_attempt_restart(a, cf, data); - CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result); + CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, (int)result); if(result) /* serious failure */ goto out; bs->last_attempt_started = *Curl_pgrs_now(data); @@ -549,7 +549,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, result = CURLE_COULDNT_CONNECT; VERBOSE(i = 0); for(a = bs->running; a; a = a->next) { - CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result); + CURL_TRC_CF(data, cf, "baller %d: result=%d", i, (int)a->result); if(a->result) result = a->result; } @@ -813,7 +813,7 @@ static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, } result = cf_ip_ballers_shutdown(&ctx->ballers, data, done); - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -826,7 +826,8 @@ static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, if(!cf->connected) { result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } diff --git a/lib/cf-socket.c b/lib/cf-socket.c index a556b8f8da13..b5923d45131c 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1238,7 +1238,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, cf->connected = TRUE; } CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T, - result, ctx->sock); + (int)result, ctx->sock); return result; } @@ -1530,7 +1530,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #endif CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu", - orig_len, result, *pnwritten); + orig_len, (int)result, *pnwritten); cf->conn->sock[cf->sockindex] = fdsave; return result; } @@ -1589,7 +1589,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } - CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread); + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread); if(!result && !ctx->got_first_byte) { ctx->first_byte_at = *Curl_pgrs_now(data); ctx->got_first_byte = TRUE; @@ -1899,7 +1899,8 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); if(result) { - CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", + (int)result); goto out; } @@ -2132,7 +2133,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); socketstate = SOCKET_READABLE(ctx->sock, 0); - CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate); + CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate); switch(socketstate) { case -1: /* error */ /* let's die here */ diff --git a/lib/cfilters.c b/lib/cfilters.c index 3c97a12a4d59..70996fceac28 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -202,7 +202,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) bool cfdone = FALSE; result = cf->cft->do_shutdown(cf, data, &cfdone); if(result) { - CURL_TRC_CF(data, cf, "shut down failed with %d", result); + CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result); return result; } else if(!cfdone) { @@ -553,7 +553,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, result = cf->cft->do_connect(cf, data, done); CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", - blocking, result, *done); + blocking, (int)result, *done); if(!result && *done) { /* Now that the complete filter chain is connected, let all filters * persist information at the connection. E.g. cf-socket sets the @@ -568,7 +568,8 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, goto out; } else if(result) { - CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); + CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", + (int)result); VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); conn_report_connect_stats(cf, data); goto out; diff --git a/lib/connect.c b/lib/connect.c index b47e55e48ea5..2a534aa8f3f1 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -360,7 +360,8 @@ static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, } result = Curl_cf_haproxy_insert_after(cf, data); if(result) { - CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", + (int)result); return result; } CURL_TRC_CF(data, cf, "added HAPROXY filter"); @@ -391,7 +392,7 @@ static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, cf->conn->socks_proxy.proxytype, cf->conn->socks_proxy.creds); if(result) { - CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result); return result; } @@ -417,7 +418,7 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, cf, data, cf->conn->http_proxy.peer); if(result) { CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", - result); + (int)result); return result; } CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); @@ -433,7 +434,7 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, ctx->transport, cf->conn->http_proxy.proxytype); if(result) { CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", - result); + (int)result); return result; } CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); @@ -481,7 +482,7 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, first_transport, tunnel_peer, ctx->transport); if(result) { - CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", result); + CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result); return result; } @@ -522,12 +523,13 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, result = Curl_cf_capsule_insert_after(cf, data); if(result) { - CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", + (int)result); return result; } result = Curl_cf_quic_insert_after(cf, origin, peer); if(result) { - CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); return result; } CURL_TRC_CF(data, cf, "added QUIC filter for origin"); @@ -548,7 +550,7 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, result = Curl_cf_ssl_insert_after(cf, data, origin, peer); if(result) { CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", - result); + (int)result); return result; } CURL_TRC_CF(data, cf, "added SSL filter for origin"); diff --git a/lib/content_encoding.c b/lib/content_encoding.c index a3a5877bfae6..73eb5201eb48 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -820,7 +820,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, result = Curl_cwriter_create(&writer, data, cwt, phase); CURL_TRC_WRITE(data, "added %s decoder %s -> %d", - is_transfer ? "transfer" : "content", cwt->name, result); + is_transfer ? "transfer" : "content", cwt->name, + (int)result); if(result) return result; diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index af63d3a2c0d3..07a6c1e7ed80 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -260,7 +260,7 @@ static OM_uint32 stub_gss_init_sec_context( used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds, (int)target_desc.length, (const char *)target_desc.value, - ctx->sent); + (int)ctx->sent); gss_release_buffer(&minor_status, &target_desc); } diff --git a/lib/curlx/strerr.c b/lib/curlx/strerr.c index 5fc3bc003bea..8e906cc6bdfb 100644 --- a/lib/curlx/strerr.c +++ b/lib/curlx/strerr.c @@ -268,7 +268,7 @@ const char *curlx_strerror(int err, char *buf, size_t buflen) !get_winsock_error(err, buf, buflen) && #endif !curlx_get_winapi_error((DWORD)err, buf, buflen)) - SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err); + SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, (unsigned int)err); #else /* !_WIN32 */ #if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) diff --git a/lib/cw-pause.c b/lib/cw-pause.c index ec611879d1e8..5561a8d7b7f6 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -117,7 +117,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d", - wlen, ctx->buf_total, (*plast)->type, result); + wlen, ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); Curl_bufq_skip(&(*plast)->b, wlen); DEBUGASSERT(ctx->buf_total >= wlen); ctx->buf_total -= wlen; @@ -128,7 +129,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, 0); CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d", - ctx->buf_total, (*plast)->type, result); + ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); } if(Curl_bufq_is_empty(&(*plast)->b)) { @@ -165,7 +167,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, wtype &= ~CLIENTWRITE_EOS; result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d", - wlen, blen, wtype, result); + wlen, blen, (unsigned int)wtype, (int)result); if(result) return result; buf += wlen; @@ -191,8 +193,8 @@ static CURLcode cw_pause_write(struct Curl_easy *data, result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten); } CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, " - "total=%zu -> %d", nwritten, type, ctx->buf_total + wlen, - result); + "total=%zu -> %d", nwritten, (unsigned int)type, + ctx->buf_total + wlen, (int)result); if(result) return result; buf += nwritten; diff --git a/lib/doh.c b/lib/doh.c index b5526b4a836f..af3d3ad97265 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -319,7 +319,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, sizeof(doh_req->req_body), &doh_req->req_body_len); if(d) { - failf(data, "Failed to encode DoH packet [%d]", d); + failf(data, "Failed to encode DoH packet [%d]", (int)d); result = CURLE_OUT_OF_MEMORY; goto error; } diff --git a/lib/ftp.c b/lib/ftp.c index 9908d50afb9b..ef154f99f7fd 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -732,7 +732,7 @@ static CURLcode getftpresponse(struct Curl_easy *data, pp->pending_resp = FALSE; CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d", - result, *nreadp, *ftpcodep); + (int)result, *nreadp, *ftpcodep); return result; } @@ -3839,7 +3839,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, /* Send any post-transfer QUOTE strings? */ if(!status && !result && !premature && data->set.postquote) result = ftp_sendquote(data, ftpc, data->set.postquote); - CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result); return result; } @@ -4448,7 +4448,8 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, ftpc->use_ssl = data->set.use_ssl; ftpc->ccc = data->set.ftp_ccc; - CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), + (int)result); return result; } diff --git a/lib/headers.c b/lib/headers.c index 195e12b371a0..b290a9e5b340 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -305,7 +305,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data, CURLH_HEADER))); CURLcode result = Curl_headers_push(data, buf, blen, htype); CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", - htype, blen, result); + htype, blen, (int)result); if(result) return result; } diff --git a/lib/hostip.c b/lib/hostip.c index d3dd7f310ba5..a18d9a62bb9d 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -734,7 +734,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, failf(data, "Could not resolve: %s:%u", hostname, port); } else { - failf(data, "Error %d resolving %s:%u", result, hostname, port); + failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); } } else if(cache_dns && *pdns) { @@ -1073,7 +1073,7 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, } else if(result) { failf(data, "Error %d resolving %s:%u", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } diff --git a/lib/http2.c b/lib/http2.c index 846222216ac6..ff609b321d8d 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -853,7 +853,7 @@ static int push_promise(struct Curl_cfilter *cf, result = http2_data_setup(cf, newhandle, &newstream); if(result) { - failf(data, "error setting up stream: %d", result); + failf(data, "error setting up stream: %d", (int)result); discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; goto fail; @@ -902,7 +902,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, stream->xfer_result = cf_h2_update_local_win(cf, data, stream); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); } } @@ -919,7 +919,7 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, " "RST-ing stream", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE); } @@ -1383,7 +1383,7 @@ static void cf_h2_header_error(struct Curl_cfilter *cf, { struct cf_h2_ctx *ctx = cf->ctx; - failf(data, "Error receiving HTTP2 header: %d(%s)", result, + failf(data, "Error receiving HTTP2 header: %d(%s)", (int)result, curl_easy_strerror(result)); if(stream) { nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1614,7 +1614,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session, nread = (ssize_t)n; CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d", - stream_id, length, stream->body_eos, nread, result); + stream_id, length, stream->body_eos, nread, (int)result); if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) { *data_flags = NGHTTP2_DATA_FLAG_EOF; @@ -1748,7 +1748,7 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen); + CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", (int)result, *pnlen); return result; } @@ -1857,7 +1857,7 @@ static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result && (result != CURLE_AGAIN)) CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu", - stream->id, len, result, *pnread); + stream->id, len, (int)result, *pnread); return result; } @@ -1908,7 +1908,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(result) { if(result != CURLE_AGAIN) { - failf(data, "Failed receiving HTTP2 data: %d(%s)", result, + failf(data, "Failed receiving HTTP2 data: %d(%s)", (int)result, curl_easy_strerror(result)); return result; } @@ -2003,7 +2003,7 @@ static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, " "window=%d/%d, connection %d/%d", - stream->id, len, result, *pnread, + stream->id, len, (int)result, *pnread, nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -2188,7 +2188,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, out: CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", - stream ? stream->id : -1, result, *pnwritten); + stream ? stream->id : -1, (int)result, *pnwritten); curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); @@ -2222,7 +2222,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(eos); result = cf_h2_body_send(cf, data, stream, buf, 0, eos, &n); CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %d, %zu, eos=%d", - stream->id, result, n, eos); + stream->id, (int)result, n, eos); if(result) goto out; *pnwritten = len; @@ -2230,7 +2230,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { result = cf_h2_body_send(cf, data, stream, buf, len, eos, pnwritten); CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %d, %zu, eos=%d", - stream->id, len, result, *pnwritten, eos); + stream->id, len, (int)result, *pnwritten, eos); } /* Call the nghttp2 send loop and flush to write ALL buffered data, @@ -2266,7 +2266,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "eos=%d, h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, len, result, *pnwritten, + stream->id, len, (int)result, *pnwritten, stream->body_eos, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), @@ -2277,7 +2277,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, " "connection-window=%d, nw_send_buffer(%zu)", - len, result, *pnwritten, + len, (int)result, *pnwritten, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } @@ -2310,7 +2310,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, result, + stream->id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -2320,7 +2320,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, else { CURL_TRC_CF(data, cf, "flush -> %d, " "connection-window=%d, nw_send_buffer(%zu)", - result, nghttp2_session_get_remote_window_size(ctx->h2), + (int)result, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); @@ -2539,7 +2539,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -2922,7 +2922,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, result = Curl_bufq_write(&ctx->inbufq, (const unsigned char *)mem, nread, &copied); if(result) { - failf(data, "error on copying HTTP Upgrade response: %d", result); + failf(data, "error on copying HTTP Upgrade response: %d", (int)result); return CURLE_RECV_ERROR; } if(copied < nread) { diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 9e3e3bd1fece..9596fc6693a4 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -154,7 +154,8 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, if(ch->hexindex == 0) { /* This is illegal data, we received junk where we expected a hexadecimal digit. */ - failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf); + failf(data, "chunk hex-length char not a hex digit: 0x%x", + (unsigned int)*buf); ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_ILLEGAL_HEX; return CURLE_RECV_ERROR; @@ -547,7 +548,7 @@ static CURLcode add_last_chunk(struct Curl_easy *data, out: curl_slist_free_all(trailers); CURL_TRC_READ(data, "http_chunk, added last chunk with trailers " - "from client -> %d", result); + "from client -> %d", (int)result); return result; } @@ -595,7 +596,7 @@ static CURLcode add_chunk(struct Curl_easy *data, if(!result) result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n); CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d", - nread, result); + nread, (int)result); if(result) return result; } diff --git a/lib/imap.c b/lib/imap.c index abb43ea2d8d7..fc3077985d7a 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -566,7 +566,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!imapc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { imapc->ssldone = ssldone; /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ diff --git a/lib/mime.c b/lib/mime.c index c41b852e486b..ec2900cc180f 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1909,7 +1909,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, /* Once we have errored, we will return the same error forever */ if(ctx->errored) { CURL_TRC_READ(data, "cr_mime_read(len=%zu) is errored -> %d, eos=0", - blen, ctx->error_result); + blen, (int)ctx->error_result); *pnread = 0; *peos = FALSE; return ctx->error_result; @@ -2026,8 +2026,8 @@ static CURLcode cr_mime_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%" FMT_OFF_T ") -> %d, %zu, %d", - blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, + ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } diff --git a/lib/mqtt.c b/lib/mqtt.c index adbb3ebc44f1..8482477b9725 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -424,7 +424,7 @@ static CURLcode mqtt_verify_connack(struct Curl_easy *data) if(ptr[0] != 0x00 || ptr[1] != 0x00) { failf(data, "Expected %02x%02x but got %02x%02x", - 0x00, 0x00, ptr[0], ptr[1]); + 0x00U, 0x00U, (unsigned char)ptr[0], (unsigned char)ptr[1]); curlx_dyn_reset(&mq->recvbuf); return CURLE_WEIRD_SERVER_REPLY; } @@ -774,7 +774,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done) result = mqtt_connect(data); if(result) { - failf(data, "Error %d sending MQTT CONNECT request", result); + failf(data, "Error %d sending MQTT CONNECT request", (int)result); return result; } mqstate(data, MQTT_FIRST, MQTT_CONNACK); diff --git a/lib/multi.c b/lib/multi.c index 14fbc3125915..aba2df3d5612 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1204,7 +1204,8 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, break; default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + failf(data, "multi_getsock: unexpected multi state %d", + (int)data->mstate); DEBUGASSERT(0); break; } @@ -1213,7 +1214,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, if(result) { if(result == CURLE_OUT_OF_MEMORY) return CURLM_OUT_OF_MEMORY; - failf(data, "error determining pollset: %d", result); + failf(data, "error determining pollset: %d", (int)result); return CURLM_INTERNAL_ERROR; } @@ -2516,7 +2517,7 @@ static CURLMcode multistate_connecting(struct Curl_easy *data, } else if(*result) { /* failure detected */ - CURL_TRC_M(data, "connect failed -> %d", *result); + CURL_TRC_M(data, "connect failed -> %d", (int)*result); multi_posttransfer(data); multi_done(data, *result, TRUE); *stream_error = TRUE; diff --git a/lib/peer.c b/lib/peer.c index 5dd3aad372fa..a1ed2251eb0d 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -445,7 +445,7 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, result = peer_create(&pp, ppeer); if(result) failf(data, "Error %d creating peer for %s:%u", - result, pp.host_user.str, pp.port); + (int)result, pp.host_user.str, pp.port); out: peer_parse_clear(&pp); @@ -522,10 +522,10 @@ CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, #endif result = peer_create(&pp, ppeer); - CURL_TRC_M(data, "connect-to peer_create2 -> %d", result); + CURL_TRC_M(data, "connect-to peer_create2 -> %d", (int)result); out: - CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, result); + CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, (int)result); peer_parse_clear(&pp); return result; } diff --git a/lib/pop3.c b/lib/pop3.c index 56157af291a0..8a62c85be46c 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -496,7 +496,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!pop3c->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { pop3c->ssldone = ssldone; /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ diff --git a/lib/request.c b/lib/request.c index de07c6aa1354..56dd2c4a15c9 100644 --- a/lib/request.c +++ b/lib/request.c @@ -334,7 +334,7 @@ static CURLcode req_flush(struct Curl_easy *data) result = Curl_xfer_send_shutdown(data, &done); if(result && data->req.shutdown_err_ignore) { infof(data, "Shutdown send direction error: %d. Broken server? " - "Proceeding as if everything is ok.", result); + "Proceeding as if everything is ok.", (int)result); result = CURLE_OK; done = TRUE; } diff --git a/lib/rtsp.c b/lib/rtsp.c index 60c09cbb4b0e..843a02264b91 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -893,7 +893,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, * writer deal with it (it will report EXCESS and fail the transfer). */ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d, " "rtspc->state=%d, req.size=%" FMT_OFF_T ")", - blen, rtspc->in_header, data->req.done, rtspc->state, + blen, rtspc->in_header, data->req.done, (int)rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { result = Curl_client_write(data, CLIENTWRITE_BODY | diff --git a/lib/sendf.c b/lib/sendf.c index 38abb0495510..7559f64f84a7 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -103,7 +103,7 @@ CURLcode Curl_client_start(struct Curl_easy *data) result = r->crt->cntrl(data, r, CURL_CRCNTRL_REWIND); if(result) { failf(data, "rewind of client reader '%s' failed: %d", - r->crt->name, result); + r->crt->name, (int)result); return result; } r = r->next; @@ -194,7 +194,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, return CURLE_OK; result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); return result; } @@ -215,7 +215,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, /* BODY arrives although we want none, bail out */ streamclose(data->conn, "ignoring body"); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " - "did not want a BODY", type, nbytes); + "did not want a BODY", (unsigned int)type, nbytes); data->req.download_done = TRUE; if(data->info.header_size) /* if headers have been received, this is fine */ @@ -259,7 +259,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) { result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); if(result) return result; } @@ -397,7 +397,7 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", - type, len, result); + (unsigned int)type, len, (int)result); return result; } @@ -732,7 +732,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d", - blen, ctx->total_len, ctx->read_len, result, + blen, ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } @@ -1055,7 +1055,7 @@ static CURLcode cr_lc_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1140,7 +1140,7 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) result = do_init_reader_stack(data, r); out: CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", - len, result); + len, (int)result); return result; } @@ -1218,7 +1218,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, out: CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *nread, *eos); + blen, (int)result, *nread, *eos); return result; } @@ -1404,7 +1404,7 @@ CURLcode Curl_creader_set_buf(struct Curl_easy *data, cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result); + CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, (int)result); return result; } @@ -1437,7 +1437,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) while(reader) { result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE); - CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, result); + CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, (int)result); if(result) break; reader = reader->next; diff --git a/lib/setopt.c b/lib/setopt.c index c01221ba7a0a..e2e30622f140 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -802,7 +802,7 @@ static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, if((arg > ok) || (arg < 0)) /* reserve other values for future use */ infof(data, "boolean setopt(%d) got unsupported argument %ld," - " treated as %d", option, arg, enabled); + " treated as %d", (int)option, arg, enabled); return CURLE_OK; } @@ -2947,6 +2947,6 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", option); + failf(data, "setopt 0x%x got bad argument", (unsigned int)option); return result; } diff --git a/lib/smtp.c b/lib/smtp.c index 6bda7ae81441..2283f5e87d2d 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -347,7 +347,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, /* Get more and convert it when needed */ result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", - blen, result, nread, eos); + blen, (int)result, nread, eos); if(result) return result; @@ -432,7 +432,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, } *peos = (bool)ctx->eos; DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d", - blen, result, *pnread, *peos)); + blen, (int)result, *pnread, *peos)); return result; } @@ -700,7 +700,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!smtpc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { smtpc->ssldone = ssldone; /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ @@ -1743,7 +1743,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, /* Clear the transfer mode for the next request */ smtp->transfer = PPTRANSFER_BODY; CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d", - status, premature, result); + (int)status, premature, (int)result); return result; } @@ -1804,7 +1804,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, out: CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d", - result, *connected, *dophase_done); + (int)result, *connected, *dophase_done); return result; } @@ -1853,7 +1853,7 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data, result = smtp_dophase_done(data, smtp, connected); CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", - result, *dophase_done); + (int)result, *dophase_done); return result; } @@ -1885,7 +1885,7 @@ static CURLcode smtp_do(struct Curl_easy *data, bool *done) return result; result = smtp_regular_transfer(data, smtpc, smtp, done); - CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); + CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done); return result; } @@ -1936,7 +1936,8 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) DEBUGF(infof(data, "DO phase is complete")); } - CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); + CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result, + *dophase_done); return result; } @@ -1978,7 +1979,7 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; out: - CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); + CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result); return result; } diff --git a/lib/socks.c b/lib/socks.c index 8c675e7bbe60..5b24741a24b2 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1277,11 +1277,11 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, case SOCKS5_ST_REQ0_SEND: case SOCKS5_ST_AUTH_SEND: case SOCKS5_ST_REQ1_SEND: - CURL_TRC_CF(data, cf, "adjust pollset out (%d)", sx->state); + CURL_TRC_CF(data, cf, "adjust pollset out (%d)", (int)sx->state); result = Curl_pollset_set_out_only(data, ps, sock); break; default: - CURL_TRC_CF(data, cf, "adjust pollset in (%d)", sx->state); + CURL_TRC_CF(data, cf, "adjust pollset in (%d)", (int)sx->state); result = Curl_pollset_set_in_only(data, ps, sock); break; } diff --git a/lib/strerror.c b/lib/strerror.c index e1089f3233d9..7b6dc0212788 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -647,14 +647,15 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) "SEC_E_ILLEGAL_MESSAGE (0x%08lx) - This error usually " "occurs when a fatal SSL/TLS alert is received (e.g. " "handshake failed). More detail may be available in " - "the Windows System event log.", err); + "the Windows System event log.", (unsigned long)err); } else { char msgbuf[256]; if(curlx_get_winapi_error((DWORD)err, msgbuf, sizeof(msgbuf))) - curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, err, msgbuf); + curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, (unsigned long)err, + msgbuf); else - curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, err); + curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, (unsigned long)err); } #else /* CURLVERBOSE */ if(err == SEC_E_OK) diff --git a/lib/tftp.c b/lib/tftp.c index 08345532c54d..df9f4fa959aa 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -874,7 +874,7 @@ static CURLcode tftp_state_machine(struct tftp_conn *state, infof(data, "%s", "TFTP finished"); break; default: - DEBUGF(infof(data, "STATE: %d", state->state)); + DEBUGF(infof(data, "STATE: %d", (int)state->state)); failf(data, "%s", "Internal state machine error"); result = CURLE_TFTP_ILLEGAL; break; diff --git a/lib/transfer.c b/lib/transfer.c index a903f6438f86..edb6d62cbc67 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -332,7 +332,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, out: Curl_multi_xfer_buf_release(data, xfer_buf); if(result) - DEBUGF(infof(data, "sendrecv_dl() -> %d", result)); + DEBUGF(infof(data, "sendrecv_dl() -> %d", (int)result)); return result; } @@ -425,7 +425,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) out: if(result) - DEBUGF(infof(data, "Curl_sendrecv() -> %d", result)); + DEBUGF(infof(data, "Curl_sendrecv() -> %d", (int)result)); return result; } @@ -777,7 +777,7 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, data->req.download_done = TRUE; } CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d", - blen, is_eos, result); + blen, is_eos, (int)result); return result; } @@ -834,7 +834,7 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, data->info.request_size += *pnwritten; DEBUGF(infof(data, "Curl_xfer_send(len=%zu, eos=%d) -> %d, %zu", - blen, eos, result, *pnwritten)); + blen, eos, (int)result, *pnwritten)); return result; } diff --git a/lib/url.c b/lib/url.c index bc9308438a76..73dc7f509ca1 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2982,7 +2982,7 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) result = Curl_conn_setup(data, conn, FIRSTSOCKET, CURL_CF_SSL_DEFAULT); if(!result) result = Curl_headers_init(data); - CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); + CURL_TRC_M(data, "Curl_conn_setup() -> %d", (int)result); } out: diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 9c57affaf9b4..f569f53d11bd 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -844,7 +844,8 @@ static CURLcode auth_create_digest_http_message( if(digest->qop) hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, - digest->nc, digest->cnonce, digest->qop, ha2); + (unsigned int)digest->nc, digest->cnonce, + digest->qop, ha2); else hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); @@ -909,7 +910,7 @@ static CURLcode auth_create_digest_http_message( nonce_quoted, uri_quoted, digest->cnonce, - digest->nc, + (unsigned int)digest->nc, digest->qop, request_digest); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index aca6237735a2..84354b4e2fee 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -456,7 +456,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(status == SEC_E_OK) output_token_len = chlg_buf[4].cbBuffer; else { /* delete the context so a new one can be made */ - infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status); + infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", + (unsigned long)status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 67cf50faf8a4..2e98e86e5f3d 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -298,7 +298,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, &attrs, NULL); if(status != SEC_E_OK) { infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", - status); + (unsigned long)status); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 52422c22e4d7..638a7d9a7777 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -1104,7 +1104,7 @@ CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; if(cerr) { CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, - cerr->type, cerr->error_code); + (int)cerr->type, cerr->error_code); switch(cerr->type) { case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: CURL_TRC_CF(data, cf, "error in version negotiation"); @@ -1141,7 +1141,7 @@ CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, result = Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx); } if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1182,7 +1182,8 @@ CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, goto out; } else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); *done = TRUE; goto out; } @@ -1196,7 +1197,7 @@ CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %zd", ctx->last_error.type, + PRIu64 ") -> %zd", (int)ctx->last_error.type, ctx->last_error.error_code, (ssize_t)nwritten); /* there are cases listed in ngtcp2 documentation where this call * may fail. Since we are doing a connection shutdown as graceful @@ -1210,7 +1211,7 @@ CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, (size_t)nwritten, &n); if(result) { CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " - "aborting shutdown", result); + "aborting shutdown", (int)result); goto out; } @@ -1229,7 +1230,8 @@ CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, goto out; } else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); *done = TRUE; goto out; } @@ -1591,7 +1593,7 @@ static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, if(ecn) CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", - buflen, gso_size, ecn); + buflen, gso_size, (unsigned int)ecn); ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, @@ -1681,7 +1683,8 @@ CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, return CURLE_OK; } if(result) { - CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", result); + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", + (int)result); return result; } if(nread == 0) { @@ -1856,7 +1859,7 @@ void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); if(result) CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", - stream->id, result); + stream->id, (int)result); } } @@ -1914,7 +1917,7 @@ bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, only "protocol frames" */ *input_pending = FALSE; result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", (int)result); alive = result ? FALSE : TRUE; } diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 2f4792e8a9f5..f23373f87d93 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -774,7 +774,7 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, result = cf_h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %d, %zu", - stream->id, len, result, *pnwritten); + stream->id, len, (int)result, *pnwritten); if(result) goto out; (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); @@ -791,7 +791,7 @@ static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnwritten); + stream ? stream->id : -1, len, (int)result, *pnwritten); CF_DATA_RESTORE(cf, save); return result; } @@ -843,7 +843,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); if(result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) -> %zu, %d", - stream->id, len, *pnread, result); + stream->id, len, *pnread, (int)result); goto out; } } @@ -875,7 +875,7 @@ static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnread); + stream ? stream->id : -1, len, (int)result, *pnread); CF_DATA_RESTORE(cf, save); return result; } diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 0a3679b7b6a6..be1a3257c327 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -134,7 +134,8 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, buflen, eos); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, buflen); + "bytes of headers", stream->id, (int)stream->xfer_result, + buflen); } } @@ -154,7 +155,7 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", - stream->id, stream->xfer_result, buflen); + stream->id, (int)stream->xfer_result, buflen); } } } @@ -545,7 +546,7 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, result = ctx->tls_vrfy_result; denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(buflen=%zu) -> %d, %zu", - stream ? stream->id : -1, buflen, result, *pnread); + stream ? stream->id : -1, buflen, (int)result, *pnread); CF_DATA_RESTORE(cf, save); return result; } @@ -817,7 +818,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } result = h3_stream_open(cf, data, buf, len, pnwritten); if(result) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); + CURL_TRC_CF(data, cf, "failed to open stream -> %d", (int)result); goto out; } VERBOSE(stream = H3_STREAM_CTX(ctx, data)); @@ -855,7 +856,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %d, %zu", - stream->id, len, result, *pnwritten); + stream->id, len, (int)result, *pnwritten); if(result) goto out; (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); @@ -874,7 +875,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = ctx->tls_vrfy_result; denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnwritten); + stream ? stream->id : -1, len, (int)result, *pnwritten); CF_DATA_RESTORE(cf, save); return result; } diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 3b568c196511..427ef94d8684 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -303,7 +303,7 @@ static void cf_quiche_stream_close(struct Curl_cfilter *cf, result = cf_flush_egress(cf, data); if(result) CURL_TRC_CF(data, cf, "[%" PRIu64 "] stream close, flush egress -> %d", - stream->id, result); + stream->id, (int)result); } } @@ -343,7 +343,8 @@ static void cf_quiche_write_hd(struct Curl_cfilter *cf, stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, blen); + "bytes of headers", stream->id, (int)stream->xfer_result, + blen); } } @@ -423,7 +424,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, if(result) { CURL_TRC_CF(x->data, x->cf, "[%" PRIu64 "] on header error %d", - stream->id, result); + stream->id, (int)result); if(!stream->xfer_result) stream->xfer_result = result; } @@ -464,7 +465,7 @@ static void cf_quiche_flush_body(struct Curl_cfilter *cf, Curl_bufq_skip(&ctx->writebuf, blen); if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu bytes" - " of data", stream->id, stream->xfer_result, blen); + " of data", stream->id, (int)stream->xfer_result, blen); } } else @@ -501,9 +502,9 @@ static void cf_quiche_recv_body(struct Curl_cfilter *cf, break; else if(result) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv_body error %d", - stream->id, result); + stream->id, (int)result); failf(data, "[%" PRIu64 "] Error %d in HTTP/3 response body for stream", - stream->id, result); + stream->id, (int)result); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -519,10 +520,14 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, quiche_h3_event *ev) { + enum quiche_h3_event_type type; + if(!stream) return; - switch(quiche_h3_event_type(ev)) { + type = quiche_h3_event_type(ev); + + switch(type) { case QUICHE_H3_EVENT_HEADERS: { struct cb_ctx cb_ctx; stream->resp_got_header = TRUE; @@ -568,7 +573,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, default: CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv, unhandled event %d", - stream->id, quiche_h3_event_type(ev)); + stream->id, (int)type); break; } } @@ -876,7 +881,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", - stream->id, result); + stream->id, (int)result); } else if(!stream->resp_got_header) { failf(data, "HTTP/3 stream %" PRIu64 " was closed cleanly, but before " @@ -927,7 +932,8 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*pnread > 0) ctx->data_recvd += *pnread; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%" - FMT_OFF_T, stream->id, blen, result, *pnread, ctx->data_recvd); + FMT_OFF_T, stream->id, blen, (int)result, *pnread, + ctx->data_recvd); return result; } @@ -1153,7 +1159,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : (uint64_t)~0, len, - result, *pnwritten); + (int)result, *pnwritten); return result; } @@ -1237,7 +1243,7 @@ static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf, body[0] = 'X'; result = cf_quiche_send(cf, data, body, 0, TRUE, &sent); CURL_TRC_CF(data, cf, "[%" PRIu64 "] DONE_SEND -> %d, %zu", - stream->id, result, sent); + stream->id, (int)result, sent); } break; } diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 2c076b2a7095..d187a7d17e8c 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -259,7 +259,7 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", - VQUIC_SEND_METHOD, pktlen, gsolen, calls, result, *psent); + VQUIC_SEND_METHOD, pktlen, gsolen, calls, (int)result, *psent); return result; } @@ -297,7 +297,7 @@ static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "vquic_cf_send(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", - pktlen, gsolen, calls, result, *psent); + pktlen, gsolen, calls, (int)result, *psent); return result; } @@ -327,7 +327,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", - VQUIC_SEND_METHOD, pktlen, gsolen, result, *psent); + VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent); } if(!result) qctx->last_io = qctx->last_op; @@ -532,7 +532,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, if(total_nread || result) CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d", - total_nread, pkts, calls, result); + total_nread, pkts, calls, (int)result); Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -616,7 +616,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, if(total_nread || result) CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d", - total_nread, pkts, calls, result); + total_nread, pkts, calls, (int)result); return result; } @@ -681,7 +681,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, if(total_nread || result) CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d", - total_nread, pkts, calls, result); + total_nread, pkts, calls, (int)result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 8bad2ec4ae94..532b4b0706a0 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2444,7 +2444,7 @@ static CURLcode myssh_statemachine(struct Curl_easy *data, if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -2469,7 +2469,7 @@ static CURLcode myssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index e0dccc7ddebf..734e64da70b7 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3183,7 +3183,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = CURLE_OK; } CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -3209,7 +3209,7 @@ static CURLcode ssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ @@ -3844,7 +3844,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, CURL_TRC_SSH(data, "DISCONNECT starts now"); myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = ssh_block_statemach(data, sshc, sshp, TRUE); - CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result); + CURL_TRC_SSH(data, "DISCONNECT is done -> %d", (int)result); } sshc_cleanup(sshc, data, TRUE); } diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 1be2e381c3a9..58b024695e86 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -95,7 +95,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) DEBUGASSERT(data); result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -128,7 +128,8 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "gtls_pull(len=%zu) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "gtls_pull(len=%zu) -> %d, %zu", blen, (int)result, + nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -2047,7 +2048,8 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, } *done = ((connssl->state == ssl_connection_complete) || (connssl->state == ssl_connection_deferred)); - CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", (int)result, + *done); return result; } @@ -2122,7 +2124,7 @@ static CURLcode gtls_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index e4bd8074abe1..3d87d4a226ac 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -141,7 +141,7 @@ static int mbedtls_bio_cf_write(void *bio, result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_WRITE; return result ? -1 : (int)nwritten; @@ -163,7 +163,7 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_READ; /* nread is never larger than int here */ @@ -464,7 +464,8 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); failf(data, "mbedTLS: %s", buf); #else - failf(data, "mbedTLS: certificate verification error 0x%08x", *flags); + failf(data, "mbedTLS: certificate verification error 0x%08x", + (unsigned int)*flags); #endif } @@ -529,7 +530,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error importing CA cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -541,7 +542,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert file %s: (-0x%04X) %s", - ssl_cafile, -ret, errorbuf); + ssl_cafile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } #else @@ -557,7 +558,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert path %s: (-0x%04X) %s", - ssl_capath, -ret, errorbuf); + ssl_capath, (unsigned int)-ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; @@ -595,7 +596,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert file %s: (-0x%04X) %s", - ssl_cert, -ret, errorbuf); + ssl_cert, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } @@ -642,7 +643,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -689,7 +690,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s", - ssl_config->primary.key, -ret, errorbuf); + ssl_config->primary.key, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else @@ -735,7 +736,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error parsing private key: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -764,7 +765,7 @@ static CURLcode mbed_load_crl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CRL file %s: (-0x%04X) %s", - ssl_crlfile, -ret, errorbuf); + ssl_crlfile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } @@ -859,7 +860,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: ssl_setup failed: " - "(-0x%04X) %s", -ret, errorbuf); + "(-0x%04X) %s", (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -910,12 +911,12 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_load(&session, sc_session->sdata, sc_session->sdata_len); if(ret) { - failf(data, "SSL session error loading: -0x%x", -ret); + failf(data, "SSL session error loading: -0x%x", (unsigned int)-ret); } else { ret = mbedtls_ssl_set_session(&backend->ssl, &session); if(ret) - failf(data, "SSL session error setting: -0x%x", -ret); + failf(data, "SSL session error setting: -0x%x", (unsigned int)-ret); else infof(data, "SSL reusing session ID"); } @@ -1054,7 +1055,7 @@ static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, mbedtls_ssl_get_version_number(&backend->ssl)); mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_handshake returned: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -1166,7 +1167,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_get_session(&backend->ssl, &session); msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED); if(ret) { - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); + failf(data, "mbedtls_ssl_get_session returned -0x%x", (unsigned int)-ret); result = CURLE_SSL_CONNECT_ERROR; goto out; } @@ -1185,7 +1186,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); if(ret) { - failf(data, "failed to serialize session: -0x%x", -ret); + failf(data, "failed to serialize session: -0x%x", (unsigned int)-ret); goto out; } @@ -1237,7 +1238,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", - len, -nwritten); + len, (unsigned int)-nwritten); switch(nwritten) { #ifdef MBEDTLS_SSL_PROTO_TLS1_3 case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1261,7 +1262,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d, %zu", - len, result, *pnwritten); + len, (int)result, *pnwritten); return result; } @@ -1305,7 +1306,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; default: - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; goto out; } @@ -1346,7 +1348,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; } else { - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; } @@ -1396,7 +1399,7 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else { char errorbuf[128]; CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", - buffersize, -nread); + buffersize, (unsigned int)-nread); switch(nread) { #ifdef MBEDTLS_SSL_SESSION_TICKETS case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1416,7 +1419,8 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; default: mbedtls_strerror(nread, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_read returned: (-0x%04X) %s", -nread, errorbuf); + failf(data, "ssl_read returned: (-0x%04X) %s", (unsigned int)-nread, + errorbuf); result = CURLE_RECV_ERROR; break; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 54ea089ae1b7..dfc14fbc304a 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -415,7 +415,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) if(result) break; - BIO_printf(mem, "%lx", X509_get_version(x)); + BIO_printf(mem, "%lx", (unsigned long)X509_get_version(x)); result = push_certinfo(data, mem, "Version", i); if(result) break; @@ -581,7 +581,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) (const uint8_t *)buf, (size_t)blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -610,7 +610,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &nread); CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -2065,7 +2065,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, break; default: DEBUGASSERT(0); - failf(data, "unexpected SSL peer type: %d", peer->type); + failf(data, "unexpected SSL peer type: %d", (int)peer->type); return CURLE_PEER_FAILED_VERIFICATION; } @@ -2494,7 +2494,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, verstr = "TLSv1.3"; break; default: - curl_msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + curl_msnprintf(unknown, sizeof(unknown), "(%x)", (unsigned int)ssl_ver); verstr = unknown; break; } @@ -3012,7 +3012,7 @@ static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, result = load_cacert_from_memory(store, conn_config->ca_info_blob); if(result) { failf(data, "error adding trust anchors from certificate blob: %d", - result); + (int)result); return result; } infof(data, " CA Blob from configuration"); @@ -3373,7 +3373,7 @@ static CURLcode ossl_init_session_and_alpns( scs->alpn ? scs->alpn : "-"); octx->reused_session = TRUE; infof(data, "SSL verify result: %lx", - SSL_get_verify_result(octx->ssl)); + (unsigned long)SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA if(ssl_config->earlydata && scs->alpn && SSL_SESSION_get_max_early_data(ssl_session) && @@ -3539,7 +3539,8 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, peer->origin->hostname, outername, 0 /* do send outer */); if(result != 1) { - infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", + (int)result); return CURLE_SSL_CONNECT_ERROR; } } @@ -4787,7 +4788,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, ossl_verify = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = ossl_verify; - infof(data, "OpenSSL verify result: %lx", ossl_verify); + infof(data, "OpenSSL verify result: %lx", (unsigned long)ossl_verify); verified = (ossl_verify == X509_V_OK); if(verified) @@ -5247,7 +5248,7 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, connssl->input_pending = FALSE; } CURL_TRC_CF(data, cf, "ossl_recv(len=%zu) -> %d, %zu (in_pending=%d)", - buffersize, result, *pnread, connssl->input_pending); + buffersize, (int)result, *pnread, connssl->input_pending); return result; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index e1a3eacd1ca5..9890f65538bf 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -118,7 +118,7 @@ static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, connssl->peer_closed = TRUE; *out_n = (uintptr_t)nread; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %d, %zu", - (size_t)len, result, nread); + (size_t)len, (int)result, nread); return ret; } @@ -141,7 +141,7 @@ static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, } *out_n = (uintptr_t)nwritten; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %d, %zu", - len, result, nwritten); + len, (int)result, nwritten); return ret; } @@ -252,7 +252,7 @@ static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: CURL_TRC_CF(data, cf, "rustls_recv(len=%zu) -> %d, %zu", - plainlen, result, *pnread); + plainlen, (int)result, *pnread); return result; } @@ -328,7 +328,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(backend->plain_out_buffered) { result = cr_flush_out(cf, data, rconn); CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", - backend->plain_out_buffered, result); + backend->plain_out_buffered, (int)result); if(result) return result; if(blen > backend->plain_out_buffered) { @@ -374,7 +374,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", - plainlen, result, *pnwritten); + plainlen, (int)result, *pnwritten); return result; } @@ -1149,7 +1149,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); + CURL_TRC_CF(data, cf, "cr_connect, state=%d", (int)connssl->state); *done = FALSE; #ifdef USE_ECH @@ -1166,7 +1166,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, result = cr_init_backend(cf, data, (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", (int)result); if(result) return result; connssl->state = ssl_connection_negotiating; @@ -1358,7 +1358,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown send failed: %d", result); + CURL_TRC_CF(data, cf, "shutdown send failed: %d", (int)result); goto out; } @@ -1375,7 +1375,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(result) { DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)result); } else if(nread == 0) { /* We got the close notify alert and are done. */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 8c714faa5ebc..623dc0cfa3e4 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -2190,7 +2190,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); else { - infof(data, "schannel: recv returned error %d", result); + infof(data, "schannel: recv returned error %d", (int)result); backend->recv_unrecoverable_err = result; } } @@ -2509,7 +2509,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, else { if(!backend->recv_connection_closed) { result = CURLE_SEND_ERROR; - failf(data, "schannel: error sending close msg: %d", result); + failf(data, "schannel: error sending close msg: %d", (int)result); goto out; } /* Looks like server already closed the connection. @@ -2532,7 +2532,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_RECV; } else if(result) { - CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", (int)result); result = CURLE_RECV_ERROR; } else if(nread == 0) { diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 913c39083c93..82ce007b3d7d 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1025,7 +1025,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->earlydata_state > ssl_earlydata_none); } out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1215,7 +1215,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = connssl->ssl_impl->shut_down(cf, data, TRUE, done); - CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); cf->shutdown = (result || *done); } @@ -1567,7 +1567,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, if(!result && !done) /* blocking failed? */ result = CURLE_SSL_SHUTDOWN_FAILED; Curl_conn_cf_discard(&cf, data); - CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); + CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", + (int)result); break; } } diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 24a568fbf467..98beeac0368e 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -902,7 +902,7 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); Curl_ssl_session_destroy(s); goto out; } @@ -912,13 +912,13 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, out: if(result) { failf(data, "[SCACHE] failed to add session for %s, error=%d", - ssl_peer_key, result); + ssl_peer_key, (int)result); } else CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, " "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, " - "quic_tp=%s], peer has %zu sessions now", - ssl_peer_key, s->ietf_tls_id, s->valid_until - now, + "quic_tp=%s], peer has %zu sessions now", ssl_peer_key, + (unsigned int)s->ietf_tls_id, s->valid_until - now, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", peer ? Curl_llist_count(&peer->sessions) : 0); return result; @@ -990,7 +990,7 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, *ps = s; CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, " "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain", - ssl_peer_key, s->ietf_tls_id, s->alpn, + ssl_peer_key, (unsigned int)s->ietf_tls_id, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", Curl_llist_count(&peer->sessions)); } @@ -1022,7 +1022,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); goto out; } diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index c6f0921311ab..9cbdecc90185 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -225,7 +225,7 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, } if(result) - CURL_TRC_SSLS(data, "error packing data: %d", result); + CURL_TRC_SSLS(data, "error packing data: %d", (int)result); return result; } @@ -311,7 +311,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, out: if(result) { - CURL_TRC_SSLS(data, "error unpacking data: %d", result); + CURL_TRC_SSLS(data, "error unpacking data: %d", (int)result); Curl_ssl_session_destroy(s); } else diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index bed18998b3bc..a619865908bf 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -323,7 +323,7 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) (const uint8_t *)buf, blen, FALSE, &nwritten); wssl->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -360,7 +360,7 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) * server response. This allows sending of ClientHello without delay. */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); wssl->io_result = result; return -1; } @@ -368,7 +368,8 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); wssl->io_result = result; - CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, (int)result, + nread); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -1720,7 +1721,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * store to verify the coming certificate from the server */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); return result; } } @@ -1913,7 +1914,7 @@ static CURLcode wssl_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } diff --git a/lib/ws.c b/lib/ws.c index d00891b83fb0..3f3a07466d42 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -727,7 +727,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf, nbytes, &nwritten); if(result) { - infof(data, "[WS] error adding data to buffer %d", result); + infof(data, "[WS] error adding data to buffer %d", (int)result); return result; } } @@ -996,7 +996,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding head: %d", - result); + (int)result); goto out; } result = ws_enc_write_payload(&ws->enc, data, ws->pending.payload, @@ -1004,7 +1004,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf, &n); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding payload: %d", - result); + (int)result); goto out; } if(n != ws->pending.payload_len) { @@ -1066,7 +1066,8 @@ static CURLcode ws_enc_send(struct Curl_easy *data, fragsize : (curl_off_t)buflen, &ws->sendbuf); if(result) { - CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", result); + CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", + (int)result); return result; } } @@ -1220,7 +1221,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_ws_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1444,7 +1445,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, if(ws_enc_reader) Curl_creader_free(data, ws_enc_reader); if(result) - CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", result); + CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", (int)result); else CURL_TRC_WS(data, "websocket established, %s mode", data->set.connect_only ? "connect-only" : "callback"); @@ -1679,7 +1680,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return result; } else if(result) { - failf(data, "[WS] flush, write error %d", result); + failf(data, "[WS] flush, write error %d", (int)result); return result; } else { @@ -1770,7 +1771,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, } CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu", - buflen, result, *pnwritten); + buflen, (int)result, *pnwritten); return result; } @@ -1846,7 +1847,7 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, out: CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T ", flags=%x, raw=%d) -> %d, %zu", - buflen, fragsize, flags, data->set.ws_raw_mode, result, + buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, *pnsent); return result; } @@ -1918,7 +1919,7 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, &ws->sendbuf); if(result) CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", - result); + (int)result); out: return result; diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index afe7a335b9ae..6a2e3c5102c1 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -948,7 +948,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl clang 19 or later if test "$compiler_num" -ge "1901"; then - tmp_CFLAGS="$tmp_CFLAGS -Wno-format-signedness" + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-signedness]) fi dnl clang 20 or later @@ -1144,7 +1144,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl Only gcc 5 or later if test "$compiler_num" -ge "500"; then tmp_CFLAGS="$tmp_CFLAGS -Warray-bounds=2" - tmp_CFLAGS="$tmp_CFLAGS -Wno-format-signedness" + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-signedness]) fi dnl Only gcc 6 or later diff --git a/src/tool_main.c b/src/tool_main.c index e5e4659b1585..5ee8fc9ddd96 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -161,7 +161,7 @@ int main(int argc, char *argv[]) /* win32_init must be called before other init routines. */ result = win32_init(); if(result) { - errorf("(%d) Windows-specific init failed", result); + errorf("(%d) Windows-specific init failed", (int)result); return (int)result; } #endif diff --git a/src/tool_operate.c b/src/tool_operate.c index c4272cf567b1..c9bb63682701 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -616,7 +616,7 @@ static CURLcode post_check_result(struct per_transfer *per, CURLcode result) if(!config->synthetic_error && result && (!global->silent || global->showerror)) { const char *msg = per->errorbuffer; - curl_mfprintf(tool_stderr, "curl: (%d) %s\n", result, + curl_mfprintf(tool_stderr, "curl: (%d) %s\n", (int)result, msg[0] ? msg : curl_easy_strerror(result)); if(result == CURLE_PEER_FAILED_VERIFICATION) fputs(CURL_CA_CERT_ERRORMSG, tool_stderr); @@ -697,7 +697,7 @@ static CURLcode post_close_output(struct per_transfer *per, if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; - errorf("curl: (%d) Failed writing body", result); + errorf("curl: (%d) Failed writing body", (int)result); } if(result && config->rm_partial) { curlx_struct_stat st; @@ -856,7 +856,7 @@ static CURLcode append2query(struct OperationConfig *config, if(uerr) { result = urlerr_cvt(uerr); errorf("(%d) Could not parse the URL, " - "failed to set query", result); + "failed to set query", (int)result); config->synthetic_error = TRUE; } else { diff --git a/src/tool_ssls.c b/src/tool_ssls.c index ac556a791c31..e9c486435d90 100644 --- a/src/tool_ssls.c +++ b/src/tool_ssls.c @@ -109,7 +109,7 @@ CURLcode tool_ssls_load(struct OperationConfig *config, result = curl_easy_ssls_import(easy, NULL, shmac, shmac_len, sdata, sdata_len); if(result) { - warnf("import of session from line %d rejected(%d)", i, result); + warnf("import of session from line %d rejected(%d)", i, (int)result); continue; } ++imported; @@ -179,7 +179,7 @@ static CURLcode tool_ssls_exp(CURL *easy, void *userptr, out: if(result) warnf("Warning: error saving SSL session for '%s': %d", session_key, - result); + (int)result); curlx_free(enc); return result; } diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index ee894d79ddeb..92bf9c43a5e6 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -564,7 +564,7 @@ void glob_show_error(struct URLGlob *glob, const char *url, FILE *error, t = glob->error; /* send error description to the error-stream */ - curl_mfprintf(error, "curl: (%d) %s\n", result, t); + curl_mfprintf(error, "curl: (%d) %s\n", (int)result, t); } CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, diff --git a/tests/libtest/cli_ftp_upload.c b/tests/libtest/cli_ftp_upload.c index 32835bc12329..df7ea57795e8 100644 --- a/tests/libtest/cli_ftp_upload.c +++ b/tests/libtest/cli_ftp_upload.c @@ -169,7 +169,7 @@ static CURLcode test_cli_ftp_upload(const char *URL) curl_global_cleanup(); curl_slist_free_all(host); - curl_mfprintf(stderr, "transfer result: %d\n", result); + curl_mfprintf(stderr, "transfer result: %d\n", (int)result); return result; #else /* !CURL_DISABLE_FTP */ (void)URL; diff --git a/tests/libtest/cli_h2_pausing.c b/tests/libtest/cli_h2_pausing.c index ac57b5c5f8aa..bf98d03ecae2 100644 --- a/tests/libtest/cli_h2_pausing.c +++ b/tests/libtest/cli_h2_pausing.c @@ -265,7 +265,7 @@ static CURLcode test_cli_h2_pausing(const char *URL) curl_mfprintf(stderr, "ERROR: [%zu] done, paused=%d, " "resumed=%d, result %d - wtf?\n", i, handles[i].paused, - handles[i].resumed, msg->data.result); + handles[i].resumed, (int)msg->data.result); result = (CURLcode)1; goto cleanup; } diff --git a/tests/libtest/cli_h2_upgrade_extreme.c b/tests/libtest/cli_h2_upgrade_extreme.c index 62aa0bc36145..9df8640cffec 100644 --- a/tests/libtest/cli_h2_upgrade_extreme.c +++ b/tests/libtest/cli_h2_upgrade_extreme.c @@ -126,7 +126,7 @@ static CURLcode test_cli_h2_upgrade_extreme(const char *URL) } else if(msg->data.result) { curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T - ": failed with %d\n", xfer_id, msg->data.result); + ": failed with %d\n", xfer_id, (int)msg->data.result); goto cleanup; } else if(status != 206) { diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index 8e6f174de5c5..2dca8f496d5d 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -147,7 +147,7 @@ static int my_progress_d_cb(void *userdata, result = curl_easy_getinfo(t->curl, CURLINFO_TLS_SSL_PTR, &tls); if(result) { curl_mfprintf(stderr, "[t-%zu] info CURLINFO_TLS_SSL_PTR failed: %d\n", - t->idx, result); + t->idx, (int)result); assert(0); } else { @@ -199,7 +199,7 @@ static int my_progress_d_cb(void *userdata, (struct rustls_connection *)tls->internals); assert(v); curl_mfprintf(stderr, "[t-%zu] info rustls TLS version 0x%x\n", - t->idx, v); + t->idx, (unsigned int)v); break; } #endif @@ -214,13 +214,13 @@ static int my_progress_d_cb(void *userdata, assert(sspi_status == SEC_E_OK); (void)sspi_status; curl_mfprintf(stderr, "[t-%zu] info Schannel TLS version 0x%08lx\n", - t->idx, info.dwProtocol); + t->idx, (unsigned long)info.dwProtocol); break; } #endif default: curl_mfprintf(stderr, "[t-%zu] info SSL_PTR backend=%d, ptr=%p\n", - t->idx, tls->backend, tls->internals); + t->idx, (int)tls->backend, tls->internals); break; } } @@ -502,7 +502,7 @@ static CURLcode test_cli_hx_download(const char *URL) t->done = 1; t->result = m->data.result; curl_mfprintf(stderr, "[t-%zu] FINISHED with result %d\n", - t->idx, t->result); + t->idx, (int)t->result); if(use_earlydata) { curl_off_t sent; curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent); diff --git a/tests/libtest/cli_hx_upload.c b/tests/libtest/cli_hx_upload.c index d0434a2eab1b..64c3bbb3a508 100644 --- a/tests/libtest/cli_hx_upload.c +++ b/tests/libtest/cli_hx_upload.c @@ -395,7 +395,7 @@ static CURLcode test_cli_hx_upload(const char *URL) curl_mfprintf(stderr, "[t-%zu] STARTING\n", t->idx); rc = curl_easy_perform(curl); - curl_mfprintf(stderr, "[t-%zu] DONE -> %d\n", t->idx, rc); + curl_mfprintf(stderr, "[t-%zu] DONE -> %d\n", t->idx, (int)rc); t->curl = NULL; curl_easy_reset(curl); } @@ -448,7 +448,7 @@ static CURLcode test_cli_hx_upload(const char *URL) t->done = 1; curl_mfprintf(stderr, "[t-%zu] FINISHED, " "result=%d, response=%ld\n", - t->idx, m->data.result, res_status); + t->idx, (int)m->data.result, res_status); if(use_earlydata) { curl_off_t sent; curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent); diff --git a/tests/libtest/cli_tls_session_reuse.c b/tests/libtest/cli_tls_session_reuse.c index 031d4cfc2ac6..ba97485a96c2 100644 --- a/tests/libtest/cli_tls_session_reuse.c +++ b/tests/libtest/cli_tls_session_reuse.c @@ -209,7 +209,7 @@ static CURLcode test_cli_tls_session_reuse(const char *URL) } else if(msg->data.result) { curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T - ": failed with %d\n", xfer_id, msg->data.result); + ": failed with %d\n", xfer_id, (int)msg->data.result); goto cleanup; } else if(status != 200) { diff --git a/tests/libtest/cli_ws_data.c b/tests/libtest/cli_ws_data.c index 856d41ba1f26..20d15c89d8ae 100644 --- a/tests/libtest/cli_ws_data.c +++ b/tests/libtest/cli_ws_data.c @@ -37,13 +37,13 @@ static CURLcode test_ws_data_m2_check_recv(const struct curl_ws_frame *frame, if(frame->flags & CURLWS_CLOSE) { curl_mfprintf(stderr, "recv_data: unexpected CLOSE frame from server, " "got %zu bytes, offset=%zu, rflags %x\n", - nread, r_offset, frame->flags); + nread, r_offset, (unsigned int)frame->flags); return CURLE_RECV_ERROR; } if(!r_offset && !(frame->flags & CURLWS_BINARY)) { curl_mfprintf(stderr, "recv_data: wrong frame, got %zu bytes, offset=%zu, " "rflags %x\n", - nread, r_offset, frame->flags); + nread, r_offset, (unsigned int)frame->flags); return CURLE_RECV_ERROR; } if(frame->offset != (curl_off_t)r_offset) { @@ -104,7 +104,7 @@ static CURLcode test_ws_data_m2_echo(const char *url, curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result != CURLE_OK) goto out; @@ -120,8 +120,8 @@ static CURLcode test_ws_data_m2_echo(const char *url, sblock = (result == CURLE_AGAIN); if(!result || (result == CURLE_AGAIN)) { curl_mfprintf(stderr, "curl_ws_send(len=%zu) -> %d, " - "%zu (%" CURL_FORMAT_CURL_OFF_T "/%zu)\n", - slen, result, nwritten, (curl_off_t)(len - slen), len); + "%zu (%" CURL_FORMAT_CURL_OFF_T "/%zu)\n", slen, + (int)result, nwritten, (curl_off_t)(len - slen), len); sbuf += nwritten; slen -= nwritten; } @@ -140,8 +140,8 @@ static CURLcode test_ws_data_m2_echo(const char *url, &nread, &frame); if(!result || (result == CURLE_AGAIN)) { rblock = (result == CURLE_AGAIN); - curl_mfprintf(stderr, "curl_ws_recv(len=%zu) -> %d, %zu (%ld/%zu) " - "\n", rlen, result, nread, (long)(len - rlen), len); + curl_mfprintf(stderr, "curl_ws_recv(len=%zu) -> %d, %zu (%ld/%zu)\n", + rlen, (int)result, nread, (long)(len - rlen), len); if(!result) { result = test_ws_data_m2_check_recv(frame, len - rlen, nread, len); if(result) diff --git a/tests/libtest/cli_ws_pingpong.c b/tests/libtest/cli_ws_pingpong.c index 2ed12c8cab5a..32961da72123 100644 --- a/tests/libtest/cli_ws_pingpong.c +++ b/tests/libtest/cli_ws_pingpong.c @@ -78,7 +78,7 @@ static CURLcode test_cli_ws_pingpong(const char *URL) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result == CURLE_OK) result = pingpong(curl, payload); diff --git a/tests/libtest/first.c b/tests/libtest/first.c index c244e5dc7e69..bd18c06752ce 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -169,7 +169,7 @@ CURLcode ws_send_ping(CURL *curl, const char *send_payload) CURLcode result = curl_ws_send(curl, send_payload, strlen(send_payload), &sent, 0, CURLWS_PING); curl_mfprintf(stderr, "ws: curl_ws_send returned %d, sent %zu\n", - result, sent); + (int)result, sent); return result; } @@ -181,13 +181,13 @@ CURLcode ws_recv_pong(CURL *curl, const char *expected_payload) CURLcode result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta); if(result) { curl_mfprintf(stderr, "ws: curl_ws_recv returned %d, received %zu\n", - result, rlen); + (int)result, rlen); return result; } if(!(meta->flags & CURLWS_PONG)) { curl_mfprintf(stderr, "recv_pong: wrong frame, got %zu bytes rflags %x\n", - rlen, meta->flags); + rlen, (unsigned int)meta->flags); return CURLE_RECV_ERROR; } @@ -207,7 +207,7 @@ void ws_close(CURL *curl) size_t sent; CURLcode result = curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE); curl_mfprintf(stderr, "ws: curl_ws_send returned %d, sent %zu\n", - result, sent); + (int)result, sent); } #endif /* CURL_DISABLE_WEBSOCKETS */ @@ -289,7 +289,7 @@ int main(int argc, const char **argv) #endif result = entry_func(URL); - curl_mfprintf(stderr, "Test ended with result %d\n", result); + curl_mfprintf(stderr, "Test ended with result %d\n", (int)result); #ifdef _WIN32 /* flush buffers of all streams regardless of mode */ @@ -298,5 +298,5 @@ int main(int argc, const char **argv) /* Regular program status codes are limited to 0..127 and 126 and 127 have * special meanings by the shell, so limit a normal return code to 125 */ - return (int)result <= 125 ? (int)result : 125; + return result <= 125 ? result : 125; } diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 33191edc5966..8f65d216480a 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -201,16 +201,16 @@ void ws_close(CURL *curl); /* close the connection */ /* ---------------------------------------------------------------- */ -#define exe_easy_setopt(A, B, C, Y, Z) \ - do { \ - CURLcode ec = curl_easy_setopt(A, B, C); \ - if(ec != CURLE_OK) { \ - curl_mfprintf(stderr, \ - "%s:%d curl_easy_setopt() failed, " \ - "with code %d (%s)\n", \ - Y, Z, ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ +#define exe_easy_setopt(A, B, C, Y, Z) \ + do { \ + CURLcode ec = curl_easy_setopt(A, B, C); \ + if(ec != CURLE_OK) { \ + curl_mfprintf(stderr, \ + "%s:%d curl_easy_setopt() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)ec, curl_easy_strerror(ec)); \ + result = ec; \ + } \ } while(0) #define res_easy_setopt(A, B, C) \ @@ -559,16 +559,16 @@ void ws_close(CURL *curl); /* close the connection */ /* ---------------------------------------------------------------- */ -#define exe_global_init(A, Y, Z) \ - do { \ - CURLcode ec = curl_global_init(A); \ - if(ec != CURLE_OK) { \ - curl_mfprintf(stderr, \ - "%s:%d curl_global_init() failed, " \ - "with code %d (%s)\n", \ - Y, Z, ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ +#define exe_global_init(A, Y, Z) \ + do { \ + CURLcode ec = curl_global_init(A); \ + if(ec != CURLE_OK) { \ + curl_mfprintf(stderr, \ + "%s:%d curl_global_init() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)ec, curl_easy_strerror(ec)); \ + result = ec; \ + } \ } while(0) #define chk_global_init(A, Y, Z) \ diff --git a/tests/libtest/lib1156.c b/tests/libtest/lib1156.c index fa75a6ae84b1..7887e0d8686d 100644 --- a/tests/libtest/lib1156.c +++ b/tests/libtest/lib1156.c @@ -102,12 +102,12 @@ static int onetest(CURL *curl, const char *url, const struct testparams *p, result = curl_easy_perform(curl); if(result != p->result) { curl_mprintf("%zu: bad error code (%d): resume=%s, fail=%s, http416=%s, " - "content-range=%s, expected=%d\n", num, result, + "content-range=%s, expected=%d\n", num, (int)result, (p->flags & F_RESUME) ? "yes" : "no", (p->flags & F_FAIL) ? "yes" : "no", (p->flags & F_HTTP416) ? "yes" : "no", (p->flags & F_CONTENTRANGE) ? "yes" : "no", - p->result); + (int)p->result); return 1; } if(hasbody && (p->flags & F_IGNOREBODY)) { diff --git a/tests/libtest/lib1485.c b/tests/libtest/lib1485.c index a4eb07a62802..46b985151325 100644 --- a/tests/libtest/lib1485.c +++ b/tests/libtest/lib1485.c @@ -47,10 +47,10 @@ static size_t t1485_header_callback(char *ptr, size_t size, size_t nmemb, /* end of a response */ result = curl_easy_getinfo(st->curl, CURLINFO_RESPONSE_CODE, &httpcode); curl_mfprintf(stderr, "header_callback, get status: %ld, %d\n", - httpcode, result); + httpcode, (int)result); if(httpcode < 100 || httpcode >= 1000) { curl_mfprintf(stderr, "header_callback, invalid status: %ld, %d\n", - httpcode, result); + httpcode, (int)result); return CURLE_WRITE_ERROR; } st->http_status = (int)httpcode; @@ -58,7 +58,7 @@ static size_t t1485_header_callback(char *ptr, size_t size, size_t nmemb, result = curl_easy_getinfo(st->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &clen); curl_mfprintf(stderr, "header_callback, info Content-Length: " - "%" CURL_FORMAT_CURL_OFF_T ", %d\n", clen, result); + "%" CURL_FORMAT_CURL_OFF_T ", %d\n", clen, (int)result); if(result) { st->result = result; return CURLE_WRITE_ERROR; diff --git a/tests/libtest/lib1509.c b/tests/libtest/lib1509.c index 1099c3c956f3..95258d678f5d 100644 --- a/tests/libtest/lib1509.c +++ b/tests/libtest/lib1509.c @@ -66,7 +66,7 @@ static CURLcode test_lib1509(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } @@ -75,7 +75,7 @@ static CURLcode test_lib1509(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1515.c b/tests/libtest/lib1515.c index dae7acce506d..f9bacf5c6033 100644 --- a/tests/libtest/lib1515.c +++ b/tests/libtest/lib1515.c @@ -128,7 +128,8 @@ static CURLcode test_lib1515(const char *URL) /* second request must succeed like the first one */ result = do_one_request(multi, target_url, dns_entry); if(result != CURLE_OK) { - curl_mfprintf(stderr, "request %s failed with %d\n", target_url, result); + curl_mfprintf(stderr, "request %s failed with %d\n", target_url, + (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib1518.c b/tests/libtest/lib1518.c index f4552f21e600..8512796e62d8 100644 --- a/tests/libtest/lib1518.c +++ b/tests/libtest/lib1518.c @@ -84,7 +84,7 @@ static CURLcode test_lib1518(const char *URL) "redirects %ld\n" "effectiveurl %s\n" "redirecturl %s\n", - result, + (int)result, curlResponseCode, curlRedirectCount, effectiveUrl, diff --git a/tests/libtest/lib1522.c b/tests/libtest/lib1522.c index 56a6992bdb41..4d073d39f6dc 100644 --- a/tests/libtest/lib1522.c +++ b/tests/libtest/lib1522.c @@ -87,7 +87,7 @@ static CURLcode test_lib1522(const char *URL) } } else { - curl_mprintf("curl_easy_perform() failed. e = %d\n", code); + curl_mprintf("curl_easy_perform() failed. e = %d\n", (int)code); } test_cleanup: curl_slist_free_all(pHeaderList); diff --git a/tests/libtest/lib1523.c b/tests/libtest/lib1523.c index 47254ffcdf14..abda172f06bf 100644 --- a/tests/libtest/lib1523.c +++ b/tests/libtest/lib1523.c @@ -66,11 +66,11 @@ static CURLcode test_lib1523(const char *URL) result = run(curl, 1, 2); if(result) - curl_mfprintf(stderr, "error (%d) %s\n", result, buffer); + curl_mfprintf(stderr, "error (%d) %s\n", (int)result, buffer); result = run(curl, 12000, 1); if(result != CURLE_OPERATION_TIMEDOUT) - curl_mfprintf(stderr, "error (%d) %s\n", result, buffer); + curl_mfprintf(stderr, "error (%d) %s\n", (int)result, buffer); else result = CURLE_OK; diff --git a/tests/libtest/lib1531.c b/tests/libtest/lib1531.c index f8c9071e3b8b..def737f9f10e 100644 --- a/tests/libtest/lib1531.c +++ b/tests/libtest/lib1531.c @@ -128,7 +128,7 @@ static CURLcode test_lib1531(const char *URL) msg = curl_multi_info_read(multi, &msgs_left); if(msg && msg->msg == CURLMSG_DONE) { curl_mprintf("HTTP transfer completed with status %d\n", - msg->data.result); + (int)msg->data.result); break; } diff --git a/tests/libtest/lib1532.c b/tests/libtest/lib1532.c index 533abb3e7892..25c5ab439a3a 100644 --- a/tests/libtest/lib1532.c +++ b/tests/libtest/lib1532.c @@ -41,7 +41,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -49,7 +49,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(httpcode != 200) { @@ -66,7 +66,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(httpcode) { diff --git a/tests/libtest/lib1533.c b/tests/libtest/lib1533.c index 6e7d1a471b67..c7057879d9b5 100644 --- a/tests/libtest/lib1533.c +++ b/tests/libtest/lib1533.c @@ -98,7 +98,7 @@ static CURLcode perform_and_check_connections(CURL *curl, result = curl_easy_perform(curl); if(result != CURLE_OK) { - curl_mfprintf(stderr, "curl_easy_perform() failed with %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed with %d\n", (int)result); return TEST_ERR_MAJOR_BAD; } diff --git a/tests/libtest/lib1534.c b/tests/libtest/lib1534.c index e672d585ee11..9c4491b8bcef 100644 --- a/tests/libtest/lib1534.c +++ b/tests/libtest/lib1534.c @@ -42,7 +42,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { @@ -60,7 +60,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -71,7 +71,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != 30) { @@ -97,7 +97,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { @@ -117,7 +117,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { diff --git a/tests/libtest/lib1535.c b/tests/libtest/lib1535.c index aae63fc9fa71..68ec25fb60f4 100644 --- a/tests/libtest/lib1535.c +++ b/tests/libtest/lib1535.c @@ -43,7 +43,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { @@ -60,7 +60,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -72,7 +72,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol != CURLPROTO_HTTP) { @@ -100,7 +100,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { @@ -121,7 +121,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { diff --git a/tests/libtest/lib1536.c b/tests/libtest/lib1536.c index cd3111619494..189392cf7f45 100644 --- a/tests/libtest/lib1536.c +++ b/tests/libtest/lib1536.c @@ -42,7 +42,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { @@ -59,7 +59,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -70,7 +70,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(!scheme || memcmp(scheme, "http", 5) != 0) { @@ -96,7 +96,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { @@ -115,7 +115,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { diff --git a/tests/libtest/lib1538.c b/tests/libtest/lib1538.c index 43c77ae5c3d6..6433e571b766 100644 --- a/tests/libtest/lib1538.c +++ b/tests/libtest/lib1538.c @@ -43,16 +43,16 @@ static CURLcode test_lib1538(const char *URL) curl_url_strerror((CURLUcode)-INT_MAX); /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ for(easyret = CURLE_OK; easyret <= CURL_LAST; easyret++) { - curl_mprintf("e%d: %s\n", easyret, curl_easy_strerror(easyret)); + curl_mprintf("e%d: %s\n", (int)easyret, curl_easy_strerror(easyret)); } for(mresult = CURLM_CALL_MULTI_PERFORM; mresult <= CURLM_LAST; mresult++) { curl_mprintf("m%d: %s\n", mresult, curl_multi_strerror(mresult)); } for(shareret = CURLSHE_OK; shareret <= CURLSHE_LAST; shareret++) { - curl_mprintf("s%d: %s\n", shareret, curl_share_strerror(shareret)); + curl_mprintf("s%d: %s\n", (int)shareret, curl_share_strerror(shareret)); } for(urlret = CURLUE_OK; urlret <= CURLUE_LAST; urlret++) { - curl_mprintf("u%d: %s\n", urlret, curl_url_strerror(urlret)); + curl_mprintf("u%d: %s\n", (int)urlret, curl_url_strerror(urlret)); } return result; diff --git a/tests/libtest/lib1541.c b/tests/libtest/lib1541.c index 2dc0d2cd2611..d6888dbb63a4 100644 --- a/tests/libtest/lib1541.c +++ b/tests/libtest/lib1541.c @@ -34,7 +34,7 @@ struct t1541_transfer_status { static void t1541_geterr(const char *name, CURLcode val, int lineno) { curl_mprintf("CURLINFO_%s returned %d, \"%s\" on line %d\n", - name, val, curl_easy_strerror(val), lineno); + name, (int)val, curl_easy_strerror(val), lineno); } static void report_time(const char *key, const char *where, curl_off_t time, diff --git a/tests/libtest/lib1555.c b/tests/libtest/lib1555.c index 4932fd24f7cd..c6c806426a05 100644 --- a/tests/libtest/lib1555.c +++ b/tests/libtest/lib1555.c @@ -44,9 +44,9 @@ static int progressCallback(void *arg, (void)ultotal; (void)ulnow; result = curl_easy_recv(t1555_curl, buffer, 256, &n); - curl_mprintf("curl_easy_recv returned %d\n", result); + curl_mprintf("curl_easy_recv returned %d\n", (int)result); result = curl_easy_send(t1555_curl, buffer, n, &n); - curl_mprintf("curl_easy_send returned %d\n", result); + curl_mprintf("curl_easy_send returned %d\n", (int)result); return 1; } diff --git a/tests/libtest/lib1556.c b/tests/libtest/lib1556.c index c91161628fa7..201ea4b15962 100644 --- a/tests/libtest/lib1556.c +++ b/tests/libtest/lib1556.c @@ -60,7 +60,7 @@ static CURLcode test_lib1556(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1558.c b/tests/libtest/lib1558.c index 4077cb8eb1f5..0832f8b1c5ee 100644 --- a/tests/libtest/lib1558.c +++ b/tests/libtest/lib1558.c @@ -36,18 +36,18 @@ static CURLcode test_lib1558(const char *URL) result = curl_easy_perform(curl); if(result) { curl_mfprintf(stderr, "curl_easy_perform() returned %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } result = curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol); if(result) { curl_mfprintf(stderr, "curl_easy_getinfo() returned %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } - curl_mprintf("Protocol: %lx\n", protocol); + curl_mprintf("Protocol: %lx\n", (unsigned long)protocol); curl_easy_cleanup(curl); curl_global_cleanup(); diff --git a/tests/libtest/lib1559.c b/tests/libtest/lib1559.c index 4cf3953b4fe2..951b5db234ce 100644 --- a/tests/libtest/lib1559.c +++ b/tests/libtest/lib1559.c @@ -47,23 +47,23 @@ static CURLcode test_lib1559(const char *URL) result = curl_easy_setopt(curl, CURLOPT_URL, longurl); curl_mprintf("CURLOPT_URL %d bytes URL == %d\n", - EXCESSIVE, result); + EXCESSIVE, (int)result); result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, longurl); curl_mprintf("CURLOPT_POSTFIELDS %d bytes data == %d\n", - EXCESSIVE, result); + EXCESSIVE, (int)result); u = curl_url(); if(u) { CURLUcode uc = curl_url_set(u, CURLUPART_URL, longurl, 0); curl_mprintf("CURLUPART_URL %d bytes URL == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); uc = curl_url_set(u, CURLUPART_SCHEME, longurl, CURLU_NON_SUPPORT_SCHEME); curl_mprintf("CURLUPART_SCHEME %d bytes scheme == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); uc = curl_url_set(u, CURLUPART_USER, longurl, 0); curl_mprintf("CURLUPART_USER %d bytes user == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); curl_url_cleanup(u); } diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 62c163c7cb5c..76fbe3cbdb94 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -76,7 +76,7 @@ static int checkparts(CURLU *u, const char *in, const char *wanted, z ? " " : "", z ? z : ""); } else - curl_msnprintf(bufp, len, "%s[%d]", buf[0] ? " | " : "", rc); + curl_msnprintf(bufp, len, "%s[%d]", buf[0] ? " | " : "", (int)rc); n = strlen(bufp); bufp += n; @@ -1612,7 +1612,7 @@ static int set_url(void) if(rc) { curl_mfprintf(stderr, "%s:%d Set URL %s returned %d (%s)\n", __FILE__, __LINE__, set_url_list[i].set, - rc, curl_url_strerror(rc)); + (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1620,7 +1620,7 @@ static int set_url(void) rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) { @@ -1631,7 +1631,7 @@ static int set_url(void) } else if(rc != set_url_list[i].ucode) { curl_mfprintf(stderr, "Set URL\nin: %s\nreturned %d (expected %d)\n", - set_url_list[i].in, rc, set_url_list[i].ucode); + set_url_list[i].in, (int)rc, (int)set_url_list[i].ucode); error++; } curl_url_cleanup(urlp); @@ -1669,8 +1669,8 @@ static int setget_parts(bool has_utf8) if(uc != setget_parts_list[i].pcode) { curl_mfprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n", - setget_parts_list[i].set, uc, - setget_parts_list[i].pcode); + setget_parts_list[i].set, + (int)uc, (int)setget_parts_list[i].pcode); error++; } if(!uc) { @@ -1683,7 +1683,7 @@ static int setget_parts(bool has_utf8) } else if(rc != CURLUE_OK) { curl_mfprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n", - setget_parts_list[i].in, rc, 0); + setget_parts_list[i].in, (int)rc, 0); error++; } } @@ -1716,7 +1716,8 @@ static int set_parts(void) if(uc != set_parts_list[i].pcode) { curl_mfprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n", - set_parts_list[i].set, uc, set_parts_list[i].pcode); + set_parts_list[i].set, + (int)uc, (int)set_parts_list[i].pcode); error++; } if(!uc) { @@ -1724,7 +1725,7 @@ static int set_parts(void) rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else if(checkurl(set_parts_list[i].in, url, set_parts_list[i].out)) { @@ -1735,7 +1736,8 @@ static int set_parts(void) } else if(rc != set_parts_list[i].ucode) { curl_mfprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n", - set_parts_list[i].in, rc, set_parts_list[i].ucode); + set_parts_list[i].in, + (int)rc, (int)set_parts_list[i].ucode); error++; } curl_url_cleanup(urlp); @@ -1763,7 +1765,7 @@ static int get_url(bool has_utf8) rc = curl_url_get(urlp, CURLUPART_URL, &url, get_url_list[i].getflags); if(rc) { curl_mfprintf(stderr, "%s:%d returned %d (%s). URL: '%s'\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc), + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc), get_url_list[i].in); error++; } @@ -1774,7 +1776,7 @@ static int get_url(bool has_utf8) } if(rc != get_url_list[i].ucode) { curl_mfprintf(stderr, "Get URL\nin: %s\nreturned %d (expected %d)\n", - get_url_list[i].in, rc, get_url_list[i].ucode); + get_url_list[i].in, (int)rc, (int)get_url_list[i].ucode); error++; } } @@ -1801,7 +1803,8 @@ static int get_parts(bool has_utf8) get_parts_list[i].urlflags); if(rc != get_parts_list[i].ucode) { curl_mfprintf(stderr, "Get parts\nin: %s\nreturned %d (expected %d)\n", - get_parts_list[i].in, rc, get_parts_list[i].ucode); + get_parts_list[i].in, + (int)rc, (int)get_parts_list[i].ucode); error++; } else if(get_parts_list[i].ucode) { @@ -1858,7 +1861,7 @@ static int append(void) ; else if(rc != append_list[i].ucode) { curl_mfprintf(stderr, "Append\nin: %s\nreturned %d (expected %d)\n", - append_list[i].in, rc, append_list[i].ucode); + append_list[i].in, (int)rc, (int)append_list[i].ucode); error++; } else if(append_list[i].ucode) { @@ -1869,7 +1872,7 @@ static int append(void) rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1897,7 +1900,7 @@ static int scopeid(void) "https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0); if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1905,7 +1908,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1916,7 +1919,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1924,7 +1927,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1935,7 +1938,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1943,7 +1946,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1954,7 +1957,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1962,7 +1965,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1973,7 +1976,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1984,7 +1987,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_ZONEID returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1995,7 +1998,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_ZONEID returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -2003,7 +2006,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -2083,7 +2086,7 @@ static int get_nothing(void) rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0); if(rc != CURLUE_NO_ZONEID) { - curl_mfprintf(stderr, "unexpected return code %d on line %d\n", rc, + curl_mfprintf(stderr, "unexpected return code %d on line %d\n", (int)rc, __LINE__); error++; curl_free(p); @@ -2196,7 +2199,7 @@ static int huge(void) if(!rc) { curl_url_get(urlp, part[i], &partp, 0); if(!partp || strcmp(partp, &bigpart[1 - (i == 4)])) { - curl_mprintf("URL %d part %u: failure\n", i, part[i]); + curl_mprintf("URL %d part %d: failure\n", i, (int)part[i]); error++; } curl_free(partp); diff --git a/tests/libtest/lib1565.c b/tests/libtest/lib1565.c index c3fedd6e217f..825d88e2ed4d 100644 --- a/tests/libtest/lib1565.c +++ b/tests/libtest/lib1565.c @@ -130,7 +130,7 @@ static CURLcode test_lib1565(const char *URL) else { curl_mfprintf(stderr, "%s:%d Got an unexpected message from curl: %d\n", - __FILE__, __LINE__, message->msg); + __FILE__, __LINE__, (int)message->msg); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1597.c b/tests/libtest/lib1597.c index 5e3fab8cade0..11da472c4d8f 100644 --- a/tests/libtest/lib1597.c +++ b/tests/libtest/lib1597.c @@ -96,7 +96,8 @@ static CURLcode test_lib1597(const char *URL) for(i = 0; prots[i].in; i++) { result = curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, prots[i].in); if(result != *prots[i].result_exp) { - curl_mprintf("unexpectedly '%s' returned %d\n", prots[i].in, result); + curl_mprintf("unexpectedly '%s' returned %d\n", prots[i].in, + (int)result); break; } } diff --git a/tests/libtest/lib1906.c b/tests/libtest/lib1906.c index 5ac2105dfe45..305b7d764604 100644 --- a/tests/libtest/lib1906.c +++ b/tests/libtest/lib1906.c @@ -45,7 +45,7 @@ static CURLcode test_lib1906(const char *URL) if(result != CURLE_COULDNT_CONNECT && result != CURLE_OPERATION_TIMEDOUT) { curl_mfprintf(stderr, "failure expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); if(result == CURLE_OK) result = TEST_ERR_MAJOR_BAD; /* force an error return */ goto test_cleanup; @@ -65,7 +65,7 @@ static CURLcode test_lib1906(const char *URL) if(result) curl_mfprintf(stderr, "success expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); /* print URL */ curl_url_get(curlu, CURLUPART_URL, &url_after, 0); diff --git a/tests/libtest/lib1907.c b/tests/libtest/lib1907.c index 6aac37515225..3b91d846f739 100644 --- a/tests/libtest/lib1907.c +++ b/tests/libtest/lib1907.c @@ -39,7 +39,7 @@ static CURLcode test_lib1907(const char *URL) if(!result) curl_mfprintf(stderr, "failure expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); /* print the used URL */ if(!curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url_after)) diff --git a/tests/libtest/lib1911.c b/tests/libtest/lib1911.c index a463b0fa1668..662093a61fec 100644 --- a/tests/libtest/lib1911.c +++ b/tests/libtest/lib1911.c @@ -78,7 +78,7 @@ static CURLcode test_lib1911(const char *URL) default: /* all other return codes are unexpected */ curl_mfprintf(stderr, "curl_easy_setopt(%s...) returned %d\n", - o->name, result); + o->name, (int)result); error++; break; } diff --git a/tests/libtest/lib1915.c b/tests/libtest/lib1915.c index f838e7bdb381..eebf7ba755b1 100644 --- a/tests/libtest/lib1915.c +++ b/tests/libtest/lib1915.c @@ -124,7 +124,7 @@ static CURLcode test_lib1915(const char *URL) curl = NULL; if(result == CURLE_OPERATION_TIMEDOUT) /* we expect that on Windows */ result = CURLE_COULDNT_CONNECT; - curl_mprintf("First request returned %d\n", result); + curl_mprintf("First request returned %d\n", (int)result); result = CURLE_OK; easy_init(curl); @@ -141,7 +141,7 @@ static CURLcode test_lib1915(const char *URL) result = curl_easy_perform(curl); curl_easy_cleanup(curl); curl = NULL; - curl_mprintf("Second request returned %d\n", result); + curl_mprintf("Second request returned %d\n", (int)result); test_cleanup: curl_easy_cleanup(curl); diff --git a/tests/libtest/lib1916.c b/tests/libtest/lib1916.c index a67b68fc9cd5..575080d09ff0 100644 --- a/tests/libtest/lib1916.c +++ b/tests/libtest/lib1916.c @@ -45,7 +45,7 @@ static CURLcode test_lib1916(const char *URL) } result = curl_easy_perform(curl); if(result) { - curl_mprintf("result: %d\n", result); + curl_mprintf("result: %d\n", (int)result); } curl_easy_cleanup(curl); } diff --git a/tests/libtest/lib1918.c b/tests/libtest/lib1918.c index 1c8ccb78f984..4932c1cfa6bb 100644 --- a/tests/libtest/lib1918.c +++ b/tests/libtest/lib1918.c @@ -38,11 +38,11 @@ static CURLcode test_lib1918(const char *URL) if(ename->id != o->id) { curl_mprintf("name lookup id %d does not match %d\n", - ename->id, o->id); + (int)ename->id, (int)o->id); } else if(eid->id != o->id) { curl_mprintf("ID lookup %d does not match %d\n", - ename->id, o->id); + (int)ename->id, (int)o->id); } } curl_global_cleanup(); diff --git a/tests/libtest/lib1922.c b/tests/libtest/lib1922.c index fffb6d4fcc6d..f53b773df61b 100644 --- a/tests/libtest/lib1922.c +++ b/tests/libtest/lib1922.c @@ -82,7 +82,7 @@ static CURLcode test_lib1922(const char *URL) result = curl_easy_perform(curl); if(result) { curl_mfprintf(stderr, "First perform failed: %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } curl_mprintf("First request: HTTPS cache populated\n"); @@ -104,7 +104,7 @@ static CURLcode test_lib1922(const char *URL) result = curl_easy_perform(dup); if(result != CURLE_COULDNT_CONNECT) { curl_mfprintf(stderr, "Dup perform unexpected result: %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib1945.c b/tests/libtest/lib1945.c index 0eb5a6873e77..555912bb9373 100644 --- a/tests/libtest/lib1945.c +++ b/tests/libtest/lib1945.c @@ -65,7 +65,7 @@ static CURLcode test_lib1945(const char *URL) } result = curl_easy_perform(curl); if(result) { - curl_mprintf("badness: %d\n", result); + curl_mprintf("badness: %d\n", (int)result); } t1945_showem(curl, CURLH_CONNECT | CURLH_HEADER | CURLH_TRAILER | CURLH_1XX); diff --git a/tests/libtest/lib2032.c b/tests/libtest/lib2032.c index e87eb5ecad37..6e9570d1ef0a 100644 --- a/tests/libtest/lib2032.c +++ b/tests/libtest/lib2032.c @@ -47,7 +47,7 @@ static size_t callback(char *ptr, size_t size, size_t nmemb, void *data) if(result != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); ntlmcb_res = TEST_ERR_MAJOR_BAD; return failure; } @@ -153,7 +153,7 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ multi_perform(multi, &running); curl_mfprintf(stderr, "%s:%d running %d state %d\n", - __FILE__, __LINE__, running, state); + __FILE__, __LINE__, running, (int)state); abort_on_test_timeout(); @@ -177,7 +177,8 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ } state = num_handles < MAX_EASY_HANDLES ? ReadyForNewHandle : NoMoreHandles; - curl_mfprintf(stderr, "%s:%d new state %d\n", __FILE__, __LINE__, state); + curl_mfprintf(stderr, "%s:%d new state %d\n", + __FILE__, __LINE__, (int)state); } multi_timeout(multi, &timeout); diff --git a/tests/libtest/lib2082.c b/tests/libtest/lib2082.c index 87d0f8eada4b..8a404f232740 100644 --- a/tests/libtest/lib2082.c +++ b/tests/libtest/lib2082.c @@ -88,7 +88,8 @@ static CURLcode test_lib2082(const char *URL) /* libprereq */ if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } } diff --git a/tests/libtest/lib2301.c b/tests/libtest/lib2301.c index dece3553a7d5..0af579231e47 100644 --- a/tests/libtest/lib2301.c +++ b/tests/libtest/lib2301.c @@ -86,7 +86,7 @@ static CURLcode test_lib2301(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t2301_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl); result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); #if 0 if(result == CURLE_OK) t2301_websocket(curl); diff --git a/tests/libtest/lib2302.c b/tests/libtest/lib2302.c index 4c96755d8fcf..759ea5eafd24 100644 --- a/tests/libtest/lib2302.c +++ b/tests/libtest/lib2302.c @@ -48,7 +48,7 @@ static void flush_data(struct ws_data *wd) curl_mprintf("\n"); if(wd->has_meta) - curl_mprintf("RECFLAGS: %x\n", wd->meta_flags); + curl_mprintf("RECFLAGS: %x\n", (unsigned int)wd->meta_flags); else curl_mfprintf(stderr, "RECFLAGS: NULL\n"); wd->blen = 0; @@ -115,7 +115,7 @@ static CURLcode test_lib2302(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t2302_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ws_data); result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); /* always cleanup */ curl_easy_cleanup(curl); flush_data(&ws_data); diff --git a/tests/libtest/lib2304.c b/tests/libtest/lib2304.c index 013a4de929f3..e4055315a7d4 100644 --- a/tests/libtest/lib2304.c +++ b/tests/libtest/lib2304.c @@ -34,7 +34,7 @@ static CURLcode recv_any(CURL *curl) return result; curl_mfprintf(stderr, "recv_any: got %zu bytes rflags %x\n", rlen, - meta->flags); + (unsigned int)meta->flags); return CURLE_OK; } @@ -75,7 +75,7 @@ static CURLcode test_lib2304(const char *URL) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result == CURLE_OK) t2304_websocket(curl); diff --git a/tests/libtest/lib2308.c b/tests/libtest/lib2308.c index 40d77019d1e9..d582c7135c86 100644 --- a/tests/libtest/lib2308.c +++ b/tests/libtest/lib2308.c @@ -42,7 +42,7 @@ static CURLcode test_lib2308(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_curl); curl_easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); - curl_mprintf("Returned %d, should be %d.\n", result, CURLE_WRITE_ERROR); + curl_mprintf("Returned %d, should be %d.\n", (int)result, CURLE_WRITE_ERROR); fflush(stdout); curl_easy_cleanup(curl); curl_global_cleanup(); diff --git a/tests/libtest/lib2309.c b/tests/libtest/lib2309.c index c58b7ebbb4c6..212d82aa162d 100644 --- a/tests/libtest/lib2309.c +++ b/tests/libtest/lib2309.c @@ -51,7 +51,8 @@ static CURLcode test_lib2309(const char *URL) curldupe = curl_easy_duphandle(curl); if(curldupe) { result = curl_easy_perform(curldupe); - curl_mprintf("Returned %d, should be %d.\n", result, CURLE_WRITE_ERROR); + curl_mprintf("Returned %d, should be %d.\n", (int)result, + CURLE_WRITE_ERROR); fflush(stdout); curl_easy_cleanup(curldupe); } diff --git a/tests/libtest/lib2405.c b/tests/libtest/lib2405.c index a0c9a91755b9..143e8b8a060f 100644 --- a/tests/libtest/lib2405.c +++ b/tests/libtest/lib2405.c @@ -43,7 +43,7 @@ #define test_check(expected_fds) \ if(result != CURLE_OK) { \ - curl_mfprintf(stderr, "test failed with code: %d\n", result); \ + curl_mfprintf(stderr, "test failed with code: %d\n", (int)result); \ goto test_cleanup; \ } \ else if(fd_count != (expected_fds)) { \ diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index 04c39c1e3535..c2bb39e22196 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -57,7 +57,7 @@ static CURLcode send_header(CURL *curl, int flags, size_t size) } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nsent == 0); return result; } @@ -86,7 +86,7 @@ static CURLcode recv_header(CURL *curl, int *flags, curl_off_t *offset, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nread == 0); return result; } @@ -128,7 +128,7 @@ static CURLcode send_chunk(CURL *curl, int flags, const char *buffer, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nsent == 0); return result; } @@ -157,7 +157,7 @@ static CURLcode recv_chunk(CURL *curl, int flags, curl_off_t *offset, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nread == 0); return result; } @@ -234,7 +234,7 @@ static CURLcode test_lib2700(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib3010.c b/tests/libtest/lib3010.c index 7a668b075133..7552b021ec77 100644 --- a/tests/libtest/lib3010.c +++ b/tests/libtest/lib3010.c @@ -39,7 +39,8 @@ static CURLcode test_lib3010(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &follow_url); @@ -50,7 +51,8 @@ static CURLcode test_lib3010(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib3026.c b/tests/libtest/lib3026.c index fb56da7ef846..f072dee282fb 100644 --- a/tests/libtest/lib3026.c +++ b/tests/libtest/lib3026.c @@ -76,7 +76,7 @@ static CURLcode test_lib3026(const char *URL) if(results[i] != CURLE_OK) { curl_mfprintf(stderr, "%s:%d thread[%u]: curl_global_init() failed," "with code %d (%s)\n", __FILE__, __LINE__, - i, results[i], curl_easy_strerror(results[i])); + i, (int)results[i], curl_easy_strerror(results[i])); result = TEST_ERR_MAJOR_BAD; } } @@ -133,7 +133,7 @@ static CURLcode test_lib3026(const char *URL) if(results[i] != CURLE_OK) { curl_mfprintf(stderr, "%s:%d thread[%u]: curl_global_init() failed," "with code %d (%s)\n", __FILE__, __LINE__, - i, results[i], curl_easy_strerror(results[i])); + i, (int)results[i], curl_easy_strerror(results[i])); result = TEST_ERR_MAJOR_BAD; } } diff --git a/tests/libtest/lib3033.c b/tests/libtest/lib3033.c index 7fa4731badef..f43c1696ca36 100644 --- a/tests/libtest/lib3033.c +++ b/tests/libtest/lib3033.c @@ -69,7 +69,8 @@ static CURLcode t3033_req_test(CURLM *multi, CURL *curl, result = msg->data.result; if(result != CURLE_OK) { - curl_mfprintf(stderr, "curl_multi_info_read() returned %d\n", result); + curl_mfprintf(stderr, "curl_multi_info_read() returned %d\n", + (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib3034.c b/tests/libtest/lib3034.c index 5c693c5ce854..6a5444085cfb 100644 --- a/tests/libtest/lib3034.c +++ b/tests/libtest/lib3034.c @@ -57,7 +57,7 @@ static CURLcode test_lib3034(const char *URL) if(result != CURLE_SEND_FAIL_REWIND) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib3100.c b/tests/libtest/lib3100.c index 8d7988af0e49..4d3badb31db0 100644 --- a/tests/libtest/lib3100.c +++ b/tests/libtest/lib3100.c @@ -54,7 +54,7 @@ static CURLcode test_lib3100(const char *URL) result = curl_easy_perform(curl); if(result != CURLE_OK) { - curl_mfprintf(stderr, "Failed to send DESCRIBE: %d\n", result); + curl_mfprintf(stderr, "Failed to send DESCRIBE: %d\n", (int)result); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib506.c b/tests/libtest/lib506.c index 0f6d75c9799d..73b282dd2e52 100644 --- a/tests/libtest/lib506.c +++ b/tests/libtest/lib506.c @@ -70,7 +70,7 @@ static void t506_test_lock(CURL *curl, curl_lock_data data, pcounter = &user->cookie_counter; break; default: - curl_mfprintf(stderr, "lock: no such data: %d\n", data); + curl_mfprintf(stderr, "lock: no such data: %d\n", (int)data); return; } @@ -109,7 +109,7 @@ static void t506_test_unlock(CURL *curl, curl_lock_data data, void *useptr) locknum = 2; break; default: - curl_mfprintf(stderr, "unlock: no such data: %d\n", data); + curl_mfprintf(stderr, "unlock: no such data: %d\n", (int)data); return; } @@ -158,7 +158,7 @@ static void *t506_test_fire(void *ptr) if(result) { int i = 0; curl_mfprintf(stderr, "perform URL '%s' repeat %d failed, curlcode %d\n", - tdata->url, i, result); + tdata->url, i, (int)result); } curl_mprintf("CLEANUP\n"); @@ -369,7 +369,8 @@ static CURLcode test_lib506(const char *URL) curl_mprintf("SHARE_CLEANUP\n"); scode = curl_share_cleanup(share); if(scode != CURLSHE_OK) - curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", scode); + curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", + (int)scode); curl_mprintf("GLOBAL_CLEANUP\n"); curl_global_cleanup(); diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index e18a0191abed..d4c894d1d083 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -204,7 +204,7 @@ static int t530_checkForCompletion(CURLM *multi, int *success) } else { curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", - t530_tag(), message->msg); + t530_tag(), (int)message->msg); result = 1; *success = 0; } @@ -394,23 +394,23 @@ static CURLcode test_lib530(const char *URL) callback calls */ result = testone(URL, 0, 0); /* no callback fails */ if(result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 1, 0); /* fail 1st call to timer callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 2, 0); /* fail 2nd call to timer callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 0, 2); /* fail 2nd call to socket callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 0, 3); /* fail 3rd call to socket callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); return CURLE_OK; } diff --git a/tests/libtest/lib540.c b/tests/libtest/lib540.c index b8f2d3d8a486..ec9d3da7c95b 100644 --- a/tests/libtest/lib540.c +++ b/tests/libtest/lib540.c @@ -158,7 +158,7 @@ static CURLcode loop(int num, CURLM *multi, const char *url, if(msg->msg == CURLMSG_DONE) { size_t i; CURL *curl = msg->easy_handle; - curl_mfprintf(stderr, "R: %d - %s\n", msg->data.result, + curl_mfprintf(stderr, "R: %d - %s\n", (int)msg->data.result, curl_easy_strerror(msg->data.result)); curl_multi_remove_handle(multi, curl); curl_easy_cleanup(curl); @@ -170,7 +170,7 @@ static CURLcode loop(int num, CURLM *multi, const char *url, } } else - curl_mfprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); + curl_mfprintf(stderr, "E: CURLMsg (%d)\n", (int)msg->msg); } res_test_timedout(); diff --git a/tests/libtest/lib554.c b/tests/libtest/lib554.c index 7b882959ddaa..458d9bcb6de8 100644 --- a/tests/libtest/lib554.c +++ b/tests/libtest/lib554.c @@ -93,7 +93,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) } if(formrc) - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); /* Now add the same data with another name and make it not look like a file upload but still using the callback */ @@ -110,7 +110,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(2) = %d\n", formrc); + curl_mprintf("curl_formadd(2) = %d\n", (int)formrc); /* Fill in the filename field */ formrc = curl_formadd(&formpost, @@ -119,7 +119,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_COPYCONTENTS, "postit2.c", CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(3) = %d\n", formrc); + curl_mprintf("curl_formadd(3) = %d\n", (int)formrc); /* Fill in a submit field too */ formrc = curl_formadd(&formpost, @@ -130,7 +130,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(4) = %d\n", formrc); + curl_mprintf("curl_formadd(4) = %d\n", (int)formrc); formrc = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "somename", @@ -140,7 +140,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(5) = %d\n", formrc); + curl_mprintf("curl_formadd(5) = %d\n", (int)formrc); curl = curl_easy_init(); if(!curl) { diff --git a/tests/libtest/lib574.c b/tests/libtest/lib574.c index 6f1f061ee2b2..0a03d1e954a8 100644 --- a/tests/libtest/lib574.c +++ b/tests/libtest/lib574.c @@ -55,12 +55,12 @@ static CURLcode test_lib574(const char *URL) result = curl_easy_perform(curl); if(result) { - curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", (int)result); goto test_cleanup; } result = curl_easy_perform(curl); if(result) { - curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib582.c b/tests/libtest/lib582.c index 408053ebc8d6..4b3f042852ec 100644 --- a/tests/libtest/lib582.c +++ b/tests/libtest/lib582.c @@ -151,7 +151,7 @@ static int t582_checkForCompletion(CURLM *multi, int *success) } else { curl_mfprintf(stderr, "Got an unexpected message from curl: %d\n", - message->msg); + (int)message->msg); result = 1; *success = 0; } diff --git a/tests/libtest/lib586.c b/tests/libtest/lib586.c index 8609a1d16907..8cb1652ad78b 100644 --- a/tests/libtest/lib586.c +++ b/tests/libtest/lib586.c @@ -60,7 +60,7 @@ static void t586_test_lock(CURL *curl, curl_lock_data data, what = "ssl_session"; break; default: - curl_mfprintf(stderr, "lock: no such data: %d\n", data); + curl_mfprintf(stderr, "lock: no such data: %d\n", (int)data); return; } curl_mprintf("lock: %-6s [%s]: %d\n", what, user->text, user->counter); @@ -87,7 +87,7 @@ static void t586_test_unlock(CURL *curl, curl_lock_data data, void *useptr) what = "ssl_session"; break; default: - curl_mfprintf(stderr, "unlock: no such data: %d\n", data); + curl_mfprintf(stderr, "unlock: no such data: %d\n", (int)data); return; } curl_mprintf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter); @@ -118,7 +118,7 @@ static void *t586_test_fire(void *ptr) if(result != CURLE_OK) { int i = 0; curl_mfprintf(stderr, "perform URL '%s' repeat %d failed, curlcode %d\n", - tdata->url, i, result); + tdata->url, i, (int)result); } curl_mprintf("CLEANUP\n"); @@ -231,7 +231,8 @@ static CURLcode test_lib586(const char *URL) curl_mprintf("SHARE_CLEANUP\n"); scode = curl_share_cleanup(share); if(scode != CURLSHE_OK) - curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", scode); + curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", + (int)scode); curl_mprintf("GLOBAL_CLEANUP\n"); curl_global_cleanup(); diff --git a/tests/libtest/lib650.c b/tests/libtest/lib650.c index 0d3c41998693..8909a7baf424 100644 --- a/tests/libtest/lib650.c +++ b/tests/libtest/lib650.c @@ -79,7 +79,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_CONTENTHEADER, headers, CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); goto test_cleanup; } @@ -101,7 +101,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(2) = %d\n", formrc); + curl_mprintf("curl_formadd(2) = %d\n", (int)formrc); goto test_cleanup; } @@ -121,7 +121,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(3) = %d\n", formrc); + curl_mprintf("curl_formadd(3) = %d\n", (int)formrc); goto test_cleanup; } @@ -132,7 +132,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_FILECONTENT, libtest_arg2, CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(4) = %d\n", formrc); + curl_mprintf("curl_formadd(4) = %d\n", (int)formrc); goto test_cleanup; } @@ -152,7 +152,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(5) = %d\n", formrc); + curl_mprintf("curl_formadd(5) = %d\n", (int)formrc); goto test_cleanup; } @@ -164,7 +164,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(6) = %d\n", formrc); + curl_mprintf("curl_formadd(6) = %d\n", (int)formrc); goto test_cleanup; } diff --git a/tests/libtest/lib651.c b/tests/libtest/lib651.c index 784e17f02758..a905a22d2b56 100644 --- a/tests/libtest/lib651.c +++ b/tests/libtest/lib651.c @@ -53,7 +53,7 @@ static CURLcode test_lib651(const char *URL) CURLFORM_COPYCONTENTS, testbuf, CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); curl = curl_easy_init(); if(!curl) { diff --git a/tests/libtest/lib655.c b/tests/libtest/lib655.c index e74d2de17d6d..73c634f0be2f 100644 --- a/tests/libtest/lib655.c +++ b/tests/libtest/lib655.c @@ -91,7 +91,7 @@ static CURLcode test_lib655(const char *URL) if(result != CURLE_ABORTED_BY_CALLBACK) { curl_mfprintf(stderr, "curl_easy_perform should have returned " "CURLE_ABORTED_BY_CALLBACK but instead returned error %d\n", - result); + (int)result); if(result == CURLE_OK) result = TEST_ERR_FAILURE; goto test_cleanup; diff --git a/tests/libtest/lib658.c b/tests/libtest/lib658.c index 3d9b4575d460..8ed465715fea 100644 --- a/tests/libtest/lib658.c +++ b/tests/libtest/lib658.c @@ -61,8 +61,8 @@ static CURLcode test_lib658(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " - "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + "with code %d (%s)\n", __FILE__, __LINE__, + (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib659.c b/tests/libtest/lib659.c index d1a78e206dbf..c2fb30d4ea24 100644 --- a/tests/libtest/lib659.c +++ b/tests/libtest/lib659.c @@ -60,7 +60,7 @@ static CURLcode test_lib659(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib661.c b/tests/libtest/lib661.c index 12cb4b7250fd..85fb92f66563 100644 --- a/tests/libtest/lib661.c +++ b/tests/libtest/lib661.c @@ -156,7 +156,7 @@ static CURLcode test_lib661(const char *URL) test_cleanup: if(result) - curl_mfprintf(stderr, "test encountered error %d\n", result); + curl_mfprintf(stderr, "test encountered error %d\n", (int)result); curl_slist_free_all(slist); curl_free(newURL); curl_easy_cleanup(curl); diff --git a/tests/libtest/lib670.c b/tests/libtest/lib670.c index 037ca9c30a35..fb46697a4549 100644 --- a/tests/libtest/lib670.c +++ b/tests/libtest/lib670.c @@ -122,7 +122,7 @@ static CURLcode test_lib670(const char *URL) if(result != CURLE_OK) { curl_mfprintf(stderr, "Something went wrong when building the " - "mime structure: %d\n", result); + "mime structure: %d\n", (int)result); goto test_cleanup; } @@ -143,7 +143,7 @@ static CURLcode test_lib670(const char *URL) CURLFORM_CONTENTLEN, (curl_off_t)2, CURLFORM_END); if(formrc) { - curl_mfprintf(stderr, "curl_formadd() = %d\n", formrc); + curl_mfprintf(stderr, "curl_formadd() = %d\n", (int)formrc); goto test_cleanup; } diff --git a/tests/libtest/lib674.c b/tests/libtest/lib674.c index 11fb8be84922..761ee3e16f7c 100644 --- a/tests/libtest/lib674.c +++ b/tests/libtest/lib674.c @@ -62,7 +62,7 @@ static CURLcode test_lib674(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib677.c b/tests/libtest/lib677.c index fdb7294caaa0..106fbb26034f 100644 --- a/tests/libtest/lib677.c +++ b/tests/libtest/lib677.c @@ -84,7 +84,7 @@ static CURLcode test_lib677(const char *URL) } else if(ec) { curl_mfprintf(stderr, "curl_easy_send() failed, with code %d (%s)\n", - ec, curl_easy_strerror(ec)); + (int)ec, curl_easy_strerror(ec)); result = ec; goto test_cleanup; } @@ -105,7 +105,7 @@ static CURLcode test_lib677(const char *URL) } else if(ec) { curl_mfprintf(stderr, "curl_easy_recv() failed, with code %d (%s)\n", - ec, curl_easy_strerror(ec)); + (int)ec, curl_easy_strerror(ec)); result = ec; goto test_cleanup; } diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index 0497754109a0..174495a52d88 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -250,7 +250,7 @@ static int t758_checkForCompletion(CURLM *multi, int *success) } else { curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", - t758_tag(), message->msg); + t758_tag(), (int)message->msg); result = 1; *success = 0; } @@ -491,7 +491,7 @@ static CURLcode test_lib758(const char *URL) callback calls */ result = t758_one(URL, 0, 0); /* no callback fails */ if(result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t758_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t758_tag(), (int)result); return result; } diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl index 2e7a01835ab6..71c80b0039af 100755 --- a/tests/libtest/mk-lib1521.pl +++ b/tests/libtest/mk-lib1521.pl @@ -244,20 +244,20 @@ static void errlongzero(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to 0 returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errlong(const char *name, CURLcode result, int lineno) { $allowednumerrors curl_mprintf("%s set to non-zero returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errneg(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to -1 returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errstring(const char *name, CURLcode result, int lineno) @@ -266,25 +266,25 @@ when given a strange string input */ $allowedstringerrors curl_mprintf("%s set to a string returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void err(const char *name, CURLcode result, int lineno) { curl_mprintf("%s returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errnull(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to NULL returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void t1521_geterr(const char *name, CURLcode result, int lineno) { curl_mprintf("CURLINFO_%s returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static curl_progress_callback progresscb; diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 8726897fd66a..a2241291fdaf 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -492,7 +492,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, const unsigned char *store = ipv4_pref; if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A)) goto error; - logmsg("[%d] response A (%x) '%s'", qid, QTYPE_A, + logmsg("[%d] response A (%x) '%s'", qid, (unsigned int)QTYPE_A, curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf))); } if(!ancount_a) @@ -503,7 +503,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, const unsigned char *store = ipv6_pref; if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA)) goto error; - logmsg("[%d] response AAAA (%x) '%s'", qid, QTYPE_AAAA, + logmsg("[%d] response AAAA (%x) '%s'", qid, (unsigned int)QTYPE_AAAA, curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf))); } if(!ancount_aaaa) @@ -516,8 +516,8 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, httpsrr.dlen); goto error; } - logmsg("[%d] response HTTPS (%x), %zu bytes", qid, QTYPE_HTTPS, - httpsrr.dlen); + logmsg("[%d] response HTTPS (%x), %zu bytes", qid, + (unsigned int)QTYPE_HTTPS, httpsrr.dlen); } else logmsg("[%d] response HTTPS, no record", qid); diff --git a/tests/server/sws.c b/tests/server/sws.c index db32c211c670..f219bb15abfc 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -1794,7 +1794,7 @@ static void http_connect(curl_socket_t *infdp, static void http_upgrade(struct sws_httprequest *req) { (void)req; - logmsg("Upgraded to ... %u", req->upgrade_request); + logmsg("Upgraded to ... %d", (int)req->upgrade_request); /* left to implement */ } diff --git a/tests/tunit/tool1623.c b/tests/tunit/tool1623.c index a79ccc97c4bc..e15aab89047b 100644 --- a/tests/tunit/tool1623.c +++ b/tests/tunit/tool1623.c @@ -104,14 +104,14 @@ static CURLcode test_tool1623(const char *arg) ParameterError err = GetSizeParameter(check[i].input, &output); if(err != check[i].err) curl_mprintf("'%s' unexpectedly returned %d \n", - check[i].input, err); + check[i].input, (int)err); else if(check[i].amount != output) curl_mprintf("'%s' unexpectedly gave %" FMT_OFF_T "\n", check[i].input, output); else { #if 0 /* enable for debugging */ if(err) - curl_mprintf("'%s' returned %d\n", check[i].input, err); + curl_mprintf("'%s' returned %d\n", check[i].input, (int)err); else curl_mprintf("'%s' == %" FMT_OFF_T "\n", check[i].input, output); #endif diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index f634cb4af2c7..4c58d8330c37 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -155,7 +155,7 @@ static CURLcode test_unit1650(const char *arg) buffer, sizeof(buffer), &size); if(rc != req[i].rc) { curl_mfprintf(stderr, "req %zu: Expected return code %d got %d\n", i, - req[i].rc, rc); + (int)req[i].rc, (int)rc); abort_if(rc != req[i].rc, "return code"); } if(size != req[i].size) { @@ -184,7 +184,7 @@ static CURLcode test_unit1650(const char *arg) resp[i].type, &d); if(rc != resp[i].rc) { curl_mfprintf(stderr, "resp %zu: Expected return code %d got %d\n", i, - resp[i].rc, rc); + (int)resp[i].rc, (int)rc); abort_if(rc != resp[i].rc, "return code"); } len = sizeof(buffer); @@ -240,7 +240,7 @@ static CURLcode test_unit1650(const char *arg) i, CURL_DNS_TYPE_A, &d); if(!rc) { /* none of them should work */ - curl_mfprintf(stderr, "%zu: %d\n", i, rc); + curl_mfprintf(stderr, "%zu: %d\n", i, (int)rc); abort_if(!rc, "error rc"); } } @@ -254,7 +254,7 @@ static CURLcode test_unit1650(const char *arg) sizeof(full49) - i - 1, CURL_DNS_TYPE_A, &d); if(!rc) { /* none of them should work */ - curl_mfprintf(stderr, "2 %zu: %d\n", i, rc); + curl_mfprintf(stderr, "2 %zu: %d\n", i, (int)rc); abort_if(!rc, "error rc"); } } @@ -272,7 +272,8 @@ static CURLcode test_unit1650(const char *arg) curl_msnprintf((char *)buffer, sizeof(buffer), "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); if(rc || strcmp((const char *)buffer, "127.0.0.1")) { - curl_mfprintf(stderr, "bad address decoded: %s, rc == %d\n", buffer, rc); + curl_mfprintf(stderr, "bad address decoded: %s, rc == %d\n", buffer, + (int)rc); abort_if(rc || strcmp((const char *)buffer, "127.0.0.1"), "bad address"); } fail_if(d.numcname, "bad cname counter"); diff --git a/tests/unit/unit1656.c b/tests/unit/unit1656.c index 11e17d962da2..4dea282c9e60 100644 --- a/tests/unit/unit1656.c +++ b/tests/unit/unit1656.c @@ -42,7 +42,7 @@ static bool do_test(const struct test_spec *spec, size_t i, result = GTime2str(dbuf, in, in + strlen(in)); if(result != spec->result_exp) { curl_mfprintf(stderr, "test %zu: expect result %d, got %d\n", - i, spec->result_exp, result); + i, (int)spec->result_exp, (int)result); return FALSE; } else if(!result && strcmp(spec->exp_output, curlx_dyn_ptr(dbuf))) { diff --git a/tests/unit/unit1657.c b/tests/unit/unit1657.c index 6fa6d9e9ff27..79f026bebad6 100644 --- a/tests/unit/unit1657.c +++ b/tests/unit/unit1657.c @@ -72,7 +72,7 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i, curlx_dyn_reset(buf); result = spec->setbuf(spec, buf); if(result) { - curl_mfprintf(stderr, "test %zu: error setting buf %d\n", i, result); + curl_mfprintf(stderr, "test %zu: error setting buf %d\n", i, (int)result); return FALSE; } in = curlx_dyn_ptr(buf); @@ -80,7 +80,7 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i, result = ptr ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; if(result != spec->result_exp) { curl_mfprintf(stderr, "test %zu: expect result %d, got %d\n", - i, spec->result_exp, result); + i, (int)spec->result_exp, (int)result); return FALSE; } return TRUE; diff --git a/tests/unit/unit1660.c b/tests/unit/unit1660.c index f9259f1c764b..f2d72705d5d4 100644 --- a/tests/unit/unit1660.c +++ b/tests/unit/unit1660.c @@ -127,7 +127,7 @@ static CURLcode test_unit1660(const char *arg) if(result != headers[i].result) { curl_mfprintf(stderr, "Curl_hsts_parse(%s) failed: %d\n", - headers[i].hdr, result); + headers[i].hdr, (int)result); unitfail++; continue; } diff --git a/tests/unit/unit1664.c b/tests/unit/unit1664.c index b2f1f07b40b7..17a04a37f244 100644 --- a/tests/unit/unit1664.c +++ b/tests/unit/unit1664.c @@ -367,7 +367,7 @@ static CURLcode test_unit1664(const char *arg) const char *orgline = line; int rc = curlx_str_newline(&line); curl_mprintf("%d: (%%%02x) %d, line %d\n", - i, *orgline, rc, (int)(line - orgline)); + i, (unsigned int)*orgline, rc, (int)(line - orgline)); } } diff --git a/tests/unit/unit1666.c b/tests/unit/unit1666.c index 362b16b4d548..c2f20c977a58 100644 --- a/tests/unit/unit1666.c +++ b/tests/unit/unit1666.c @@ -49,7 +49,7 @@ static bool test1666(const struct test_1666 *spec, size_t i, result = encodeOID(dbuf, oid, oid + spec->size); if(result != spec->result_exp) { curl_mfprintf(stderr, "test %zu: expect result %d, got %d\n", - i, spec->result_exp, result); + i, (int)spec->result_exp, (int)result); if(!spec->result_exp) { curl_mfprintf(stderr, "test %zu: expected output '%s'\n", i, spec->dotted); diff --git a/tests/unit/unit1667.c b/tests/unit/unit1667.c index bcbf632a7eb2..345bd052cf76 100644 --- a/tests/unit/unit1667.c +++ b/tests/unit/unit1667.c @@ -57,7 +57,7 @@ static bool test1667(const struct test_1667 *spec, size_t i, result = ASN1tostr(dbuf, &elem); if(result != spec->result_exp) { curl_mfprintf(stderr, "test %zu (type %u): expect result %d, got %d\n", - i, spec->tag, spec->result_exp, result); + i, spec->tag, (int)spec->result_exp, (int)result); if(!spec->result_exp) { curl_mfprintf(stderr, "test %zu: expected output '%s'\n", i, spec->out); diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index 29b76e61c8f6..7e869651d006 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -343,7 +343,7 @@ static CURLcode test_unit1675(const char *arg) uc = curl_url_set(base, CURLUPART_URL, tests[i].base, 0); if(uc) { curl_mfprintf(stderr, "failed to parse %u base %s -> %d\n", i, - tests[i].base, uc); + tests[i].base, (int)uc); fails++; goto loop_end; } @@ -358,7 +358,7 @@ static CURLcode test_unit1675(const char *arg) if(uc) { curl_mfprintf(stderr, "failed to parse %u href %s://%s:%s%s -> %d\n", i, tests[i].scheme, tests[i].host, tests[i].port, - tests[i].path, uc); + tests[i].path, (int)uc); fails++; goto loop_end; } @@ -425,7 +425,7 @@ static CURLcode test_unit1675(const char *arg) (u.options && tests[i].options && strcmp(u.options, tests[i].options)) || offset != tests[i].offset) { - curl_mfprintf(stderr, "%d: parse_hostname_login('%s') host failed:" + curl_mfprintf(stderr, "%u: parse_hostname_login('%s') host failed:" " expected '%d/%s/%s/%s/%zu', got '%d/%s/%s/%s/%zu'\n", i, tests[i].in, (int)tests[i].uc, tests[i].user, tests[i].password, tests[i].options, tests[i].offset, diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index e0eecf5b1395..689e1dbd0e83 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -263,7 +263,7 @@ static void check_result(const struct test_case *tc, struct test_result *tr) /* on CI we encounter the TIMEOUT result, since images get less CPU * and events are not as sharply timed. */ curl_msprintf(msg, "%d: expected result %d but got %d", - tc->id, tc->result_exp, tr->result); + tc->id, (int)tc->result_exp, (int)tr->result); fail(msg); } if(tr->cf4.creations != tc->exp_cf4_creations) { diff --git a/tests/unit/unit2603.c b/tests/unit/unit2603.c index 195f796a60ed..0198ccf7f4c2 100644 --- a/tests/unit/unit2603.c +++ b/tests/unit/unit2603.c @@ -76,7 +76,7 @@ static void parse_success(const struct tcase *t) result = Curl_h1_req_parse_read(&p, buf, buflen, t->default_scheme, t->custom_method, 0, &nread); if(result) { - curl_mfprintf(stderr, "got result %d parsing: '%s'\n", result, buf); + curl_mfprintf(stderr, "got result %d parsing: '%s'\n", (int)result, buf); fail("error consuming"); } in_consumed += nread; diff --git a/tests/unit/unit2604.c b/tests/unit/unit2604.c index 2143413713fd..770aec15be85 100644 --- a/tests/unit/unit2604.c +++ b/tests/unit/unit2604.c @@ -87,9 +87,9 @@ static CURLcode test_unit2604(const char *arg) const char *cp = i == 0 ? cp0 : list[i].cp; CURLcode result = Curl_get_pathname(&cp, &path, list[i].home); curl_mprintf("%d - Curl_get_pathname(\"%s\", ... \"%s\") == %d\n", i, - list[i].cp, list[i].home, list[i].result); + list[i].cp, list[i].home, (int)list[i].result); if(result != list[i].result) { - curl_mprintf("... returned %d\n", result); + curl_mprintf("... returned %d\n", (int)result); unitfail++; } if(!result) { diff --git a/tests/unit/unit2605.c b/tests/unit/unit2605.c index d26c8a1299e6..e3962a22e8ef 100644 --- a/tests/unit/unit2605.c +++ b/tests/unit/unit2605.c @@ -83,7 +83,7 @@ static CURLcode test_unit2605(const char *arg) result = Curl_ssh_range(curl, list[i].r, list[i].filesize, &start, &size); if(result != list[i].result) { - curl_mprintf("... returned %d\n", result); + curl_mprintf("... returned %d\n", (int)result); unitfail++; } if(!result) { From 97aed9c960a80fce1591efb5638a96be874a34fc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 10 Jun 2026 13:03:41 +0200 Subject: [PATCH 0374/1062] tidy-up: drop stray comparisons with literal zero Drop from: - strcmp, strcmpi, strncmp, memcmp, lstat, getrlimit, setrlimit, fseek, fstat - autotools detection snippets. - smooth-gtk-thread: simplify `!var != 0` expression. Closes #21947 --- docs/examples/anyauthput.c | 2 +- docs/examples/ephiperfifo.c | 2 +- docs/examples/evhiperfifo.c | 2 +- docs/examples/fileupload.c | 2 +- docs/examples/ftpupload.c | 2 +- docs/examples/ghiper.c | 2 +- docs/examples/hiperfifo.c | 2 +- docs/examples/http2-upload.c | 2 +- docs/examples/httpput.c | 2 +- docs/examples/smooth-gtk-thread.c | 2 +- docs/examples/synctime.c | 16 ++--- lib/cf-h2-proxy.c | 2 +- lib/cookie.c | 2 +- lib/curl_fnmatch.c | 20 +++--- lib/ftplistparser.c | 2 +- lib/http2.c | 2 +- lib/http_proxy.c | 2 +- lib/rtsp.c | 2 +- lib/vauth/digest.c | 2 +- lib/vauth/ntlm.c | 4 +- lib/vssh/libssh2.c | 6 +- lib/vtls/cipher_suite.c | 2 +- lib/vtls/gtls.c | 4 +- lib/vtls/mbedtls.c | 4 +- lib/vtls/schannel_verify.c | 2 +- lib/vtls/wolfssl.c | 2 +- m4/curl-functions.m4 | 108 +++++++++++++++--------------- src/tool_vms.c | 2 +- tests/libtest/first.c | 4 +- tests/libtest/lib1536.c | 2 +- tests/libtest/lib1560.c | 2 +- tests/libtest/lib3102.c | 2 +- tests/libtest/lib518.c | 8 +-- tests/libtest/lib537.c | 8 +-- tests/libtest/lib571.c | 4 +- tests/libtest/lib576.c | 2 +- tests/server/first.c | 2 +- tests/server/tftpd.c | 2 +- tests/unit/unit1304.c | 8 +-- tests/unit/unit1620.c | 6 +- tests/unit/unit1663.c | 6 +- tests/unit/unit1676.c | 15 ++--- tests/unit/unit3205.c | 10 +-- 43 files changed, 142 insertions(+), 143 deletions(-) diff --git a/docs/examples/anyauthput.c b/docs/examples/anyauthput.c index 9b08bbe41ba2..b1bd9f0fbf2f 100644 --- a/docs/examples/anyauthput.c +++ b/docs/examples/anyauthput.c @@ -109,7 +109,7 @@ int main(int argc, const char **argv) if(!fp) return 2; - if(fstat(fileno(fp), &file_info) != 0) { + if(fstat(fileno(fp), &file_info)) { fclose(fp); return 1; /* cannot continue */ } diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c index 12afb5621e8d..292bfc741ee5 100644 --- a/docs/examples/ephiperfifo.c +++ b/docs/examples/ephiperfifo.c @@ -412,7 +412,7 @@ static int init_fifo(struct GlobalInfo *g) struct epoll_event epev; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index c5ddbf56ba5b..079eab1e8abf 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -382,7 +382,7 @@ static int init_fifo(struct GlobalInfo *g) curl_socket_t sockfd; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/fileupload.c b/docs/examples/fileupload.c index f444da9b9e58..0c2b3a57af5a 100644 --- a/docs/examples/fileupload.c +++ b/docs/examples/fileupload.c @@ -64,7 +64,7 @@ int main(void) } /* to get the file size */ - if(fstat(fileno(fd), &file_info) != 0) { + if(fstat(fileno(fd), &file_info)) { fclose(fd); curl_global_cleanup(); return 1; /* cannot continue */ diff --git a/docs/examples/ftpupload.c b/docs/examples/ftpupload.c index 415bafe68ee2..07546f313e90 100644 --- a/docs/examples/ftpupload.c +++ b/docs/examples/ftpupload.c @@ -96,7 +96,7 @@ int main(void) } /* to get the file size */ - if(fstat(fileno(hd_src), &file_info) != 0) { + if(fstat(fileno(hd_src), &file_info)) { fclose(hd_src); return 1; /* cannot continue */ } diff --git a/docs/examples/ghiper.c b/docs/examples/ghiper.c index b0b5e022d648..fd6041761e24 100644 --- a/docs/examples/ghiper.c +++ b/docs/examples/ghiper.c @@ -399,7 +399,7 @@ int init_fifo(void) const char *fifo = "hiper.fifo"; int socket; - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c index d2a5f11724e0..96719530cf46 100644 --- a/docs/examples/hiperfifo.c +++ b/docs/examples/hiperfifo.c @@ -383,7 +383,7 @@ static int init_fifo(struct GlobalInfo *g) curl_socket_t sockfd; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index aeac13ca2b22..4a3dc0c4c7be 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -234,7 +234,7 @@ static int setup(struct input *t, int num, const char *upload) return 1; } - if(fstat(fileno(t->in), &file_info) != 0) { + if(fstat(fileno(t->in), &file_info)) { fprintf(stderr, "error: could not stat file %s: %s\n", upload, strerror(errno)); fclose(t->out); diff --git a/docs/examples/httpput.c b/docs/examples/httpput.c index 977d31ccaf29..fb33c8ac406a 100644 --- a/docs/examples/httpput.c +++ b/docs/examples/httpput.c @@ -96,7 +96,7 @@ int main(int argc, const char **argv) return 2; /* get the file size of the local file */ - if(fstat(fileno(hd_src), &file_info) != 0) { + if(fstat(fileno(hd_src), &file_info)) { fclose(hd_src); return 1; /* cannot continue */ } diff --git a/docs/examples/smooth-gtk-thread.c b/docs/examples/smooth-gtk-thread.c index 2bbe1a39e928..82ce1a2256de 100644 --- a/docs/examples/smooth-gtk-thread.c +++ b/docs/examples/smooth-gtk-thread.c @@ -210,7 +210,7 @@ int main(int argc, const char **argv) g_signal_connect(G_OBJECT(top_window), "delete-event", G_CALLBACK(cb_delete), NULL); - if(!g_thread_create(&create_thread, progress_bar, FALSE, NULL) != 0) + if(!g_thread_create(&create_thread, progress_bar, FALSE, NULL)) g_warning("cannot create the thread"); gtk_main(); diff --git a/docs/examples/synctime.c b/docs/examples/synctime.c index ed10f9d3b816..1157edbe3fa0 100644 --- a/docs/examples/synctime.c +++ b/docs/examples/synctime.c @@ -149,7 +149,7 @@ static size_t SyncTime_CURL_WriteHeader(void *ptr, size_t size, size_t nmemb, int i; SYSTime.wMilliseconds = 500; /* adjust to midpoint, 0.5 sec */ for(i = 0; i < 12; i++) { - if(strcmp(MthStr[i], TmpStr2) == 0) { + if(!strcmp(MthStr[i], TmpStr2)) { SYSTime.wMonth = (WORD)(i + 1); break; } @@ -232,26 +232,26 @@ int main(int argc, const char *argv[]) if(argc > 1) { int OptionIndex = 1; while(OptionIndex < argc) { - if(strncmp(argv[OptionIndex], "--server=", 9) == 0) + if(!strncmp(argv[OptionIndex], "--server=", 9)) snprintf(conf.timeserver, sizeof(conf.timeserver) - 1, "%s", &argv[OptionIndex][9]); - if(strcmp(argv[OptionIndex], "--showall") == 0) + if(!strcmp(argv[OptionIndex], "--showall")) ShowAllHeader = 1; - if(strcmp(argv[OptionIndex], "--synctime") == 0) + if(!strcmp(argv[OptionIndex], "--synctime")) AutoSyncTime = 1; - if(strncmp(argv[OptionIndex], "--proxy-user=", 13) == 0) + if(!strncmp(argv[OptionIndex], "--proxy-user=", 13)) snprintf(conf.proxy_user, sizeof(conf.proxy_user) - 1, "%s", &argv[OptionIndex][13]); - if(strncmp(argv[OptionIndex], "--proxy=", 8) == 0) + if(!strncmp(argv[OptionIndex], "--proxy=", 8)) snprintf(conf.http_proxy, sizeof(conf.http_proxy) - 1, "%s", &argv[OptionIndex][8]); - if((strcmp(argv[OptionIndex], "--help") == 0) || - (strcmp(argv[OptionIndex], "/?") == 0)) { + if(!strcmp(argv[OptionIndex], "--help") || + !strcmp(argv[OptionIndex], "/?")) { showUsage(); return 0; } diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 91403e1c6ed5..938b00402567 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -576,7 +576,7 @@ static int proxy_h2_on_header(nghttp2_session *session, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { int http_status; struct http_resp *resp; diff --git a/lib/cookie.c b/lib/cookie.c index e6b6e4f13296..81d8d1416d2f 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -687,7 +687,7 @@ static CURLcode parse_netscape(struct Cookie *co, * Firefox's cookie files, they are prefixed #HttpOnly_ and the rest * remains as usual, so we skip 10 characters of the line. */ - if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { + if(!strncmp(lineptr, "#HttpOnly_", 10)) { lineptr += 10; co->httponly = TRUE; } diff --git a/lib/curl_fnmatch.c b/lib/curl_fnmatch.c index dde956c3ac2a..d165554f8db5 100644 --- a/lib/curl_fnmatch.c +++ b/lib/curl_fnmatch.c @@ -96,25 +96,25 @@ static int parsekeyword(const unsigned char **pattern, unsigned char *charset) #undef KEYLEN *pattern = p; /* move caller's pattern pointer */ - if(strcmp(keyword, "digit") == 0) + if(!strcmp(keyword, "digit")) charset[CURLFNM_DIGIT] = 1; - else if(strcmp(keyword, "alnum") == 0) + else if(!strcmp(keyword, "alnum")) charset[CURLFNM_ALNUM] = 1; - else if(strcmp(keyword, "alpha") == 0) + else if(!strcmp(keyword, "alpha")) charset[CURLFNM_ALPHA] = 1; - else if(strcmp(keyword, "xdigit") == 0) + else if(!strcmp(keyword, "xdigit")) charset[CURLFNM_XDIGIT] = 1; - else if(strcmp(keyword, "print") == 0) + else if(!strcmp(keyword, "print")) charset[CURLFNM_PRINT] = 1; - else if(strcmp(keyword, "graph") == 0) + else if(!strcmp(keyword, "graph")) charset[CURLFNM_GRAPH] = 1; - else if(strcmp(keyword, "space") == 0) + else if(!strcmp(keyword, "space")) charset[CURLFNM_SPACE] = 1; - else if(strcmp(keyword, "blank") == 0) + else if(!strcmp(keyword, "blank")) charset[CURLFNM_BLANK] = 1; - else if(strcmp(keyword, "upper") == 0) + else if(!strcmp(keyword, "upper")) charset[CURLFNM_UPPER] = 1; - else if(strcmp(keyword, "lower") == 0) + else if(!strcmp(keyword, "lower")) charset[CURLFNM_LOWER] = 1; else return SETCHARSET_FAIL; diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index f205848b6d0c..23fbd1f07c40 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -939,7 +939,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, parser->item_length++; if(c == ' ') { mem[parser->item_offset + parser->item_length - 1] = 0; - if(strcmp("", mem + parser->item_offset) == 0) { + if(!strcmp("", mem + parser->item_offset)) { finfo->filetype = CURLFILETYPE_DIRECTORY; finfo->size = 0; } diff --git a/lib/http2.c b/lib/http2.c index ff609b321d8d..ca579f6019fc 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1505,7 +1505,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { /* nghttp2 guarantees :status is received first and only once. */ char buffer[32]; size_t hlen; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index b01affeeb931..d214afee771c 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -512,7 +512,7 @@ CURLcode Curl_http_proxy_inspect_tunnel_response( capsule_protocol = Curl_dynhds_cget(&resp->headers, "capsule-protocol"); if(capsule_protocol) { - if(strncmp(capsule_protocol->value, "?1", 2) == 0 && + if(!strncmp(capsule_protocol->value, "?1", 2) && !capsule_protocol->value[2]) { infof(data, "CONNECT-UDP tunnel established, response %d", resp->status); diff --git a/lib/rtsp.c b/lib/rtsp.c index 843a02264b91..8c5cdd564388 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -658,7 +658,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data, while(blen && buf[0] != '$') { if(!in_body && buf[0] == 'R' && data->set.rtspreq != RTSPREQ_RECEIVE) { - if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + if(!strncmp(buf, "RTSP/", (blen < 5) ? blen : 5)) { /* This could be the next response, no consume and return */ if(*pconsumed) { DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index f569f53d11bd..0ed60d903982 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -379,7 +379,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return result; /* We only support md5 sessions */ - if(strcmp(algorithm, "md5-sess") != 0) + if(strcmp(algorithm, "md5-sess")) return CURLE_BAD_CONTENT_ENCODING; /* Get the qop-values from the qop-options */ diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index f4af3755f14e..2803c05d9f0e 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -361,8 +361,8 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, ntlm->flags = 0; if((type2len < 32) || - (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || - (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { + memcmp(type2, NTLMSSP_SIGNATURE, 8) || + memcmp(type2 + 8, type2_marker, sizeof(type2_marker))) { /* This was not a good enough type-2 message */ infof(data, "NTLM handshake failure (bad type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 734e64da70b7..06cbd68309f7 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -662,14 +662,14 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, if(!curlx_str_number(&p, &port, 0xffff) && (kh_name_end && (port == conn->origin->port))) { kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); - if(strncmp(store->name + 1, - conn->origin->hostname, kh_name_size) == 0) { + if(!strncmp(store->name + 1, conn->origin->hostname, + kh_name_size)) { found = TRUE; break; } } } - else if(strcmp(store->name, conn->origin->hostname) == 0) { + else if(!strcmp(store->name, conn->origin->hostname)) { found = TRUE; break; } diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c index 1fc6e9f8c6f9..197055fc0de5 100644 --- a/lib/vtls/cipher_suite.c +++ b/lib/vtls/cipher_suite.c @@ -639,7 +639,7 @@ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) if(cs_len > 0 && cs_str_to_zip(cs_str, cs_len, zip) == 0) { for(i = 0; i < CS_LIST_LEN; i++) { - if(memcmp(cs_list[i].zip, zip, sizeof(zip)) == 0) + if(!memcmp(cs_list[i].zip, zip, sizeof(zip))) return cs_list[i].id; } } diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 58b024695e86..1a7a4a1e5bbc 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -205,12 +205,12 @@ static gnutls_datum_t load_file(const char *file) f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0) + if(fseek(f, 0, SEEK_END)) goto out; filelen = ftell(f); if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) goto out; - if(fseek(f, 0, SEEK_SET) != 0) + if(fseek(f, 0, SEEK_SET)) goto out; ptr = curlx_malloc((size_t)filelen); if(!ptr) diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 3d87d4a226ac..57727613ae9a 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -307,7 +307,7 @@ static CURLcode mbed_set_selected_ciphers( /* Add default TLSv1.3 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0) + if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; selected[count++] = id; @@ -360,7 +360,7 @@ static CURLcode mbed_set_selected_ciphers( /* Add default TLSv1.2 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0) + if(!strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; /* No duplicates allowed (so selected cannot overflow) */ diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index cd00287ff232..b3586d1221cc 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -107,7 +107,7 @@ static const char *c_memmem(const void *haystack, size_t haystacklen, return NULL; first = *(const char *)needle; for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) + if(((*p) == first) && !memcmp(p, needle, needlelen)) return p; return NULL; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index a619865908bf..be2b755f0be8 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -856,7 +856,7 @@ static CURLcode wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) { size_t n; - if((strncmp(str, "TLS13", 5) == 0) != tls13) + if((!strncmp(str, "TLS13", 5)) != tls13) continue; /* if there already is data in the string, add colon separator */ diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 8c5a03e83bbb..3a273770168d 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -507,7 +507,7 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ AC_LANG_PROGRAM([[ $curl_includes_unistd ]],[[ - if(alarm(0) != 0) + if(alarm(0)) return 1; ]]) ],[ @@ -598,7 +598,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ $curl_includes_libgen $curl_includes_unistd ]],[[ - if(basename(0) != 0) + if(basename(0)) return 1; ]]) ],[ @@ -658,7 +658,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(closesocket(0) != 0) + if(closesocket(0)) return 1; ]]) ],[ @@ -688,7 +688,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(closesocket(0) != 0) + if(closesocket(0)) return 1; ]]) ],[ @@ -749,7 +749,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(CloseSocket(0) != 0) + if(CloseSocket(0)) return 1; ]]) ],[ @@ -767,7 +767,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(CloseSocket(0) != 0) + if(CloseSocket(0)) return 1; ]]) ],[ @@ -850,7 +850,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ AC_LANG_PROGRAM([[ $curl_includes_fcntl ]],[[ - if(fcntl(0, 0, 0) != 0) + if(fcntl(0, 0, 0)) return 1; ]]) ],[ @@ -916,7 +916,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ $curl_includes_fcntl ]],[[ int flags = 0; - if(fcntl(0, F_SETFL, flags | O_NONBLOCK) != 0) + if(fcntl(0, F_SETFL, flags | O_NONBLOCK)) return 1; ]]) ],[ @@ -1097,7 +1097,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(fsetxattr(0, "", 0, 0, 0) != 0) + if(fsetxattr(0, "", 0, 0, 0)) return 1; ]]) ],[ @@ -1115,7 +1115,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(fsetxattr(0, 0, 0, 0, 0, 0) != 0) + if(fsetxattr(0, 0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1205,7 +1205,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(getaddrinfo(0, 0, 0, &ai) != 0) + if(getaddrinfo(0, 0, 0, &ai)) return 1; ]]) ],[ @@ -1240,7 +1240,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(getaddrinfo(0, 0, 0, &ai) != 0) + if(getaddrinfo(0, 0, 0, &ai)) return 1; ]]) ],[ @@ -1451,7 +1451,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0)) return 1; ]]) ],[ @@ -1470,7 +1470,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1489,7 +1489,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0, 0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1574,7 +1574,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(gethostname((void *)s, 0) != 0) + if(gethostname((void *)s, 0)) return 1; ]]) ],[ @@ -1609,7 +1609,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(gethostname((void *)s, 0) != 0) + if(gethostname((void *)s, 0)) return 1; ]]) ],[ @@ -1641,7 +1641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ int FUNCALLCONV gethostname($tst_arg1, $tst_arg2); ]],[[ char s[1]; - if(gethostname(($tst_arg1)s, 0) != 0) + if(gethostname(($tst_arg1)s, 0)) return 1; ]]) ],[ @@ -1709,7 +1709,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getpeername(0, (void *)0, (void *)0) != 0) + if(getpeername(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1743,7 +1743,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getpeername(0, (void *)0, (void *)0) != 0) + if(getpeername(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1807,7 +1807,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getsockname(0, (void *)0, (void *)0) != 0) + if(getsockname(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1841,7 +1841,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getsockname(0, (void *)0, (void *)0) != 0) + if(getsockname(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1928,7 +1928,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ AC_LANG_PROGRAM([[ $curl_includes_ifaddrs ]],[[ - if(getifaddrs(0) != 0) + if(getifaddrs(0)) return 1; ]]) ],[ @@ -2168,7 +2168,7 @@ AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ ]],[[ time_t clock = 1170352587; struct tm result; - if(localtime_r(&clock, &result) != 0) + if(localtime_r(&clock, &result)) return 1; (void)result; ]]) @@ -2289,7 +2289,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ ]],[[ char ipv4res[sizeof("255.255.255.255")]; unsigned char ipv4a[5] = ""; - if(inet_ntop(0, ipv4a, ipv4res, 0) != 0) + if(inet_ntop(0, ipv4a, ipv4res, 0)) return 1; ]]) ],[ @@ -2332,7 +2332,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ return 1; /* fail */ if(!ipv4ptr[0]) return 1; /* fail */ - if(memcmp(ipv4res, "192.168.100.1", 13) != 0) + if(memcmp(ipv4res, "192.168.100.1", 13)) return 1; /* fail */ /* - */ ipv6res[0] = '\0'; @@ -2356,7 +2356,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ return 1; /* fail */ if(!ipv6ptr[0]) return 1; /* fail */ - if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24) != 0) + if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) return 1; /* fail */ /* - */ return 0; @@ -2450,7 +2450,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ ]],[[ unsigned char ipv4a[4 + 1] = ""; const char *ipv4src = "192.168.100.1"; - if(inet_pton(0, ipv4src, ipv4a) != 0) + if(inet_pton(0, ipv4src, ipv4a)) return 1; ]]) ],[ @@ -2599,7 +2599,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ AC_LANG_PROGRAM([[ $curl_includes_stropts ]],[[ - if(ioctl(0, 0, 0) != 0) + if(ioctl(0, 0, 0)) return 1; ]]) ],[ @@ -2657,7 +2657,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ $curl_includes_stropts ]],[[ int flags = 0; - if(ioctl(0, FIONBIO, &flags) != 0) + if(ioctl(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -2714,7 +2714,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ #include ]],[[ struct ifreq ifr; - if(ioctl(0, SIOCGIFADDR, &ifr) != 0) + if(ioctl(0, SIOCGIFADDR, &ifr)) return 1; ]]) ],[ @@ -2772,7 +2772,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(ioctlsocket(0, 0, 0) != 0) + if(ioctlsocket(0, 0, 0)) return 1; ]]) ],[ @@ -2802,7 +2802,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(ioctlsocket(0, 0, 0) != 0) + if(ioctlsocket(0, 0, 0)) return 1; ]]) ],[ @@ -2861,7 +2861,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ $curl_includes_winsock2 ]],[[ unsigned long flags = 0; - if(ioctlsocket(0, FIONBIO, &flags) != 0) + if(ioctlsocket(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -2918,7 +2918,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(IoctlSocket(0, 0, 0) != 0) + if(IoctlSocket(0, 0, 0)) return 1; ]]) ],[ @@ -2935,7 +2935,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(IoctlSocket(0, 0, 0) != 0) + if(IoctlSocket(0, 0, 0)) return 1; ]]) ],[ @@ -2993,7 +2993,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ $curl_includes_bsdsocket ]],[[ long flags = 0; - if(IoctlSocket(0, FIONBIO, &flags) != 0) + if(IoctlSocket(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -3064,7 +3064,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(memrchr("", 0, 0) != 0) + if(memrchr("", 0, 0)) return 1; ]]) ],[ @@ -3096,7 +3096,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(memrchr("", 0, 0) != 0) + if(memrchr("", 0, 0)) return 1; ]]) ],[ @@ -3181,7 +3181,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(sigaction(0, 0, 0) != 0) + if(sigaction(0, 0, 0)) return 1; ]]) ],[ @@ -3266,7 +3266,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(siginterrupt(0, 0) != 0) + if(siginterrupt(0, 0)) return 1; ]]) ],[ @@ -3351,7 +3351,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(signal(0, 0) != 0) + if(signal(0, 0)) return 1; ]]) ],[ @@ -3425,7 +3425,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(sigsetjmp(env, 0) != 0) + if(sigsetjmp(env, 0)) return 1; ]]) ],[ @@ -3458,7 +3458,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(sigsetjmp(env, 0) != 0) + if(sigsetjmp(env, 0)) return 1; ]]) ],[ @@ -3521,7 +3521,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(socket(0, 0, 0) != 0) + if(socket(0, 0, 0)) return 1; ]]) ],[ @@ -3555,7 +3555,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(socket(0, 0, 0) != 0) + if(socket(0, 0, 0)) return 1; ]]) ],[ @@ -3641,7 +3641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ $curl_includes_sys_socket ]],[[ int sv[2]; - if(socketpair(0, 0, 0, sv) != 0) + if(socketpair(0, 0, 0, sv)) return 1; ]]) ],[ @@ -3726,7 +3726,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(strcasecmp("", "") != 0) + if(strcasecmp("", "")) return 1; ]]) ],[ @@ -3810,7 +3810,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(strcmpi(0, 0) != 0) + if(strcmpi(0, 0)) return 1; ]]) ],[ @@ -3919,7 +3919,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ $curl_includes_string ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; ]]) ],[ @@ -3942,7 +3942,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ char *strerror_r(int errnum, char *workbuf, $arg3 bufsize); ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; (void)s; ]]) @@ -4005,7 +4005,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ int strerror_r(int errnum, char *resultbuf, $arg3 bufsize); ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; (void)s; ]]) @@ -4160,7 +4160,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(stricmp(0, 0) != 0) + if(stricmp(0, 0)) return 1; ]]) ],[ @@ -4246,7 +4246,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMSET_S], [ $curl_includes_string ]],[[ char buf[2]; - if(memset_s(buf, sizeof(buf), 0, sizeof(buf)) != 0) + if(memset_s(buf, sizeof(buf), 0, sizeof(buf))) return 1; ]]) ],[ diff --git a/src/tool_vms.c b/src/tool_vms.c index 5a006f39abf3..ced0efe2ed15 100644 --- a/src/tool_vms.c +++ b/src/tool_vms.c @@ -59,7 +59,7 @@ int is_vms_shell(void) } /* Have to make sure some one did not set shell to DCL */ - if(strcmp(shell, "DCL") == 0) { + if(!strcmp(shell, "DCL")) { vms_shell = 1; return 1; } diff --git a/tests/libtest/first.c b/tests/libtest/first.c index bd18c06752ce..a512cddae975 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -79,7 +79,7 @@ int cgetopt(int argc, const char * const argv[], const char *optstring) } arg = argv[coptind]; - if(arg && strcmp(arg, "--") == 0) { + if(arg && !strcmp(arg, "--")) { coptind++; return -1; } @@ -248,7 +248,7 @@ int main(int argc, const char **argv) entry_name = argv[1]; entry_func = NULL; for(tmp = 0; s_entries[tmp].ptr; ++tmp) { - if(strcmp(entry_name, s_entries[tmp].name) == 0) { + if(!strcmp(entry_name, s_entries[tmp].name)) { entry_func = s_entries[tmp].ptr; break; } diff --git a/tests/libtest/lib1536.c b/tests/libtest/lib1536.c index 189392cf7f45..f6632c736106 100644 --- a/tests/libtest/lib1536.c +++ b/tests/libtest/lib1536.c @@ -73,7 +73,7 @@ static CURLcode test_lib1536(const char *URL) __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } - if(!scheme || memcmp(scheme, "http", 5) != 0) { + if(!scheme || memcmp(scheme, "http", 5)) { curl_mfprintf(stderr, "%s:%d scheme of http resource is incorrect; " "expected 'http' but is %s\n", __FILE__, __LINE__, scheme ? "invalid" : "NULL"); diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 76fbe3cbdb94..5e15e9c051b2 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -2135,7 +2135,7 @@ static int clear_url(void) rc = curl_url_get(u, clear_url_list[i].part, &p, 0); if(rc != clear_url_list[i].ucode || - (p && clear_url_list[i].out && strcmp(p, clear_url_list[i].out) != 0)) { + (p && clear_url_list[i].out && strcmp(p, clear_url_list[i].out))) { curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); error++; diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index c083896381ca..1923e61c7853 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -63,7 +63,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) if(last_issuer) { /* If the last certificate's issuer matches the current certificate's * subject, then the chain is in order */ - if(strcmp(last_issuer, subject) != 0) { + if(strcmp(last_issuer, subject)) { curl_mfprintf(stderr, "cert %d issuer does not match cert %d subject\n", cert - 1, cert); diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index b4b4c63cedeb..311d4b092c60 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -99,7 +99,7 @@ static int t518_test_rlimit(int keep_open) /* get initial open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t518_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); return -1; @@ -135,7 +135,7 @@ static int t518_test_rlimit(int keep_open) (rl.rlim_cur < OPEN_MAX)) { curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(setrlimit(RLIMIT_NOFILE, &rl)) { /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); @@ -146,7 +146,7 @@ static int t518_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(setrlimit(RLIMIT_NOFILE, &rl)) { /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); @@ -155,7 +155,7 @@ static int t518_test_rlimit(int keep_open) /* get current open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t518_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); return -3; diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 9b99b7489948..45ab68289f1f 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -96,7 +96,7 @@ static int t537_test_rlimit(int keep_open) /* get initial open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t537_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); return -1; @@ -136,7 +136,7 @@ static int t537_test_rlimit(int keep_open) (rl.rlim_cur < OPEN_MAX)) { curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(setrlimit(RLIMIT_NOFILE, &rl)) { /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); @@ -147,7 +147,7 @@ static int t537_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(setrlimit(RLIMIT_NOFILE, &rl)) { /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); @@ -156,7 +156,7 @@ static int t537_test_rlimit(int keep_open) /* get current open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t537_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); return -3; diff --git a/tests/libtest/lib571.c b/tests/libtest/lib571.c index a5e479d37552..24e18dc6ab68 100644 --- a/tests/libtest/lib571.c +++ b/tests/libtest/lib571.c @@ -67,7 +67,7 @@ static size_t rtp_write(char *data, size_t size, size_t nmemb, void *stream) data += 4; for(i = 0; i < message_size; i += RTP_DATA_SIZE) { if(message_size - i > RTP_DATA_SIZE) { - if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE) != 0) { + if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE)) { curl_mprintf("RTP PAYLOAD CORRUPTED [%s]\n", data + i); #if 0 return failure; @@ -75,7 +75,7 @@ static size_t rtp_write(char *data, size_t size, size_t nmemb, void *stream) } } else { - if(memcmp(RTP_DATA, data + i, message_size - i) != 0) { + if(memcmp(RTP_DATA, data + i, message_size - i)) { curl_mprintf("RTP PAYLOAD END CORRUPTED (%d), [%s]\n", message_size - i, data + i); #if 0 diff --git a/tests/libtest/lib576.c b/tests/libtest/lib576.c index a50d34c02503..d06661a90a1b 100644 --- a/tests/libtest/lib576.c +++ b/tests/libtest/lib576.c @@ -73,7 +73,7 @@ static long chunk_bgn(const void *f, void *ptr, int remains) "-------------------------------------------" "------------------\n"); } - if(strcmp(finfo->filename, "someothertext.txt") == 0) { + if(!strcmp(finfo->filename, "someothertext.txt")) { curl_mprintf("# THIS CONTENT WAS SKIPPED IN CHUNK_BGN CALLBACK #\n"); return CURL_CHUNK_BGN_FUNC_SKIP; } diff --git a/tests/server/first.c b/tests/server/first.c index c57617249885..31ba5f72b4df 100644 --- a/tests/server/first.c +++ b/tests/server/first.c @@ -39,7 +39,7 @@ int main(int argc, const char **argv) entry_name = argv[1]; entry_func = NULL; for(tmp = 0; s_entries[tmp].ptr; ++tmp) { - if(strcmp(entry_name, s_entries[tmp].name) == 0) { + if(!strcmp(entry_name, s_entries[tmp].name)) { entry_func = s_entries[tmp].ptr; break; } diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 1bfd2f3c94ce..b7e774db0c3f 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -970,7 +970,7 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) curlx_fclose(server); for(pf = formata; pf->f_mode; pf++) - if(strcmp(pf->f_mode, mode) == 0) + if(!strcmp(pf->f_mode, mode)) break; if(!pf->f_mode) { nak(TFTP_EBADOP); diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index 3c51cceaa7e6..0c788818a5cf 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -119,10 +119,10 @@ static CURLcode test_unit1304(const char *arg) Curl_netrc_init(&store); res = Curl_netrc_scan(data, &store, "example.com", NULL, arg, &cr_out); fail_unless(res == NETRC_OK, "Host should have been found"); - fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, + fail_unless(!strncmp(Curl_creds_passwd(cr_out), "passwd", 6), "password should be 'passwd'"); fail_unless(!t1304_no_user(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, + fail_unless(!strncmp(Curl_creds_user(cr_out), "admin", 5), "login should be 'admin'"); Curl_netrc_cleanup(&store); @@ -132,10 +132,10 @@ static CURLcode test_unit1304(const char *arg) Curl_netrc_init(&store); res = Curl_netrc_scan(data, &store, "curl.example.com", NULL, arg, &cr_out); fail_unless(res == NETRC_OK, "Host should have been found"); - fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, + fail_unless(!strncmp(Curl_creds_passwd(cr_out), "none", 4), "password should be 'none'"); fail_unless(!t1304_no_user(cr_out), "returned NULL!"); - fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, + fail_unless(!strncmp(Curl_creds_user(cr_out), "none", 4), "login should be 'none'"); Curl_netrc_cleanup(&store); diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c index 57a90e85cdc7..6ba9a5bb0d60 100644 --- a/tests/unit/unit1620.c +++ b/tests/unit/unit1620.c @@ -50,13 +50,13 @@ static void t1620_parse(const char *input, if(!unitfail) { fail_unless(!userstr || !exp_username || - strcmp(userstr, exp_username) == 0, + !strcmp(userstr, exp_username), "userstr should be equal to exp_username"); fail_unless(!passwdstr || !exp_password || - strcmp(passwdstr, exp_password) == 0, + !strcmp(passwdstr, exp_password), "passwdstr should be equal to exp_password"); fail_unless(!options || !exp_options || - strcmp(options, exp_options) == 0, + !strcmp(options, exp_options), "options should be equal to exp_options"); } diff --git a/tests/unit/unit1663.c b/tests/unit/unit1663.c index f79fd75a81d1..42fe782f87d5 100644 --- a/tests/unit/unit1663.c +++ b/tests/unit/unit1663.c @@ -56,11 +56,11 @@ static void t1663_parse(const char *input_data, fail_unless(!!exp_host == !!host, "host expectation failed"); if(!unitfail) { - fail_unless(!dev || !exp_dev || strcmp(dev, exp_dev) == 0, + fail_unless(!dev || !exp_dev || !strcmp(dev, exp_dev), "dev should be equal to exp_dev"); - fail_unless(!iface || !exp_iface || strcmp(iface, exp_iface) == 0, + fail_unless(!iface || !exp_iface || !strcmp(iface, exp_iface), "iface should be equal to exp_iface"); - fail_unless(!host || !exp_host || strcmp(host, exp_host) == 0, + fail_unless(!host || !exp_host || !strcmp(host, exp_host), "host should be equal to exp_host"); } diff --git a/tests/unit/unit1676.c b/tests/unit/unit1676.c index 85f74e2bb80a..5f33f9f2850d 100644 --- a/tests/unit/unit1676.c +++ b/tests/unit/unit1676.c @@ -92,23 +92,22 @@ static CURLcode test_unit1676(const char *arg) if(result == CURLE_OK) { /* Walk certinfo entries to find dh(p), dh(g), and dh(pub_key) */ for(slist = data->info.certs.certinfo[0]; slist; slist = slist->next) { - if(strncmp(slist->data, "dh(p):", 6) == 0) + if(!strncmp(slist->data, "dh(p):", 6)) dhp_value = slist->data + 6; - else if(strncmp(slist->data, "dh(g):", 6) == 0) + else if(!strncmp(slist->data, "dh(g):", 6)) dhg_value = slist->data + 6; - else if(strncmp(slist->data, "dh(pub_key):", 12) == 0) + else if(!strncmp(slist->data, "dh(pub_key):", 12)) dhpk_value = slist->data + 12; } abort_unless(dhp_value, "dh(p) not found in certinfo"); abort_unless(dhg_value, "dh(g) not found in certinfo"); abort_unless(dhpk_value, "dh(pub_key) not found in certinfo"); - fail_if(strcmp(dhp_value, dhg_value) == 0, + fail_if(!strcmp(dhp_value, dhg_value), "dh(p) and dh(g) have the same value (bug: g re-reads p)"); - fail_unless(strcmp(dhp_value, "17") == 0, "dh(p) expected 17 (0x11)"); - fail_unless(strcmp(dhg_value, "34") == 0, "dh(g) expected 34 (0x22)"); - fail_unless(strcmp(dhpk_value, "51") == 0, - "dh(pub_key) expected 51 (0x33)"); + fail_unless(!strcmp(dhp_value, "17"), "dh(p) expected 17 (0x11)"); + fail_unless(!strcmp(dhg_value, "34"), "dh(g) expected 34 (0x22)"); + fail_unless(!strcmp(dhpk_value, "51"), "dh(pub_key) expected 51 (0x33)"); } curl_easy_cleanup(data); diff --git a/tests/unit/unit3205.c b/tests/unit/unit3205.c index 686620c292fe..9d448e1f788d 100644 --- a/tests/unit/unit3205.c +++ b/tests/unit/unit3205.c @@ -544,7 +544,7 @@ static CURLcode test_unit3205(const char *arg) Curl_cipher_suite_get_str(test->id, buf, sizeof(buf), TRUE); - if(expect && strcmp(buf, expect) != 0) { + if(expect && strcmp(buf, expect)) { curl_mfprintf(stderr, "Curl_cipher_suite_get_str FAILED for 0x%04x, " "result = \"%s\", expected = \"%s\"\n", test->id, buf, expect); @@ -559,17 +559,17 @@ static CURLcode test_unit3205(const char *arg) /* suites matched by EDH alias will return the DHE name */ if(test->id >= 0x0011 && test->id < 0x0017) { - if(expect && memcmp(expect, "EDH-", 4) == 0) { + if(expect && !memcmp(expect, "EDH-", 4)) { curlx_strcopy(alt, sizeof(alt), expect, strlen(expect)); expect = (const char *)memcpy(alt, "DHE-", sizeof("DHE-") - 1); } - if(expect && memcmp(expect + 4, "EDH-", 4) == 0) { + if(expect && !memcmp(expect + 4, "EDH-", 4)) { curlx_strcopy(alt, sizeof(alt), expect, strlen(expect)); expect = (const char *)memcpy(alt + 4, "DHE-", sizeof("DHE-") - 1) - 4; } } - if(expect && strcmp(buf, expect) != 0) { + if(expect && strcmp(buf, expect)) { curl_mfprintf(stderr, "Curl_cipher_suite_get_str FAILED for 0x%04x, " "result = \"%s\", expected = \"%s\"\n", test->id, buf, expect); @@ -598,7 +598,7 @@ static CURLcode test_unit3205(const char *arg) test->str, id, test->id); unitfail++; } - if(len > 64 || strncmp(ptr, test->str, len) != 0) { + if(len > 64 || strncmp(ptr, test->str, len)) { curl_mfprintf(stderr, "Curl_cipher_suite_walk_str ABORT for \"%s\" " "unexpected pointers\n", test->str); From a6971ce90aec2a35e830bddcec16c4ca60dcedc9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 13:37:22 +0200 Subject: [PATCH 0375/1062] connect: turn conn_get_first_origin into static This function is only used within this source file. Closes #21948 --- lib/connect.c | 28 +++++++++++++++------------- lib/connect.h | 5 ----- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index 2a534aa8f3f1..a24530a6e065 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -446,6 +446,20 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, #endif /* !CURL_DISABLE_HTTP */ #endif /* CURL_DISABLE_PROXY */ +/* Get the origin curl connects its socket to. + * Can be origin or the first proxy. */ +static struct Curl_peer *conn_get_first_origin(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; +} + static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -457,7 +471,7 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, * do we use for it? Only on the first hop we can do Happy Eyeballs. * first_origin and first_peer differ on --connect-to. */ struct Curl_peer *first_origin = - Curl_conn_get_first_origin(cf->conn, cf->sockindex); + conn_get_first_origin(cf->conn, cf->sockindex); struct Curl_peer *first_peer = Curl_conn_get_first_peer(cf->conn, cf->sockindex); struct Curl_peer *tunnel_peer = NULL; @@ -818,18 +832,6 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, (conn->via_peer ? conn->via_peer : conn->origin); } -struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn, - int sockindex) -{ -#ifndef CURL_DISABLE_PROXY - if(conn->socks_proxy.peer) - return conn->socks_proxy.peer; - if(conn->http_proxy.peer) - return conn->http_proxy.peer; -#endif - return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; -} - struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, int sockindex) { diff --git a/lib/connect.h b/lib/connect.h index 968314ea8e74..3530d7050402 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -136,11 +136,6 @@ struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, int sockindex); -/* Get the origin curl connects its socket to. - * Can be origin or the first proxy. */ -struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn, - int sockindex); - /* Get the peer curl connects its socket to. * Can be origin, "connect-to" or the first proxy. */ struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, From 74096802ee4ee330f23e008895ff1f4d288761d8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 14:01:11 +0200 Subject: [PATCH 0376/1062] CURLOPT_WRITEFUNCTION.md: mention redirects Reported-by: BazaarAcc32 on github Fixes #21945 Closes #21950 --- docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md index 407484debb27..2e240d7b68e2 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md @@ -47,6 +47,11 @@ defined in the curl.h header file: *CURL_MAX_WRITE_SIZE* (the usual default is the write callback, you can get up to *CURL_MAX_HTTP_HEADER* bytes of header data passed into it. This usually means 100K. +The CURLOPT_WRITEFUNCTION(3) callback receives the final response payload. +When CURLOPT_FOLLOWLOCATION(3) is enabled, libcurl automatically handles +intermediate 3xx redirects, meaning their HTTP bodies are skipped and not +passed to this callback. + This function may be called with zero bytes data if the transferred file is empty. @@ -60,8 +65,8 @@ aborted and the libcurl function used returns *CURLE_WRITE_ERROR*. You can also abort the transfer by returning CURL_WRITEFUNC_ERROR (added in 7.87.0), which makes *CURLE_WRITE_ERROR* get returned. -If the callback function returns CURL_WRITEFUNC_PAUSE it pauses this -transfer. See curl_easy_pause(3) for further details. +If the callback function returns CURL_WRITEFUNC_PAUSE it pauses this transfer. +See curl_easy_pause(3) for further details. Set this option to NULL to get the internal default function used instead of your callback. The internal default function writes the data to the FILE * From 7ec25148c06b049d3252172ff17fae85b19c54c9 Mon Sep 17 00:00:00 2001 From: alhudz Date: Wed, 10 Jun 2026 18:30:13 +0530 Subject: [PATCH 0377/1062] digest: flush proxy state on proxy or credential change Closes #21951 --- lib/http_digest.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/http_digest.c b/lib/http_digest.c index 6949b3bd0d02..0e10700407e8 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -62,6 +62,27 @@ CURLcode Curl_input_digest(struct Curl_easy *data, return Curl_auth_decode_digest_http_message(header, digest); } +/* Flush the Digest state if it was created for a different origin or with + different credentials than the ones now in use, then link the current + ones. */ +static void digest_flush_stale(struct digestdata *digest, + struct Curl_peer *peer, + struct Curl_creds *creds) +{ + bool flush = FALSE; + if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) + flush = TRUE; + else if(digest->creds && !Curl_creds_same(creds, digest->creds)) + flush = TRUE; + + if(flush) + /* flush Digest state */ + Curl_auth_digest_cleanup(digest); + + Curl_peer_link(&digest->origin, peer); + Curl_creds_link(&digest->creds, creds); +} + CURLcode Curl_output_digest(struct Curl_easy *data, bool proxy, const unsigned char *request, @@ -88,29 +109,17 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; + digest_flush_stale(digest, data->conn->http_proxy.peer, + data->conn->http_proxy.creds); allocuserpwd = &data->req.hd_proxy_auth; creds = data->conn->http_proxy.creds; authp = &data->state.authproxy; #endif } else { - bool flush = FALSE; DEBUGASSERT(data->conn->origin); - if(data->state.digest.origin && - !Curl_peer_same_destination(data->conn->origin, - data->state.digest.origin)) - flush = TRUE; - else if(data->state.digest.creds && - !Curl_creds_same(data->state.creds, data->state.digest.creds)) - flush = TRUE; - - if(flush) - /* flush host Digest state */ - Curl_auth_digest_cleanup(&data->state.digest); - - Curl_peer_link(&data->state.digest.origin, data->conn->origin); - Curl_creds_link(&data->state.digest.creds, data->state.creds); digest = &data->state.digest; + digest_flush_stale(digest, data->conn->origin, data->state.creds); allocuserpwd = &data->req.hd_auth; creds = data->state.creds; authp = &data->state.authhost; From 30c9c79cf8d2dfc37a8d005095a3e0626cddcd75 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 10 Jun 2026 13:18:30 +0200 Subject: [PATCH 0378/1062] cf-socket: make Curl_addr2string static Move as sockaddr2string() into cf-socket.c where its only callers are. Mark as UNITTEST for unit1609. Move "struct Curl_sockaddr_ex" into sockaddr.h, so connect.h and cf-socket.h can be included without all the system headers needed. Closes #21946 --- lib/cf-ip-happy.c | 1 + lib/cf-socket.c | 76 +++++++++++++++++++++++++++++++++++---- lib/cf-socket.h | 19 ---------- lib/connect.c | 57 ----------------------------- lib/connect.h | 3 -- lib/sockaddr.h | 18 ++++++++++ lib/vquic/cf-ngtcp2-cmn.c | 1 + lib/vquic/cf-quiche.c | 1 + tests/unit/unit1607.c | 5 ++- tests/unit/unit1609.c | 4 +-- tests/unit/unit2600.c | 1 + 11 files changed, 95 insertions(+), 91 deletions(-) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 35b1a8d325ff..5f99db59d3ce 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -56,6 +56,7 @@ #include "multiif.h" #include "progress.h" #include "select.h" +#include "sockaddr.h" #include "vquic/vquic.h" /* for quic cfilters */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index b5923d45131c..e30f605adb05 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -26,6 +26,9 @@ #ifdef HAVE_NETINET_IN_H #include /* may need it */ #endif +#ifdef HAVE_SYS_UN_H +#include /* for sockaddr_un */ +#endif #ifdef HAVE_LINUX_TCP_H #include #elif defined(HAVE_NETINET_TCP_H) @@ -64,11 +67,13 @@ #include "curl_addrinfo.h" #include "select.h" #include "multiif.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "progress.h" #include "conncache.h" #include "multihandle.h" #include "rand.h" +#include "sockaddr.h" #include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" @@ -78,6 +83,63 @@ #include "curlx/strparse.h" +/* retrieves ip address and port from a sockaddr structure. note it calls + * curlx_inet_ntop which sets errno on fail, not SOCKERRNO. + * @unittest 1607 + */ +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port); +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port) +{ + struct sockaddr_in *si = NULL; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = NULL; +#endif +#ifdef USE_UNIX_SOCKETS + struct sockaddr_un *su = NULL; +#else + (void)salen; +#endif + + switch(sa->sa_family) { + case AF_INET: + si = (struct sockaddr_in *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si->sin_port); + return TRUE; + } + break; +#ifdef USE_IPV6 + case AF_INET6: + si6 = (struct sockaddr_in6 *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si6->sin6_port); + return TRUE; + } + break; +#endif +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { + su = (struct sockaddr_un *)sa; + curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ + *port = 0; + return TRUE; +#endif + default: + break; + } + + addr[0] = '\0'; + *port = 0; + errno = SOCKEAFNOSUPPORT; + return FALSE; +} + static void tcpnodelay(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t sockfd) @@ -1042,8 +1104,8 @@ static void set_local_ip(struct Curl_cfilter *cf, infof(data, "getsockname() failed with errno %d: %s", error, curlx_strerror(error, buffer, sizeof(buffer))); } - else if(!Curl_addr2string((struct sockaddr *)&ssloc, slen, - ctx->ip.local_ip, &ctx->ip.local_port)) { + else if(!sockaddr2string((struct sockaddr *)&ssloc, slen, + ctx->ip.local_ip, &ctx->ip.local_port)) { infof(data, "ssloc inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); } @@ -1060,9 +1122,9 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, /* store remote address and port used in this connection attempt */ ctx->ip.transport = ctx->transport; - if(!Curl_addr2string(&ctx->addr.curl_sa_addr, - (curl_socklen_t)ctx->addr.addrlen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { + if(!sockaddr2string(&ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { char buffer[STRERROR_LEN]; ctx->error = errno; @@ -2085,8 +2147,8 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, error, curlx_strerror(error, buffer, sizeof(buffer))); return; } - if(!Curl_addr2string((struct sockaddr *)&ssrem, plen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { + if(!sockaddr2string((struct sockaddr *)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); return; diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 767fd30e15ab..37ddc02576b3 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -25,8 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ - struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; @@ -34,23 +32,6 @@ struct connectdata; struct Curl_sockaddr_ex; struct ip_quadruple; -/* - * 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. - */ -struct Curl_sockaddr_ex { - int family; - int socktype; - int protocol; - unsigned int addrlen; - union { - struct sockaddr sa; - struct Curl_sockaddr_storage buf; - } addr; -}; #define curl_sa_addr addr.sa #define curl_sa_addrbuf addr.buf diff --git a/lib/connect.c b/lib/connect.c index a24530a6e065..d3533d47679c 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -26,9 +26,6 @@ #ifdef HAVE_NETINET_IN_H #include /* may need it */ #endif -#ifdef HAVE_SYS_UN_H -#include /* for sockaddr_un */ -#endif #ifdef HAVE_LINUX_TCP_H #include #elif defined(HAVE_NETINET_TCP_H) @@ -60,7 +57,6 @@ #include "cf-ip-happy.h" #include "cf-socket.h" #include "multiif.h" -#include "curlx/inet_ntop.h" #include "curlx/strparse.h" #include "vtls/vtls.h" /* for vtls cfilters */ #include "vquic/vquic.h" /* for QUIC cfilters */ @@ -212,59 +208,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) return FALSE; } -/* retrieves ip address and port from a sockaddr structure. note it calls - curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */ -bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port) -{ - struct sockaddr_in *si = NULL; -#ifdef USE_IPV6 - struct sockaddr_in6 *si6 = NULL; -#endif -#ifdef USE_UNIX_SOCKETS - struct sockaddr_un *su = NULL; -#else - (void)salen; -#endif - - switch(sa->sa_family) { - case AF_INET: - si = (struct sockaddr_in *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { - *port = ntohs(si->sin_port); - return TRUE; - } - break; -#ifdef USE_IPV6 - case AF_INET6: - si6 = (struct sockaddr_in6 *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { - *port = ntohs(si6->sin6_port); - return TRUE; - } - break; -#endif -#ifdef USE_UNIX_SOCKETS - case AF_UNIX: - if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { - su = (struct sockaddr_un *)sa; - curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); - } - else - addr[0] = 0; /* socket with no name */ - *port = 0; - return TRUE; -#endif - default: - break; - } - - addr[0] = '\0'; - *port = 0; - errno = SOCKEAFNOSUPPORT; - return FALSE; -} - /* * Used to extract socket and connectdata struct for the most recent * transfer on the given Curl_easy. diff --git a/lib/connect.h b/lib/connect.h index 3530d7050402..9ae8aec59f1a 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -72,9 +72,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); -bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port); - /* * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' * argument specifies if it is the end of a connection or a stream. diff --git a/lib/sockaddr.h b/lib/sockaddr.h index 2b0333508763..916360d08954 100644 --- a/lib/sockaddr.h +++ b/lib/sockaddr.h @@ -40,4 +40,22 @@ struct Curl_sockaddr_storage { } buffer; }; +/* + * 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. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr sa; + struct Curl_sockaddr_storage buf; + } addr; +}; + #endif /* HEADER_CURL_SOCKADDR_H */ diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 638a7d9a7777..dc15928f1954 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -62,6 +62,7 @@ #include "curlx/dynbuf.h" #include "http1.h" #include "select.h" +#include "sockaddr.h" #include "transfer.h" #include "bufref.h" #include "vquic/vquic.h" diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 427ef94d8684..1edd597ad72e 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -41,6 +41,7 @@ #include "progress.h" #include "select.h" #include "http1.h" +#include "sockaddr.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index 303f9849507e..d4052b762fb0 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -23,7 +23,6 @@ ***************************************************************************/ #include "unitcheck.h" #include "urldata.h" -#include "connect.h" #include "curl_addrinfo.h" static CURLcode t1607_setup(void) @@ -146,8 +145,8 @@ static CURLcode test_unit1607(const char *arg) if(tests[i].address[j] == &skip) continue; - if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen, - ipaddress, &port)) { + if(addr && !sockaddr2string(addr->ai_addr, addr->ai_addrlen, + ipaddress, &port)) { curl_mfprintf(stderr, "%s:%d tests[%zu] failed. " "getaddressinfo failed.\n", __FILE__, __LINE__, i); diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index c09edd22caa4..39ad6f90aa5e 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -145,8 +145,8 @@ static CURLcode test_unit1609(const char *arg) if(!addr && !tests[i].address[j]) break; - if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen, - ipaddress, &port)) { + if(addr && !sockaddr2string(addr->ai_addr, addr->ai_addrlen, + ipaddress, &port)) { curl_mfprintf(stderr, "%s:%d tests[%zu] failed. Curl_addr2string failed.\n", __FILE__, __LINE__, i); diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index 689e1dbd0e83..a8affbb9eb51 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -46,6 +46,7 @@ #include "cf-ip-happy.h" #include "multiif.h" #include "select.h" +#include "sockaddr.h" #include "curl_addrinfo.h" #include "curl_trc.h" From 946306b3e5f62f52ff72db85506eb5b789c4e66d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 11 Jun 2026 08:29:22 +0200 Subject: [PATCH 0379/1062] cf-ip-happy: update documentation Reported-by: correctmost on github Fixes #21957 Closes #21959 --- lib/cf-ip-happy.c | 10 ---------- lib/cf-ip-happy.h | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 5f99db59d3ce..92f15ff3471b 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -989,16 +989,6 @@ struct Curl_cftype Curl_cft_ip_happy = { cf_ip_happy_query, }; -/** - * Create an IP happy eyeball connection filter that uses the, once resolved, - * address information to connect on ip families based on connection - * configuration. - * @param pcf output, the created cfilter - * @param data easy handle used in creation - * @param conn connection the filter is created for - * @param cf_create method to create the sub-filters performing the - * actual connects. - */ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct Curl_peer *origin, diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index d2994aad43fb..dd9b29ac8f6a 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -33,18 +33,15 @@ struct Curl_peer; struct Curl_sockaddr_ex; /** - * Create a cfilter for making an "ip" connect to a peer. + * Create a cfilter to connect to `origin` via an optional `peer` + * using `transport_peer` and `addr`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. * `pcf`: the filter created on success * `data`: the transfer initiating the connect - * `peer`: the peer to connect to - * `transport_peer': the transport used for the peer connect * `conn`: the connection that gets connected - * `addr`: the socket address to connect to - * `tunnel_peer`: NULL or the peer to tunnel through - * `tunnel_transport`: the transport that goes through the tunnel * - * Such a filter may be used in "happy eyeball" scenarios, and its - * `connect` implementation needs to support non-blocking. Once connected, + * The filter is used in "happy eyeball" scenarios. Once connected, * it MAY be installed in the connection filter chain to serve transfers. */ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, @@ -57,6 +54,12 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_peer *tunnel_peer, uint8_t tunnel_transport); +/** + * Create an IP happy eyeball connection filter that connects to `origin` + * via an optional `peer` using `transport_peer`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. + */ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, struct Curl_peer *origin, From 8a867c206227f9b7a11f77f528902cf379bf0d43 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 11 Jun 2026 09:37:46 +0200 Subject: [PATCH 0380/1062] h3proxy: no stream userdata Do not set the easy handle opening a proxy tunnel as userdata on the stream. The ease handle might go out of scope long before the tunnel stream is closed. Closes #21962 --- lib/vquic/cf-ngtcp2-proxy.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index f23373f87d93..562b1bc0f1fd 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -934,18 +934,17 @@ static CURLcode cf_h3_proxy_submit(struct Curl_cfilter *cf, int rv; DEBUGASSERT(stream->id == -1); - rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + /* Do NOT set `data` as stream user data. The transfer `data` may + * get cleaned up long before the tunnel goes down. */ + rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, NULL); if(rv) { failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); result = CURLE_SEND_ERROR; goto out; } stream->id = sid; - ++ctx->used_bidi_streams; - - /* Do NOT set `data` as stream user data. The transfer `data` may - * get cleaned up long before the tunnel goes down. */ ts->stream = stream; + ++ctx->used_bidi_streams; CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); } From 9d93d4abe185bffac28a29e182e18abac0782f18 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 09:46:09 +0200 Subject: [PATCH 0381/1062] SECURITY-ADVISORY.md: expand Fill in missing information and rephrase a little Closes #21964 --- docs/SECURITY-ADVISORY.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/SECURITY-ADVISORY.md b/docs/SECURITY-ADVISORY.md index 4f3e1df2c9ad..34c342509d57 100644 --- a/docs/SECURITY-ADVISORY.md +++ b/docs/SECURITY-ADVISORY.md @@ -6,11 +6,14 @@ SPDX-License-Identifier: curl # Anatomy of a curl security advisory -As described in the [Security Process](https://curl.se/dev/secprocess.html) -document, when a security vulnerability has been reported to the project and -confirmed, we author an advisory document for the issue. It should ideally -be written in cooperation with the reporter to make sure all the angles and -details of the problem are gathered and described correctly and succinctly. +As described in the [vulnerability disclosure +policy](https://curl.se/dev/vuln-disclosure.html), when a vulnerability has +been reported to the project and and it has been confirmed by the team, we +author an advisory document for the issue. + +

This advisory document should ideally be written in cooperation with the +reporter to make sure all the angles and details of the problem are gathered +and described correctly and succinctly. ## New document @@ -31,13 +34,22 @@ in the same directory. It holds a large array with all published curl vulnerabilities. All fields should be filled in accordingly, separated by a pipe character (`|`). -The eleven fields for each CVE in `vuln.pm` are, in order: - -HTML page name, first vulnerable version, last vulnerable version, name of -the issue, CVE Id, announce date (`YYYYMMDD`), report to the project date -(`YYYYMMDD`), CWE, awarded reward amount (USD), area (single word), C-issue -(`-` if not a C issue at all, `OVERFLOW` , `OVERREAD`, `DOUBLE_FREE`, -`USE_AFTER_FREE`, `NULL_MISTAKE`, `UNINIT`) +The fields for every CVE in `vuln.pm` are, in order: + +1. HTML page name +2. first vulnerable version +3. last vulnerable version +4. name of the issue +5. CVE Id +6. announce date (`YYYYMMDD`) +7. report to the project date (`YYYYMMDD`) +8. CWE +9. awarded reward amount (USD) +10. area (single word) +11. C-issue (`-` if not a C issue at all, `OVERFLOW` , `OVERREAD`, `DOUBLE_FREE`, `USE_AFTER_FREE`, `NULL_MISTAKE`, `UNINIT`, `BAD_FREE`) +12. affected components: `both`, `lib` or `tool` +13. severity: `low`, `medium`, `high` or `critical` +14. URL to the initial report (often on HackerOne) ### `Makefile` From 9cf6b70ad7e90ded63d0e4c5da3af9c447e1ef43 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 09:29:22 +0200 Subject: [PATCH 0382/1062] multi: remove a stale comment It tricks humans and AIs alike. Closes #21961 --- lib/multi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index aba2df3d5612..80fc1d824c51 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2193,8 +2193,6 @@ static CURLMcode multistate_do(struct Curl_easy *data, /* Perform the protocol's DO action */ result = multi_do(data, &dophase_done); - /* When multi_do() returns failure, data->conn might be NULL! */ - if(!result) { if(!dophase_done) { #ifndef CURL_DISABLE_FTP From b7c9229cc6d22fbbdd9f69d9f2ff4f9f280480a7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Jun 2026 23:41:51 +0200 Subject: [PATCH 0383/1062] CURLINFO_CONTENT_LENGTH_UPLOAD_T.md: expand Fixes #21953 Reported-by: BazaarAcc32 on github Closes #21956 --- docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md index 219481f43df1..7b5d05852155 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md @@ -31,6 +31,11 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONTENT_LENGTH_UPLOAD_T, Pass a pointer to a *curl_off_t* to receive the specified size of the upload. Stores -1 if the size is not known. +This is the size set by the client prior to the transfer start. The expected +upload amount. Compare this with CURLINFO_SIZE_UPLOAD_T(3), which is the +amount of data that was actually uploaded in the end. In many cases those two +numbers are identical. + # %PROTOCOLS% # EXAMPLE From 2b336e6b73c3da9a0cf645aada31853c80985963 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 10:59:05 +0200 Subject: [PATCH 0384/1062] content_encoding: fix non-last chunked rejection Even when two 'chunked' are listed and neither is the last encoding the transfer is rejected. Verified by test 1722 and 1723 Reported-by: violet12331 on hackerone Closes #21966 --- lib/content_encoding.c | 51 +++++++++++++++++------------------ tests/data/Makefile.am | 2 +- tests/data/test1722 | 61 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test1723 | 61 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 tests/data/test1722 create mode 100644 tests/data/test1723 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 73eb5201eb48..fd0807725997 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -789,17 +789,6 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, } cwt = find_unencode_writer(name, namelen, phase); - if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { - /* A 'chunked' transfer encoding has already been added. - * Ignore duplicates. See #13451. - * Also RFC 9112, ch. 6.1: - * "A sender MUST NOT apply the chunked transfer coding more than - * once to a message body." - */ - CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); - return CURLE_OK; - } - if(is_transfer && !is_chunked && Curl_cwriter_get_by_name(data, "chunked")) { /* RFC 9112, ch. 6.1: @@ -814,21 +803,31 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, "Transfer-Encoding"); return CURLE_BAD_CONTENT_ENCODING; } - - if(!cwt) - cwt = &error_writer; /* Defer error at use. */ - - result = Curl_cwriter_create(&writer, data, cwt, phase); - CURL_TRC_WRITE(data, "added %s decoder %s -> %d", - is_transfer ? "transfer" : "content", cwt->name, - (int)result); - if(result) - return result; - - result = Curl_cwriter_add(data, writer); - if(result) { - Curl_cwriter_free(data, writer); - return result; + if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { + /* A 'chunked' transfer encoding has already been added. + * Ignore duplicates. See #13451. + * Also RFC 9112, ch. 6.1: + * "A sender MUST NOT apply the chunked transfer coding more than + * once to a message body." + */ + CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); + } + else { + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ + + result = Curl_cwriter_create(&writer, data, cwt, phase); + CURL_TRC_WRITE(data, "added %s decoder %s -> %d", + is_transfer ? "transfer" : "content", cwt->name, + (int)result); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } } if(is_chunked) has_chunked = TRUE; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 50a5e5629e93..5e5395b126a1 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -225,7 +225,7 @@ test1685 test1686 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ -test1720 test1721 \ +test1720 test1721 test1722 test1723 \ \ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ diff --git a/tests/data/test1722 b/tests/data/test1722 new file mode 100644 index 000000000000..e51f26f4b432 --- /dev/null +++ b/tests/data/test1722 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + +# Server-side + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 +Transfer-Encoding: chunked, another +Connection: mooo + +40%CR +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%CR +30%CR +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%CR +21;heresatest=moooo%CR +cccccccccccccccccccccccccccccccc +%CR +0%CR +%CR + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 + + + +# Client-side + + +http + + +HTTP with chunked Transfer-Encoding not listed last + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +61 + + + + diff --git a/tests/data/test1723 b/tests/data/test1723 new file mode 100644 index 000000000000..ad93c3016883 --- /dev/null +++ b/tests/data/test1723 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + +# Server-side + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 +Transfer-Encoding: chunked, chunked, another +Connection: mooo + +40%CR +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%CR +30%CR +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%CR +21;heresatest=moooo%CR +cccccccccccccccccccccccccccccccc +%CR +0%CR +%CR + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 + + + +# Client-side + + +http + + +HTTP with two chunked Transfer-Encoding not listed last + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +61 + + + + From f763847982dcbcb95ee6f02b4fa654994ef60c5b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 15:44:28 +0200 Subject: [PATCH 0385/1062] cf-ip-happy.c: minor comment typo --- lib/cf-ip-happy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 92f15ff3471b..249c4d1c23e3 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -886,7 +886,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, ctx->ballers.winner->cf = NULL; cf_ip_happy_ctx_clear(ctx, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - /* whatever errors where reported by ballers, clear our errorbuf */ + /* whatever errors were reported by ballers, clear our errorbuf */ Curl_reset_fail(data); if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) From 08ae71f33dfb912a8f5f5f3b1b5d42b1ab136f8d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 15:39:06 +0200 Subject: [PATCH 0386/1062] CURLOPT_WRITEFUNCTION.md: remove stray reference to HSTS It appears to have landed here by mistake Closes #21968 --- docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md index 2e240d7b68e2..792ed3d3b8ff 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md @@ -72,9 +72,6 @@ Set this option to NULL to get the internal default function used instead of your callback. The internal default function writes the data to the FILE * given with CURLOPT_WRITEDATA(3). -This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to -do that. - # DEFAULT fwrite(3) From 7f45bb8f5b369ae6d952a0f959321af2a57c03fe Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Jun 2026 15:46:23 +0200 Subject: [PATCH 0387/1062] http_digest: return better error It is not a content encoding error. Found by the GitHub AI thing. Closes #21969 --- lib/http_digest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http_digest.c b/lib/http_digest.c index 0e10700407e8..640553867f83 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -54,7 +54,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data, } if(!checkprefix("Digest", header) || !ISBLANK(header[6])) - return CURLE_BAD_CONTENT_ENCODING; + return CURLE_AUTH_ERROR; header += strlen("Digest"); curlx_str_passblanks(&header); From 663b156a37943866ff6ffc98ce62bb9ae5a40789 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 11 Jun 2026 17:41:21 +0200 Subject: [PATCH 0388/1062] GHA/windows: bump Cygwin Action and adjust version number It seems the commit hash behind the v6.1 tag is changing, and the latest version is actually v6.0.2, which is currently mapped to the v6.1 hash. Fixing: ``` warning[ref-version-mismatch]: action's hash pin has mismatched or missing version comment --> .github/workflows/windows.yml:98:87 | 98 | - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6.1 | --------------------------------------------------------------------------- ^^^^ points to commit 3f0a3f9f988f | | | is pointed to by tag v6.0.1 ``` Ref: https://github.com/cygwin/cygwin-install-action/issues/59 Closes #21974 --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 75753861449e..2d4dc5540e7e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -95,7 +95,7 @@ jobs: install: 'libssl-devel libssh2-devel' } fail-fast: false steps: - - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6.1 + - uses: cygwin/cygwin-install-action@3f0a3f9f988f7e96b8c18098ae05eaec175f5b52 # v6.0.2 with: platform: ${{ matrix.platform }} work-vol: 'D:' From c0d433d0809326b4015232765ac913ab336a0172 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:30:28 +0000 Subject: [PATCH 0389/1062] GHA: update debian:bookworm-slim Docker digest to 96e378d Closes #21958 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 213caebc9ab8..369583f62fdc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:0104b334637a5f19aa9c983a91b54c89887c0984081f2068983107a6f6c21eeb +FROM debian:bookworm-slim@sha256:96e378d7e6531ac9a15ad505478fcc2e69f371b10f5cdf87857c4b8188404716 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From 04a85a1d385fb94ef076764c926a8c0006eff19f Mon Sep 17 00:00:00 2001 From: sourceturner <186975065+sourceturner@users.noreply.github.com> Date: Sat, 6 Jun 2026 17:23:49 +0200 Subject: [PATCH 0390/1062] asyn-thrdd: add IPv6 guards It seems that the usual '#ifdef USE_IPV6' guards have been overlooked in lib/asyn-thrdd.c. This commit makes sure that the code compiles if IPv6 is not available. Closes #21881 --- lib/asyn-thrdd.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 9ce41b20d01e..c64f876830fd 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -481,8 +481,13 @@ static void async_thrdd_report_item(struct Curl_easy *data, struct dynbuf tmp; const char *sep = ""; const struct Curl_addrinfo *ai = item->res; - int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; CURLcode result; + int ai_family; +#ifdef USE_IPV6 + ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; +#else + ai_family = AF_INET; +#endif if(!CURL_TRC_DNS_is_verbose(data)) return; @@ -792,10 +797,12 @@ const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, if(thrdd->res_A) return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); break; +#ifdef USE_IPV6 case AF_INET6: if(thrdd->res_AAAA) return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); break; +#endif default: break; } From 2a606c68faa10ac726a246d3a78f03488b22c26c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 9 Jun 2026 11:10:00 +0200 Subject: [PATCH 0391/1062] tidy-up: miscellaneous - GHA/windows: drop redundant double-quotes. - CMake/PickyWarnings: improve/shorten comment. - INTERNALS: fix typo in LibreSSL release date. - drop redundant parentheses from single variables and sole `#if` expressions. - cf-ip-happy: fix missing space from error string. - telnet: fix parentheses in commented PP code. - lib1922: fix typo test output text. - smbserver: unfold lines. - smbserver: use f-string. - smbserver: initialize binary string as b``. - fix typos in comments. Closes #21972 --- .github/workflows/windows.yml | 2 +- CMake/PickyWarnings.cmake | 2 +- docs/INTERNALS.md | 2 +- docs/examples/chkspeed.c | 2 +- docs/examples/http2-download.c | 2 +- docs/examples/http2-upload.c | 2 +- lib/cf-ip-happy.c | 2 +- lib/http2.c | 4 ++-- lib/multi.c | 16 ++++++++-------- lib/peer.h | 2 +- lib/telnet.c | 4 ++-- lib/vquic/cf-ngtcp2-proxy.c | 2 +- lib/vtls/schannel_verify.c | 2 +- tests/data/test1922 | 2 +- tests/libtest/lib1922.c | 2 +- tests/smbserver.py | 13 +++++-------- 16 files changed, 29 insertions(+), 32 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2d4dc5540e7e..8d7cd2b4cfb3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -735,7 +735,7 @@ jobs: if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 2 run: | - if "bld/src/curl.exe" --disable -V 2>/dev/null | grep smb; then + if bld/src/curl.exe --disable -V 2>/dev/null | grep smb; then python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt fi diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index 4815c4fc8373..a4f0d52c9bee 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -391,7 +391,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-Wno-conversion") # Avoid false positives endif() endif() - elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1951) # Skip for untested/unreleased newer versions + elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1951) # Enable for tested versions only list(APPEND _picky "-Wall") list(APPEND _picky "-wd4061") # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label list(APPEND _picky "-wd4191") # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)' diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index ee32a2dbfd13..4fb876d21c03 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -32,7 +32,7 @@ We aim to support these or later versions. - libidn2 2.0.0 (2017-03-29) - libgsasl 1.6.0 (2010-12-14) - libpsl 0.16.0 (2016-12-10) -- LibreSSL 2.9.1 (2019-04-22) +- LibreSSL 2.9.1 (2019-04-21) - libssh 0.9.0 (2019-06-28) - libssh2 1.9.0 (2019-06-20) - mbedTLS 3.2.0 (2022-07-11) diff --git a/docs/examples/chkspeed.c b/docs/examples/chkspeed.c index 4274eb0c9051..7010acc4aeca 100644 --- a/docs/examples/chkspeed.c +++ b/docs/examples/chkspeed.c @@ -105,7 +105,7 @@ int main(int argc, const char *argv[]) case 'm': case 'M': if(argv[0][2] == '=') { - int m = atoi((*argv) + 3); + int m = atoi(*argv + 3); switch(m) { case 1: url = URL_1M; diff --git a/docs/examples/http2-download.c b/docs/examples/http2-download.c index 80c3365bb255..3d612781bbfe 100644 --- a/docs/examples/http2-download.c +++ b/docs/examples/http2-download.c @@ -176,7 +176,7 @@ static int setup(struct transfer *t, int num) /* HTTP/2 please */ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); -#if (CURLPIPE_MULTIPLEX > 0) +#if CURLPIPE_MULTIPLEX > 0 /* wait for pipe connection to confirm */ curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index 4a3dc0c4c7be..cad74afc50de 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -275,7 +275,7 @@ static int setup(struct input *t, int num, const char *upload) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); -#if (CURLPIPE_MULTIPLEX > 0) +#if CURLPIPE_MULTIPLEX > 0 /* wait for pipe connection to confirm */ curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 249c4d1c23e3..53b79e2fd444 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -342,7 +342,7 @@ static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer); if(!bs->cf_create) { failf(data, "unsupported transport type %u%s", - transport_peer, tunnel_peer ? "to proxy" : ""); + transport_peer, tunnel_peer ? " to proxy" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } Curl_peer_link(&bs->origin, origin); diff --git a/lib/http2.c b/lib/http2.c index ca579f6019fc..ec7267124405 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -47,11 +47,11 @@ #include "curlx/dynbuf.h" #include "headers.h" -#if (NGHTTP2_VERSION_NUM < 0x010c00) +#if NGHTTP2_VERSION_NUM < 0x010c00 #error too old nghttp2 version, upgrade! #endif -#if (NGHTTP2_VERSION_NUM >= 0x010c00) +#if NGHTTP2_VERSION_NUM >= 0x010c00 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif diff --git a/lib/multi.c b/lib/multi.c index 80fc1d824c51..d0fa68ab4c45 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2504,7 +2504,7 @@ static CURLMcode multistate_connecting(struct Curl_easy *data, } if(!Curl_xfer_recv_is_paused(data)) { *result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); - if(connected && !(*result)) { + if(connected && !*result) { if(!data->conn->bits.reuse && Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { /* new connection, can multiplex, wake pending handles */ @@ -2531,7 +2531,7 @@ static CURLMcode multistate_protoconnect(struct Curl_easy *data, { bool protocol_connected = FALSE; - if(!(*result) && data->conn->bits.reuse) { + if(!*result && data->conn->bits.reuse) { /* ftp seems to hang when protoconnect on reused connection since we * handle PROTOCONNECT in general inside the filters, it seems wrong to * restart this on a reused connection. @@ -2539,14 +2539,14 @@ static CURLMcode multistate_protoconnect(struct Curl_easy *data, multistate(data, MSTATE_DO); return CURLM_CALL_MULTI_PERFORM; } - if(!(*result)) + if(!*result) *result = protocol_connect(data, &protocol_connected); - if(!(*result) && !protocol_connected) { + if(!*result && !protocol_connected) { /* switch to waiting state */ multistate(data, MSTATE_PROTOCONNECTING); return CURLM_CALL_MULTI_PERFORM; } - else if(!(*result)) { + else if(!*result) { /* protocol connect has completed, go WAITDO or DO */ multistate(data, MSTATE_DO); return CURLM_CALL_MULTI_PERFORM; @@ -2567,7 +2567,7 @@ static CURLMcode multistate_protoconnecting(struct Curl_easy *data, /* protocol-specific connect phase */ *result = protocol_connecting(data, &protocol_connected); - if(!(*result) && protocol_connected) { + if(!*result && protocol_connected) { /* after the connect has completed, go WAITDO or DO */ multistate(data, MSTATE_DO); return CURLM_CALL_MULTI_PERFORM; @@ -2590,7 +2590,7 @@ static CURLMcode multistate_doing(struct Curl_easy *data, /* we continue DOING until the DO phase is complete */ DEBUGASSERT(data->conn); *result = protocol_doing(data, &dophase_done); - if(!(*result)) { + if(!*result) { if(dophase_done) { /* after DO, go DO_DONE or DO_MORE */ multistate(data, data->conn->bits.do_more ? @@ -2619,7 +2619,7 @@ static CURLMcode multistate_doing_more(struct Curl_easy *data, DEBUGASSERT(data->conn); *result = multi_do_more(data, &control); - if(!(*result)) { + if(!*result) { if(control != DOMORE_INCOMPLETE) { /* if DONE, advance to DO_DONE if GOBACK, go back to DOING */ diff --git a/lib/peer.h b/lib/peer.h index c18bad4501bc..1ceb230739b2 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -78,7 +78,7 @@ void Curl_peer_unlink(struct Curl_peer **ppeer); /* TRUE if both peers are NULL or have completely same properties. */ bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2); -/* TRUE if both peers are NULL or have properties except the scheme. */ +/* TRUE if both peers are NULL or have same properties except the scheme. */ bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, diff --git a/lib/telnet.c b/lib/telnet.c index 83114ef2aa64..29a4f750e8ae 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -75,8 +75,8 @@ #define CURL_SB_LEN(x) ((x)->subend - (x)->subpointer) /* For posterity: -#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) -#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ +#define CURL_SB_PEEK(x) (*(x)->subpointer & 0xff) +#define CURL_SB_EOF(x) ((x)->subpointer >= (x)->subend) */ /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 562b1bc0f1fd..9481bd038d34 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -583,7 +583,7 @@ static nghttp3_ssize cb_h3_tunnel_read_data(nghttp3_conn *conn, nwritten += vec[nvecs].len; ++nvecs; } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ + DEBUGASSERT(nvecs > 0); /* we SHOULD have been able to peek */ } if(!nwritten) { diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index b3586d1221cc..e5fe2249a66e 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -107,7 +107,7 @@ static const char *c_memmem(const void *haystack, size_t haystacklen, return NULL; first = *(const char *)needle; for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && !memcmp(p, needle, needlelen)) + if((*p == first) && !memcmp(p, needle, needlelen)) return p; return NULL; diff --git a/tests/data/test1922 b/tests/data/test1922 index dcf30557ffe1..9c35ccff6f18 100644 --- a/tests/data/test1922 +++ b/tests/data/test1922 @@ -78,7 +78,7 @@ Proxy-Connection: Keep-Alive -First request: HTTPS cache populated +First request: HSTS cache populated Dup effective URL: https://hsts.example.com/%TESTNUMBER # CURLE_COULDNT_CONNECT (7) is intentional: The proxy rejects the CONNECT diff --git a/tests/libtest/lib1922.c b/tests/libtest/lib1922.c index f53b773df61b..66e805b60829 100644 --- a/tests/libtest/lib1922.c +++ b/tests/libtest/lib1922.c @@ -85,7 +85,7 @@ static CURLcode test_lib1922(const char *URL) (int)result, curl_easy_strerror(result)); goto test_cleanup; } - curl_mprintf("First request: HTTPS cache populated\n"); + curl_mprintf("First request: HSTS cache populated\n"); dup = curl_easy_duphandle(curl); if(!dup) { diff --git a/tests/smbserver.py b/tests/smbserver.py index 49c6162463b5..44daefc15a11 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -198,8 +198,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): # Currently we only support reading files. if disposition != imp_smb.FILE_OPEN: - raise SmbError(STATUS_ACCESS_DENIED, - "Only support reading files") + raise SmbError(STATUS_ACCESS_DENIED, "Only support reading files") # Check to see if the path we were given is actually a # magic path which needs generating on the fly. @@ -292,16 +291,14 @@ def get_share_path(self, conn_data, root_fid, tid): if root_fid > 0: # If we have a rootFid, the path is relative to that fid path = conn_data["OpenedFiles"][root_fid]["FileName"] - log.debug("RootFid present %s!" % path) + log.debug(f'RootFid present {path}!') else: if "path" in conn_shares[tid]: path = conn_shares[tid]["path"] else: - raise SmbError(STATUS_ACCESS_DENIED, - "Connection share had no path") + raise SmbError(STATUS_ACCESS_DENIED, "Connection share had no path") else: - raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, - "TID was invalid") + raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, "TID was invalid") return path @@ -315,7 +312,7 @@ def get_server_path(self, requested_filename): log.debug("[SMB] Created %s (%d) for storing '%s'", filename, fid, requested_filename) - contents = "" + contents = b'' if requested_filename == VERIFIED_REQ: log.debug("[SMB] Verifying server is alive") From e35ba09f47d55ff315a381032856e35f6da3f6dd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 11 Jun 2026 17:22:30 +0200 Subject: [PATCH 0392/1062] tidy-up: add spaces around equal operators where missing Found via regex search: `=[^~>= ]` Closes #21975 --- .github/scripts/randcurl.pl | 4 +- docs/examples/adddocsref.pl | 2 +- docs/examples/version-check.pl | 12 +- docs/libcurl/mksymbolsmanpage.pl | 4 +- docs/libcurl/symbols.pl | 8 +- lib/optiontable.pl | 12 +- scripts/badwords | 4 +- scripts/cd2cd | 12 +- scripts/cd2nroff | 14 +- scripts/cdall | 2 +- scripts/checksrc.pl | 50 +++---- scripts/managen | 86 ++++++------ scripts/mdlinkcheck | 2 +- scripts/nroff2cd | 2 +- scripts/release-notes.pl | 14 +- scripts/singleuse.pl | 4 +- scripts/top-complexity | 14 +- scripts/top-length | 14 +- src/mkhelp.pl | 4 +- tests/allversions.pm | 2 +- tests/appveyor.pm | 2 +- tests/ftpserver.pl | 116 ++++++++-------- tests/getpart.pm | 36 ++--- tests/globalconfig.pm | 72 +++++----- tests/http2-server.pl | 2 +- tests/http3-server.pl | 2 +- tests/libtest/mk-lib1521.pl | 12 +- tests/libtest/test1013.pl | 4 +- tests/libtest/test1022.pl | 4 +- tests/memanalyze.pl | 6 +- tests/memanalyzer.pm | 18 +-- tests/pathhelp.pm | 2 +- tests/runtests.pl | 229 +++++++++++++++---------------- tests/secureserver.pl | 2 +- tests/serverhelp.pm | 3 +- tests/servers.pm | 157 +++++++++++---------- tests/test1119.pl | 24 ++-- tests/test1135.pl | 6 +- tests/test1139.pl | 22 +-- tests/test1140.pl | 4 +- tests/test1165.pl | 20 +-- tests/test1167.pl | 14 +- tests/test1173.pl | 14 +- tests/test1175.pl | 4 +- tests/test1177.pl | 8 +- tests/test1222.pl | 6 +- tests/test1275.pl | 16 +-- tests/test1276.pl | 4 +- tests/test1477.pl | 18 +-- tests/test1486.pl | 2 +- tests/test1488.pl | 8 +- tests/test1544.pl | 6 +- tests/test745.pl | 2 +- tests/test971.pl | 8 +- tests/testcurl.pl | 64 ++++----- tests/testutil.pm | 6 +- 56 files changed, 593 insertions(+), 596 deletions(-) diff --git a/.github/scripts/randcurl.pl b/.github/scripts/randcurl.pl index 83fc0e795f8f..9478813c8b3b 100755 --- a/.github/scripts/randcurl.pl +++ b/.github/scripts/randcurl.pl @@ -142,7 +142,7 @@ sub runone { $a .= " ".addarg(); } - my $cmd="$curl$a $url"; + my $cmd = "$curl$a $url"; my $rc = system("$cmd >curl-output 2>&1 > 8; #my $rc = system("valgrind -q $cmd >/dev/null 2>&1 > 8; @@ -194,7 +194,7 @@ sub runconfig { print C "$a\n"; close(C); - my $cmd="$curl -K config $url"; + my $cmd = "$curl -K config $url"; my $rc = system("$cmd >curl-output 2>&1 > 8; diff --git a/docs/examples/adddocsref.pl b/docs/examples/adddocsref.pl index cbc48c040716..8c9d7f8c20bf 100755 --- a/docs/examples/adddocsref.pl +++ b/docs/examples/adddocsref.pl @@ -30,7 +30,7 @@ use File::Copy; -my $docroot="https://curl.se/libcurl/c"; +my $docroot = "https://curl.se/libcurl/c"; for my $f (@ARGV) { open(NEW, ">$f.new"); diff --git a/docs/examples/version-check.pl b/docs/examples/version-check.pl index e6ad784fc811..a80f3ae632c2 100755 --- a/docs/examples/version-check.pl +++ b/docs/examples/version-check.pl @@ -47,15 +47,15 @@ my %rem; while() { if(/(^CURL[^ \n]*) *(.*)/) { - my ($sym, $rest)=($1, $2); - my @a=split(/ +/, $rest); + my ($sym, $rest) = ($1, $2); + my @a = split(/ +/, $rest); - $doc{$sym}=$a[0]; # when it was introduced + $doc{$sym} = $a[0]; # when it was introduced if($a[2]) { # this symbol is documented to have been present the last time # in this release - $rem{$sym}=$a[2]; + $rem{$sym} = $a[2]; } } } @@ -63,9 +63,9 @@ close(S); sub age { - my ($ver)=@_; + my ($ver) = @_; - my @s=split(/\./, $ver); + my @s = split(/\./, $ver); return $s[0]*10000+$s[1]*100+($s[2] || 0); } diff --git a/docs/libcurl/mksymbolsmanpage.pl b/docs/libcurl/mksymbolsmanpage.pl index c70e99495758..9db028e7c4fc 100755 --- a/docs/libcurl/mksymbolsmanpage.pl +++ b/docs/libcurl/mksymbolsmanpage.pl @@ -70,7 +70,7 @@ ; sub nameref { - my ($n)=@_; + my ($n) = @_; if($n =~ /^CURLOPT_/) { if($n eq "CURLOPT_RTSPHEADER") { $n = "CURLOPT_HTTPHEADER"; @@ -289,7 +289,7 @@ sub nameref { while() { if($_ =~ /^(CURL[A-Z0-9_.]*) *(.*)/i) { - my ($symbol, $rest)=($1,$2); + my ($symbol, $rest) = ($1, $2); my ($intro, $dep, $rem); if($rest =~ s/^([0-9.]*) *//) { $intro = $1; diff --git a/docs/libcurl/symbols.pl b/docs/libcurl/symbols.pl index 4126fe54e1a6..2717ac3a5550 100755 --- a/docs/libcurl/symbols.pl +++ b/docs/libcurl/symbols.pl @@ -50,7 +50,7 @@ open F, ") { if(/^(CURL[^ ]*)[ \t]*(.*)/) { - my ($sym, $vers)=($1, $2); + my ($sym, $vers) = ($1, $2); my $intr; my $rm; @@ -77,11 +77,11 @@ sub str2num { # is there removed info? if($vers =~ /([\d.]+)[ \t-]+([\d.-]+)[ \t]+([\d.]+)/) { - ($intr, $dep, $rm)=($1, $2, $3); + ($intr, $dep, $rm) = ($1, $2, $3); } # is it a dep-only line? elsif($vers =~ /([\d.]+)[ \t-]+([\d.]+)/) { - ($intr, $dep)=($1, $2); + ($intr, $dep) = ($1, $2); } else { $intr = $vers; diff --git a/lib/optiontable.pl b/lib/optiontable.pl index db0659cbb039..de6fb55151f2 100755 --- a/lib/optiontable.pl +++ b/lib/optiontable.pl @@ -39,7 +39,7 @@ HEAD ; -my $lastnum=0; +my $lastnum = 0; my %opt; my %type; @@ -47,7 +47,7 @@ my %alias; sub add { - my($optstr, $typestr, $num)=@_; + my($optstr, $typestr, $num) = @_; my $name; # remove all spaces from the type $typestr =~ s/ //g; @@ -59,7 +59,7 @@ sub add { } if($optstr =~ /^CURLOPT_(.*)/) { - $name=$1; + $name = $1; } $ext =~ s/CURLOPTTYPE_//; $ext =~ s/CBPOINT/CBPTR/; @@ -89,7 +89,7 @@ sub add { $fl .= $1; # the end - my @p=split(/, */, $fl); + my @p = split(/, */, $fl); add($p[0], $p[1], $p[2]); undef $fl; } @@ -106,14 +106,14 @@ sub add { } if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) { - my($opt, $type, $num)=($1,$2,$3); + my($opt, $type, $num) = ($1, $2, $3); add($opt, $type, $num); } # alias for an older option # old = new if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) { - my ($o, $n)=($1, $2); + my ($o, $n) = ($1, $2); # skip obsolete ones if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) { $o =~ s/^CURLOPT_//; diff --git a/scripts/badwords b/scripts/badwords index f833573af7c8..7d371b3beec8 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -184,14 +184,14 @@ while() { if(/^---:([^:]*):(.*)/) { # whitelist file + word my $word = lc($2); - $wl{"$1:$word"}=1; + $wl{"$1:$word"} = 1; } elsif($_ =~ /^---(.+)/) { # whitelist word push @whitelist, $1; } elsif($_ =~ /^(.*)([:=])(.*)/) { - my ($bad, $sep, $better)=($1, $2, $3); + my ($bad, $sep, $better) = ($1, $2, $3); if($sep eq "=") { $alt{$bad} = $better; push @exact, $bad; diff --git a/scripts/cd2cd b/scripts/cd2cd index 182cb62c8a34..e66e3f844fa1 100755 --- a/scripts/cd2cd +++ b/scripts/cd2cd @@ -79,7 +79,7 @@ sub outseealso { sub single { my @head; my @seealso; - my ($f)=@_; + my ($f) = @_; my $title; my $section; my $source; @@ -103,13 +103,13 @@ sub single { next; } if(/^Title: *(.*)/i) { - $title=$1; + $title = $1; } elsif(/^Section: *(.*)/i) { - $section=$1; + $section = $1; } elsif(/^Source: *(.*)/i) { - $source=$1; + $source = $1; } elsif(/^See-also: +(.*)/i) { $salist = 0; @@ -130,10 +130,10 @@ sub single { } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^---/) { diff --git a/scripts/cd2nroff b/scripts/cd2nroff index d051433a72c6..99c2e8a576bf 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -199,7 +199,7 @@ sub single { my @proto; my @tls; my $d; - my ($f)=@_; + my ($f) = @_; my $copyright; my $errors = 0; my $fh; @@ -234,13 +234,13 @@ sub single { next; } if(/^Title: *(.*)/i) { - $title=$1; + $title = $1; } elsif(/^Section: *(.*)/i) { - $section=$1; + $section = $1; } elsif(/^Source: *(.*)/i) { - $source=$1; + $source = $1; } elsif(/^See-also: +(.*)/i) { $list = 1; # 1 for see-also @@ -260,7 +260,7 @@ sub single { $list = 3; # 3 for TLS backend } elsif(/^Added-in: *(.*)/i) { - $addedin=$1; + $addedin = $1; if(($addedin !~ /^[0-9.]+[0-9]\z/) && ($addedin ne "n/a")) { print STDERR "$f:$line:1:ERROR: invalid version number in Added-in line: $addedin\n"; @@ -285,10 +285,10 @@ sub single { } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^---/) { diff --git a/scripts/cdall b/scripts/cdall index 1ea0f42f255f..15111a5c4231 100755 --- a/scripts/cdall +++ b/scripts/cdall @@ -29,7 +29,7 @@ use strict; use warnings; sub convert { - my ($dir)=@_; + my ($dir) = @_; opendir(my $dh, $dir) || die "could not open $dir"; my @cd = grep { /\.md\z/ && -f "$dir/$_" } readdir($dh); closedir $dh; diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 41bf846bcd42..7b9f814709af 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -35,8 +35,8 @@ my $serrors = 0; my $suppressed; # skipped problems my $file; -my $dir="."; -my $wlist=""; +my $dir = "."; +my $wlist = ""; my @alist; my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys'; my $verbose = 0; @@ -206,10 +206,10 @@ sub readskiplist { open(my $W, '<', "$dir/checksrc.skip") or return; - my @all=<$W>; + my @all = <$W>; for(@all) { $windows_os ? $_ =~ s/\r?\n$// : chomp; - $skiplist{$_}=1; + $skiplist{$_} = 1; } close($W); } @@ -431,8 +431,8 @@ sub accept_violations { print "'$r' is not a warning to accept!\n"; exit; } - $ignore{$r}=999999; - $ignore_used{$r}=0; + $ignore{$r} = 999999; + $ignore_used{$r} = 0; } } @@ -463,9 +463,9 @@ sub enable_warn { $line, length($what) + 11, $file, $l, "No warning was inhibited!"); } - $ignore_set{$what}=0; - $ignore_used{$what}=0; - $ignore{$what}=0; + $ignore_set{$what} = 0; + $ignore_used{$what} = 0; + $ignore{$what} = 0; } sub checksrc { my ($cmd, $line, $file, $l) = @_; @@ -474,9 +474,9 @@ sub checksrc { $what =~ s: *\*/$::; # cut off end of C comment # print "ENABLE $enable WHAT $what\n"; if($enable eq "disable") { - my ($warn, $scope)=($1, $2); + my ($warn, $scope) = ($1, $2); if($what =~ /([^ ]*) +(.*)/) { - ($warn, $scope)=($1, $2); + ($warn, $scope) = ($1, $2); } else { $warn = $what; @@ -484,7 +484,7 @@ sub checksrc { } # print "IGNORE $warn for SCOPE $scope\n"; if($scope eq "all") { - $scope=999999; + $scope = 999999; } # Comparing for a literal zero rather than the scalar value zero @@ -502,9 +502,9 @@ sub checksrc { "$warn already disabled from line $ignore_set{$warn}"); } else { - $ignore{$warn}=$scope; - $ignore_set{$warn}=$line; - $ignore_line[$line]=$l; + $ignore{$warn} = $scope; + $ignore_set{$warn} = $line; + $ignore_line[$line] = $l; } } elsif($enable eq "enable") { @@ -528,8 +528,8 @@ sub scanfile { my ($file) = @_; my $line = 1; - my $prevl=""; - my $prevpl=""; + my $prevl = ""; + my $prevpl = ""; my $l = ""; my $prep = 0; my $prevp = 0; @@ -542,8 +542,8 @@ sub scanfile { open(my $R, '<', $file) || die "failed to open $file"; - my $incomment=0; - my @copyright=(); + my $incomment = 0; + my @copyright = (); my %includes; checksrc_clear(); # for file based ignores accept_violations(); @@ -649,7 +649,7 @@ sub scanfile { } else { # still within a comment - $l=""; + $l = ""; } } @@ -702,7 +702,7 @@ sub scanfile { my $nostr = nostrings($l); # check spaces after for/if/while/function call if($nostr =~ /^(.*)(for|if|while|switch| ([a-zA-Z0-9_]+)) \((.)/) { - my ($leading, $word, $extra, $first)=($1,$2,$3,$4); + my ($leading, $word, $extra, $first) = ($1, $2, $3, $4); if($1 =~ / *\#/) { # this is a #if, treat it differently } @@ -898,15 +898,15 @@ sub scanfile { # check for comma without space if($l =~ /^(.*),[^ \n]/) { - my $pref=$1; - my $ign=0; + my $pref = $1; + my $ign = 0; if($pref =~ / *\#/) { # this is a #if, treat it differently - $ign=1; + $ign = 1; } elsif($pref =~ /\/\*/) { # this is a comment - $ign=1; + $ign = 1; } elsif($pref =~ /[\"\']/) { $ign = 1; diff --git a/scripts/managen b/scripts/managen index 2b82e74a8bbf..2da6ec935429 100755 --- a/scripts/managen +++ b/scripts/managen @@ -66,7 +66,7 @@ my $indent = 4; # get the long name version, return the man page string sub manpageify { - my ($k, $manpage)=@_; + my ($k, $manpage) = @_; my $trail = ''; # the matching pattern might include a trailing dot that cannot be part of # the option name @@ -85,7 +85,7 @@ sub manpageify { return "--$k$trail"; } -my $colwidth=79; # max number of columns +my $colwidth = 79; # max number of columns sub prefixline { my ($num) = @_; @@ -220,7 +220,7 @@ sub printdesc { } sub seealso { - my($standalone, $data)=@_; + my($standalone, $data) = @_; if($standalone) { return sprintf ".SH \"SEE ALSO\"\n$data\n"; @@ -231,7 +231,7 @@ sub seealso { } sub overrides { - my ($standalone, $data)=@_; + my ($standalone, $data) = @_; if($standalone) { return ".SH \"OVERRIDES\"\n$data\n"; } @@ -263,7 +263,7 @@ my %protexists = ( ); sub protocols { - my ($f, $line, $manpage, $standalone, $data)=@_; + my ($f, $line, $manpage, $standalone, $data) = @_; my @e = split(/ +/, $data); for my $pr (@e) { if(!$protexists{$pr}) { @@ -282,7 +282,7 @@ sub protocols { } sub too_old { - my ($version)=@_; + my ($version) = @_; my $a = 999999; if($version =~ /^(\d+)\.(\d+)\.(\d+)/) { $a = $1 * 1000 + $2 * 10 + $3; @@ -299,7 +299,7 @@ sub too_old { } sub added { - my ($standalone, $data)=@_; + my ($standalone, $data) = @_; if(too_old($data)) { # do not mention ancient additions return ""; @@ -560,7 +560,7 @@ sub maybespace { } sub single { - my ($dir, $manpage, $f, $standalone)=@_; + my ($dir, $manpage, $f, $standalone) = @_; my $fh; open($fh, "<:crlf", "$dir/$f") || die "could not find $dir/$f"; @@ -597,28 +597,28 @@ sub single { next; } if(/^Short: *(.)/i) { - $short=$1; + $short = $1; } elsif(/^Long: *(.*)/i) { - $long=$1; + $long = $1; } elsif(/^Added: *(.*)/i) { - $added=$1; + $added = $1; } elsif(/^Tags: *(.*)/i) { - $tags=$1; + $tags = $1; } elsif(/^Arg: *(.*)/i) { - $arg=$1; + $arg = $1; } elsif(/^Magic: *(.*)/i) { - $magic=$1; + $magic = $1; } elsif(/^Mutexed: *(.*)/i) { - $mutexed=$1; + $mutexed = $1; } elsif(/^Protocols: *(.*)/i) { - $protocols=$1; + $protocols = $1; } elsif(/^See-also: +(.+)/i) { if(@seealso) { @@ -628,16 +628,16 @@ sub single { push @seealso, $1; } elsif(/^See-also:/i) { - $list=2; + $list = 2; } elsif(/^ *- (.*)/i && ($list == 2)) { push @seealso, $1; } elsif(/^Requires: *(.*)/i) { - $requires=$1; + $requires = $1; } elsif(/^Category: *(.*)/i) { - $category=$1; + $category = $1; } elsif(/^Example: +(.+)/i) { push @examples, $1; @@ -650,20 +650,20 @@ sub single { push @examples, $1; } elsif(/^Multi: *(.*)/i) { - $multi=$1; + $multi = $1; } elsif(/^Scope: *(.*)/i) { - $scope=$1; + $scope = $1; } elsif(/^Experimental: yes/i) { - $experimental=1; + $experimental = 1; } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^Help: *(.*)/i) { @@ -855,7 +855,7 @@ sub single { " is built to support $requires.\n"; } if($mutexed) { - my @m=split(/ /, $mutexed); + my @m = split(/ /, $mutexed); my $mstr; my $num = scalar(@m); my $count = 0; @@ -875,8 +875,8 @@ sub single { "This option is mutually exclusive with $mstr.\n"); } if($examples[0]) { - my $s =""; - $s="s" if($examples[1]); + my $s = ""; + $s = "s" if($examples[1]); foreach my $e (@examples) { my $check = $e; # verify the used options @@ -933,7 +933,7 @@ sub single { if(length($e) > $maxwidth) { $r = maybespace($r); } - my $slash =""; + my $slash = ""; $e = substr($e, length($r)); if(length($e) > 0) { $slash = "\\"; @@ -967,7 +967,7 @@ sub single { } sub getshortlong { - my ($dir, $f)=@_; + my ($dir, $f) = @_; $f =~ s/^.*\///; open(F, "<:crlf", "$dir/$f") || die "could not find $dir/$f"; @@ -988,13 +988,13 @@ sub getshortlong { next; } if(/^Short: (.)/i) { - $short=$1; + $short = $1; } elsif(/^Long: (.*)/i) { - $long=$1; + $long = $1; } elsif(/^Help: (.*)/i) { - $help=$1; + $help = $1; my $len = length($help); if($len >= 49) { printf STDERR "$f:$line:1:WARN: oversized help text: %d characters\n", @@ -1002,13 +1002,13 @@ sub getshortlong { } } elsif(/^Arg: (.*)/i) { - $arg=$1; + $arg = $1; } elsif(/^Protocols: (.*)/i) { - $protocols=$1; + $protocols = $1; } elsif(/^Category: (.*)/i) { - $category=$1; + $category = $1; } elsif(/^---/) { last; @@ -1016,14 +1016,14 @@ sub getshortlong { } close(F); if($short) { - $optshort{$short}=$long; + $optshort{$short} = $long; } if($long) { - $optlong{$long}=$short; - $helplong{$long}=$help; - $arglong{$long}=$arg; - $protolong{$long}=$protocols; - $catlong{$long}=$category; + $optlong{$long} = $short; + $helplong{$long} = $help; + $arglong{$long} = $arg; + $protolong{$long} = $protocols; + $catlong{$long} = $category; } } @@ -1035,7 +1035,7 @@ sub indexoptions { } sub header { - my ($dir, $manpage, $f)=@_; + my ($dir, $manpage, $f) = @_; my $fh; open($fh, "<:crlf", "$dir/$f") || die "could not find $dir/$f"; @@ -1192,7 +1192,7 @@ sub listglobals { } } if(/^Long: *(.*)/i) { - $long=$1; + $long = $1; } elsif(/^Scope: global/i) { push @globalopts, $long; diff --git a/scripts/mdlinkcheck b/scripts/mdlinkcheck index 3c86ddb75ada..1910bd670e80 100755 --- a/scripts/mdlinkcheck +++ b/scripts/mdlinkcheck @@ -90,7 +90,7 @@ if(defined $ARGV[0] && $ARGV[0] eq "--dry-run") { } # list all files to scan for links -my @files=`git ls-files docs include lib scripts src`; +my @files = `git ls-files docs include lib scripts src`; sub storelink { my ($f, $line, $link) = @_; diff --git a/scripts/nroff2cd b/scripts/nroff2cd index 6b2e3226a922..782da8c14b3a 100755 --- a/scripts/nroff2cd +++ b/scripts/nroff2cd @@ -42,7 +42,7 @@ use warnings; my $nroff2cd = "0.1"; # to keep check sub single { - my ($f)=@_; + my ($f) = @_; open(F, "<:crlf", $f) || return 1; my $line; diff --git a/scripts/release-notes.pl b/scripts/release-notes.pl index 7288bbe5146b..0a7cdf371b21 100755 --- a/scripts/release-notes.pl +++ b/scripts/release-notes.pl @@ -58,8 +58,8 @@ use warnings; my $cleanup = (@ARGV && $ARGV[0] eq "cleanup"); -my @gitlog=`git log @^{/RELEASE-NOTES:.synced}..` if(!$cleanup); -my @releasenotes=`cat RELEASE-NOTES`; +my @gitlog = `git log @^{/RELEASE-NOTES:.synced}..` if(!$cleanup); +my @releasenotes = `cat RELEASE-NOTES`; my @o; # the entire new RELEASE-NOTES my @refused; # [num] = [2 bits of use info] @@ -68,7 +68,7 @@ for my $l (@releasenotes) { if($l =~ /^ o .*\[(\d+)\]/) { # referenced, set bit 0 - $refused[$1]=1; + $refused[$1] = 1; my $m = $l; chomp $m; $m =~ s/^ o //; @@ -107,7 +107,7 @@ sub getref { # 'https://elsewhere.example.com/discussion' sub extract { - my ($ref)=@_; + my ($ref) = @_; if($ref =~ /^(\#|)(\d+)/) { # return the plain number return $2; @@ -175,7 +175,7 @@ sub extract { # call at the end of a parsed commit sub onecommit { - my ($short)=@_; + my ($short) = @_; my $ref = ''; if($dupe{$short}) { @@ -199,7 +199,7 @@ sub onecommit { if($ref) { my $r = getref(); $refs[$r] = $ref; - $moreinfo{$short}=$r; + $moreinfo{$short} = $r; $refused[$r] |= 1; } } @@ -220,7 +220,7 @@ sub onecommit { push @o, sprintf " o %s%s\n", $f, $moreinfo{$f}? sprintf(" [%d]", $moreinfo{$f}): ""; if($moreinfo{$f}) { - $refused[$moreinfo{$f}]=3; + $refused[$moreinfo{$f}] = 3; } } push @o, " --- new entries are listed above this ---"; diff --git a/scripts/singleuse.pl b/scripts/singleuse.pl index 957334215078..a1d2194d4ef9 100755 --- a/scripts/singleuse.pl +++ b/scripts/singleuse.pl @@ -198,13 +198,13 @@ sub doublecheck { $file = $1; } if($l =~ /^([0-9a-f]+) T _?(.*)/) { - my ($name)=($2); + my ($name) = ($2); #print "Define $name in $file\n"; $file =~ s/^libcurl_la-//; $exist{$name} = $file; } elsif($l =~ /^ U _?(.*)/) { - my ($name)=($1); + my ($name) = ($1); #print "Uses $name in $file\n"; $uses{$name} .= "$file, "; } diff --git a/scripts/top-complexity b/scripts/top-complexity index 38b68a3300e8..e6cd22956bbe 100755 --- a/scripts/top-complexity +++ b/scripts/top-complexity @@ -30,15 +30,15 @@ use warnings; # Check for a command in the PATH of the test server. # sub checkcmd { - my ($cmd)=@_; + my ($cmd) = @_; my @paths; if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { # PATH separator is different - @paths=(split(';', $ENV{'PATH'})); + @paths = (split(';', $ENV{'PATH'})); } else { - @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", - "/sbin", "/usr/bin", "/usr/local/bin"); + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); } for(@paths) { if(-x "$_/$cmd" && ! -d "$_/$cmd") { @@ -97,15 +97,15 @@ my $alllines = 0; for my $l (@output) { chomp $l; if($l =~/^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { - my ($score, $len, $path, $func)=($1, $2, $3, $4); + my ($score, $len, $path, $func) = ($1, $2, $3, $4); my $allow = 0; if($whitelist{$func} && ($score <= $whitelist{$func})) { $allow = 1; } - $where{"$path:$func"}=$score; - $perm{"$path:$func"}=$allow; + $where{"$path:$func"} = $score; + $perm{"$path:$func"} = $allow; if(($score > $cutoff) && !$allow) { $error++; } diff --git a/scripts/top-length b/scripts/top-length index eaf69b3025f6..10dabde39d81 100755 --- a/scripts/top-length +++ b/scripts/top-length @@ -30,15 +30,15 @@ use warnings; # Check for a command in the PATH of the test server. # sub checkcmd { - my ($cmd)=@_; + my ($cmd) = @_; my @paths; if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { # PATH separator is different - @paths=(split(';', $ENV{'PATH'})); + @paths = (split(';', $ENV{'PATH'})); } else { - @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", - "/sbin", "/usr/bin", "/usr/local/bin"); + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); } for(@paths) { if(-x "$_/$cmd" && ! -d "$_/$cmd") { @@ -97,15 +97,15 @@ my $alllines = 0; for my $l (@output) { chomp $l; if($l =~/^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { - my ($score, $length, $path, $func)=($1, $2, $3, $4); + my ($score, $length, $path, $func) = ($1, $2, $3, $4); my $allow = 0; if($whitelist{$func} && ($length <= $whitelist{$func})) { $allow = 1; } - $where{"$path:$func"}=$length; - $perm{"$path:$func"}=$allow; + $where{"$path:$func"} = $length; + $perm{"$path:$func"} = $allow; if(($length > $cutoff) && !$allow) { $error++; } diff --git a/src/mkhelp.pl b/src/mkhelp.pl index 89a4a9a1f205..9c60d7a64b6c 100755 --- a/src/mkhelp.pl +++ b/src/mkhelp.pl @@ -86,9 +86,9 @@ HEAD ; - my $c=0; + my $c = 0; for(split(//, $gzippedContent)) { - my $num=ord($_); + my $num = ord($_); if(!($c % 12)) { print " "; } diff --git a/tests/allversions.pm b/tests/allversions.pm index 8ae47fc59988..576980b5bff1 100644 --- a/tests/allversions.pm +++ b/tests/allversions.pm @@ -42,7 +42,7 @@ sub allversions { } elsif(!$before && /^- ([0-9.]+): (.*)/) { - $pastversion{$1}=$2; + $pastversion{$1} = $2; $relcount++; } } diff --git a/tests/appveyor.pm b/tests/appveyor.pm index 9cfc5a9b2d88..4a91af400e2e 100644 --- a/tests/appveyor.pm +++ b/tests/appveyor.pm @@ -65,7 +65,7 @@ sub appveyor_create_test_result { ' \\ '$appveyor_baseurl/api/tests'`; print "AppVeyor API result: $appveyor_result\n" if($appveyor_result); - $APPVEYOR_TEST_NAMES{$testnum}=$testname; + $APPVEYOR_TEST_NAMES{$testnum} = $testname; } sub appveyor_update_test_result { diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index c1a677e4000b..4d0fbc8a595f 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -106,7 +106,7 @@ BEGIN #********************************************************************** # global vars used for filenames # -my $PORTFILE="ftpserver.port"; # server port filename +my $PORTFILE = "ftpserver.port"; # server port filename my $portfile; # server port file path my $pidfile; # server pid filename my $mainsockf_pidfile; # pid file for primary connection sockfilt process @@ -171,7 +171,7 @@ BEGIN # $ftptargetdir is keeping the fake "name" of LIST directory. # my $ftplistparserstate; -my $ftptargetdir=""; +my $ftptargetdir = ""; #********************************************************************** # global variables used when running an FTP server to keep state info @@ -712,7 +712,7 @@ sub disc_handshake { } sub close_dataconn { - my ($closed)=@_; # non-zero if already disconnected + my ($closed) = @_; # non-zero if already disconnected my $datapid = processexists($datasockf_pidfile); @@ -944,8 +944,8 @@ sub DATA_smtp { return 0; # failed to open output my $line; - my $ulsize=0; - my $disc=0; + my $ulsize = 0; + my $disc = 0; my $raw; while(5 == (sysread \*SFREAD, $line, 5)) { if($line eq "DATA\n") { @@ -978,7 +978,7 @@ sub DATA_smtp { } elsif($line eq "DISC\n") { # disconnect! - $disc=1; + $disc = 1; printf SFWRITE "ACKD\n"; last; } @@ -2018,7 +2018,7 @@ sub QUIT_pop3 { ################ ################ FTP commands ################ -my $rest=0; +my $rest = 0; sub REST_ftp { $rest = $_[0]; logmsg "Set REST position to $rest\n" @@ -2135,7 +2135,7 @@ sub LIST_ftp { } sub NLST_ftp { - my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README"); + my @ftpdir = ("file", "with space", "fake", "..", " ..", "funny", "README"); if($datasockf_conn eq 'no') { if($nodataconn425) { @@ -2242,7 +2242,7 @@ sub SIZE_ftp { } } else { - $size=0; + $size = 0; @data = getpart("reply", "data$testpart"); for(@data) { $size += length($_); @@ -2320,7 +2320,7 @@ sub RETR_ftp { my @data = getpart("reply", "data$testpart"); - my $size=0; + my $size = 0; for(@data) { $size += length($_); } @@ -2344,7 +2344,7 @@ sub RETR_ftp { senddata $send; } close_dataconn(0); - $retrweirdo=0; # switch off the weirdo again! + $retrweirdo = 0; # switch off the weirdo again! } else { my $sz = "($size bytes)"; @@ -2372,7 +2372,7 @@ sub RETR_ftp { } sub STOR_ftp { - my $testno=$_[0]; + my $testno = $_[0]; my $filename = "$logdir/upload.$testno"; @@ -2403,8 +2403,8 @@ sub STOR_ftp { return 0; # failed to open output my $line; - my $ulsize=0; - my $disc=0; + my $ulsize = 0; + my $disc = 0; while(5 == (sysread DREAD, $line, 5)) { if($line eq "DATA\n") { my $i; @@ -2425,7 +2425,7 @@ sub STOR_ftp { } elsif($line eq "DISC\n") { # disconnect! - $disc=1; + $disc = 1; printf DWRITE "ACKD\n"; last; } @@ -2454,7 +2454,7 @@ sub STOR_ftp { } sub PASV_ftp { - my ($arg, $cmd)=@_; + my ($arg, $cmd) = @_; my $pasvport; # kill previous data connection sockfilt when alive @@ -2561,10 +2561,10 @@ sub PASV_ftp { if($cmd ne "EPSV") { # PASV reply - my $p=$listenaddr; + my $p = $listenaddr; $p =~ s/\./,/g; if($pasvbadip) { - $p="1,2,3,4"; + $p = "1,2,3,4"; } sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\r\n", int($pasvport / 256), int($pasvport % 256)); @@ -2833,16 +2833,16 @@ sub customize { while(<$custom>) { if($_ =~ /REPLY \"([A-Z]+ [A-Za-z0-9+-\/=\*. ]+)\" (.*)/) { - $fulltextreply{$1}=eval "qq{$2}"; + $fulltextreply{$1} = eval "qq{$2}"; logmsg "FTPD: set custom reply for $1\n"; } elsif($_ =~ /REPLY(LF|) ([A-Za-z0-9+\/=\*]*) (.*)/) { - $commandreply{$2}=eval "qq{$3}"; + $commandreply{$2} = eval "qq{$3}"; if($1 ne "LF") { - $commandreply{$2}.="\r\n"; + $commandreply{$2} .= "\r\n"; } else { - $commandreply{$2}.="\n"; + $commandreply{$2} .= "\n"; } if($2 eq "") { logmsg "FTPD: set custom reply for empty command\n"; @@ -2854,11 +2854,11 @@ sub customize { elsif($_ =~ /COUNT ([A-Z]+) (.*)/) { # we blank the custom reply for this command when having # been used this number of times - $customcount{$1}=$2; + $customcount{$1} = $2; logmsg "FTPD: blank custom reply for $1 command after $2 uses\n"; } elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) { - $delayreply{$1}=$2; + $delayreply{$1} = $2; logmsg "FTPD: delay reply for $1 with $2 seconds\n"; } elsif($_ =~ /POSTFETCH (.*)/) { @@ -2866,22 +2866,22 @@ sub customize { $postfetch = $1; } elsif($_ =~ /SLOWDOWNDATA/) { - $ctrldelay=0; - $datadelay=0.005; + $ctrldelay = 0; + $datadelay = 0.005; logmsg "FTPD: send response data with 5ms delay per byte\n"; } elsif($_ =~ /SLOWDOWN/) { - $ctrldelay=0.005; - $datadelay=0.005; + $ctrldelay = 0.005; + $datadelay = 0.005; logmsg "FTPD: send response with 5ms delay between each byte\n"; } elsif($_ =~ /RETRWEIRDO/) { logmsg "FTPD: instructed to use RETRWEIRDO\n"; - $retrweirdo=1; + $retrweirdo = 1; } elsif($_ =~ /RETRNOSIZE/) { logmsg "FTPD: instructed to use RETRNOSIZE\n"; - $retrnosize=1; + $retrnosize = 1; } elsif($_ =~ /RETRSIZE (\d+)/) { $retrsize= $1; @@ -2889,33 +2889,33 @@ sub customize { } elsif($_ =~ /PASVBADIP/) { logmsg "FTPD: instructed to use PASVBADIP\n"; - $pasvbadip=1; + $pasvbadip = 1; } elsif($_ =~ /NODATACONN425/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN425\n"; - $nodataconn425=1; - $nodataconn=1; + $nodataconn425 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN421/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN421\n"; - $nodataconn421=1; - $nodataconn=1; + $nodataconn421 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN150/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN150\n"; - $nodataconn150=1; - $nodataconn=1; + $nodataconn150 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN\n"; - $nodataconn=1; + $nodataconn = 1; } elsif($_ =~ /^STOR (.*)/) { - $storeresp=$1; + $storeresp = $1; logmsg "FTPD: instructed to use respond to STOR with '$storeresp'\n"; } elsif($_ =~ /CAPA (.*)/) { @@ -3150,7 +3150,7 @@ sub customize { } else { # clear it after use - $commandreply{"welcome"}=""; + $commandreply{"welcome"} = ""; if($welcome !~ /\r\n\z/) { $welcome .= "\r\n"; } @@ -3217,21 +3217,21 @@ sub customize { # IMAP is different with its identifier first on the command line if(($full =~ /^([^ ]+) ([^ ]+) (.*)/) || ($full =~ /^([^ ]+) ([^ ]+)/)) { - $cmdid=$1; # set the global variable - $FTPCMD=$2; - $FTPARG=$3; + $cmdid = $1; # set the global variable + $FTPCMD = $2; + $FTPARG = $3; } # IMAP authentication cancellation elsif($full =~ /^\*$/) { # Command id has already been set - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # IMAP long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]*={0,2}$/i) { # Command id has already been set - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "$full BAD Command\r\n"; @@ -3239,19 +3239,19 @@ sub customize { } } elsif($full =~ /^([A-Z]{3,4})(\s(.*))?$/i) { - $FTPCMD=$1; - $FTPARG=$3; + $FTPCMD = $1; + $FTPARG = $3; } elsif($proto eq "pop3") { # POP3 authentication cancellation if($full =~ /^\*$/) { - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # POP3 long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]*={0,2}$/i) { - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "-ERR Unrecognized command\r\n"; @@ -3261,13 +3261,13 @@ sub customize { elsif($proto eq "smtp") { # SMTP authentication cancellation if($full =~ /^\*$/) { - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # SMTP long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]{0,512}={0,2}$/i) { - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "500 Unrecognized command\r\n"; @@ -3312,7 +3312,7 @@ sub customize { if($text && ($text ne "")) { if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) { # used enough times so blank the custom command reply - $commandreply{$FTPCMD}=""; + $commandreply{$FTPCMD} = ""; } sendcontrol $text; diff --git a/tests/getpart.pm b/tests/getpart.pm index fd3a99915e15..74c7f6cb2303 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -49,8 +49,8 @@ use Memoize; my @xml; # test data file contents my $xmlfile; # test data filename -my $warning=0; -my $trace=0; +my $warning = 0; +my $trace = 0; # Normalize the part function arguments for proper caching. This includes the # filename in the arguments since that is an implied parameter that affects the @@ -66,12 +66,12 @@ sub testcaseattr { my %hash; for(@xml) { if(($_ =~ /^ *\]*)/)) { - my $attr=$1; + my $attr = $1; while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|\'([^\']*)\')//) { - my ($var, $cont)=($1, $2); + my ($var, $cont) = ($1, $2); $cont =~ s/^\"(.*)\"$/$1/; $cont =~ s/^\'(.*)\'$/$1/; - $hash{$var}=$cont; + $hash{$var} = $cont; } } } @@ -82,10 +82,10 @@ sub getpartattr { # if $part is undefined (ie only one argument) then # return the attributes of the section - my ($section, $part)=@_; + my ($section, $part) = @_; my %hash; - my $inside=0; + my $inside = 0; # print "Section: $section, part: $part\n"; @@ -98,13 +98,13 @@ sub getpartattr { !(defined($part))) ) { $inside++; - my $attr=$1; + my $attr = $1; while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|\'([^\']*)\')//) { - my ($var, $cont)=($1, $2); + my ($var, $cont) = ($1, $2); $cont =~ s/^\"(.*)\"$/$1/; $cont =~ s/^\'(.*)\'$/$1/; - $hash{$var}=$cont; + $hash{$var} = $cont; } last; } @@ -121,10 +121,10 @@ sub getpartattr { memoize('getpartattr', NORMALIZER => 'normalize_part'); # cache each result sub getpart { - my ($section, $part)=@_; + my ($section, $part) = @_; my @this; - my $inside=0; + my $inside = 0; my $line; for(@xml) { @@ -176,7 +176,7 @@ sub getpart { memoize('getpart', NORMALIZER => 'normalize_part'); # cache each result sub partexists { - my ($section, $part)=@_; + my ($section, $part) = @_; my $inside = 0; @@ -199,7 +199,7 @@ sub partexists { # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result sub loadtest { - my ($file, $original)=@_; + my ($file, $original) = @_; if(defined $xmlfile && $file eq $xmlfile) { # This test is already loaded @@ -288,7 +288,7 @@ sub checktest { # write the test to the given file sub savetest { - my ($file)=@_; + my ($file) = @_; if(open(my $xmlh, ">", $file)) { binmode $xmlh; # for crapage systems, use binary @@ -328,7 +328,7 @@ sub striparray { # pass array *REFERENCES* ! # sub compareparts { - my ($firstref, $secondref)=@_; + my ($firstref, $secondref) = @_; # we cannot compare arrays index per index since with data chunks, # they may not be "evenly" distributed @@ -384,7 +384,7 @@ sub compareparts { # Write a given array to the specified file # sub writearray { - my ($filename, $arrayref)=@_; + my ($filename, $arrayref) = @_; open(my $temp, ">", $filename) || die "Failure writing file"; binmode($temp,":raw"); # Cygwin fix @@ -398,7 +398,7 @@ sub writearray { # Load a specified file and return it as an array # sub loadarray { - my ($filename)=@_; + my ($filename) = @_; my @array; if(open(my $temp, "<", $filename)) { diff --git a/tests/globalconfig.pm b/tests/globalconfig.pm index c9a95427b25d..9b3d1bf76c76 100644 --- a/tests/globalconfig.pm +++ b/tests/globalconfig.pm @@ -90,54 +90,54 @@ use File::Spec; # # config variables overridden by command-line options -our $verbose; # 1 to show verbose test output -our $torture; # 1 to enable torture testing -our $proxy_address; # external HTTP proxy address -our $listonly; # only list the tests -our $buildinfo; # dump buildinfo.txt -our $run_duphandle; # run curl with --test-duphandle to verify handle duplication -our $run_event_based; # run curl with --test-event to test the event API -our $automakestyle; # use automake-like test status output format -our $anyway; # continue anyway, even if a test fail -our $CURLVERSION=""; # curl's reported version number -our $CURLVERNUM=""; # curl's reported version number (without -DEV) -our $randseed = 0; # random number seed -our $maxtime; # curl command timeout override -our $mintotal; # minimum number of tests to run +our $verbose; # 1 to show verbose test output +our $torture; # 1 to enable torture testing +our $proxy_address; # external HTTP proxy address +our $listonly; # only list the tests +our $buildinfo; # dump buildinfo.txt +our $run_duphandle; # run curl with --test-duphandle to verify handle duplication +our $run_event_based; # run curl with --test-event to test the event API +our $automakestyle; # use automake-like test status output format +our $anyway; # continue anyway, even if a test fail +our $CURLVERSION = ""; # curl's reported version number +our $CURLVERNUM = ""; # curl's reported version number (without -DEV) +our $randseed = 0; # random number seed +our $maxtime; # curl command timeout override +our $mintotal; # minimum number of tests to run # paths our $pwd = getcwd(); # current working directory our $srcdir = $ENV{'srcdir'} || '.'; # root of the test source code -our $perlcmd=shell_quote($^X); -our $perl="$perlcmd -I. " . shell_quote("-I$srcdir"); # invoke perl like this -our $LOGDIR="log"; # root of the log directory; this is different for - # each runner in multiprocess mode -our $LIBDIR=dirsepadd("./libtest/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $UNITDIR=dirsepadd("./unit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $TUNITDIR=dirsepadd("./tunit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $SRVDIR=dirsepadd("./server/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $TESTDIR="$srcdir/data"; -our $CURL=dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . +our $perlcmd = shell_quote($^X); +our $perl = "$perlcmd -I. " . shell_quote("-I$srcdir"); # invoke perl like this +our $LOGDIR = "log"; # root of the log directory; this is different for + # each runner in multiprocess mode +our $LIBDIR = dirsepadd("./libtest/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $UNITDIR = dirsepadd("./unit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $TUNITDIR = dirsepadd("./tunit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $SRVDIR = dirsepadd("./server/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $TESTDIR = "$srcdir/data"; +our $CURL = dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . "curl".exe_ext('TOOL'); # what curl binary to run on the tests -our $CURLINFO=dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . +our $CURLINFO = dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . "curlinfo".exe_ext('TOOL'); # what curlinfo binary to run on the tests -our $VCURL=$CURL; # what curl binary to use to verify the servers with - # VCURL is handy to set to the system one when the one you - # built hangs or crashes and thus prevent verification +our $VCURL = $CURL; # what curl binary to use to verify the servers with + # VCURL is handy to set to the system one when the one you + # built hangs or crashes and thus prevent verification # the path to the script that analyzes the memory debug output file -our $memanalyze="$perl " . shell_quote("$srcdir/memanalyze.pl"); +our $memanalyze = "$perl " . shell_quote("$srcdir/memanalyze.pl"); our $valgrind; # path to valgrind, or empty if disabled our $dev_null = File::Spec->devnull(); # null device path, eg: /dev/null # paths in $LOGDIR -our $LOCKDIR = "lock"; # root of the server directory with lock files -our $PIDDIR = "server"; # root of the server directory with PID files -our $SERVERIN="server.input"; # what curl sent the server -our $PROXYIN="proxy.input"; # what curl sent the proxy -our $MEMDUMP="memdump"; # file that the memory debugging creates -our $SERVERCMD="server.cmd"; # copy server instructions here -our $DNSCMD="dnsd.cmd"; # write DNS instructions here +our $LOCKDIR = "lock"; # root of the server directory with lock files +our $PIDDIR = "server"; # root of the server directory with PID files +our $SERVERIN = "server.input"; # what curl sent the server +our $PROXYIN = "proxy.input"; # what curl sent the proxy +our $MEMDUMP = "memdump"; # file that the memory debugging creates +our $SERVERCMD = "server.cmd"; # copy server instructions here +our $DNSCMD = "dnsd.cmd"; # write DNS instructions here # other config variables our @protocols; # array of lowercase supported protocol servers diff --git a/tests/http2-server.pl b/tests/http2-server.pl index 3ab38803f174..0729a00593a5 100755 --- a/tests/http2-server.pl +++ b/tests/http2-server.pl @@ -110,7 +110,7 @@ my $certfile = abs_path("certs/$cert.pem"); my $keyfile = abs_path("certs/$cert.key"); -my $cmdline="$nghttpx --backend=$connect ". +my $cmdline = "$nghttpx --backend=$connect ". "--backend-keep-alive-timeout=500ms ". "--frontend=\"*,$listenport;no-tls\" ". "--frontend=\"*,$listenport2\" ". diff --git a/tests/http3-server.pl b/tests/http3-server.pl index 13cc49a9dff0..b86a6a58b388 100755 --- a/tests/http3-server.pl +++ b/tests/http3-server.pl @@ -110,7 +110,7 @@ my $certfile = abs_path("certs/$cert.pem"); my $keyfile = abs_path("certs/$cert.key"); -my $cmdline="$nghttpx --http2-proxy --backend=$connect ". +my $cmdline = "$nghttpx --http2-proxy --backend=$connect ". "--backend-keep-alive-timeout=500ms ". "--frontend=\"*,$listenport\" ". "--frontend=\"*,$listenport;quic\" ". diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl index 71c80b0039af..6590d5257a7d 100755 --- a/tests/libtest/mk-lib1521.pl +++ b/tests/libtest/mk-lib1521.pl @@ -442,10 +442,10 @@ next; } if($_ =~ /^CURLOPT(?:DEPRECATED)?\(([^ ]*), ([^ ]*), (\d*)[,)]/) { - my ($name, $type, $val)=($1, $2, $3); - my $w=" "; - my $w2="$w$w"; - my $w3="$w$w$w"; + my ($name, $type, $val) = ($1, $2, $3); + my $w = " "; + my $w2 = "$w$w"; + my $w3 = "$w$w$w"; my $opt = $name; $opt =~ s/^CURLOPT_//; my $exists = "${w}{\n"; @@ -563,7 +563,7 @@ } elsif($type eq "CURLOPTTYPE_FUNCTIONPOINT") { if($name =~ /([^ ]*)FUNCTION/) { - my $l=lc($1); + my $l = lc($1); $l =~ s/^curlopt_//; print $fh "${fpref}\n$i${l}cb);\n$fcheck"; } @@ -594,7 +594,7 @@ } elsif($infomode && ($_ =~ /^CURLINFO_([^ ]*) *= *CURLINFO_([^ ]*)/)) { - my ($info, $type)=($1, $2); + my ($info, $type) = ($1, $2); my $c = " result = curl_easy_getinfo(curl, CURLINFO_$info,"; my $check = " if(result)\n t1521_geterr(\"$info\", result, __LINE__);\n"; if($type eq "STRING") { diff --git a/tests/libtest/test1013.pl b/tests/libtest/test1013.pl index e03e99af4364..154c4227a6e3 100755 --- a/tests/libtest/test1013.pl +++ b/tests/libtest/test1013.pl @@ -32,10 +32,10 @@ exit 3; } -my $what=$ARGV[2]; +my $what = $ARGV[2]; # Read the output of curl --version -my $curl_protocols=""; +my $curl_protocols = ""; open(CURL, $ARGV[1]) || die "Cannot get curl $what list\n"; while() { $curl_protocols = $_ if(/$what:/i); diff --git a/tests/libtest/test1022.pl b/tests/libtest/test1022.pl index c25ae3ce6c28..49e295433b7e 100755 --- a/tests/libtest/test1022.pl +++ b/tests/libtest/test1022.pl @@ -31,7 +31,7 @@ exit 3; } -my $what=$ARGV[2]; +my $what = $ARGV[2]; # Read the output of curl --version open(CURL, $ARGV[1]) || die "Cannot open curl --version list in $ARGV[1]\n"; @@ -47,7 +47,7 @@ open(CURLCONFIG, '-|', 'sh', $ARGV[0], "--$what") || die "Cannot get curl-config --$what list\n"; $_ = ; chomp; -my $filever=$_; +my $filever = $_; if($what eq "version") { if($filever =~ /^libcurl ([\.\d]+((-DEV)|(-rc\d)|(-\d+))?)$/) { $curlconfigversion = $1; diff --git a/tests/memanalyze.pl b/tests/memanalyze.pl index e30b263c4128..15382e527af4 100755 --- a/tests/memanalyze.pl +++ b/tests/memanalyze.pl @@ -34,16 +34,16 @@ while(@ARGV) { if($ARGV[0] eq "-v") { - $verbose=1; + $verbose = 1; shift @ARGV; } elsif($ARGV[0] eq "-t") { - $trace=1; + $trace = 1; shift @ARGV; } elsif($ARGV[0] eq "-l") { # only show what alloc that caused a memlimit failure - $showlimit=1; + $showlimit = 1; shift @ARGV; } else { diff --git a/tests/memanalyzer.pm b/tests/memanalyzer.pm index 2a395da2e0fd..110b39d24111 100644 --- a/tests/memanalyzer.pm +++ b/tests/memanalyzer.pm @@ -46,7 +46,7 @@ my $memsum; my $maxmem; sub newtotal { - my ($newtot)=@_; + my ($newtot) = @_; # count a max here if($newtot > $maxmem) { @@ -152,8 +152,8 @@ sub memanalyze { newtotal($totalmem); $frees++; - $sizeataddr{$addr}=-1; # set -1 to mark as freed - $getmem{$addr}="$source:$linenum"; + $sizeataddr{$addr} = -1; # set -1 to mark as freed + $getmem{$addr} = "$source:$linenum"; } } elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) { @@ -177,7 +177,7 @@ sub memanalyze { newtotal($totalmem); $mallocs++; - $getmem{$addr}="$source:$linenum"; + $getmem{$addr} = "$source:$linenum"; } elsif($function =~ /calloc\((\d*),(\d*)\) = 0x([0-9a-f]*)/) { $size = $1 * $2; @@ -203,7 +203,7 @@ sub memanalyze { newtotal($totalmem); $callocs++; - $getmem{$addr}="$source:$linenum"; + $getmem{$addr} = "$source:$linenum"; } elsif($function =~ /realloc\((\(nil\)|0x([0-9a-f]*)), (\d*)\) = 0x([0-9a-f]*)/) { my ($oldaddr, $newsize, $newaddr) = ($2, $3, $4); @@ -231,7 +231,7 @@ sub memanalyze { newtotal($totalmem); $reallocs++; - $getmem{$newaddr}="$source:$linenum"; + $getmem{$newaddr} = "$source:$linenum"; } elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) { # strdup(a5b50) (8) = df7c0 @@ -258,8 +258,8 @@ sub memanalyze { $dup = $1; $size = $2; $addr = $3; - $getmem{$addr}="$source:$linenum"; - $sizeataddr{$addr}=$size; + $getmem{$addr} = "$source:$linenum"; + $sizeataddr{$addr} = $size; $totalmem += $size; $memsum += $size; @@ -306,7 +306,7 @@ sub memanalyze { push @res, "Close without open: $line\n"; } else { - $filedes{$1}=0; # closed now + $filedes{$1} = 0; # closed now $openfile--; } } diff --git a/tests/pathhelp.pm b/tests/pathhelp.pm index e481255a3a55..500e43f12576 100644 --- a/tests/pathhelp.pm +++ b/tests/pathhelp.pm @@ -197,7 +197,7 @@ sub dirsepadd { # This does the same thing as String::ShellQuote but does not need a package. # sub shell_quote { - my ($s)=@_; + my ($s) = @_; if($^O eq 'MSWin32') { $s = '"' . $s . '"'; } diff --git a/tests/runtests.pl b/tests/runtests.pl index a8072e32cd93..14c07a39a7c2 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -106,16 +106,16 @@ BEGIN my %custom_skip_reasons; -my $ACURL=$VCURL; # what curl binary to use to talk to APIs (relevant for CI) - # ACURL is handy to set to the system one for reliability -my $CURLCONFIG="../curl-config"; # curl-config from current build +my $ACURL = $VCURL; # what curl binary to use to talk to APIs (relevant for CI) + # ACURL is handy to set to the system one for reliability +my $CURLCONFIG = "../curl-config"; # curl-config from current build # Normally, all test cases should be run, but at times it is handy to # run a particular one: -my $TESTCASES="all"; +my $TESTCASES = "all"; # To run specific test cases, set them like: -# $TESTCASES="1 2 3 7 8"; +# $TESTCASES = "1 2 3 7 8"; ####################################################################### # No variables below this point should need to be modified @@ -214,7 +214,7 @@ sub logmsg { # enable logmsg buffering for the given runner ID # sub logmsg_bufferfortest { - my ($runnerid)=@_; + my ($runnerid) = @_; if($jobs) { # Only enable buffering in multiprocess mode $singletest_bufferedrunner = $runnerid; @@ -321,7 +321,7 @@ sub catch_usr1 { } if($ENV{"NGHTTPX"}) { my $cmd = "\"$ENV{'NGHTTPX'}\" -v 2>$dev_null"; - my $nghttpx_version=join(' ', `$cmd`); + my $nghttpx_version = join(' ', `$cmd`); $nghttpx_h3 = $nghttpx_version =~ /nghttp3\//; chomp $nghttpx_h3; } @@ -384,10 +384,10 @@ sub cleardir { # Given two array references, this function will store them in two temporary # files, run 'diff' on them, store the result and return the diff output! sub showdiff { - my ($logdir, $firstref, $secondref)=@_; + my ($logdir, $firstref, $secondref) = @_; - my $file1="$logdir/check-generated"; - my $file2="$logdir/check-expected"; + my $file1 = "$logdir/check-generated"; + my $file2 = "$logdir/check-expected"; open(my $temp, ">", $file1) || die "Failure writing diff file"; for(@$firstref) { @@ -427,7 +427,7 @@ sub showdiff { # some pattern that is allowed to differ, output test results # sub compare { - my ($runnerid, $testnum, $testname, $subject, $firstref, $secondref)=@_; + my ($runnerid, $testnum, $testname, $subject, $firstref, $secondref) = @_; my $result = compareparts($firstref, $secondref); @@ -454,14 +454,14 @@ sub compare { ####################################################################### # Numeric-sort words in a string sub numsortwords { - my ($string)=@_; + my ($string) = @_; return join(' ', sort { $a <=> $b } split(' ', $string)); } ####################################################################### # Parse and store the protocols in curl's Protocols: line sub parseprotocols { - my ($line)=@_; + my ($line) = @_; @protocols = split(' ', lc($line)); @@ -512,13 +512,13 @@ sub checksystemfeatures { my $libcurl; my $versretval; my $versnoexec; - my @version=(); + my @version = (); my @disabled; my $dis = ""; - my $curlverout="$LOGDIR/curlverout.log"; - my $curlvererr="$LOGDIR/curlvererr.log"; - my $versioncmd=exerunner() . shell_quote($CURL) . " --version 1>$curlverout 2>$curlvererr"; + my $curlverout = "$LOGDIR/curlverout.log"; + my $curlvererr = "$LOGDIR/curlvererr.log"; + my $versioncmd = exerunner() . shell_quote($CURL) . " --version 1>$curlverout 2>$curlvererr"; unlink($curlverout); unlink($curlvererr); @@ -549,7 +549,7 @@ sub checksystemfeatures { $dis = join(", ", @disabled); } - $resolver="stock"; + $resolver = "stock"; for(@version) { chomp; @@ -610,7 +610,7 @@ sub checksystemfeatures { } if($libcurl =~ /ares/i) { $feature{"c-ares"} = 1; - $resolver="c-ares"; + $resolver = "c-ares"; } if($libcurl =~ /nghttp2/i) { # nghttp2 supports h2c @@ -707,7 +707,7 @@ sub checksystemfeatures { if(!$feature{"c-ares"} || $feature{"asyn-rr"}) { # this means threaded resolver $feature{"threaded-resolver"} = 1; - $resolver="threaded"; + $resolver = "threaded"; # does not count as "real" c-ares $feature{"c-ares"} = 0; @@ -748,7 +748,7 @@ sub checksystemfeatures { my $add_httptls; for(@protocols) { if($_ =~ /^https(-ipv6|)$/) { - $add_httptls=1; + $add_httptls = 1; last; } } @@ -857,11 +857,11 @@ sub checksystemfeatures { "TrackMemory feature (--enable-debug)"; } - my $hostname=join(' ', runclientoutput("hostname")); + my $hostname = join(' ', runclientoutput("hostname")); chomp $hostname; - my $hosttype=join(' ', runclientoutput("uname -a")); + my $hosttype = join(' ', runclientoutput("uname -a")); chomp $hosttype; - my $hostos=$^O; + my $hostos = $^O; # display summary information about curl and the test host logmsg("********* System characteristics ******** \n", @@ -1008,7 +1008,7 @@ sub citest_finishtestrun { # add one set of test timings from the runner to global set sub updatetesttimings { - my ($testnum, %testtimings)=@_; + my ($testnum, %testtimings) = @_; if(defined $testtimings{"timeprepini"}) { $timeprepini{$testnum} = $testtimings{"timeprepini"}; @@ -1205,7 +1205,7 @@ sub singletest_count { if($why && !$listonly) { # there is a problem, count it as "skipped" $skipped{$why}++; - $teststat[$testnum]=$why; # store reason for this test case + $teststat[$testnum] = $why; # store reason for this test case if(!$short) { if($skipped{$why} <= 3) { @@ -1242,7 +1242,7 @@ sub normalize_text { ####################################################################### # Verify test succeeded sub singletest_check { - my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind)=@_; + my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind) = @_; # Skip all the verification on torture tests if($torture) { @@ -1254,7 +1254,7 @@ sub singletest_check { my $logdir = getrunnerlogdir($runnerid); my @err = getpart("verify", "errorcode"); my $errorcode = $err[0] || "0"; - my $ok=""; + my $ok = ""; my $res; chomp $errorcode; my $testname= (getpart("client", "name"))[0]; @@ -1297,7 +1297,7 @@ sub singletest_check { } # get the mode attribute - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@validstdout); normalize_text(\@actual); @@ -1351,7 +1351,7 @@ sub singletest_check { my %hash = getpartattr("verify", "stderr"); # get the mode attribute - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@validstderr); normalize_text(\@actual); @@ -1470,7 +1470,7 @@ sub singletest_check { if(@replycheckpart) { my %replycheckpartattr = getpartattr("reply", "datacheck".$partsuffix); # get the mode attribute - my $filemode=$replycheckpartattr{'mode'}; + my $filemode = $replycheckpartattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@replycheckpart); } @@ -1501,7 +1501,7 @@ sub singletest_check { } } # get the mode attribute - my $filemode=$replyattr{'mode'}; + my $filemode = $replyattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@reply); } @@ -1520,7 +1520,7 @@ sub singletest_check { my @out = loadarray($CURLOUT); # get the mode attribute - my $filemode=$replyattr{'mode'}; + my $filemode = $replyattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@out); } @@ -1625,12 +1625,12 @@ sub singletest_check { my $outputok; for my $partsuffix (('', '1', '2', '3', '4')) { - my @outfile=getpart("verify", "file".$partsuffix); + my @outfile = getpart("verify", "file".$partsuffix); if(@outfile || partexists("verify", "file".$partsuffix) ) { # we are supposed to verify a dynamically generated file! my %hash = getpartattr("verify", "file".$partsuffix); - my $filename=$hash{'name'}; + my $filename = $hash{'name'}; if(!$filename) { logmsg " $testnum: IGNORED: section verify=>file$partsuffix ". "has no name attribute\n"; @@ -1655,12 +1655,12 @@ sub singletest_check { $timevrfyend{$testnum} = Time::HiRes::time(); return -1; } - my @generated=loadarray($filename); + my @generated = loadarray($filename); # what parts to cut off from the file my @stripfilepar = getpart("verify", "stripfile".$partsuffix); - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@outfile); normalize_text(\@generated); @@ -1720,7 +1720,7 @@ sub singletest_check { if(@dnsd) { # we are supposed to verify a dynamically generated file! my %hash = getpartattr("verify", "dns"); - my $hostname=$hash{'host'}; + my $hostname = $hash{'host'}; # Verify the sent DNS requests my @out = loadarray("$logdir/dnsd.input"); @@ -1775,12 +1775,12 @@ sub singletest_check { } else { my @memdata = memanalyze("$logdir/$MEMDUMP", 0, 0, 0); - my $leak=0; + my $leak = 0; for(@memdata) { if($_ ne "") { # well it could be other memory problems as well, but # we call it leak for short here - $leak=1; + $leak = 1; } } if($leak) { @@ -1925,12 +1925,12 @@ sub singletest_check { ####################################################################### # Report a successful test sub singletest_success { - my ($testnum, $count, $total, $errorreturncode)=@_; + my ($testnum, $count, $total, $errorreturncode) = @_; my $sofar= time()-$start; my $esttotal = $sofar/$count * $total; my $estleft = $esttotal - $sofar; - my $timeleft=sprintf("remaining: %02d:%02d", + my $timeleft = sprintf("remaining: %02d:%02d", $estleft / 60, $estleft % 60); my $took = $timevrfyend{$testnum} - $timeprepini{$testnum}; @@ -1946,7 +1946,7 @@ sub singletest_success { logmsg "PASS: $testnum - $testname\n"; } - if($errorreturncode==2) { + if($errorreturncode == 2) { # ignored test success $passedign .= "$testnum "; logmsg "Warning: test$testnum result is ignored, but passed!\n"; @@ -1962,7 +1962,7 @@ sub singletest_success { # arrived. # sub singletest { - my ($runnerid, $testnum, $count, $total)=@_; + my ($runnerid, $testnum, $count, $total) = @_; # start buffering logmsg; stop it on return logmsg_bufferfortest($runnerid); @@ -2283,7 +2283,7 @@ sub runtimestats { # 0=unknown test, 1=use test result, 2=ignore test result # sub ignoreresultcode { - my ($testnum)=@_; + my ($testnum) = @_; if(defined $ignoretestcodes{$testnum}) { return $ignoretestcodes{$testnum}; } @@ -2294,7 +2294,7 @@ sub ignoreresultcode { # Put the given runner ID onto the queue of runners ready for a new task # sub runnerready { - my ($runnerid)=@_; + my ($runnerid) = @_; push @runnersidle, $runnerid; } @@ -2302,7 +2302,7 @@ sub runnerready { # Create test runners # sub createrunners { - my ($numrunners)=@_; + my ($numrunners) = @_; if(! $numrunners) { $numrunners++; } @@ -2320,7 +2320,7 @@ sub createrunners { # Pick a test runner for the given test # sub pickrunner { - my ($testnum)=@_; + my ($testnum) = @_; scalar(@runnersidle) || die "No runners available"; return pop @runnersidle; @@ -2340,17 +2340,17 @@ sub pickrunner { $args = join(' ', @ARGV); $valgrind = checktestcmd("valgrind"); -my $number=0; -my $fromnum=-1; +my $number = 0; +my $fromnum = -1; my @testthis; while(@ARGV) { if($ARGV[0] eq "-v") { # verbose output - $verbose=1; + $verbose = 1; } elsif($ARGV[0] eq "-c") { # use this path to curl instead of default - $DBGCURL=$CURL=$ARGV[1]; + $DBGCURL = $CURL = $ARGV[1]; shift @ARGV; } elsif($ARGV[0] eq "-vc") { @@ -2360,29 +2360,29 @@ sub pickrunner { # the development version as then it will not be able to run any tests # since it cannot verify the servers! - $VCURL=shell_quote($ARGV[1]); + $VCURL = shell_quote($ARGV[1]); shift @ARGV; } elsif($ARGV[0] eq "-ac") { # use this curl only to talk to APIs (currently only CI test APIs) - $ACURL=shell_quote($ARGV[1]); + $ACURL = shell_quote($ARGV[1]); shift @ARGV; } elsif($ARGV[0] eq "-d") { # have the servers display protocol output - $debugprotocol=1; + $debugprotocol = 1; } elsif(($ARGV[0] eq "-e") || ($ARGV[0] eq "--test-event")) { # run the tests cases event based if possible - $run_event_based=1; + $run_event_based = 1; } elsif($ARGV[0] eq "--test-duphandle") { # run the tests with --test-duphandle - $run_duphandle=1; + $run_duphandle = 1; } elsif($ARGV[0] eq "-f") { # force - run the test case even if listed in DISABLED - $run_disabled=1; + $run_disabled = 1; } elsif($ARGV[0] eq "-E") { # load additional reasons to skip tests @@ -2408,33 +2408,33 @@ sub pickrunner { } elsif($ARGV[0] eq "-g") { # run this test with gdb - $gdbthis=1; + $gdbthis = 1; } elsif($ARGV[0] eq "-gl") { # run this test with lldb - $gdbthis=2; + $gdbthis = 2; } elsif($ARGV[0] eq "-gw") { # run this test with windowed gdb - $gdbthis=1; - $gdbxwin=1; + $gdbthis = 1; + $gdbxwin = 1; } elsif($ARGV[0] eq "-s") { # short output - $short=1; + $short = 1; } elsif($ARGV[0] eq "-am") { # automake-style output - $short=1; - $automakestyle=1; + $short = 1; + $automakestyle = 1; } elsif($ARGV[0] =~ /-m=(\d+)/) { - my ($num)=($1); - $maxtime=$num; + my ($num) = ($1); + $maxtime = $num; } elsif($ARGV[0] =~ /--min=(\d+)/) { - my ($num)=($1); - $mintotal=$num; + my ($num) = ($1); + $mintotal = $num; } elsif($ARGV[0] eq "-n") { # no valgrind @@ -2446,11 +2446,11 @@ sub pickrunner { } elsif($ARGV[0] eq "-R") { # execute in scrambled order - $scrambleorder=1; + $scrambleorder = 1; } elsif($ARGV[0] =~ /^-t(.*)/) { # torture - $torture=1; + $torture = 1; my $xtra = $1; if($xtra =~ s/(\d+)$//) { @@ -2460,8 +2460,8 @@ sub pickrunner { elsif($ARGV[0] =~ /--shallow=(\d+)/) { # Fail no more than this amount per tests when running # torture. - my ($num)=($1); - $shallow=$num; + my ($num) = ($1); + $shallow = $num; } elsif($ARGV[0] =~ /--repeat=(\d+)/) { # Repeat-run the given tests this many times @@ -2477,7 +2477,7 @@ sub pickrunner { } elsif($ARGV[0] eq "-a") { # continue anyway, even if a test fail - $anyway=1; + $anyway = 1; } elsif($ARGV[0] eq "-o") { shift @ARGV; @@ -2489,11 +2489,11 @@ sub pickrunner { } } elsif($ARGV[0] eq "-p") { - $postmortem=1; + $postmortem = 1; } elsif($ARGV[0] eq "-P") { shift @ARGV; - $proxy_address=$ARGV[0]; + $proxy_address = $ARGV[0]; } elsif($ARGV[0] eq "-L") { # require additional library file @@ -2502,14 +2502,14 @@ sub pickrunner { } elsif($ARGV[0] eq "-l") { # lists the test case names only - $listonly=1; + $listonly = 1; } elsif($ARGV[0] eq "--buildinfo") { - $buildinfo=1; + $buildinfo = 1; } elsif($ARGV[0] =~ /^-j(.*)/) { # parallel jobs - $jobs=1; + $jobs = 1; my $xtra = $1; if($xtra =~ s/(\d+)$//) { $jobs = $1; @@ -2517,7 +2517,7 @@ sub pickrunner { } elsif($ARGV[0] eq "-k") { # keep stdout and stderr files after tests - $keepoutfiles=1; + $keepoutfiles = 1; } elsif($ARGV[0] eq "-r") { # run time statistics needs Time::HiRes @@ -2530,8 +2530,8 @@ sub pickrunner { keys(%timetoolend) = 2000; keys(%timesrvrlog) = 2000; keys(%timevrfyend) = 2000; - $timestats=1; - $fullstats=0; + $timestats = 1; + $fullstats = 0; } } elsif($ARGV[0] eq "-rf") { @@ -2545,13 +2545,13 @@ sub pickrunner { keys(%timetoolend) = 2000; keys(%timesrvrlog) = 2000; keys(%timevrfyend) = 2000; - $timestats=1; - $fullstats=1; + $timestats = 1; + $fullstats = 1; } } elsif($ARGV[0] eq "-u") { # error instead of warning on server unexpectedly alive - $err_unexpected=1; + $err_unexpected = 1; } elsif(($ARGV[0] eq "-h") || ($ARGV[0] eq "--help")) { # show help text @@ -2620,20 +2620,20 @@ sub pickrunner { } elsif($ARGV[0] =~ /^!(\d+)/) { $fromnum = -1; - $disabled{$1}=$1; + $disabled{$1} = $1; } elsif($ARGV[0] =~ /^~(\d+)/) { $fromnum = -1; - $ignored{$1}=$1; + $ignored{$1} = $1; } elsif($ARGV[0] =~ /^!(.+)/) { - $disabled_keywords{lc($1)}=$1; + $disabled_keywords{lc($1)} = $1; } elsif($ARGV[0] =~ /^~(.+)/) { - $ignored_keywords{lc($1)}=$1; + $ignored_keywords{lc($1)} = $1; } elsif($ARGV[0] =~ /^([-[{a-zA-Z].*)/) { - $enabled_keywords{lc($1)}=$1; + $enabled_keywords{lc($1)} = $1; } else { print "Unknown option: $ARGV[0]\n"; @@ -2645,8 +2645,7 @@ sub pickrunner { delete $ENV{'DEBUGINFOD_URLS'} if($ENV{'DEBUGINFOD_URLS'} && $no_debuginfod); if(!$randseed) { - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime(time); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); # seed of the month. December 2019 becomes 201912 $randseed = ($year + 1900) * 100 + $mon + 1; print "Using curl: $CURL\n"; @@ -2661,7 +2660,7 @@ sub pickrunner { srand $randseed; if(@testthis && ($testthis[0] ne "")) { - $TESTCASES=join(" ", @testthis); + $TESTCASES = join(" ", @testthis); } if($valgrind) { @@ -2681,27 +2680,27 @@ sub pickrunner { # that old version any longer and delete this check) runclient("valgrind --help 2>&1 | grep -- --tool >$dev_null 2>&1"); if(($? >> 8)) { - $valgrind_tool=""; + $valgrind_tool = ""; } open(my $curlh, "<", $CURL); my $l = <$curlh>; if($l =~ /^\#\!/) { # A shell script. This is typically when built with libtool, - $valgrind="../libtool --mode=execute $valgrind"; + $valgrind = "../libtool --mode=execute $valgrind"; } close($curlh); # valgrind 3 renamed the --logfile option to --log-file!!! # (this happened in 2005, so we could probably do not need to care about # that old version any longer and delete this check) - my $ver=join(' ', runclientoutput("valgrind --version")); + my $ver = join(' ', runclientoutput("valgrind --version")); # cut off all but digits and dots $ver =~ s/[^0-9.]//g; if($ver =~ /^(\d+)/) { $ver = $1; if($ver < 3) { - $valgrind_logfile="--logfile"; + $valgrind_logfile = "--logfile"; } } } @@ -2804,7 +2803,7 @@ sub disabledtests { for my $t (@pp) { if($t =~ /(\d+)/) { my ($n) = $1; - $disabled{$n}=$n; # disable this test number + $disabled{$n} = $n; # disable this test number if(! -f "$srcdir/data/test$n") { print STDERR "WARNING! Non-existing test $n in $file!\n"; # fail hard to make user notice @@ -2834,7 +2833,7 @@ sub disabledtests { my @cmds = grep { /^test([0-9]+)$/ && -f "$TESTDIR/$_" } readdir(DIR); closedir(DIR); - $TESTCASES=""; # start with no test cases + $TESTCASES = ""; # start with no test cases # cut off everything but the digits for(@cmds) { @@ -2846,17 +2845,17 @@ sub disabledtests { # skip disabled test cases my $why = "configured as DISABLED"; $skipped{$why}++; - $teststat[$n]=$why; # store reason for this test case + $teststat[$n] = $why; # store reason for this test case next; } $TESTCASES .= " $n"; } } else { - my $verified=""; + my $verified = ""; for(split(" ", $TESTCASES)) { if(-e "$TESTDIR/test$_") { - $verified.="$_ "; + $verified .= "$_ "; } } if($verified eq "") { @@ -2884,7 +2883,7 @@ sub disabledtests { } my $r = rand @all; push @rand, $all[$r]; - $all[$r]=""; + $all[$r] = ""; $TESTCASES = join(" ", @all); } $TESTCASES = join(" ", @rand); @@ -2893,7 +2892,7 @@ sub disabledtests { # Display the contents of the given file. Line endings are canonicalized # and excessively long files are elided sub displaylogcontent { - my ($file)=@_; + my ($file) = @_; if(open(my $single, "<", $file)) { my $linecount = 0; my $truncate; @@ -2931,7 +2930,7 @@ sub displaylogcontent { } sub displaylogs { - my ($runnerid, $testnum)=@_; + my ($runnerid, $testnum) = @_; my $logdir = getrunnerlogdir($runnerid); opendir(DIR, $logdir) || die "cannot open dir: $!"; @@ -2995,15 +2994,15 @@ sub displaylogs { my $failed; my $failedign; my $failedre; -my $ok=0; -my $ign=0; -my $total=0; -my $executed=0; -my $retry_done=0; -my $lasttest=0; +my $ok = 0; +my $ign = 0; +my $total = 0; +my $executed = 0; +my $retry_done = 0; +my $lasttest = 0; my @at = split(" ", $TESTCASES); -my $count=0; -my $endwaitcnt=0; +my $count = 0; +my $endwaitcnt = 0; $start = time(); @@ -3265,7 +3264,7 @@ sub displaylogs { } if(%skipped && !$short) { - my $s=0; + my $s = 0; # Temporary hash to print the restraints sorted by the number # of their occurrences my %restraints; @@ -3279,7 +3278,7 @@ sub displaylogs { # now gather all test case numbers that had this reason for being # skipped - my $c=0; + my $c = 0; my $max = 9; for(0 .. scalar @teststat) { my $t = $_; diff --git a/tests/secureserver.pl b/tests/secureserver.pl index 7e64e1d894c8..d0da33e352b8 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -48,7 +48,7 @@ BEGIN my $stunnel = "stunnel"; -my $verbose=0; # set to 1 for debugging +my $verbose = 0; # set to 1 for debugging my $accept_port = 8991; # our default, weird enough my $target_port = 8999; # default test http-server port diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 7544c8b62fd1..66c971b49953 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -77,8 +77,7 @@ our $logfile; # server log filename, for logmsg # sub logmsg { my ($seconds, $usec) = Time::HiRes::gettimeofday(); - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime($seconds); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($seconds); my $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec); # we see warnings on Windows run that $logfile is used uninitialized # TODO: not found yet where this comes from diff --git a/tests/servers.pm b/tests/servers.pm index 33fcaaa5507f..166050758e63 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -119,32 +119,32 @@ use testutil qw( my %serverpidfile; # all server pid filenames, identified by server id my %serverportfile;# all server port filenames, identified by server id -my $sshdvernum; # for socks server, ssh daemon version number -my $sshdverstr; # for socks server, ssh daemon version string -my $sshderror; # for socks server, ssh daemon version error -my %doesntrun; # servers that do not work, identified by pidfile +my $sshdvernum; # for socks server, ssh daemon version number +my $sshdverstr; # for socks server, ssh daemon version string +my $sshderror; # for socks server, ssh daemon version error +my %doesntrun; # servers that do not work, identified by pidfile my %PORT = (nolisten => 47); # port we use for a local non-listening service -my $server_response_maxtime=13; +my $server_response_maxtime = 13; my $httptlssrv = find_httptlssrv(); -my %run; # running server -my %runcert; # cert file currently in use by an SSL running server -my $CLIENTIP="127.0.0.1"; # address which curl uses for incoming connections -my $CLIENT6IP="[::1]"; # address which curl uses for incoming connections +my %run; # running server +my %runcert; # cert file currently in use by an SSL running server +my $CLIENTIP = "127.0.0.1"; # address which curl uses for incoming connections +my $CLIENT6IP = "[::1]"; # address which curl uses for incoming connections my $posix_pwd = build_sys_abs_path($pwd); # current working directory in POSIX format -my $h2cver = "h2c"; # this version is decided by the nghttp2 lib being used -my $HOSTIP="127.0.0.1"; # address on which the test server listens -my $HOST6IP="[::1]"; # address on which the test server listens -my $HTTPUNIXPATH; # HTTP server Unix domain socket path -my $SOCKSUNIXPATH; # socks server Unix domain socket path +my $h2cver = "h2c"; # this version is decided by the nghttp2 lib being used +my $HOSTIP = "127.0.0.1"; # address on which the test server listens +my $HOST6IP = "[::1]"; # address on which the test server listens +my $HTTPUNIXPATH; # HTTP server Unix domain socket path +my $SOCKSUNIXPATH; # socks server Unix domain socket path my $SSHSRVMD5 = "[uninitialized]"; # MD5 of ssh server public key my $SSHSRVSHA256 = "[uninitialized]"; # SHA256 of ssh server public key -my $USER; # name of the current user -my $sshdid; # for socks server, ssh daemon version id -my $ftpchecktime=1; # time it took to verify our test FTP server +my $USER; # name of the current user +my $sshdid; # for socks server, ssh daemon version id +my $ftpchecktime = 1; # time it took to verify our test FTP server my $SERVER_TIMEOUT_SEC = 15; # time for a server to spin up # Variables shared with runtests.pl -our $SOCKSIN="socksd-request.log"; # what curl sent to the SOCKS proxy +our $SOCKSIN = "socksd-request.log"; # what curl sent to the SOCKS proxy our $err_unexpected; # error instead of warning on server unexpectedly alive our $debugprotocol; # nonzero for verbose server logs our $stunnel; # path to stunnel command @@ -153,15 +153,15 @@ our $stunnel; # path to stunnel command # Check for a command in the PATH of the test server. # sub checkcmd { - my ($cmd, @extrapaths)=@_; + my ($cmd, @extrapaths) = @_; my @paths; if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { # PATH separator is different - @paths=(split(';', $ENV{'PATH'}), @extrapaths); + @paths = (split(';', $ENV{'PATH'}), @extrapaths); } else { - @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", - "/sbin", "/usr/bin", "/usr/local/bin", @extrapaths); + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin", @extrapaths); } for(@paths) { if(-x "$_/$cmd" . exe_ext('SYS') && ! -d "$_/$cmd" . exe_ext('SYS')) { @@ -291,7 +291,7 @@ sub checkdied { # 4 for an unsupported server type # sub serverfortest { - my (@what)=@_; + my (@what) = @_; for(my $i = scalar(@what) - 1; $i >= 0; $i--) { my $srvrline = $what[$i]; @@ -332,7 +332,7 @@ sub serverfortest { # Return the pids (yes plural) of the new child process to the parent. # sub startnew { - my ($cmd, $pidfile, $timeout, $fakepidfile)=@_; + my ($cmd, $pidfile, $timeout, $fakepidfile) = @_; logmsg "startnew: $cmd\n" if($verbose); @@ -515,7 +515,7 @@ sub getexternalproxyflags { sub verifyhttp { my ($proto, $ipvnum, $idnum, $ip, $port_or_path, $do_http3) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $bonus=""; + my $bonus = ""; # $port_or_path contains a path for Unix sockets, sws ignores the port my $port = ($ipvnum eq "unix") ? 80 : $port_or_path; my $infix = ($do_http3) ? "_h3" : ""; @@ -530,7 +530,7 @@ sub verifyhttp { if($proto eq "gopher") { # gopher is funny - $bonus="1/"; + $bonus = "1/"; } my $flags = "--max-time $server_response_maxtime "; @@ -602,8 +602,8 @@ sub verifyhttp { sub verifyftp { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -894,8 +894,8 @@ sub verifypid { sub verifysmb { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -954,8 +954,8 @@ sub verifysmb { sub verifytelnet { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -1164,7 +1164,7 @@ sub runhttpserver { # sub runhttp2server { my ($verb) = @_; - my $proto="http/2"; + my $proto = "http/2"; my $ipvnum = 4; my $idnum = 0; my $exe = "$perl " . shell_quote("$srcdir/http2-server.pl"); @@ -1225,7 +1225,7 @@ sub runhttp2server { # sub runhttp3server { my ($verb, $cert) = @_; - my $proto="http/3"; + my $proto = "http/3"; my $ipvnum = 4; my $idnum = 0; my $exe = "$perl " . shell_quote("$srcdir/http3-server.pl"); @@ -1676,7 +1676,7 @@ sub rundnsserver { my $portfile = $serverportfile{$server}; my $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); - my $cmd=server_exe('dnsd'); + my $cmd = server_exe('dnsd'); $cmd .= " --port 0"; $cmd .= " --verbose" if($debugprotocol); $cmd .= " --pidfile \"$pidfile\""; @@ -1800,7 +1800,7 @@ sub sshkeyalgostr { # sub runsshserver { my ($id, $verb, $ipv6) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'ssh'; my $ipvnum = 4; my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; @@ -1925,7 +1925,7 @@ sub runsshserver { # sub runmqttserver { my ($id, $verb, $ipv6) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'mqtt'; my $port = protoport($proto); my $ipvnum = 4; @@ -1951,7 +1951,7 @@ sub runmqttserver { unlink($portfile); # need to see a new one # start our MQTT server - on a random port! - my $cmd=server_exe('mqttd'). + my $cmd = server_exe('mqttd'). " --port 0". " --pidfile $pidfile". " --portfile $portfile". @@ -1988,7 +1988,7 @@ sub runmqttserver { # sub runsocksserver { my ($id, $verb, $ipv6, $is_unix) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'socks'; my $ipvnum = 4; my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; @@ -2014,9 +2014,9 @@ sub runsocksserver { unlink($portfile); # need to see a new one # start our socks server, get commands from the FTP cmd file - my $cmd=""; + my $cmd = ""; if($is_unix) { - $cmd=server_exe('socksd'). + $cmd = server_exe('socksd'). " --pidfile $pidfile". " --reqfile $LOGDIR/$SOCKSIN". " --logfile $logfile". @@ -2025,7 +2025,7 @@ sub runsocksserver { " --config $LOGDIR/$SERVERCMD"; $portfile = "none"; } else { - $cmd=server_exe('socksd'). + $cmd = server_exe('socksd'). " --port 0". " --pidfile $pidfile". " --portfile $portfile". @@ -2433,7 +2433,7 @@ sub startservers { return ("failed starting ". uc($what) ." server", $serr); } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "ftp-ipv6") { @@ -2450,7 +2450,7 @@ sub startservers { } logmsg sprintf("* pid ftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'ftp-ipv6'}="$pid $pid2"; + $run{'ftp-ipv6'} = "$pid $pid2"; } } elsif($what eq "gopher") { @@ -2469,7 +2469,7 @@ sub startservers { } logmsg sprintf ("* pid gopher => %d %d\n", $pid, $pid2) if($verbose); - $run{'gopher'}="$pid $pid2"; + $run{'gopher'} = "$pid $pid2"; } } elsif($what eq "gopher-ipv6") { @@ -2488,7 +2488,7 @@ sub startservers { } logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'gopher-ipv6'}="$pid $pid2"; + $run{'gopher-ipv6'} = "$pid $pid2"; } } elsif($what eq "http") { @@ -2507,7 +2507,7 @@ sub startservers { } logmsg sprintf ("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } } elsif($what eq "http-proxy") { @@ -2526,7 +2526,7 @@ sub startservers { } logmsg sprintf ("* pid http-proxy => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-proxy'}="$pid $pid2"; + $run{'http-proxy'} = "$pid $pid2"; } } elsif($what eq "http-ipv6") { @@ -2545,7 +2545,7 @@ sub startservers { } logmsg sprintf("* pid http-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-ipv6'}="$pid $pid2"; + $run{'http-ipv6'} = "$pid $pid2"; } } elsif($what eq "rtsp") { @@ -2561,7 +2561,7 @@ sub startservers { return ("failed starting RTSP server", $serr); } logmsg sprintf("* pid rtsp => %d %d\n", $pid, $pid2) if($verbose); - $run{'rtsp'}="$pid $pid2"; + $run{'rtsp'} = "$pid $pid2"; } } elsif($what eq "rtsp-ipv6") { @@ -2578,7 +2578,7 @@ sub startservers { } logmsg sprintf("* pid rtsp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'rtsp-ipv6'}="$pid $pid2"; + $run{'rtsp-ipv6'} = "$pid $pid2"; } } elsif($what =~ /^(ftp|imap|pop3|smtp)s$/) { @@ -2605,7 +2605,7 @@ sub startservers { return ("failed starting $cproto server", $serr); } logmsg sprintf("* pid $cproto => %d %d\n", $pid, $pid2) if($verbose); - $run{$cproto}="$pid $pid2"; + $run{$cproto} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -2616,7 +2616,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "file") { @@ -2663,7 +2663,7 @@ sub startservers { return ("failed starting HTTP server (for https/https-mtls)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -2673,7 +2673,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "http/2") { @@ -2704,7 +2704,7 @@ sub startservers { return ("failed starting HTTP server (for http/2)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{'http/2'}) { ($serr, $pid, $pid2, $PORT{"http2"}, $PORT{"http2tls"}) = @@ -2714,7 +2714,7 @@ sub startservers { } logmsg sprintf ("* pid http/2 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http/2'}="$pid $pid2"; + $run{'http/2'} = "$pid $pid2"; } } elsif($what eq "http/3") { @@ -2745,7 +2745,7 @@ sub startservers { return ("failed starting HTTP server (for http/3)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{'http/3'}) { ($serr, $pid, $pid2, $PORT{"http3"}) = runhttp3server($verbose); @@ -2754,7 +2754,7 @@ sub startservers { } logmsg sprintf ("* pid http/3 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http/3'}="$pid $pid2"; + $run{'http/3'} = "$pid $pid2"; } } elsif($what eq "gophers") { @@ -2777,15 +2777,14 @@ sub startservers { } if(!$run{'gopher'}) { my $port; - ($serr, $pid, $pid2, $port) = - runhttpserver("gopher", $verbose, 0); + ($serr, $pid, $pid2, $port) = runhttpserver("gopher", $verbose, 0); $PORT{'gopher'} = $port; if($pid <= 0) { return ("failed starting GOPHER server", $serr); } logmsg sprintf("* pid gopher => %d %d\n", $pid, $pid2) if($verbose); logmsg "GOPHERPORT => $port\n" if($verbose); - $run{'gopher'}="$pid $pid2"; + $run{'gopher'} = "$pid $pid2"; } if(!$run{'gophers'}) { my $port; @@ -2798,7 +2797,7 @@ sub startservers { logmsg sprintf("* pid gophers => %d %d\n", $pid, $pid2) if($verbose); logmsg "GOPHERSPORT => $port\n" if($verbose); - $run{'gophers'}="$pid $pid2"; + $run{'gophers'} = "$pid $pid2"; } } elsif($what eq "https-proxy") { @@ -2829,7 +2828,7 @@ sub startservers { } logmsg sprintf("* pid https-proxy => %d %d\n", $pid, $pid2) if($verbose); - $run{'https-proxy'}="$pid $pid2"; + $run{'https-proxy'} = "$pid $pid2"; } } elsif($what eq "httptls") { @@ -2851,7 +2850,7 @@ sub startservers { } logmsg sprintf("* pid httptls => %d %d\n", $pid, $pid2) if($verbose); - $run{'httptls'}="$pid $pid2"; + $run{'httptls'} = "$pid $pid2"; } } elsif($what eq "httptls-ipv6") { @@ -2873,7 +2872,7 @@ sub startservers { } logmsg sprintf("* pid httptls-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'httptls-ipv6'}="$pid $pid2"; + $run{'httptls-ipv6'} = "$pid $pid2"; } } elsif($what eq "dns") { @@ -2890,7 +2889,7 @@ sub startservers { return ("failed starting DNS server", $serr); } logmsg sprintf("* pid dns => %d %d\n", $pid, $pid2) if($verbose); - $run{'dns'}="$pid $pid2"; + $run{'dns'} = "$pid $pid2"; } } elsif($what eq "tftp") { @@ -2907,7 +2906,7 @@ sub startservers { return ("failed starting TFTP server", $serr); } logmsg sprintf("* pid tftp => %d %d\n", $pid, $pid2) if($verbose); - $run{'tftp'}="$pid $pid2"; + $run{'tftp'} = "$pid $pid2"; } } elsif($what eq "tftp-ipv6") { @@ -2924,7 +2923,7 @@ sub startservers { return ("failed starting TFTP-IPv6 server", $serr); } logmsg sprintf("* pid tftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'tftp-ipv6'}="$pid $pid2"; + $run{'tftp-ipv6'} = "$pid $pid2"; } } elsif($what eq "sftp" || $what eq "scp") { @@ -2934,7 +2933,7 @@ sub startservers { return ("failed starting SSH server", $serr); } logmsg sprintf("* pid ssh => %d %d\n", $pid, $pid2) if($verbose); - $run{'ssh'}="$pid $pid2"; + $run{'ssh'} = "$pid $pid2"; } } elsif($what eq "socks4" || $what eq "socks5" ) { @@ -2944,7 +2943,7 @@ sub startservers { return ("failed starting socks server", $serr); } logmsg sprintf("* pid socks => %d %d\n", $pid, $pid2) if($verbose); - $run{'socks'}="$pid $pid2"; + $run{'socks'} = "$pid $pid2"; } } elsif($what eq "socks5unix") { @@ -2954,7 +2953,7 @@ sub startservers { return ("failed starting socks5unix server", $serr); } logmsg sprintf("* pid socks5unix => %d %d\n", $pid, $pid2) if($verbose); - $run{'socks5unix'}="$pid $pid2"; + $run{'socks5unix'} = "$pid $pid2"; } } elsif($what eq "mqtt" ) { @@ -2970,7 +2969,7 @@ sub startservers { return ("failed starting mqtt server", $serr); } logmsg sprintf("* pid mqtt => %d %d\n", $pid, $pid2) if($verbose); - $run{'mqtt'}="$pid $pid2"; + $run{'mqtt'} = "$pid $pid2"; } } elsif($what eq "mqtts" ) { @@ -2990,7 +2989,7 @@ sub startservers { return ("failed starting mqtt server", $serr); } logmsg sprintf("* pid mqtt => %d %d\n", $pid, $pid2) if($verbose); - $run{'mqtt'}="$pid $pid2"; + $run{'mqtt'} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -3000,7 +2999,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "http-unix") { @@ -3019,7 +3018,7 @@ sub startservers { } logmsg sprintf("* pid http-unix => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-unix'}="$pid $pid2"; + $run{'http-unix'} = "$pid $pid2"; } } elsif($what eq "dict") { @@ -3030,7 +3029,7 @@ sub startservers { } logmsg sprintf ("* pid DICT => %d %d\n", $pid, $pid2) if($verbose); - $run{'dict'}="$pid $pid2"; + $run{'dict'} = "$pid $pid2"; } } elsif($what eq "smb") { @@ -3041,7 +3040,7 @@ sub startservers { } logmsg sprintf ("* pid SMB => %d %d\n", $pid, $pid2) if($verbose); - $run{'smb'}="$pid $pid2"; + $run{'smb'} = "$pid $pid2"; } } elsif($what eq "telnet") { @@ -3053,7 +3052,7 @@ sub startservers { } logmsg sprintf ("* pid neg TELNET => %d %d\n", $pid, $pid2) if($verbose); - $run{'telnet'}="$pid $pid2"; + $run{'telnet'} = "$pid $pid2"; } } else { diff --git a/tests/test1119.pl b/tests/test1119.pl index 845453d8a10b..4797c758bd9b 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -48,14 +48,14 @@ } # we may get the directory root pointed out -my $root=$ARGV[0] || "."; +my $root = $ARGV[0] || "."; # need an include directory when building out-of-tree my $i = ($ARGV[1]) ? "-I$ARGV[1] " : ''; -my $verbose=0; -my $summary=0; -my $misses=0; +my $verbose = 0; +my $summary = 0; +my $misses = 0; my @manrefs; my @syms; @@ -80,7 +80,7 @@ sub scanenum { } sub scanheader { - my ($f)=@_; + my ($f) = @_; open(my $h, "<", $f); while(<$h>) { if(/^#define ((LIB|)CURL[A-Za-z0-9_]*)/) { @@ -146,24 +146,24 @@ sub scanman_md_dir { open(my $s, "<", "$root/docs/libcurl/symbols-in-versions"); while(<$s>) { if(/(^[^ \n]+) +(.*)/) { - my ($sym, $rest)=($1, $2); + my ($sym, $rest) = ($1, $2); if($doc{$sym}) { print "Detected duplicate symbol: $sym\n"; $misses++; next; } - $doc{$sym}=$sym; - my @a=split(/ +/, $rest); + $doc{$sym} = $sym; + my @a = split(/ +/, $rest); if($a[2]) { # this symbol is documented to have been present the last time # in this release - $rem{$sym}=$a[2]; + $rem{$sym} = $a[2]; } } } close $s; -my $ignored=0; +my $ignored = 0; for my $e (sort @syms) { # OBSOLETE - names that are placeholders for a position where we # previously had a name, that is now removed. The OBSOLETE names should @@ -188,7 +188,7 @@ sub scanman_md_dir { if($verbose) { print $e."\n"; } - $doc{$e}="used"; + $doc{$e} = "used"; next; } else { @@ -223,7 +223,7 @@ sub scanman_md_dir { my %warned; for my $r (@manrefs) { if($r =~ /^([^:]+):(.*)/) { - my ($sym, $file)=($1, $2); + my ($sym, $file) = ($1, $2); if(!$doc{$sym} && !$warned{$sym, $file}) { print "$file: $sym is not a public symbol\n"; $warned{$sym, $file} = 1; diff --git a/tests/test1135.pl b/tests/test1135.pl index 40f18aaa2293..152adef46376 100755 --- a/tests/test1135.pl +++ b/tests/test1135.pl @@ -62,9 +62,9 @@ } } -my $verbose=0; -my $summary=0; -my $misses=0; +my $verbose = 0; +my $summary = 0; +my $misses = 0; my @out; foreach my $f (@incs) { diff --git a/tests/test1139.pl b/tests/test1139.pl index a11c9d03a3ba..7e5ddb4f04ac 100755 --- a/tests/test1139.pl +++ b/tests/test1139.pl @@ -42,11 +42,11 @@ use warnings; # we may get the directory roots pointed out -my $root=$ARGV[0] || "."; -my $buildroot=$ARGV[1] || "."; +my $root = $ARGV[0] || "."; +my $buildroot = $ARGV[1] || "."; my $syms = "$root/docs/libcurl/symbols-in-versions"; my $curlh = "$root/include/curl/curl.h"; -my $errors=0; +my $errors = 0; # the prepopulated alias list is the CURLINFO_* defines that are used for the # debug function callback and the fact that they use the same prefix as the @@ -105,7 +105,7 @@ sub scanmdpage { die "no curl.h"; while(<$r>) { if(/^\#define (CURL(OPT|INFO|MOPT)_\w+) (.*)/) { - $alias{$1}=$3; + $alias{$1} = $3; } } close($r); @@ -198,11 +198,11 @@ sub scanmdpage { $no++; chomp; if(/struct LongShort aliases/) { - $list=1; + $list = 1; } elsif($list) { if(/^ \{(\"[^,]*\").*\'(.)\',/) { - my ($l, $s)=($1, $2); + my ($l, $s) = ($1, $2); my $sh; my $lo; my $title; @@ -212,12 +212,12 @@ sub scanmdpage { if($l =~ /\"(.*)\"/) { # long option $lo = $1; - $title="--$lo"; + $title = "--$lo"; } if($s ne " ") { # a short option $sh = $s; - $title="-$sh, $title"; + $title = "-$sh, $title"; } push @getparam, $title; $opts{$title} |= 1; @@ -265,7 +265,7 @@ sub scanmdpage { chomp; my $l= $_; if(/^ \{ \" *(.*)/) { - my $str=$1; + my $str = $1; my $combo; if($str =~ /^-(.), --([a-z0-9.-]*)/) { # figure out the -short, --long combo @@ -295,10 +295,10 @@ sub scanmdpage { my $exists; my $missing; if($where & 1) { - $exists=" tool_getparam.c"; + $exists = " tool_getparam.c"; } else { - $missing=" tool_getparam.c"; + $missing = " tool_getparam.c"; } if($where & 2) { $exists.= " curl.1"; diff --git a/tests/test1140.pl b/tests/test1140.pl index 45da989ea667..219cfb689500 100755 --- a/tests/test1140.pl +++ b/tests/test1140.pl @@ -50,7 +50,7 @@ sub manpresent { elsif(-r "$docsroot/$man" || -r "$docsroot/libcurl/$man" || -r "$docsroot/libcurl/opts/$man") { - $manp{$man}=1; + $manp{$man} = 1; return 1; } return 0; @@ -65,7 +65,7 @@ sub file { chomp; my $l = $_; while($l =~ s/\\f(.)([^ ]*)\\f(.)//) { - my ($pre, $str, $post)=($1, $2, $3); + my ($pre, $str, $post) = ($1, $2, $3); if($str =~ /^\\f[ib]/i) { print "error: $f:$line: double-highlight\n"; $errors++; diff --git a/tests/test1165.pl b/tests/test1165.pl index bcf8b50fb713..e3f45b2ada21 100755 --- a/tests/test1165.pl +++ b/tests/test1165.pl @@ -38,15 +38,15 @@ my %docs; # we may get the directory root pointed out -my $root=$ARGV[0] || "."; -my $DOCS="CURL-DISABLE.md"; +my $root = $ARGV[0] || "."; +my $DOCS = "CURL-DISABLE.md"; sub scanconf { - my ($f)=@_; + my ($f) = @_; open S, "<$f"; while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_TYPECHECK)$/) { $disable{$sym} = 1; } @@ -67,11 +67,11 @@ sub scan_configure { } sub scanconf_cmake { - my ($hashr, $f)=@_; + my ($hashr, $f) = @_; open S, "<$f"; while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_INSTALL|CURL_DISABLE_SRP|CURL_DISABLE_TYPECHECK)$/) { $hashr->{$sym} = 1; } @@ -94,11 +94,11 @@ sub scan_cmake_config_h { ); sub scan_file { - my ($source)=@_; + my ($source) = @_; open F, "<$source"; while() { while(s/(CURL_DISABLE_[A-Z0-9_]+)//) { - my ($sym)=($1); + my ($sym) = ($1); if(!$whitelisted{$sym}) { $file{$sym} = $source; @@ -109,7 +109,7 @@ sub scan_file { } sub scan_dir { - my ($dir)=@_; + my ($dir) = @_; opendir(my $dh, $dir) || die "Cannot opendir $dir: $!"; my @cfiles = grep { /\.[ch]\z/ && -f "$dir/$_" } readdir($dh); closedir $dh; @@ -132,7 +132,7 @@ sub scan_docs { while() { $line++; if(/^## `(CURL_DISABLE_[A-Z0-9_]+)`/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_TYPECHECK)$/) { $docs{$sym} = $line; } diff --git a/tests/test1167.pl b/tests/test1167.pl index dd0097e3716a..eea5fe45f1ca 100755 --- a/tests/test1167.pl +++ b/tests/test1167.pl @@ -47,29 +47,29 @@ $Cpreprocessor = 'cpp'; } -my $verbose=0; +my $verbose = 0; # verbose mode when -v is the first argument if($ARGV[0] eq "-v") { - $verbose=1; + $verbose = 1; shift; } # we may get the directory root pointed out -my $root=$ARGV[0] || "."; +my $root = $ARGV[0] || "."; # need an include directory when building out-of-tree my $i = ($ARGV[1]) ? "-I$ARGV[1] " : ''; my $incdir = "$root/include/curl"; -my $summary=0; -my $misses=0; +my $summary = 0; +my $misses = 0; my @syms; sub scanenums { - my ($file)=@_; + my ($file) = @_; my $skipit = 0; open H_IN, "-|", "$Cpreprocessor -DCURL_DISABLE_DEPRECATION $i$file" || @@ -117,7 +117,7 @@ sub scanenums { } sub scanheader { - my ($f)=@_; + my ($f) = @_; scanenums($f); open(H, '<', $f); while() { diff --git a/tests/test1173.pl b/tests/test1173.pl index baeff857626b..f469564f2957 100755 --- a/tests/test1173.pl +++ b/tests/test1173.pl @@ -32,10 +32,10 @@ use File::Basename; # get the filename first -my $symbolsinversions=shift @ARGV; +my $symbolsinversions = shift @ARGV; # we may get the directory roots pointed out -my @manpages=@ARGV; +my @manpages = @ARGV; my $errors = 0; my %docsdirs; @@ -84,10 +84,10 @@ sub allsymbols { while(<$f>) { if($_ =~ /^([^ ]*) +(.*)/) { my ($name, $info) = ($1, $2); - $symbol{$name}=$name; + $symbol{$name} = $name; if($info =~ /([0-9.]+) +([0-9.]+)/) { - $deprecated{$name}=$info; + $deprecated{$name} = $info; } } } @@ -98,7 +98,7 @@ sub allsymbols { 'curl.1' => 1 ); sub checkref { - my ($f, $sec, $file, $line)=@_; + my ($f, $sec, $file, $line) = @_; my $present = 0; #print STDERR "check $f.$sec\n"; if($ref{"$f.$sec"}) { @@ -108,7 +108,7 @@ sub checkref { foreach my $d (keys %docsdirs) { if(-f "$d/$f.$sec") { $present = 1; - $ref{"$f.$sec"}=1; + $ref{"$f.$sec"} = 1; last; } } @@ -140,7 +140,7 @@ sub scanmanpage { my $shc = 0; my $optpage = 0; # option or function my @sh; - my $SH=""; + my $SH = ""; my @separators; my @sepline; diff --git a/tests/test1175.pl b/tests/test1175.pl index 54e0a1fe762e..b929ff41ee86 100755 --- a/tests/test1175.pl +++ b/tests/test1175.pl @@ -41,7 +41,7 @@ sub getdocserrors { ; } else { - $docs{$symbol}=1; + $docs{$symbol} = 1; } } } @@ -57,7 +57,7 @@ sub getincludeerrors { # removed! } else { - $error{$symbol}=$added; + $error{$symbol} = $added; } } } diff --git a/tests/test1177.pl b/tests/test1177.pl index 32377986a585..cf5bd44fa744 100755 --- a/tests/test1177.pl +++ b/tests/test1177.pl @@ -30,14 +30,14 @@ use strict; use warnings; -my $manpage=$ARGV[0]; -my $header=$ARGV[1]; -my $source=$ARGV[2]; +my $manpage = $ARGV[0]; +my $header = $ARGV[1]; +my $source = $ARGV[2]; my %manversion; my %headerversion; my %manname; my %sourcename; -my $error=0; +my $error = 0; open(my $m, "<", $manpage); while(<$m>) { diff --git a/tests/test1222.pl b/tests/test1222.pl index 96f6d60b23e2..df7bfa1c0fbf 100755 --- a/tests/test1222.pl +++ b/tests/test1222.pl @@ -54,7 +54,7 @@ # Scan header file for public function and enum values. Flag them with # the version they are deprecated in, if some. sub scan_header { - my ($f)=@_; + my ($f) = @_; my $line = ""; my $incomment = 0; my $inenum = 0; @@ -147,7 +147,7 @@ sub scan_header { # Each option has to be declared as ".IP From c2b050e4e49630246b93ff2e2bab2ae41402c74c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 11:44:18 +0200 Subject: [PATCH 0459/1062] servers: deduplicate `storerequest()` across two servers Closes #22041 --- tests/server/first.h | 2 ++ tests/server/rtspd.c | 60 ++------------------------------------ tests/server/sws.c | 69 +++++--------------------------------------- tests/server/util.c | 54 ++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 118 deletions(-) diff --git a/tests/server/first.h b/tests/server/first.h index ad5cec41b392..3bd75a2d88f0 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -141,6 +141,8 @@ extern int write_pidfile(const char *filename); extern int write_portfile(const char *filename, int port); extern void set_advisor_read_lock(const char *filename); extern void clear_advisor_read_lock(const char *filename); +extern void storerequest(const char *reqbuf, size_t totalsize, + const char *filename); static volatile int got_exit_signal = 0; static volatile int exit_signal = 0; #ifdef _WIN32 diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index d6d32ddac283..36973185ed7b 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -544,61 +544,6 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) return 1; /* done */ } -/* store the entire request in a file */ -static void rtspd_storerequest(const char *reqbuf, size_t totalsize) -{ - int error = 0; - char errbuf[STRERROR_LEN]; - size_t written; - size_t writeleft; - FILE *dump; - char dumpfile[256]; - - snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, REQUEST_DUMP); - - if(!reqbuf) - return; - if(totalsize == 0) - return; - - do { - dump = curlx_fopen(dumpfile, "ab"); - /* !checksrc! disable ERRNOVAR 1 */ - } while(!dump && ((error = errno) == EINTR)); - if(!dump) { - logmsg("Error opening file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Failed to write request input to %s", dumpfile); - return; - } - - writeleft = totalsize; - do { - written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); - if(got_exit_signal) - goto storerequest_cleanup; - if(written > 0) - writeleft -= written; - error = errno; - /* !checksrc! disable ERRNOVAR 1 */ - } while((writeleft > 0) && (error == EINTR)); - - if(writeleft == 0) - logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); - else if(writeleft > 0) { - logmsg("Error writing file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", - totalsize - writeleft, totalsize, dumpfile); - } - -storerequest_cleanup: - - if(curlx_fclose(dump)) - logmsg("Error closing file %s error (%d) %s", dumpfile, - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); -} - /* return 0 on success, non-zero on failure */ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) { @@ -671,7 +616,7 @@ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) if(fail) { /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - rtspd_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP); return 1; } @@ -706,7 +651,8 @@ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) reqbuf[req->offset] = '\0'; /* dump the request to an external file */ - rtspd_storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); + storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset, + REQUEST_DUMP); if(got_exit_signal) return 1; diff --git a/tests/server/sws.c b/tests/server/sws.c index dc376a70f51a..04e6025fa5fe 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -100,6 +100,9 @@ static size_t num_sockets = 0; #define REQUEST_PROXY_DUMP "proxy.input" #define RESPONSE_PROXY_DUMP "proxy.response" +#define REQUEST_DUMP_FILENAME \ + (is_proxy ? REQUEST_PROXY_DUMP : REQUEST_DUMP) + /* file in which additional instructions may be found */ static const char *cmdfile = "log/server.cmd"; @@ -739,62 +742,6 @@ static int sws_ProcessRequest(struct sws_httprequest *req) return 1; /* done */ } -/* store the entire request in a file */ -static void sws_storerequest(const char *reqbuf, size_t totalsize) -{ - int error = 0; - char errbuf[STRERROR_LEN]; - size_t written; - size_t writeleft; - FILE *dump; - char dumpfile[256]; - - snprintf(dumpfile, sizeof(dumpfile), "%s/%s", - logdir, is_proxy ? REQUEST_PROXY_DUMP : REQUEST_DUMP); - - if(!reqbuf) - return; - if(totalsize == 0) - return; - - do { - dump = curlx_fopen(dumpfile, "ab"); - /* !checksrc! disable ERRNOVAR 1 */ - } while(!dump && ((error = errno) == EINTR)); - if(!dump) { - logmsg("[2] Error opening file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Failed to write request input "); - return; - } - - writeleft = totalsize; - do { - written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); - if(got_exit_signal) - goto storerequest_cleanup; - if(written > 0) - writeleft -= written; - error = errno; - /* !checksrc! disable ERRNOVAR 1 */ - } while((writeleft > 0) && (error == EINTR)); - - if(writeleft == 0) - logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); - else if(writeleft > 0) { - logmsg("Error writing file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", - totalsize - writeleft, totalsize, dumpfile); - } - -storerequest_cleanup: - - if(curlx_fclose(dump)) - logmsg("Error closing file %s error (%d) %s", dumpfile, - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); -} - /* returns -1 on failure */ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) { @@ -1114,7 +1061,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); req->offset = 0; /* read websocket traffic */ @@ -1157,7 +1104,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) logmsg("log the websocket traffic"); /* dump the incoming websocket traffic to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); req->offset = 0; } init_httprequest(req); @@ -1199,7 +1146,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) if(fail) { /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); return -1; } @@ -1230,7 +1177,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) /* at the end of a request dump it to an external file */ if(fail || req->done_processing) - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); if(got_exit_signal) return -1; @@ -2379,7 +2326,7 @@ static int test_sws(int argc, const char *argv[]) if(req->connmon) { const char *keepopen = "[DISCONNECT]\n"; - sws_storerequest(keepopen, strlen(keepopen)); + storerequest(keepopen, strlen(keepopen), REQUEST_DUMP_FILENAME); req->connmon = FALSE; } diff --git a/tests/server/util.c b/tests/server/util.c index a6511598dca3..77d635a9e907 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -268,6 +268,60 @@ void clear_advisor_read_lock(const char *filename) } } +/* store the entire request in a file */ +void storerequest(const char *reqbuf, size_t totalsize, const char *filename) +{ + int error = 0; + char errbuf[STRERROR_LEN]; + size_t written; + size_t writeleft; + FILE *dump; + char dumpfile[256]; + + snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, filename); + + if(!reqbuf) + return; + if(totalsize == 0) + return; + + do { + dump = curlx_fopen(dumpfile, "ab"); + /* !checksrc! disable ERRNOVAR 1 */ + } while(!dump && ((error = errno) == EINTR)); + if(!dump) { + logmsg("storerequest: Error opening file %s error (%d) %s", dumpfile, + error, curlx_strerror(error, errbuf, sizeof(errbuf))); + return; + } + + writeleft = totalsize; + do { + written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); + if(got_exit_signal) + goto storerequest_cleanup; + if(written > 0) + writeleft -= written; + error = errno; + /* !checksrc! disable ERRNOVAR 1 */ + } while((writeleft > 0) && (error == EINTR)); + + if(writeleft == 0) + logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); + else if(writeleft > 0) { + logmsg("Error writing file %s error (%d) %s", dumpfile, + error, curlx_strerror(error, errbuf, sizeof(errbuf))); + logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", + totalsize - writeleft, totalsize, dumpfile); + } + +storerequest_cleanup: + + if(curlx_fclose(dump)) + logmsg("Error closing file %s error (%d) %s", dumpfile, + errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); +} + /* vars used to keep around previous signal handlers */ typedef void (*SIGHANDLER_T)(int); From bd10924b47c8bf94127eaee8c56e7b5e5418da46 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 16 Jun 2026 10:12:24 +0200 Subject: [PATCH 0460/1062] url: connection credentials origin When tying credentials to a connection (NTLM, Negotiate) also link the origin the credentials are for. This prevents a connection reuse with the same credentials, but intended for another origin. The mis-reuse could happen for a forwarding proxy and NTLM (although, in the mind of the person writing this, it is an insane setup). Closes #22040 --- lib/http_negotiate.c | 9 ++++++++- lib/http_ntlm.c | 8 ++++++++ lib/url.c | 24 +++++++++++------------- lib/urldata.h | 1 + 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 4aa67f01d5b6..891369b5bc2b 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -42,6 +42,7 @@ static void http_auth_nego_reset(struct connectdata *conn, conn->proxy_negotiate_state = GSS_AUTHNONE; else { conn->http_negotiate_state = GSS_AUTHNONE; + Curl_peer_unlink(&conn->creds_origin); Curl_creds_unlink(&conn->creds); } if(neg_ctx) @@ -132,13 +133,19 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(result) http_auth_nego_reset(conn, neg_ctx, proxy); - if(!proxy) { + if(!result && !proxy) { /* Start it up. From this time onwards, the connection is tied * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } if(conn->creds && !Curl_creds_same(creds, conn->creds)) { DEBUGASSERT(0); /* should not happen. */ return CURLE_FAILED_INIT; } + Curl_peer_link(&conn->creds_origin, data->state.origin); Curl_creds_link(&conn->creds, creds); } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 1442fd6f7a7d..dc9911fdacb2 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -93,6 +93,8 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, else if(*state == NTLMSTATE_TYPE3) { infof(data, "NTLM handshake rejected"); Curl_auth_ntlm_remove(conn, proxy); + Curl_peer_unlink(&conn->creds_origin); + Curl_creds_unlink(&conn->creds); *state = NTLMSTATE_NONE; return CURLE_REMOTE_ACCESS_DENIED; } @@ -184,10 +186,16 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(!proxy) { /* Start it up. From this time onwards, the connection is tied * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } if(conn->creds && !Curl_creds_same(creds, conn->creds)) { DEBUGASSERT(0); /* should not happen. */ return CURLE_FAILED_INIT; } + Curl_peer_link(&conn->creds_origin, data->state.origin); Curl_creds_link(&conn->creds, creds); } result = Curl_auth_create_ntlm_type1_message(data, creds, "HTTP", diff --git a/lib/url.c b/lib/url.c index 8da2aca92432..3018dc438b8a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -515,6 +515,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_creds_unlink(&conn->socks_proxy.creds); #endif Curl_creds_unlink(&conn->creds); + Curl_peer_unlink(&conn->creds_origin); curlx_safefree(conn->options); curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); @@ -1011,18 +1012,11 @@ static bool url_match_auth_ntlm(struct connectdata *conn, possible. (Especially we must not reuse the same connection if partway through a handshake!) */ if(m->want_ntlm_http) { - if(!Curl_creds_same(m->data->state.creds, conn->creds)) { - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM if it does - not have any auth ongoing. */ -#ifdef USE_SPNEGO - if((conn->http_ntlm_state == NTLMSTATE_NONE) && - (conn->http_negotiate_state == GSS_AUTHNONE)) { -#else - if(conn->http_ntlm_state == NTLMSTATE_NONE) { -#endif - m->found = conn; - } + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) { + /* connection credentials in play and not the same or not for the + * same origin. */ return FALSE; } } @@ -1079,7 +1073,9 @@ static bool url_match_auth_nego(struct connectdata *conn, already authenticating with the right credentials. If not, keep looking so that we can reuse Negotiate connections if possible. */ if(m->want_nego_http) { - if(!Curl_creds_same(m->needle->creds, conn->creds)) + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) return FALSE; } else if(conn->http_negotiate_state != GSS_AUTHNONE) { @@ -1816,6 +1812,7 @@ static CURLcode url_set_conn_login(struct Curl_easy *data, { /* If our protocol needs a password and we have none, use the defaults */ if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) { + Curl_peer_link(&conn->creds_origin, data->state.origin); if(data->state.creds) Curl_creds_link(&conn->creds, data->state.creds); else @@ -1825,6 +1822,7 @@ static CURLcode url_set_conn_login(struct Curl_easy *data, else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { /* for protocols that do not handle credentials per request, * the connection credentials are set by the initial transfer. */ + Curl_peer_link(&conn->creds_origin, data->state.origin); Curl_creds_link(&conn->creds, data->state.creds); } diff --git a/lib/urldata.h b/lib/urldata.h index a0d7fa421df8..232364fcf3a7 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -301,6 +301,7 @@ struct connectdata { struct proxy_info http_proxy; #endif struct Curl_creds *creds; /* When connection itself is tied to credentials */ + struct Curl_peer *creds_origin; /* origin tied credentials are for */ char *options; /* options string, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ From b56cb3b71eaadb5da9b19e333ac0769739dacbc8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 16 Jun 2026 15:50:08 +0200 Subject: [PATCH 0461/1062] _URL.md: remove the zone-id mention While correct, it felt random and misplaced there. Closes #22048 --- docs/cmdline-opts/_URL.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index 2e69eb0bffa1..700e711359d9 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -20,9 +20,5 @@ handshakes. This improves speed. Connection reuse can only be done for URLs specified for a single command line invocation and cannot be performed between separate curl runs. -Provide an IPv6 zone id in the URL with an escaped percentage sign. Like in - - http://[fe80::3%25eth0]/ - Everything provided on the command line that is not a command line option or its argument, curl assumes is a URL and treats it as such. From 1bb75af8e9cb5b3ba897894e31309cb4442a4c15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:14:06 +0000 Subject: [PATCH 0462/1062] GHA: update google/boringssl to v0.20260616.0 Closes #22046 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index ee74d5c47a7b..40c91a74a708 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.0.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260526.0 + BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7a347dcec40c..f5c6f999bd97 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -37,7 +37,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.0.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260526.0 + BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.679 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com From be8f24323e8dc11c46f36b34118c7cf9856875fc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 15:40:07 +0200 Subject: [PATCH 0463/1062] perl: `open... || ` -> `open... or` (cont.) Also: unfold a few lines, fix a space, add a missing parentheses. Follow-up to 678e63934cc4bd1941b20c5111d37a6e530d2a5d #22036 Closes #22047 --- scripts/cd2cd | 4 ++-- scripts/managen | 12 ++++++------ scripts/mdlinkcheck | 2 +- scripts/mk-ca-bundle.pl | 2 +- scripts/nroff2cd | 4 ++-- tests/allversions.pm | 2 +- tests/ftpserver.pl | 10 +++++----- tests/libtest/test613.pl | 10 +++++----- tests/runner.pm | 3 +-- tests/runtests.pl | 10 ++++------ tests/test1119.pl | 6 ++---- tests/test1139.pl | 18 +++++++----------- tests/test1140.pl | 3 +-- tests/test1167.pl | 2 +- tests/test1173.pl | 6 ++---- tests/test1222.pl | 2 +- tests/test1488.pl | 3 +-- tests/valgrind.pm | 3 +-- 18 files changed, 44 insertions(+), 58 deletions(-) diff --git a/scripts/cd2cd b/scripts/cd2cd index e66e3f844fa1..ead664dfd66f 100755 --- a/scripts/cd2cd +++ b/scripts/cd2cd @@ -89,7 +89,7 @@ sub single { my $salist = 0; my $copyright; my $spdx; - open(F, "<:crlf", $f) || + open(F, "<:crlf", $f) or return 1; while() { $line++; @@ -212,7 +212,7 @@ HEAD close(F); if($inplace) { - open(O, ">$f") || return 1; + open(O, ">$f") or return 1; print O @desc; close(O); } diff --git a/scripts/managen b/scripts/managen index d05e8b9386d3..2bf1c3d75ded 100755 --- a/scripts/managen +++ b/scripts/managen @@ -562,7 +562,7 @@ sub maybespace { sub single { my ($dir, $manpage, $f, $standalone) = @_; my $fh; - open($fh, "<:crlf", "$dir/$f") || + open($fh, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my $short; my $long; @@ -969,7 +969,7 @@ sub single { sub getshortlong { my ($dir, $f) = @_; $f =~ s/^.*\///; - open(F, "<:crlf", "$dir/$f") || + open(F, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my $short; my $long; @@ -1037,7 +1037,7 @@ sub indexoptions { sub header { my ($dir, $manpage, $f) = @_; my $fh; - open($fh, "<:crlf", "$dir/$f") || + open($fh, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my @d = render($manpage, $fh, $f, 1); close($fh); @@ -1047,7 +1047,7 @@ sub header { sub sourcecategories { my ($dir) = @_; my %cats; - open(H, "<$dir/../../src/tool_help.h") || + open(H, "<$dir/../../src/tool_help.h") or die "cannot find the header file"; while() { if(/^\#define CURLHELP_([A-Z0-9]*)/) { @@ -1177,7 +1177,7 @@ sub listglobals { # Find all global options and output them foreach my $f (sort @files) { - open(F, "<:crlf", "$dir/$f") || + open(F, "<:crlf", "$dir/$f") or die "could not read $dir/$f"; my $long; my $start = 0; @@ -1222,7 +1222,7 @@ sub mainpage { # $manpage is 1 for nroff, 0 for ASCII my $ret; my $fh; - open($fh, "<:crlf", "$dir/mainpage.idx") || + open($fh, "<:crlf", "$dir/mainpage.idx") or die "no $dir/mainpage.idx file"; print <

) { chomp; if($_ =~ /^\#\# SHA256: (.*)/) { diff --git a/scripts/nroff2cd b/scripts/nroff2cd index 782da8c14b3a..374b0cbdab58 100755 --- a/scripts/nroff2cd +++ b/scripts/nroff2cd @@ -43,7 +43,7 @@ my $nroff2cd = "0.1"; # to keep check sub single { my ($f) = @_; - open(F, "<:crlf", $f) || + open(F, "<:crlf", $f) or return 1; my $line; my $title; @@ -67,7 +67,7 @@ sub single { # remove leading directory $f =~ s/(.*?\/)//; close(F); - open(F, "<:crlf", $f) || return 1; + open(F, "<:crlf", $f) or return 1; } if($d =~ /^\.TH ([^ ]*) (\d) \"(.*?)\" ([^ \n]*)/) { # header, this needs to be the first thing after leading comments diff --git a/tests/allversions.pm b/tests/allversions.pm index 576980b5bff1..0dba43e0fed4 100644 --- a/tests/allversions.pm +++ b/tests/allversions.pm @@ -32,7 +32,7 @@ our %pastversion; sub allversions { my ($file) = @_; - open(A, "<$file") || + open(A, "<$file") or die "cannot open the versions file $file\n"; my $before = 1; my $relcount; diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 4d0fbc8a595f..bbfa05ce7419 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -215,7 +215,7 @@ sub exit_signal_handler { sub ftpmsg { # append to the server.input file - open(my $input, ">>", "$logdir/server$idstr.input") || + open(my $input, ">>", "$logdir/server$idstr.input") or logmsg "failed to open $logdir/server$idstr.input\n"; print $input @_; @@ -940,7 +940,7 @@ sub DATA_smtp { logmsg "Store test number $testno in $filename\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $line; @@ -1290,7 +1290,7 @@ sub APPEND_imap { logmsg "Store test number $testno in $filename\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $received = 0; @@ -2399,7 +2399,7 @@ sub STOR_ftp { sendcontrol "125 Gimme gimme gimme!\r\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $line; @@ -2826,7 +2826,7 @@ sub customize { %customcount = (); # %delayreply = (); # - open(my $custom, "<", "$logdir/$SERVERCMD") || + open(my $custom, "<", "$logdir/$SERVERCMD") or return 1; logmsg "FTPD: Getting commands from $logdir/$SERVERCMD\n"; diff --git a/tests/libtest/test613.pl b/tests/libtest/test613.pl index 314f3829ac93..a404bb613925 100755 --- a/tests/libtest/test613.pl +++ b/tests/libtest/test613.pl @@ -41,16 +41,16 @@ sub errout { if($ARGV[0] eq "prepare") { my $dirname = $ARGV[1]; - mkdir $dirname || errout "$!"; + mkdir $dirname or errout "$!"; chdir $dirname; # Create the files in alphabetical order, to increase the chances # of receiving a consistent set of directory contents regardless # of whether the server alphabetizes the results or not. - mkdir "asubdir" || errout "$!"; + mkdir "asubdir" or errout "$!"; chmod 0777, "asubdir"; - open(FILE, ">plainfile.txt") || errout "$!"; + open(FILE, ">plainfile.txt") or errout "$!"; binmode FILE; print FILE "Test file to support curl test suite\n"; close(FILE); @@ -59,7 +59,7 @@ sub errout { utime time, timegm(0,0,12,1,0,100), "plainfile.txt"; chmod 0666, "plainfile.txt"; - open(FILE, ">emptyfile.txt") || errout "$!"; + open(FILE, ">emptyfile.txt") or errout "$!"; binmode FILE; close(FILE); # The mtime is specifically chosen to be an even number so that it can be @@ -67,7 +67,7 @@ sub errout { utime time, timegm(0,0,12,1,0,100), "emptyfile.txt"; chmod 0666, "emptyfile.txt"; - open(FILE, ">rofile.txt") || errout "$!"; + open(FILE, ">rofile.txt") or errout "$!"; binmode FILE; print FILE "Read-only test file to support curl test suite\n"; close(FILE); diff --git a/tests/runner.pm b/tests/runner.pm index fd665b948424..9e514f0e238d 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -998,8 +998,7 @@ sub singletest_run { logmsg "$CMDLINE\n"; } - open(my $cmdlog, ">", "$LOGDIR/$CURLLOG") || - die "Failure writing log file"; + open(my $cmdlog, ">", "$LOGDIR/$CURLLOG") or die "Failure writing log file"; print $cmdlog "$CMDLINE\n"; close($cmdlog) or die "Failure writing log file"; diff --git a/tests/runtests.pl b/tests/runtests.pl index f660fa6e262c..35606c740e5c 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -356,8 +356,7 @@ sub cleardir { my $file; # Get all files - opendir(my $dh, $dir) || - return 0; # cannot open dir + opendir(my $dh, $dir) or return 0; # cannot open dir while($file = readdir($dh)) { # Do not clear the $PIDDIR or $LOCKDIR since those need to live beyond # one test @@ -2675,7 +2674,7 @@ sub pickrunner { # seed of the month. December 2019 becomes 201912 $randseed = ($year + 1900) * 100 + $mon + 1; print "Using curl: $CURL\n"; - open(my $curlvh, "-|", exerunner() . shell_quote($CURL) . " --version 2>$dev_null") || + open(my $curlvh, "-|", exerunner() . shell_quote($CURL) . " --version 2>$dev_null") or die "could not get curl version!"; my @c = <$curlvh>; close($curlvh) or die "could not get curl version!"; @@ -2958,8 +2957,7 @@ sub displaylogcontent { sub displaylogs { my ($runnerid, $testnum) = @_; my $logdir = getrunnerlogdir($runnerid); - opendir(DIR, $logdir) || - die "cannot open dir: $!"; + opendir(DIR, $logdir) or die "cannot open dir: $!"; my @logs = readdir(DIR); closedir(DIR); @@ -3155,7 +3153,7 @@ sub displaylogs { $endwaitcnt = 0; # This runner is ready to be serviced my $testnum = $runnersrunning{$ridready}; - defined $testnum || die "Internal error: test for runner $ridready unknown"; + defined $testnum or die "Internal error: test for runner $ridready unknown"; delete $runnersrunning{$ridready}; my ($error, $again) = singletest($ridready, $testnum, $countforrunner{$ridready}, $totaltests); if($again) { diff --git a/tests/test1119.pl b/tests/test1119.pl index 75bca88a67d2..47ec214120c1 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -92,8 +92,7 @@ sub scanheader { sub scanallheaders { my $d = "$root/include/curl"; - opendir(my $dh, $d) || - die "Cannot opendir: $!"; + opendir(my $dh, $d) or die "Cannot opendir: $!"; my @headers = grep { /.h\z/ } readdir($dh); closedir $dh; foreach my $h (@headers) { @@ -130,8 +129,7 @@ sub checkmanpage { sub scanman_md_dir { my ($d) = @_; - opendir(my $dh, $d) || - die "Cannot opendir: $!"; + opendir(my $dh, $d) or die "Cannot opendir: $!"; my @mans = grep { /.md\z/ } readdir($dh); closedir $dh; for my $m (@mans) { diff --git a/tests/test1139.pl b/tests/test1139.pl index 7e5ddb4f04ac..81a33bb5375e 100755 --- a/tests/test1139.pl +++ b/tests/test1139.pl @@ -67,8 +67,7 @@ sub scanmdpage { my ($file, @words) = @_; - open(my $mh, "<", $file) || - die "could not open $file"; + open(my $mh, "<", $file) or die "could not open $file"; my @m; while(<$mh>) { if($_ =~ /^## (.*)/) { @@ -101,8 +100,7 @@ sub scanmdpage { my $r; # check for define aliases -open($r, "<", $curlh) || - die "no curl.h"; +open($r, "<", $curlh) or die "no curl.h"; while(<$r>) { if(/^\#define (CURL(OPT|INFO|MOPT)_\w+) (.*)/) { $alias{$1} = $3; @@ -113,8 +111,7 @@ sub scanmdpage { my @curlopt; my @curlinfo; my @curlmopt; -open($r, "<", $syms) || - die "no input file"; +open($r, "<", $syms) or die "no input file"; while(<$r>) { chomp; my $l= $_; @@ -187,8 +184,7 @@ sub scanmdpage { ######################################################################### # parse the curl code that parses the command line arguments! -open($r, "<", "$root/src/tool_getparam.c") || - die "no input file"; +open($r, "<", "$root/src/tool_getparam.c") or die "no input file"; my $list; my @getparam; # store all parsed parameters @@ -230,7 +226,8 @@ sub scanmdpage { ######################################################################### # parse the curl.1 man page, extract all documented command line options # The man page may or may not be rebuilt, so check both possible locations -open($r, "<", "$buildroot/docs/cmdline-opts/curl.1") || open($r, "<", "$root/docs/cmdline-opts/curl.1") || +open($r, "<", "$buildroot/docs/cmdline-opts/curl.1") or + open($r, "<", "$root/docs/cmdline-opts/curl.1") or die "failed getting curl.1"; my @manpage; # store all parsed parameters while(<$r>) { @@ -258,8 +255,7 @@ sub scanmdpage { ######################################################################### # parse the curl code that outputs the curl -h list -open($r, "<", "$root/src/tool_listhelp.c") || - die "no input file"; +open($r, "<", "$root/src/tool_listhelp.c") or die "no input file"; my @toolhelp; # store all parsed parameters while(<$r>) { chomp; diff --git a/tests/test1140.pl b/tests/test1140.pl index 219cfb689500..d7f56043f451 100755 --- a/tests/test1140.pl +++ b/tests/test1140.pl @@ -58,8 +58,7 @@ sub manpresent { sub file { my ($f) = @_; - open(my $fh, "<", $f) || - die "test1140.pl could not open $f"; + open(my $fh, "<", $f) or die "test1140.pl could not open $f"; my $line = 1; while(<$fh>) { chomp; diff --git a/tests/test1167.pl b/tests/test1167.pl index 06b2b2b6cd69..a0bdb385db91 100755 --- a/tests/test1167.pl +++ b/tests/test1167.pl @@ -72,7 +72,7 @@ sub scanenums { my ($file) = @_; my $skipit = 0; - open H_IN, "-|", "$Cpreprocessor -DCURL_DISABLE_DEPRECATION $i$file" || + open(H_IN, "-|", "$Cpreprocessor -DCURL_DISABLE_DEPRECATION $i$file") or die "Cannot preprocess $file"; while() { my ($line, $linenum) = ($_, $.); diff --git a/tests/test1173.pl b/tests/test1173.pl index f469564f2957..14894c625bd0 100755 --- a/tests/test1173.pl +++ b/tests/test1173.pl @@ -79,8 +79,7 @@ CURLOPT_RANDOM_FILE => 1, ); sub allsymbols { - open(my $f, "<", $symbolsinversions) || - die "$symbolsinversions: $|"; + open(my $f, "<", $symbolsinversions) or die "$symbolsinversions: $|"; while(<$f>) { if($_ =~ /^([^ ]*) +(.*)/) { my ($name, $info) = ($1, $2); @@ -144,8 +143,7 @@ sub scanmanpage { my @separators; my @sepline; - open(my $m, "<", $file) || - die "test1173.pl could not open $file"; + open(my $m, "<", $file) or die "test1173.pl could not open $file"; if($file =~ /[\/\\](CURL|curl_)([^\/\\]*).3/) { # This is a man page for libcurl. It requires an example unless it is # considered deprecated. diff --git a/tests/test1222.pl b/tests/test1222.pl index a601688ee605..0d6a5ffc2cd6 100755 --- a/tests/test1222.pl +++ b/tests/test1222.pl @@ -238,7 +238,7 @@ sub scan_man_page { } # Read symbols-in-versions. -open(my $fh, "<", "$root/docs/libcurl/symbols-in-versions") || +open(my $fh, "<", "$root/docs/libcurl/symbols-in-versions") or die "$root/docs/libcurl/symbols-in-versions"; while(<$fh>) { if($_ =~ /^((?:CURL|LIBCURL)\S+)\s+\S+\s*(\S*)\s*(\S*)$/) { diff --git a/tests/test1488.pl b/tests/test1488.pl index c16c44de296d..b940fb7f14d0 100755 --- a/tests/test1488.pl +++ b/tests/test1488.pl @@ -97,8 +97,7 @@ sub checkmanpage { sub scanman_md_dir { my ($d) = @_; - opendir(my $dh, $d) || - die "Cannot opendir: $!"; + opendir(my $dh, $d) or die "Cannot opendir: $!"; my @mans = grep { /.md\z/ } readdir($dh); closedir $dh; for my $m (@mans) { diff --git a/tests/valgrind.pm b/tests/valgrind.pm index 1fbd8ecf64b5..1d0a629d80c0 100644 --- a/tests/valgrind.pm +++ b/tests/valgrind.pm @@ -40,8 +40,7 @@ use File::Basename; sub valgrindparse { my ($file) = @_; my @o; - open(my $val, "<", $file) || - return; + open(my $val, "<", $file) or return; @o = <$val>; close($val); return @o; From aacb90bee9a11209c3f7d26cb7d91956b95b9524 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 18:52:45 +0200 Subject: [PATCH 0464/1062] cmake/FindGSS: prioritize MIT over GNU in pkg-config detection To match the non-pkg-config path, and also suspected user expectation. This comes with a small incompatibility in return for more consistency. Bug: https://github.com/curl/curl/pull/22052#discussion_r3422424979 Follow-up to 9e19a577eb93caae74c9793848efdf57480b04df #15176 Closes #22053 --- CMake/FindGSS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 2b37cbe48576..93efb497864b 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -51,7 +51,7 @@ set(_gss_LIBRARY_DIRS "") if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(_gss ${_gnu_modname} ${_mit_modname}) + pkg_search_module(_gss ${_mit_modname} ${_gnu_modname}) list(APPEND _gss_root_hints "${_gss_PREFIX}") set(_gss_version "${_gss_VERSION}") endif() From 6125d5d6c5aa26280d6dd44eb6f2c3123c7e68bf Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Tue, 16 Jun 2026 10:52:45 -0700 Subject: [PATCH 0465/1062] CI: improve labeler tag detection --- .github/labeler.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index a54e13c05ffc..f7adaba865e6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -39,6 +39,7 @@ authentication: - any-glob-to-all-files: "{\ CMake/FindGSS.cmake,\ CMake/FindLibgsasl.cmake,\ + docs/internals/CREDENTIALS.md,\ docs/libcurl/opts/CURLINFO_HTTPAUTH*,\ docs/libcurl/opts/CURLINFO_PROXYAUTH*,\ docs/libcurl/opts/CURLOPT_KRB*,\ @@ -49,10 +50,13 @@ authentication: docs/libcurl/opts/CURLOPT_XOAUTH*,\ lib/*gssapi*,\ lib/*ntlm*,\ + lib/creds.*,\ + lib/curl_ntlm*,\ lib/curl_sasl.*,\ lib/http_aws*,\ lib/http_digest.*,\ lib/http_negotiate.*,\ + lib/http_ntlm.*,\ lib/vauth/**\ }" @@ -129,6 +133,8 @@ connecting & proxies: docs/libcurl/opts/CURLOPT_SOCKS*,\ docs/libcurl/opts/CURLOPT_TCP*,\ docs/libcurl/opts/CURLOPT_TIMEOUT*,\ + lib/cf-https-connect.*,\ + lib/cf-ip-happy.*,\ lib/cf-*proxy.*,\ lib/cf-socket.*,\ lib/cfilters.*,\ @@ -136,9 +142,10 @@ connecting & proxies: lib/connect.*,\ lib/http_proxy.*,\ lib/if2ip.*,\ - lib/noproxy.*,\ + lib/proxy.*,\ lib/socks.*,\ src/tool_cb_soc.*,\ + tests/http/*proxy*,\ tests/http/*socks*,\ tests/server/socksd.c\ }" @@ -295,11 +302,14 @@ HTTP/3: .github/workflows/http3-linux.yml,\ CMake/FindNGHTTP3.cmake,\ CMake/FindNGTCP2.cmake,\ + docs/cmdline-opts/proxy-http3.md,\ docs/HTTP3.md,\ docs/examples/http3*,\ + lib/cf-h3-proxy.*,\ lib/vquic/**,\ tests/http3-server.pl,\ - tests/nghttpx.conf\ + tests/nghttpx.conf,\ + tests/http/*httpsrr*\ }" IMAP: @@ -381,6 +391,8 @@ name lookup: lib/socketpair*,\ lib/thrdpool.*,\ lib/thrdqueue.*,\ + tests/http/testenv/dnsd.*,\ + tests/http/*httpsrr*,\ tests/http/*resolve.py,\ tests/server/dnsd.c,\ tests/server/resolve.c\ @@ -458,6 +470,7 @@ tests: - all: - changed-files: - any-glob-to-any-file: + - 'docs/tests/**' - 'tests/**' TFTP: From 528c05a9876d77c9752709db3d24bcf31ebf57eb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 20:26:41 +0200 Subject: [PATCH 0466/1062] configure: tidy up `OPT_APPLE_SECTRUST` initialization The OS detection variable is not initialized at the time of assigning its value to `OPT_APPLE_SECTRUST`. Replace the current empty value with `no`. This keeps existing, desired, behavior. Closes #22054 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d93316506869..1752bb10a278 100644 --- a/configure.ac +++ b/configure.ac @@ -306,7 +306,7 @@ AS_HELP_STRING([--with-rustls=PATH],[where to look for Rustls, PATH points to th fi ]) -OPT_APPLE_SECTRUST=$curl_cv_apple +OPT_APPLE_SECTRUST=no AC_ARG_WITH(apple-sectrust, AS_HELP_STRING([--with-apple-sectrust],[enable Apple OS native certificate verification]),[ OPT_APPLE_SECTRUST=$withval From 73d060950e4659c3e47a0104a73023a932ce6b6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:48:28 +0000 Subject: [PATCH 0467/1062] GHA: bump pip cryptography from 46.0.7 to 48.0.1 Closes #22055 --- tests/http/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/requirements.txt b/tests/http/requirements.txt index 62c680420d5a..a31374e06446 100644 --- a/tests/http/requirements.txt +++ b/tests/http/requirements.txt @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: curl -cryptography==46.0.7 +cryptography==48.0.1 filelock==3.29.0 psutil==7.2.2 pytest==9.0.3 From 92db819714dc80944aa5302cbb94bd58d6983518 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 16 Jun 2026 12:07:08 +0200 Subject: [PATCH 0468/1062] cf-dns: pass peer for result lookups The DNS filter knows the peer it resolves and the code parts that want the results know the peer as well. Pass it to lookup methods to make sure results match. Background: when tunneling, the resolved peer is not always the one that other filters are looking for. Especially when HTTPS-RR results are accessed in TLS filters, those will differ. This prevents a HTTPS-RR for a proxy to be used for the origin when ECH is activated. To make ECH work through a tunnel, we need to start an additional resolve. Something to be fixed after 8.21. Closes #22042 --- docs/KNOWN_BUGS.md | 4 +++ lib/cf-dns.c | 74 +++++++++++++++++++++++++-------------- lib/cf-dns.h | 20 +++++++---- lib/cf-https-connect.c | 6 ++-- lib/cf-ip-happy.c | 15 ++++---- lib/socks.c | 6 ++-- lib/vquic/cf-ngtcp2-cmn.c | 2 +- lib/vquic/cf-quiche.c | 2 +- lib/vtls/openssl.c | 5 +-- lib/vtls/rustls.c | 6 ++-- lib/vtls/wolfssl.c | 10 +++--- 11 files changed, 96 insertions(+), 54 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index 32a76242efd0..8bf35114c084 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -48,6 +48,10 @@ Certain Windows installations may be missing CA roots. [curl issue 20897](https://github.com/curl/curl/issues/20897) [curl issue 12303](https://github.com/curl/curl/issues/12303) +## ECH not working through Proxy Tunnels + +[curl issue 22043](https://github.com/curl/curl/issues/22043) + # Email protocols ## IMAP `SEARCH ALL` truncated response diff --git a/lib/cf-dns.c b/lib/cf-dns.c index c737ce14f435..631c987be262 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -452,29 +452,35 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, /* Return the resolv result from the first "resolv" filter, starting * the given filter `cf` downwards. */ -static CURLcode cf_dns_result(struct Curl_cfilter *cf) +static CURLcode cf_dns_result(struct Curl_cfilter *cf, + struct Curl_peer *peer) { 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; + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + return CURLE_OK; /* ok, but no results */ } } return CURLE_FAILED_INIT; } -/* Return the result of the DNS resolution. Searches for a "resolv" +/* Return the result of the DNS resolution for peer. 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) +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - return cf_dns_result(conn->cfilter[sockindex]); + return cf_dns_result(conn->cfilter[sockindex], peer); } static const struct Curl_addrinfo *cf_dns_get_nth_ai( @@ -507,6 +513,7 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( */ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_peer *peer, int ai_family, unsigned int index) { @@ -514,12 +521,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, 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(cf, ctx->dns->addr, ai_family, index); - else - return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->resolv_result) + return NULL; + else if(ctx->dns) + return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else + return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + } } } return NULL; @@ -530,11 +539,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, * 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, + struct Curl_peer *peer, + 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); + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer, + ai_family, index); } #ifdef USE_HTTPSRR @@ -542,35 +554,43 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, * 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) +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { 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; - if(ctx->dns) - return ctx->dns->hinfo; - else - return Curl_resolv_get_https(data, ctx->resolv_id); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns) + return ctx->dns->hinfo; + else + return Curl_resolv_get_https(data, ctx->resolv_id); + } } } return NULL; } -bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { 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; - if(ctx->dns) - return TRUE; - else - return Curl_resolv_knows_https(data, ctx->resolv_id); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } } } - return FALSE; + return TRUE; } #endif /* USE_HTTPSRR */ diff --git a/lib/cf-dns.h b/lib/cf-dns.h index f6902b8f7b2f..891b1efea606 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -45,25 +45,33 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, uint8_t transport, bool complete_resolve); -CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + struct Curl_peer *peer, 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, + struct Curl_peer *peer, 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); -bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); #else -#define Curl_conn_dns_get_https(a, b) NULL -#define Curl_conn_dns_resolved_https(a, b) TRUE +#define Curl_conn_dns_get_https(a, b, c) NULL +#define Curl_conn_dns_resolved_https(a, b, c) TRUE #endif extern struct Curl_cftype Curl_cft_dns; diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index a2b7b40ab6f9..1a2e966ef66a 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -304,7 +304,8 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, size_t i; /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https(data, cf->sockindex); + rr = Curl_conn_dns_get_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); /* We do not support `rr->no_def_alpn`. */ if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { @@ -493,7 +494,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->httpsrr_resolved) { - ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); #ifdef DEBUGBUILD if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 68feca3062a1..963ccea94d78 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -118,15 +118,18 @@ UNITTEST void debug_set_transport_provider( struct cf_ai_iter { struct Curl_cfilter *cf; + struct Curl_peer *peer; int ai_family; unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, struct Curl_cfilter *cf, + struct Curl_peer *peer, int ai_family) { iter->cf = cf; + iter->peer = peer; /* not linked, ctx->ballers owns and has same lifetime */ iter->ai_family = ai_family; iter->n = 0; } @@ -139,7 +142,7 @@ static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, if(!iter->cf) return NULL; - addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + addr = Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n); if(addr) iter->n++; @@ -150,7 +153,7 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, struct Curl_easy *data) { return (iter->cf && - !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + !!Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n)); } @@ -766,16 +769,16 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, if(ctx->ballers.transport_peer == TRNSPRT_UNIX) { #ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_UNIX); + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_UNIX); #else return CURLE_UNSUPPORTED_PROTOCOL; #endif } else { /* TCP/UDP/QUIC */ #ifdef USE_IPV6 - cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, AF_INET6); + cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, ctx->ballers.peer, AF_INET6); #endif - cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_INET); + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_INET); } CURL_TRC_CF(data, cf, "init ip ballers for transport %u", @@ -854,7 +857,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->dns_resolved) { - result = Curl_conn_dns_result(cf->conn, cf->sockindex); + result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer); if(!result) ctx->dns_resolved = TRUE; else if(result == CURLE_AGAIN) { diff --git a/lib/socks.c b/lib/socks.c index 9ae8cd79699c..6c458c505d2f 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -343,7 +343,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, else if(!dns_done) return CURLPX_OK; - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; @@ -862,10 +862,10 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, #ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_V4) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET6, 0); #endif if(!ai) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index dc15928f1954..e1ca18cf69c8 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -1055,7 +1055,7 @@ CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, *done = FALSE; if(cf_ngtcp2_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 1edd597ad72e..31a3957ec372 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -1392,7 +1392,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, *done = FALSE; if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index dfc14fbc304a..eb6839cfba4b 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3502,7 +3502,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->peer); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -4970,7 +4970,8 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, if(ssl_connect_1 == connssl->connecting_state) { if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 39b15b889c6a..5183844a6fbd 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -981,8 +981,9 @@ init_config_builder_ech(struct Curl_easy *data, } } else { + const struct ssl_connect_data *connssl = cf->ctx; const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.peer); if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); @@ -1162,7 +1163,8 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, /* if we do ECH and need the HTTPS-RR information for it, * we delay the connect until it arrives or DNS resolve fails. */ if(cr_ech_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index b6a98d775b4f..c55490eb833f 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1217,7 +1217,8 @@ static CURLcode wssl_init_ssl_handle( #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG static CURLcode wssl_init_ech(struct wssl_ctx *wctx, struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct ssl_peer *peer) { int trying_ech_now = 0; @@ -1247,7 +1248,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->peer); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -1412,7 +1413,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG if(CURLECH_ENABLED(data)) { - result = wssl_init_ech(wctx, cf, data); + result = wssl_init_ech(wctx, cf, data, peer); if(result) goto out; } @@ -2120,7 +2121,8 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, /* if we do ECH and need the HTTPS-RR information for it, * we delay the connect until it arrives or DNS resolve fails. */ if(Curl_wssl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } From 7806fb36c530027d7367e22e9299d0dde6ae5bb0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 16 Jun 2026 23:22:58 +0200 Subject: [PATCH 0469/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 102 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 20 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 386f0b01f9d8..8c3a69c531d4 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1486 - Contributors: 3719 + Authors: 1489 + Contributors: 3728 This release includes the following changes: @@ -19,14 +19,19 @@ This release includes the following changes: This release includes the following bugfixes: o _ENVIRONMENT.md. Windows does case insensitive env variables [214] + o _URL.md: remove the zone-id mention [227] + o AmigaOS: curl_setup.h avoid explicit_bzero with clib2 [283] + o AmigaOS: fix build fallouts, re-add to CI [279] o asyn-thrdd: add IPv6 guards [195] o asyn-thrdd: fix result processing without wakeup socketpair [2] o autotools: mbedtls detection fixes [163] o BINDINGS: Update Hollywood link [181] o BUFQ.md: re-sync with source code [111] + o build: enable `-Wlogical-op` picky warning for GCC 4.4+ [277] o build: omit zlib pkg-config reference for Android [130] o cf-h2-prox: fix peer leak [132] o cf-h2-proxy: drop interim responses [47] + o cf-https-connect: do not engage on proxy origin [236] o cf-ip-happy.c: minor comment typo o cf-ip-happy: update documentation [223] o cf-socket: make Curl_addr2string static [224] @@ -35,12 +40,15 @@ This release includes the following bugfixes: o cfilters: fix busy loop on blocked transfers [72] o chunked: reject invalid bytes in trailer [210] o CIPHERS.md: fix the example that uses only TLS 1.3 [137] + o cmake/FindGSS: fix comment, adjust custom flavor property name [261] + o cmake/FindGSS: prioritize MIT over GNU in pkg-config detection [196] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] o cmake: fix three issues generating lib options in config files [126] o cmake: fix zstd CMake config name [5] o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] + o config2setopts: use default protocol properly [286] o connect: remove deref of freed pointer in trace call [128] o content_encoding: fix limit failure message [171] o content_encoding: fix non-last chunked rejection [209] @@ -54,6 +62,7 @@ This release includes the following bugfixes: o creds: remove two unused functions [158] o curl_easy_pause.md: rephrase the stream cache when pause clause [120] o curl_easy_setopt.md: change options when no transfer runs [122] + o curl_formdata: fix to pass long where missing, document `CURLFORM_NAMELENGTH` [243] o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] o curl_sha512_256: fix result code on error [166] @@ -91,7 +100,7 @@ This release includes the following bugfixes: o ftplistparser: clear strings.target if not symlink [148] o gnutls: allow building with nettle 4.0 [96] o gnutls: fix more nettle 4+ compatibility issues [94] - o GnuTLS: require 3.7.2 for earlydata [103] + o gnutls: require 3.7.2 for earlydata [103] o gsasl: fix potential double free [56] o gtls: fix ignored return and uninitialized status in OCSP check [49] o gtls: fix some typos [15] @@ -110,11 +119,14 @@ This release includes the following bugfixes: o idn: replace header guards with forward declaration [100] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o KNOWN_BUGS: remove stale Threads::Threads entry [135] + o krb5_sspi: fix error message on `DecryptMessage()` fail [269] + o ldap: base64 encode binary LDIF values with WinLDAP [273] o ldap: fix minor leak on write callback error [24] o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] o ldap: switch off chasing referrals [114] o lib678: fix to not be perma-skipped [10] o lib: make `__STDC_VERSION__` literals `L` (where missing) + o lib: transfer origin and proxy handling [276] o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] o libssh2: do not use deprecated macros when unavailable [177] @@ -144,6 +156,7 @@ This release includes the following bugfixes: o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o quic: count zero length packets against max [179] + o ratelimits: use minimal burst rate [245] o resolve: mention in error that IP address is expected [205] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] @@ -153,6 +166,7 @@ This release includes the following bugfixes: o schannel: check `schannel_sha256sum()` success, and more [165] o schannel: enforce Extended Key Usage for custom CA roots [29] o schannel: error on TLS 1.3-only with cipher list [136] + o schannel: fix https proxy for client cert and certinfo [280] o schannel: fix revoke_best_effort setting for proxy [70] o schannel: use fopen instead CreateFile [191] o schannel_verify: avoid out of blob access [11] @@ -169,14 +183,23 @@ This release includes the following bugfixes: o setopt: more careful cleanup of the HSTS cache [45] o show-headers.md: mention bold headers and --no-styled-output [17] o sigv4: URL encode the user name in the header [193] + o smb: integer overflow proof a size check [263] o smbserver: update internal id generation for Python 3 [238] + o socket: introduce `SOCK_EAGAIN()` and use it [278] + o socket: use name `sockerr` for socket error variables [271] + o socks_sspi: invalid response length is a fatal error [272] + o socks_sspi: store socks5_gssapi_enctype [262] o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o src: fix comment typos [83] o ssl native_ca_store: always reinit [211] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] + o sspi: free libcurl allocated memory with curlx_free [274] + o telnet: drop an `int` cast no longer necessary [270] + o telnet: drop redundant interim variables [275] o telnet: fix error message typos [186] + o telnet: fix old copy-paste typo in variable name [281] o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] o test1588: use %TESTNUMBER, not hard-coded number [118] o test1981: explicitly set the locale [85] @@ -191,6 +214,7 @@ This release includes the following bugfixes: o tidy-up: drop stray casts for allocated pointers [174] o tidy-up: miscellaneous [106] o tls: fix incomplete mTLS config in conn reuse and session cache [108] + o tls: wolfssl: fixes for PQC key shares [239] o tool: warn when --ssl and --ftp-ssl-control override each other [129] o tool_formparse.c: fix two minor comment typos [25] o tool_formparse: polish error message + make two functions static [1] @@ -200,11 +224,13 @@ This release includes the following bugfixes: o tool_urlglob: avoid overflow at end of range [22] o tool_urlglob: better 'Duplicate glob name' position [82] o tool_urlglob: make globbing error reported for correct position [91] + o tool_writeout: fix %time{} output for %s [231] o transfer: clear referer when set to NULL [112] o unit1675: fix potential memory leak on dynbuf fail path [197] o unix-sockets: ignore proxy settings [6] o URL-SYNTAX: document more URL parsing details [134] o url: compare full origin when setting credentials [42] + o url: connection credentials origin [228] o url: connection reuse fixes for starttls [68] o url: detect proxy changes read from environment [110] o url: fix connection reuse for starttls protocols [27] @@ -225,6 +251,7 @@ This release includes the following bugfixes: o urlapi: URL decode hostname before IP address normalization [207] o user-agent.md: mention double quotes too [3] o var: use a dedicated pointer for the alloc [219] + o verify-release: verify more thoroughly with git [249] o vquic: drop stray casts for `iovec.iov_len` [162] o vtls: more large buffer support and error checks for SHA-256 [164] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] @@ -233,8 +260,10 @@ This release includes the following bugfixes: o VULN-DISCLOSURE-POLICY.md: emphasize comm as a human [180] o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] + o VULN-DISCLOSURE-POLICY: non-released code [253] o websockets: auto-tunnel through http proxy [102] o windows: update MS SDK versions in comments [60] + o winldap: avoid NULL pointer deref on `ldap_get_dn()` fail [242] o ws: make pong sending lazy [201] o x509asn1: fix DH public key parameter extraction [44] o x509asn1: fix operator order in do_pubkey [21] @@ -260,25 +289,29 @@ This release would not have looked like this without help, code, reports and advice from friends like these: 0xN3R3K3, 11soda11, Ady Elouej, A Johnston, Alan De Smet, alhudz, - ambikeesshh, amitbidlan, Andreas Falkenhahn, Andrei Rybak, Andrew Nesbitt, - Aritra Basu, azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, - BazaarAcc32 on github, Bill Mill, ByteRay on hackerone, chrizilla on github, - co-authors in libssh2, correctmost on github, Dan Fandrich, - Daniel Gustafsson, Daniel Stenberg, Dario Vinella, dependabot[bot], - dyingc on github, Earnestly on github, Elise Vance, Emanuel Krollmann, - Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, Gao Liyou, - Guancheng Li, Guannan Wang, Harry Sintonen, Hem Parekh, htasta, jeffhuang, - Jeremy Nicoll, Jiashuo Liang, Johannes Schlatow, Josef Cejka, Joshua Rogers, + alienowo on hackerone, ambikeesshh, amitbidlan, Andreas Falkenhahn, + Andrei Rybak, Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, + Bartel Sielski, Bastian Jesuiter, BazaarAcc32 on github, Bill Mill, + ByteRay on hackerone, chrizilla on github, co-authors in libssh2, + correctmost on github, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, + Dario Vinella, Darren Banfi, Dave Walker, daviey on hackerone, + dependabot[bot], dyingc on github, Earnestly on github, Elise Vance, + Emanuel Krollmann, Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, + Filipe Casal, Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, + Hem Parekh, htasta, jeffhuang, Jeremy Nicoll, Jiashuo Liang, + jjchuck on hackerone, Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, Mark Esler, Max Dymond, mik, Mike-menny on github, - Muhamad Arga Reksapati, mulan_dh on hackerone, parasol-aser, penpal, - Peter Krefting, Philip H., Rainer Jung, Randall S. Becker, Raymond Steen, - Ray Satiro, renjian on hackerone, renovate[bot], Ross Burton, Sergio Correia, - sfan5 on github, Shintomon Mathew, Sollace on github, Song X. Gao, - sourceturner, Stefan Eissing, Tim Martin, tiymat, Trail of Bits, Vasiliy-Kkk, - vectorqueue on hackerone, vegagent on hackerone, Viktor Szakats, - violet12331 on hackerone, Will Cosgrove, Xi Ruoyao, x-xiang on github, + Muhamad Arga Reksapati, mulan_dh on hackerone, oreadvanthink on github, + parasol-aser, penpal, Peter Krefting, Philip H., Rainer Jung, + Randall S. Becker, Raymond Steen, Ray Satiro, renjian on hackerone, + renovate[bot], Ross Burton, Saud Alshareef, Sergio Correia, sfan5 on github, + Shintomon Mathew, Sollace on github, Song X. Gao, sourceturner, + Stefan Eissing, Tatsuhiro Tsujikawa, Tim Martin, tiymat, + Tobias Frauenschläger, Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, + vegagent on hackerone, Viktor Szakats, violet12331 on hackerone, + Will Cosgrove, wulin-nudt on github, Xi Ruoyao, x-xiang on github, Yedaya Katsman, zhanhb on github, Zhanpeng Liu - (85 contributors) + (96 contributors) References to bug reports and discussions on issues: @@ -474,6 +507,7 @@ References to bug reports and discussions on issues: [192] = https://curl.se/bug/?i=21927 [193] = https://curl.se/bug/?i=21923 [195] = https://curl.se/bug/?i=21881 + [196] = https://curl.se/bug/?i=22052 [197] = https://curl.se/bug/?i=21922 [199] = https://curl.se/bug/?i=21914 [200] = https://curl.se/bug/?i=21910 @@ -502,6 +536,34 @@ References to bug reports and discussions on issues: [224] = https://curl.se/bug/?i=21946 [225] = https://curl.se/bug/?i=21951 [226] = https://curl.se/bug/?i=21945 + [227] = https://curl.se/bug/?i=22048 + [228] = https://curl.se/bug/?i=22040 [230] = https://curl.se/bug/?i=21949 + [231] = https://curl.se/bug/?i=22038 [235] = https://curl.se/bug/?i=21944 + [236] = https://curl.se/bug/?i=22033 [238] = https://curl.se/bug/?i=21937 + [239] = https://curl.se/bug/?i=22030 + [242] = https://curl.se/bug/?i=22000 + [243] = https://curl.se/bug/?i=22017 + [245] = https://curl.se/bug/?i=22016 + [249] = https://curl.se/bug/?i=22018 + [253] = https://curl.se/bug/?i=22025 + [261] = https://curl.se/bug/?i=22013 + [262] = https://curl.se/bug/?i=22004 + [263] = https://curl.se/bug/?i=22001 + [269] = https://curl.se/bug/?i=22003 + [270] = https://curl.se/bug/?i=22002 + [271] = https://curl.se/bug/?i=21998 + [272] = https://curl.se/bug/?i=21999 + [273] = https://curl.se/bug/?i=21926 + [274] = https://curl.se/bug/?i=21990 + [275] = https://curl.se/bug/?i=21995 + [276] = https://curl.se/bug/?i=21967 + [277] = https://curl.se/bug/?i=21893 + [278] = https://curl.se/bug/?i=21992 + [279] = https://curl.se/bug/?i=21993 + [280] = https://curl.se/bug/?i=21986 + [281] = https://curl.se/bug/?i=21979 + [283] = https://curl.se/bug/?i=21989 + [286] = https://curl.se/bug/?i=21983 From 74ac8e74ec61575f506645ce5eb786ff8fb5503d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 16 Jun 2026 13:42:05 +0200 Subject: [PATCH 0470/1062] creds: create with empty user+pass Allow creation of a `Curl_creds` instance with empty username and password (not NULL username/password). There are authentication schemes like that do not use the actual values of username/password but trigger on the mere existance. We have no test cases for this, so this is a shot in the dark here. Fixes #21943 Reported-by: Dan Fandrich Closes #22044 --- lib/creds.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index a11816ca548d..d22c166a4275 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -51,7 +51,7 @@ CURLcode Curl_creds_create(const char *user, Curl_creds_unlink(pcreds); /* Everything empty/NULL, this is the NULL credential */ - if(!ulen && !plen && !olen && !salen && !sslen) + if(!user && !passwd && !olen && !salen && !sslen) goto out; if((ulen > CURL_MAX_INPUT_LENGTH) || @@ -108,15 +108,18 @@ CURLcode Curl_creds_merge(const char *user, struct Curl_creds *creds_out = NULL; CURLcode result; - if(!user || !user[0]) - user = Curl_creds_user(creds_in); - if(!passwd || !passwd[0]) - passwd = Curl_creds_passwd(creds_in); - result = Curl_creds_create(user, passwd, - Curl_creds_oauth_bearer(creds_in), - Curl_creds_sasl_authzid(creds_in), - Curl_creds_sasl_service(creds_in), - source, &creds_out); + if(!creds_in) { + result = Curl_creds_create(user, passwd, NULL, NULL, NULL, + source, &creds_out); + } + else { + result = Curl_creds_create(user ? user : Curl_creds_user(creds_in), + passwd ? passwd : Curl_creds_passwd(creds_in), + Curl_creds_oauth_bearer(creds_in), + Curl_creds_sasl_authzid(creds_in), + Curl_creds_sasl_service(creds_in), + source, &creds_out); + } Curl_creds_link(pcreds_out, creds_out); Curl_creds_unlink(&creds_out); return result; From 39caaff7b3a5f571b49b8a6a7168c1f19089c878 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 17 Jun 2026 11:02:19 +0200 Subject: [PATCH 0471/1062] libtest: unify on easy_setopt macro - drop the old test_setopt() which did the same thing - remove #if 0'ed macros from first.h These macros now store results in 'result' more aggressively, but I believe that is generally favorable. Closes #22057 --- tests/libtest/first.h | 78 +++++++---------------------------------- tests/libtest/lib1156.c | 12 +++---- tests/libtest/lib1517.c | 16 ++++----- tests/libtest/lib1518.c | 10 +++--- tests/libtest/lib1520.c | 14 ++++---- tests/libtest/lib1522.c | 6 ++-- tests/libtest/lib1525.c | 28 +++++++-------- tests/libtest/lib1526.c | 28 +++++++-------- tests/libtest/lib1527.c | 26 +++++++------- tests/libtest/lib1528.c | 16 ++++----- tests/libtest/lib1529.c | 10 +++--- tests/libtest/lib1530.c | 6 ++-- tests/libtest/lib1533.c | 18 +++++----- tests/libtest/lib1540.c | 2 +- tests/libtest/lib1549.c | 6 ++-- tests/libtest/lib1553.c | 2 +- tests/libtest/lib1571.c | 16 ++++----- tests/libtest/lib1576.c | 20 +++++------ tests/libtest/lib1582.c | 14 ++++---- tests/libtest/lib1591.c | 12 +++---- tests/libtest/lib1598.c | 14 ++++---- tests/libtest/lib1686.c | 4 +++ tests/libtest/lib1906.c | 1 - tests/libtest/lib1933.c | 12 +++---- tests/libtest/lib1934.c | 14 ++++---- tests/libtest/lib1935.c | 14 ++++---- tests/libtest/lib1936.c | 14 ++++---- tests/libtest/lib1937.c | 18 +++++----- tests/libtest/lib1938.c | 20 +++++------ tests/libtest/lib1955.c | 14 ++++---- tests/libtest/lib1956.c | 14 ++++---- tests/libtest/lib1957.c | 14 ++++---- tests/libtest/lib1958.c | 14 ++++---- tests/libtest/lib1959.c | 14 ++++---- tests/libtest/lib1960.c | 20 +++++------ tests/libtest/lib1970.c | 18 +++++----- tests/libtest/lib1971.c | 18 +++++----- tests/libtest/lib1972.c | 16 ++++----- tests/libtest/lib1973.c | 16 ++++----- tests/libtest/lib1974.c | 12 +++---- tests/libtest/lib1975.c | 18 +++++----- tests/libtest/lib1978.c | 16 ++++----- tests/libtest/lib2023.c | 12 +++---- tests/libtest/lib2502.c | 2 +- tests/libtest/lib2504.c | 12 +++---- tests/libtest/lib2505.c | 12 +++---- tests/libtest/lib2506.c | 16 ++++----- tests/libtest/lib3025.c | 8 ++--- tests/libtest/lib3034.c | 16 ++++----- tests/libtest/lib3100.c | 18 +++++----- tests/libtest/lib3101.c | 16 ++++----- tests/libtest/lib3102.c | 10 +++--- tests/libtest/lib3103.c | 14 ++++---- tests/libtest/lib3104.c | 14 ++++---- tests/libtest/lib500.c | 12 +++---- tests/libtest/lib501.c | 4 +-- tests/libtest/lib505.c | 12 +++---- tests/libtest/lib506.c | 34 +++++++++--------- tests/libtest/lib508.c | 14 ++++---- tests/libtest/lib509.c | 2 +- tests/libtest/lib510.c | 18 +++++----- tests/libtest/lib511.c | 8 ++--- tests/libtest/lib513.c | 14 ++++---- tests/libtest/lib514.c | 14 ++++---- tests/libtest/lib515.c | 10 +++--- tests/libtest/lib516.c | 8 ++--- tests/libtest/lib518.c | 4 +-- tests/libtest/lib519.c | 10 +++--- tests/libtest/lib520.c | 6 ++-- tests/libtest/lib521.c | 8 ++--- tests/libtest/lib523.c | 10 +++--- tests/libtest/lib524.c | 6 ++-- tests/libtest/lib536.c | 12 +++---- tests/libtest/lib537.c | 4 +-- tests/libtest/lib539.c | 12 +++---- tests/libtest/lib541.c | 8 ++--- tests/libtest/lib542.c | 8 ++--- tests/libtest/lib544.c | 10 +++--- tests/libtest/lib547.c | 26 +++++++------- tests/libtest/lib549.c | 10 +++--- tests/libtest/lib552.c | 22 ++++++------ tests/libtest/lib553.c | 14 ++++---- tests/libtest/lib554.c | 16 ++++----- tests/libtest/lib556.c | 6 ++-- tests/libtest/lib559.c | 6 ++-- tests/libtest/lib562.c | 6 ++-- tests/libtest/lib566.c | 4 +-- tests/libtest/lib567.c | 16 ++++----- tests/libtest/lib568.c | 40 ++++++++++----------- tests/libtest/lib569.c | 22 ++++++------ tests/libtest/lib570.c | 24 ++++++------- tests/libtest/lib571.c | 30 ++++++++-------- tests/libtest/lib572.c | 40 ++++++++++----------- tests/libtest/lib574.c | 8 ++--- tests/libtest/lib576.c | 10 +++--- tests/libtest/lib578.c | 16 ++++----- tests/libtest/lib579.c | 22 ++++++------ tests/libtest/lib586.c | 4 +-- tests/libtest/lib589.c | 10 +++--- tests/libtest/lib590.c | 14 ++++---- tests/libtest/lib598.c | 18 +++++----- tests/libtest/lib599.c | 12 +++---- tests/libtest/lib643.c | 8 ++--- tests/libtest/lib650.c | 12 +++---- tests/libtest/lib651.c | 8 ++--- tests/libtest/lib652.c | 12 +++---- tests/libtest/lib654.c | 8 ++--- tests/libtest/lib655.c | 22 ++++++------ tests/libtest/lib661.c | 56 ++++++++++++++--------------- tests/libtest/lib666.c | 10 +++--- tests/libtest/lib667.c | 8 ++--- tests/libtest/lib668.c | 8 ++--- tests/libtest/lib670.c | 18 +++++----- tests/libtest/lib676.c | 12 +++---- tests/libtest/lib694.c | 12 +++---- tests/libtest/lib695.c | 4 +-- tests/libtest/lib757.c | 4 +-- tests/libtest/lib758.c | 2 +- 118 files changed, 802 insertions(+), 851 deletions(-) diff --git a/tests/libtest/first.h b/tests/libtest/first.h index a84be4166963..8a31aa950e10 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -59,24 +59,6 @@ extern int unitfail; /* for unittests */ #include #endif -#ifndef UNITTESTS -#define test_setopt(A, B, C) \ - do { \ - result = curl_easy_setopt(A, B, C); \ - if(result != CURLE_OK) \ - goto test_cleanup; \ - } while(0) -#endif /* !UNITTESTS */ - -#if 0 -#define test_multi_setopt(A, B, C) \ - do { \ - result = curl_multi_setopt(A, B, C); \ - if(result != CURLE_OK) \ - goto test_cleanup; \ - } while(0) -#endif - extern const char *libtest_arg2; /* set by first.c to the argv[2] or NULL */ extern const char *libtest_arg3; /* set by first.c to the argv[3] or NULL */ extern const char *libtest_arg4; /* set by first.c to the argv[4] or NULL */ @@ -202,16 +184,15 @@ void ws_close(CURL *curl); /* close the connection */ /* ---------------------------------------------------------------- */ -#define exe_easy_setopt(A, B, C, Y, Z) \ - do { \ - CURLcode ec = curl_easy_setopt(A, B, C); \ - if(ec != CURLE_OK) { \ - curl_mfprintf(stderr, \ - "%s:%d curl_easy_setopt() failed, " \ - "with code %d (%s)\n", \ - Y, Z, (int)ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ +#define exe_easy_setopt(A, B, C, Y, Z) \ + do { \ + result = curl_easy_setopt(A, B, C); \ + if(result) \ + curl_mfprintf(stderr, \ + "%s:%d curl_easy_setopt() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)result, \ + curl_easy_strerror(result)); \ } while(0) #define res_easy_setopt(A, B, C) \ @@ -241,11 +222,6 @@ void ws_close(CURL *curl); /* close the connection */ } \ } while(0) -#if 0 -#define res_multi_setopt(A, B, C) \ - exe_multi_setopt(A, B, C, __FILE__, __LINE__) -#endif - #define chk_multi_setopt(A, B, C, Y, Z) \ do { \ exe_multi_setopt(A, B, C, Y, Z); \ @@ -297,11 +273,6 @@ void ws_close(CURL *curl); /* close the connection */ } \ } while(0) -#if 0 -#define res_multi_remove_handle(A, B) \ - exe_multi_remove_handle(A, B, __FILE__, __LINE__) -#endif - #define chk_multi_remove_handle(A, B, Y, Z) \ do { \ exe_multi_remove_handle(A, B, Y, Z); \ @@ -435,11 +406,6 @@ void ws_close(CURL *curl); /* close the connection */ } \ } while(0) -#if 0 -#define res_multi_poll(A, B, C, D, E) \ - exe_multi_poll(A, B, C, D, E, __FILE__, __LINE__) -#endif - #define chk_multi_poll(A, B, C, D, E, Y, Z) \ do { \ exe_multi_poll(A, B, C, D, E, Y, Z); \ @@ -467,18 +433,6 @@ void ws_close(CURL *curl); /* close the connection */ #define res_multi_wakeup(A) \ exe_multi_wakeup(A, __FILE__, __LINE__) -#if 0 -#define chk_multi_wakeup(A, Y, Z) \ - do { \ - exe_multi_wakeup(A, Y, Z); \ - if(result) \ - goto test_cleanup; \ - } while(0) - -#define multi_wakeup(A) \ - chk_multi_wakeup(A, __FILE__, __LINE__) -#endif - /* ---------------------------------------------------------------- */ #define exe_select_test(A, B, C, D, E, Y, Z) \ @@ -531,11 +485,6 @@ void ws_close(CURL *curl); /* close the connection */ #define res_test_timedout() \ exe_test_timedout(TEST_HANG_TIMEOUT, __FILE__, __LINE__) -#if 0 -#define res_test_timedout_custom(T) \ - exe_test_timedout(T, __FILE__, __LINE__) -#endif - #define chk_test_timedout(T, Y, Z) \ do { \ exe_test_timedout(T, Y, Z); \ @@ -562,14 +511,13 @@ void ws_close(CURL *curl); /* close the connection */ #define exe_global_init(A, Y, Z) \ do { \ - CURLcode ec = curl_global_init(A); \ - if(ec != CURLE_OK) { \ + result = curl_global_init(A); \ + if(result) \ curl_mfprintf(stderr, \ "%s:%d curl_global_init() failed, " \ "with code %d (%s)\n", \ - Y, Z, (int)ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ + Y, Z, (int)result, \ + curl_easy_strerror(result)); \ } while(0) #define chk_global_init(A, Y, Z) \ diff --git a/tests/libtest/lib1156.c b/tests/libtest/lib1156.c index 7887e0d8686d..b8339c245613 100644 --- a/tests/libtest/lib1156.c +++ b/tests/libtest/lib1156.c @@ -92,12 +92,12 @@ static int onetest(CURL *curl, const char *url, const struct testparams *p, if(p->flags & F_HTTP416) replyselector += 2; curl_msnprintf(urlbuf, sizeof(urlbuf), "%s%04u", url, replyselector); - test_setopt(curl, CURLOPT_URL, urlbuf); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESUME_FROM, (p->flags & F_RESUME) ? 3L : 0L); - test_setopt(curl, CURLOPT_RANGE, !(p->flags & F_RESUME) ? + easy_setopt(curl, CURLOPT_URL, urlbuf); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_RESUME_FROM, (p->flags & F_RESUME) ? 3L : 0L); + easy_setopt(curl, CURLOPT_RANGE, !(p->flags & F_RESUME) ? "3-1000000" : (char *)NULL); - test_setopt(curl, CURLOPT_FAILONERROR, (p->flags & F_FAIL) ? 1L : 0L); + easy_setopt(curl, CURLOPT_FAILONERROR, (p->flags & F_FAIL) ? 1L : 0L); hasbody = 0; result = curl_easy_perform(curl); if(result != p->result) { @@ -149,7 +149,7 @@ static CURLcode test_lib1156(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, writedata); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, writedata); #ifdef SINGLETEST if(SINGLETEST == i) diff --git a/tests/libtest/lib1517.c b/tests/libtest/lib1517.c index 276426f55466..30e80a7bceb7 100644 --- a/tests/libtest/lib1517.c +++ b/tests/libtest/lib1517.c @@ -75,29 +75,29 @@ static CURLcode test_lib1517(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t1517_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t1517_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); #if 0 /* detect HTTP error codes >= 400 */ - test_setopt(curl, CURLOPT_FAILONERROR, 1L); + easy_setopt(curl, CURLOPT_FAILONERROR, 1L); #endif /* Perform the request, result gets the return code */ diff --git a/tests/libtest/lib1518.c b/tests/libtest/lib1518.c index a047e51badda..09b85148191c 100644 --- a/tests/libtest/lib1518.c +++ b/tests/libtest/lib1518.c @@ -49,13 +49,13 @@ static CURLcode test_lib1518(const char *URL) if(!urlu || rc) { goto test_cleanup; } - test_setopt(curl, CURLOPT_CURLU, urlu); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_CURLU, urlu); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); } else { - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* to make it explicit and visible in this test: */ - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); } /* Perform the request, result gets the return code */ @@ -67,7 +67,7 @@ static CURLcode test_lib1518(const char *URL) curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &curlRedirectCount); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effectiveUrl); curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &redirectUrl); - test_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); curl_mprintf("result %d\n" "status %ld\n" diff --git a/tests/libtest/lib1520.c b/tests/libtest/lib1520.c index a76c64a5a91d..9ed257591ee0 100644 --- a/tests/libtest/lib1520.c +++ b/tests/libtest/lib1520.c @@ -87,13 +87,13 @@ static CURLcode test_lib1520(const char *URL) /* more addresses can be added here */ rcpt_list = curl_slist_append(rcpt_list, ""); #endif - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1520_read_cb); - test_setopt(curl, CURLOPT_READDATA, &upload_ctx); - test_setopt(curl, CURLOPT_MAIL_FROM, ""); - test_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1520_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); + easy_setopt(curl, CURLOPT_MAIL_FROM, ""); + easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1522.c b/tests/libtest/lib1522.c index 4d073d39f6dc..9f6dc335154d 100644 --- a/tests/libtest/lib1522.c +++ b/tests/libtest/lib1522.c @@ -61,9 +61,9 @@ static CURLcode test_lib1522(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Remove "Expect: 100-continue" */ pHeaderList = curl_slist_append(pHeaderList, "Expect:"); diff --git a/tests/libtest/lib1525.c b/tests/libtest/lib1525.c index 0c25f582ca52..3498941ea2ee 100644 --- a/tests/libtest/lib1525.c +++ b/tests/libtest/lib1525.c @@ -69,20 +69,20 @@ static CURLcode test_lib1525(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, hhl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1525_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1525_datalen); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, hhl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1525_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1525_datalen); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1526.c b/tests/libtest/lib1526.c index 380ebc7d0219..6e01804628cc 100644 --- a/tests/libtest/lib1526.c +++ b/tests/libtest/lib1526.c @@ -73,20 +73,20 @@ static CURLcode test_lib1526(const char *URL) } phl = tmp; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, phl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1526_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1526_datalen); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, phl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1526_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1526_datalen); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1527.c b/tests/libtest/lib1527.c index b3e0dbc9113b..ef438dc901ae 100644 --- a/tests/libtest/lib1527.c +++ b/tests/libtest/lib1527.c @@ -72,19 +72,19 @@ static CURLcode test_lib1527(const char *URL) } hhl = tmp; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1527_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1527_datalen); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1527_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1527_datalen); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1528.c b/tests/libtest/lib1528.c index d5c6030222a6..f8db6725353b 100644 --- a/tests/libtest/lib1528.c +++ b/tests/libtest/lib1528.c @@ -50,14 +50,14 @@ static CURLcode test_lib1528(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, phl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, phl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1529.c b/tests/libtest/lib1529.c index df83e54eb7d1..ccf003203598 100644 --- a/tests/libtest/lib1529.c +++ b/tests/libtest/lib1529.c @@ -43,11 +43,11 @@ static CURLcode test_lib1529(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, bURL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, bURL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1530.c b/tests/libtest/lib1530.c index 39ee30b6b805..ecce16803d1f 100644 --- a/tests/libtest/lib1530.c +++ b/tests/libtest/lib1530.c @@ -52,9 +52,9 @@ static CURLcode test_lib1530(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, "http://99.99.99.99:9999"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); + easy_setopt(curl, CURLOPT_URL, "http://99.99.99.99:9999"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1533.c b/tests/libtest/lib1533.c index c7057879d9b5..c0ad8e46cb9a 100644 --- a/tests/libtest/lib1533.c +++ b/tests/libtest/lib1533.c @@ -139,15 +139,15 @@ static CURLcode test_lib1533(const char *URL) reset_data(&data, curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)data.remaining_bytes); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1533_read_cb); - test_setopt(curl, CURLOPT_READDATA, &data); - test_setopt(curl, CURLOPT_WRITEFUNCTION, t1533_write_cb); - test_setopt(curl, CURLOPT_WRITEDATA, &data); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1533_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &data); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1533_write_cb); + easy_setopt(curl, CURLOPT_WRITEDATA, &data); result = perform_and_check_connections(curl, @@ -165,7 +165,7 @@ static CURLcode test_lib1533(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); + easy_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); reset_data(&data, curl); diff --git a/tests/libtest/lib1540.c b/tests/libtest/lib1540.c index ee18046f4270..b4fc49eb2be4 100644 --- a/tests/libtest/lib1540.c +++ b/tests/libtest/lib1540.c @@ -107,7 +107,7 @@ static CURLcode test_lib1540(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); diff --git a/tests/libtest/lib1549.c b/tests/libtest/lib1549.c index ce5d70233133..e004546f58fe 100644 --- a/tests/libtest/lib1549.c +++ b/tests/libtest/lib1549.c @@ -42,9 +42,9 @@ static CURLcode test_lib1549(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1553.c b/tests/libtest/lib1553.c index dd236cf73b58..8aa2b1a1534c 100644 --- a/tests/libtest/lib1553.c +++ b/tests/libtest/lib1553.c @@ -72,7 +72,7 @@ static CURLcode test_lib1553(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); diff --git a/tests/libtest/lib1571.c b/tests/libtest/lib1571.c index adb6cc21554a..2cf218bf224f 100644 --- a/tests/libtest/lib1571.c +++ b/tests/libtest/lib1571.c @@ -40,23 +40,23 @@ static CURLcode test_lib1571(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); if((testnum == 1571) || (testnum == 1572) || (testnum == 1575) || (testnum == 1581)) { - test_setopt(curl, CURLOPT_POSTFIELDS, "moo"); + easy_setopt(curl, CURLOPT_POSTFIELDS, "moo"); } if(testnum == 1581) { - test_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); } - test_setopt(curl, CURLOPT_CUSTOMREQUEST, "IGLOO"); + easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "IGLOO"); if((testnum == 1574) || (testnum == 1575)) { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); } else { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); } result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1576.c b/tests/libtest/lib1576.c index 5357f2ec13fe..64aaa4c5ea57 100644 --- a/tests/libtest/lib1576.c +++ b/tests/libtest/lib1576.c @@ -64,20 +64,20 @@ static CURLcode test_lib1576(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1576_read_cb); - test_setopt(curl, CURLOPT_SEEKFUNCTION, t1576_seek_callback); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1576_datalen); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1576_read_cb); + easy_setopt(curl, CURLOPT_SEEKFUNCTION, t1576_seek_callback); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1576_datalen); - test_setopt(curl, CURLOPT_CUSTOMREQUEST, "CURL"); + easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CURL"); if(testnum == 1578 || testnum == 1580) { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); } else { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); } /* Remove "Expect: 100-continue" */ pHeaderList = curl_slist_append(pHeaderList, "Expect:"); diff --git a/tests/libtest/lib1582.c b/tests/libtest/lib1582.c index 31e8d17c41c1..ec9624be940b 100644 --- a/tests/libtest/lib1582.c +++ b/tests/libtest/lib1582.c @@ -40,13 +40,13 @@ static CURLcode test_lib1582(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_NEGOTIATE); - test_setopt(curl, CURLOPT_USERPWD, ":"); - test_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - test_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_NEGOTIATE); + easy_setopt(curl, CURLOPT_USERPWD, ":"); + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1591.c b/tests/libtest/lib1591.c index f55ae8996123..97b234234fa4 100644 --- a/tests/libtest/lib1591.c +++ b/tests/libtest/lib1591.c @@ -98,12 +98,12 @@ static CURLcode test_lib1591(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1591_read_cb); - test_setopt(curl, CURLOPT_TRAILERFUNCTION, t1591_trailers_callback); - test_setopt(curl, CURLOPT_TRAILERDATA, NULL); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1591_read_cb); + easy_setopt(curl, CURLOPT_TRAILERFUNCTION, t1591_trailers_callback); + easy_setopt(curl, CURLOPT_TRAILERDATA, NULL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1598.c b/tests/libtest/lib1598.c index 7786d6dddd98..961c424e680a 100644 --- a/tests/libtest/lib1598.c +++ b/tests/libtest/lib1598.c @@ -82,13 +82,13 @@ static CURLcode test_lib1598(const char *URL) hhl = list; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); - test_setopt(curl, CURLOPT_POSTFIELDS, post_data); - test_setopt(curl, CURLOPT_TRAILERFUNCTION, t1598_trailers_callback); - test_setopt(curl, CURLOPT_TRAILERDATA, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); + easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + easy_setopt(curl, CURLOPT_TRAILERFUNCTION, t1598_trailers_callback); + easy_setopt(curl, CURLOPT_TRAILERDATA, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1686.c b/tests/libtest/lib1686.c index 332cd6287ad1..b7817463d25b 100644 --- a/tests/libtest/lib1686.c +++ b/tests/libtest/lib1686.c @@ -72,9 +72,13 @@ static CURLcode test_lib1686(const char *hostip) easy_setopt(curl, CURLOPT_URL, firsturl); result = curl_easy_perform(curl); + if(result) + goto test_cleanup; easy_setopt(curl, CURLOPT_URL, secondurl); result = curl_easy_perform(curl); + if(result) + goto test_cleanup; easy_setopt(curl, CURLOPT_USERPWD, "bob:secret"); easy_setopt(curl, CURLOPT_URL, secondurl); diff --git a/tests/libtest/lib1906.c b/tests/libtest/lib1906.c index 305b7d764604..e649263338af 100644 --- a/tests/libtest/lib1906.c +++ b/tests/libtest/lib1906.c @@ -50,7 +50,6 @@ static CURLcode test_lib1906(const char *URL) result = TEST_ERR_MAJOR_BAD; /* force an error return */ goto test_cleanup; } - result = CURLE_OK; /* reset for next use */ /* print the used URL */ curl_url_get(curlu, CURLUPART_URL, &url_after, 0); diff --git a/tests/libtest/lib1933.c b/tests/libtest/lib1933.c index bd31f9418a3e..abe4361bea09 100644 --- a/tests/libtest/lib1933.c +++ b/tests/libtest/lib1933.c @@ -42,16 +42,16 @@ static CURLcode test_lib1933(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1934.c b/tests/libtest/lib1934.c index 41959ad60ef7..1ddb8bee0b74 100644 --- a/tests/libtest/lib1934.c +++ b/tests/libtest/lib1934.c @@ -42,17 +42,17 @@ static CURLcode test_lib1934(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1935.c b/tests/libtest/lib1935.c index 9b7214405b4b..26d17c685114 100644 --- a/tests/libtest/lib1935.c +++ b/tests/libtest/lib1935.c @@ -42,17 +42,17 @@ static CURLcode test_lib1935(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1936.c b/tests/libtest/lib1936.c index b4908f09735c..dba9f1ccbac4 100644 --- a/tests/libtest/lib1936.c +++ b/tests/libtest/lib1936.c @@ -42,17 +42,17 @@ static CURLcode test_lib1936(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr:sss"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr:sss"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1937.c b/tests/libtest/lib1937.c index 03160f4ccb8c..a2493751791a 100644 --- a/tests/libtest/lib1937.c +++ b/tests/libtest/lib1937.c @@ -42,19 +42,19 @@ static CURLcode test_lib1937(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); - test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); + easy_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); - test_setopt(curl, CURLOPT_POSTFIELDS, "postData"); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_POSTFIELDS, "postData"); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1938.c b/tests/libtest/lib1938.c index 6b56d7c2f820..300a092fd5a6 100644 --- a/tests/libtest/lib1938.c +++ b/tests/libtest/lib1938.c @@ -43,20 +43,20 @@ static CURLcode test_lib1938(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); - test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); + easy_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); - test_setopt(curl, CURLOPT_POSTFIELDS, data); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(data)); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_POSTFIELDS, data); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(data)); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1955.c b/tests/libtest/lib1955.c index 79fd92d6ed2c..ff640c20d4b9 100644 --- a/tests/libtest/lib1955.c +++ b/tests/libtest/lib1955.c @@ -42,18 +42,18 @@ static CURLcode test_lib1955(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "test3: 1234"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "Content-Type: application/json"); /* 'name;' user headers with no value are used to send an empty header in the @@ -73,7 +73,7 @@ static CURLcode test_lib1955(const char *URL) curl_slist_append(list, "test_space: t\ts m\t end "); curl_slist_append(list, "tesMixCase: MixCase"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1956.c b/tests/libtest/lib1956.c index c9a4d405e26b..5d569c785b14 100644 --- a/tests/libtest/lib1956.c +++ b/tests/libtest/lib1956.c @@ -42,21 +42,21 @@ static CURLcode test_lib1956(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1957.c b/tests/libtest/lib1957.c index ea4226303141..b422e48553fa 100644 --- a/tests/libtest/lib1957.c +++ b/tests/libtest/lib1957.c @@ -42,20 +42,20 @@ static CURLcode test_lib1957(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: arbitrary"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1958.c b/tests/libtest/lib1958.c index cae62c239297..dc4ac1cd9a40 100644 --- a/tests/libtest/lib1958.c +++ b/tests/libtest/lib1958.c @@ -42,20 +42,20 @@ static CURLcode test_lib1958(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: \tarbitrary "); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1959.c b/tests/libtest/lib1959.c index 1f6e725856f3..4df8bd71cdd3 100644 --- a/tests/libtest/lib1959.c +++ b/tests/libtest/lib1959.c @@ -42,22 +42,22 @@ static CURLcode test_lib1959(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1960.c b/tests/libtest/lib1960.c index ae1f98984052..fbb0f7e597a8 100644 --- a/tests/libtest/lib1960.c +++ b/tests/libtest/lib1960.c @@ -122,16 +122,16 @@ static CURLcode test_lib1960(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, socket_cb); - test_setopt(curl, CURLOPT_OPENSOCKETDATA, &client_fd); - test_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); - test_setopt(curl, CURLOPT_SOCKOPTDATA, NULL); - test_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); - test_setopt(curl, CURLOPT_CLOSESOCKETDATA, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, socket_cb); + easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &client_fd); + easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); + easy_setopt(curl, CURLOPT_SOCKOPTDATA, NULL); + easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); + easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1970.c b/tests/libtest/lib1970.c index d9b659988c94..8fd69f6f1e4d 100644 --- a/tests/libtest/lib1970.c +++ b/tests/libtest/lib1970.c @@ -42,21 +42,21 @@ static CURLcode test_lib1970(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1971.c b/tests/libtest/lib1971.c index 4ae7bef2a3c1..81d5956a4483 100644 --- a/tests/libtest/lib1971.c +++ b/tests/libtest/lib1971.c @@ -51,21 +51,21 @@ static CURLcode test_lib1971(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1971_read_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1971_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1972.c b/tests/libtest/lib1972.c index 98609bd92963..4315798aecf7 100644 --- a/tests/libtest/lib1972.c +++ b/tests/libtest/lib1972.c @@ -53,20 +53,20 @@ static CURLcode test_lib1972(const char *URL) curl_mime_name(part, "foo"); curl_mime_data(part, "bar", CURL_ZERO_TERMINATED); - test_setopt(curl, CURLOPT_MIMEPOST, mime); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1973.c b/tests/libtest/lib1973.c index 508b16d261d1..778b7af636ba 100644 --- a/tests/libtest/lib1973.c +++ b/tests/libtest/lib1973.c @@ -42,20 +42,20 @@ static CURLcode test_lib1973(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_POSTFIELDS, "post fields\n"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POSTFIELDS, "post fields\n"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1974.c b/tests/libtest/lib1974.c index ae496591fc2d..84bd430f56e9 100644 --- a/tests/libtest/lib1974.c +++ b/tests/libtest/lib1974.c @@ -41,15 +41,15 @@ static CURLcode test_lib1974(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1975.c b/tests/libtest/lib1975.c index fded2cd8f0ed..05d6e7c67f84 100644 --- a/tests/libtest/lib1975.c +++ b/tests/libtest/lib1975.c @@ -51,23 +51,23 @@ static CURLcode test_lib1975(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1975_read_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1975_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; curl_slist_append(list, "X-Amz-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index c13c5e863c88..5a6a44a048cf 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -42,12 +42,12 @@ static CURLcode test_lib1978(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); /* We want to test a couple assumptions here. 1. the merging works with non-adjacent headers @@ -83,11 +83,11 @@ static CURLcode test_lib1978(const char *URL) curl_slist_append(list, "header-some-no-value;"); curl_slist_append(list, "header-some-no-value: value"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib2023.c b/tests/libtest/lib2023.c index 3cc1dac1b410..b0072b6b122e 100644 --- a/tests/libtest/lib2023.c +++ b/tests/libtest/lib2023.c @@ -42,12 +42,12 @@ static CURLcode send_request(CURL *curl, const char *url, int seq, curl_msnprintf(full_url, len, "%s%04d", url, seq); curl_mfprintf(stderr, "Sending new request %d to %s with credential %s " "(auth %ld)\n", seq, full_url, userpwd, auth_scheme); - test_setopt(curl, CURLOPT_URL, full_url); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_HTTPGET, 1L); - test_setopt(curl, CURLOPT_USERPWD, userpwd); - test_setopt(curl, CURLOPT_HTTPAUTH, auth_scheme); + easy_setopt(curl, CURLOPT_URL, full_url); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HTTPGET, 1L); + easy_setopt(curl, CURLOPT_USERPWD, userpwd); + easy_setopt(curl, CURLOPT_HTTPAUTH, auth_scheme); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib2502.c b/tests/libtest/lib2502.c index 4743afc11996..18382436c63b 100644 --- a/tests/libtest/lib2502.c +++ b/tests/libtest/lib2502.c @@ -74,7 +74,7 @@ static CURLcode test_lib2502(const char *URL) /* go verbose */ debug_config.nohex = TRUE; debug_config.tracetime = FALSE; - test_setopt(curl[i], CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl[i], CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl[i], CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl[i], CURLOPT_VERBOSE, 1L); /* include headers */ diff --git a/tests/libtest/lib2504.c b/tests/libtest/lib2504.c index 568c64c3f4a3..3b7b61fe8b47 100644 --- a/tests/libtest/lib2504.c +++ b/tests/libtest/lib2504.c @@ -61,17 +61,17 @@ static CURLcode test_lib2504(const char *URL) hdrs = curl_slist_append(hdrs, "Host: victim.internal"); if(hdrs) { - test_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); - test_setopt(curl, CURLOPT_HTTPHEADER, hdrs); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req1=%d\n", (int)result); dump_cookies2504(curl, "after request 1"); - test_setopt(curl, CURLOPT_HTTPHEADER, NULL); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req2=%d\n", (int)result); diff --git a/tests/libtest/lib2505.c b/tests/libtest/lib2505.c index d1bff3c6f656..5d43eec3920f 100644 --- a/tests/libtest/lib2505.c +++ b/tests/libtest/lib2505.c @@ -42,16 +42,16 @@ static CURLcode test_lib2505(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); - test_setopt(curl, CURLOPT_AUTOREFERER, 1L); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_AUTOREFERER, 1L); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req1=%d\n", (int)result); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req2=%d\n", (int)result); diff --git a/tests/libtest/lib2506.c b/tests/libtest/lib2506.c index 99a9a8405014..8779f6c9d5b6 100644 --- a/tests/libtest/lib2506.c +++ b/tests/libtest/lib2506.c @@ -42,17 +42,17 @@ static CURLcode test_lib2506(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - test_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + easy_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* CURLOPT_UNRESTRICTED_AUTH should not make a difference because the credentials come from netrc */ - test_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); + easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3025.c b/tests/libtest/lib3025.c index 362325311fe6..c7b38e465d12 100644 --- a/tests/libtest/lib3025.c +++ b/tests/libtest/lib3025.c @@ -42,10 +42,10 @@ static CURLcode test_lib3025(const char *URL) } icy = curl_slist_append(icy, "ICY 200 OK"); - test_setopt(curl, CURLOPT_HTTP200ALIASES, icy); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTP200ALIASES, icy); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3034.c b/tests/libtest/lib3034.c index 3aeca9edbdbd..0dbfee84391f 100644 --- a/tests/libtest/lib3034.c +++ b/tests/libtest/lib3034.c @@ -46,12 +46,12 @@ static CURLcode test_lib3034(const char *URL) * set the CURLOPT_READFUNCTION but not the CURLOPT_SEEKFUNCTION to force a * rewind failure (CURLE_SEND_FAIL_REWIND). */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 5L); - test_setopt(curl, CURLOPT_READFUNCTION, t3034_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 5L); + easy_setopt(curl, CURLOPT_READFUNCTION, t3034_read_cb); result = curl_easy_perform(curl); if(result != CURLE_SEND_FAIL_REWIND) { @@ -65,8 +65,8 @@ static CURLcode test_lib3034(const char *URL) curl_easy_reset(curl); /* Perform a second request, which should succeed. */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3100.c b/tests/libtest/lib3100.c index 4d3badb31db0..121d0a8490e4 100644 --- a/tests/libtest/lib3100.c +++ b/tests/libtest/lib3100.c @@ -40,17 +40,17 @@ static CURLcode test_lib3100(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - test_setopt(curl, CURLOPT_USERNAME, "user"); - test_setopt(curl, CURLOPT_PASSWORD, "password"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_USERNAME, "user"); + easy_setopt(curl, CURLOPT_PASSWORD, "password"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result != CURLE_OK) { diff --git a/tests/libtest/lib3101.c b/tests/libtest/lib3101.c index 56fc27467ad5..2e2b45357a7f 100644 --- a/tests/libtest/lib3101.c +++ b/tests/libtest/lib3101.c @@ -40,14 +40,14 @@ static CURLcode test_lib3101(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - test_setopt(curl, CURLOPT_USERNAME, "user"); - test_setopt(curl, CURLOPT_PASSWORD, "password"); - test_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_USERNAME, "user"); + easy_setopt(curl, CURLOPT_PASSWORD, "password"); + easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); result = curl_easy_perform(curl); if(result != CURLE_OK) { diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index 7efaf9803a14..59db9e45f163 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -98,17 +98,17 @@ static CURLcode test_lib3102(const char *URL) } /* Set the HTTPS URL to retrieve. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Capture certificate information */ - test_setopt(curl, CURLOPT_CERTINFO, 1L); + easy_setopt(curl, CURLOPT_CERTINFO, 1L); /* Ignore output */ - test_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* No peer verify */ - test_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - test_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3103.c b/tests/libtest/lib3103.c index 268f3f09874c..cedb21d28a9a 100644 --- a/tests/libtest/lib3103.c +++ b/tests/libtest/lib3103.c @@ -35,17 +35,17 @@ static CURLcode test_lib3103(const char *URL) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl = curl_easy_init(); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, "http://localhost/"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, "http://localhost/"); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); /* Set a cookie without Max-age or Expires */ - test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost"); + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost"); result = curl_easy_perform(curl); if(result) { diff --git a/tests/libtest/lib3104.c b/tests/libtest/lib3104.c index 4c9b9090f0cd..96ca51205e27 100644 --- a/tests/libtest/lib3104.c +++ b/tests/libtest/lib3104.c @@ -35,16 +35,16 @@ static CURLcode test_lib3104(const char *URL) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl = curl_easy_init(); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, "http://example.com/"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, "http://example.com/"); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "example.com\tFALSE\t/\tFALSE\t0\tname\tvalue"); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib500.c b/tests/libtest/lib500.c index 5797348884ab..786d8360b177 100644 --- a/tests/libtest/lib500.c +++ b/tests/libtest/lib500.c @@ -69,17 +69,17 @@ static CURLcode test_lib500(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); if(libtest_arg3 && !strcmp(libtest_arg3, "activeftp")) - test_setopt(curl, CURLOPT_FTPPORT, "-"); + easy_setopt(curl, CURLOPT_FTPPORT, "-"); if(testnum == 585 || testnum == 586 || testnum == 595 || testnum == 596) setupcallbacks(curl); diff --git a/tests/libtest/lib501.c b/tests/libtest/lib501.c index 355d99e9991c..73bf010a1a9c 100644 --- a/tests/libtest/lib501.c +++ b/tests/libtest/lib501.c @@ -42,10 +42,10 @@ static CURLcode test_lib501(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* verify that setting this to -1 is fine */ - test_setopt(curl, CURLOPT_MAXREDIRS, -1L); + easy_setopt(curl, CURLOPT_MAXREDIRS, -1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib505.c b/tests/libtest/lib505.c index b618598507be..7abaacd14ea2 100644 --- a/tests/libtest/lib505.c +++ b/tests/libtest/lib505.c @@ -111,22 +111,22 @@ static CURLcode test_lib505(const char *URL) headerlist = temp; /* enable uploading */ - test_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* pass in that last of FTP commands to run after the transfer */ - test_setopt(curl, CURLOPT_POSTQUOTE, headerlist); + easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist); /* now specify which file to upload */ - test_setopt(curl, CURLOPT_READDATA, hd_src); + easy_setopt(curl, CURLOPT_READDATA, hd_src); /* and give the size of the upload (optional) */ - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib506.c b/tests/libtest/lib506.c index 512d05b20605..8d36c32ec3c4 100644 --- a/tests/libtest/lib506.c +++ b/tests/libtest/lib506.c @@ -239,21 +239,21 @@ static CURLcode test_lib506(const char *URL) return TEST_ERR_MAJOR_BAD; } curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIELIST injected_and_clobbered\n"); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: injected_and_clobbered=yes; " "domain=host.foo.com; expires=Sat Feb 2 11:56:27 GMT 2030"); curl_mprintf("CURLOPT_COOKIELIST ALL\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "ALL"); + easy_setopt(curl, CURLOPT_COOKIELIST, "ALL"); curl_mprintf("CURLOPT_COOKIELIST session\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants"); + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants"); curl_mprintf("CURLOPT_COOKIELIST injected\n"); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: injected=yes; domain=host.foo.com; " "expires=Sat Feb 2 11:56:27 GMT 2030"); curl_mprintf("CURLOPT_COOKIELIST SESS\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "SESS"); + easy_setopt(curl, CURLOPT_COOKIELIST, "SESS"); curl_mprintf("CLEANUP\n"); curl_easy_cleanup(curl); @@ -283,14 +283,14 @@ static CURLcode test_lib506(const char *URL) url = tutil_suburl(URL, i); headers = sethost(NULL); - test_setopt(curl, CURLOPT_HTTPHEADER, headers); - test_setopt(curl, CURLOPT_URL, url); + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + easy_setopt(curl, CURLOPT_URL, url); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIEJAR\n"); - test_setopt(curl, CURLOPT_COOKIEJAR, jar); + easy_setopt(curl, CURLOPT_COOKIEJAR, jar); curl_mprintf("CURLOPT_COOKIELIST FLUSH\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "FLUSH"); + easy_setopt(curl, CURLOPT_COOKIELIST, "FLUSH"); curl_mprintf("PERFORM\n"); curl_easy_perform(curl); @@ -310,16 +310,16 @@ static CURLcode test_lib506(const char *URL) } url = tutil_suburl(URL, i); headers = sethost(NULL); - test_setopt(curl, CURLOPT_HTTPHEADER, headers); - test_setopt(curl, CURLOPT_URL, url); + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + easy_setopt(curl, CURLOPT_URL, url); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIELIST ALL\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "ALL"); + easy_setopt(curl, CURLOPT_COOKIELIST, "ALL"); curl_mprintf("CURLOPT_COOKIEJAR\n"); - test_setopt(curl, CURLOPT_COOKIEFILE, jar); + easy_setopt(curl, CURLOPT_COOKIEFILE, jar); curl_mprintf("CURLOPT_COOKIELIST RELOAD\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "RELOAD"); + easy_setopt(curl, CURLOPT_COOKIELIST, "RELOAD"); result = CURLE_OK; diff --git a/tests/libtest/lib508.c b/tests/libtest/lib508.c index 0c840d4c536b..7ecf2665c48d 100644 --- a/tests/libtest/lib508.c +++ b/tests/libtest/lib508.c @@ -71,25 +71,25 @@ static CURLcode test_lib508(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t508_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t508_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib509.c b/tests/libtest/lib509.c index f888bbf156a4..38da36428fc6 100644 --- a/tests/libtest/lib509.c +++ b/tests/libtest/lib509.c @@ -99,7 +99,7 @@ static CURLcode test_lib509(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_USERAGENT, "test509"); /* uses curlx_strdup() */ + easy_setopt(curl, CURLOPT_USERAGENT, "test509"); /* uses curlx_strdup() */ asize = (int)sizeof(a); /* uses curlx_realloc() */ diff --git a/tests/libtest/lib510.c b/tests/libtest/lib510.c index 28fb93d58653..a4acbdddb623 100644 --- a/tests/libtest/lib510.c +++ b/tests/libtest/lib510.c @@ -87,29 +87,29 @@ static CURLcode test_lib510(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t510_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t510_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* enforce chunked transfer by setting the header */ - test_setopt(curl, CURLOPT_HTTPHEADER, slist); + easy_setopt(curl, CURLOPT_HTTPHEADER, slist); if(testnum == 565) { - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - test_setopt(curl, CURLOPT_USERPWD, "foo:bar"); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "foo:bar"); } /* Perform the request, result gets the return code */ diff --git a/tests/libtest/lib511.c b/tests/libtest/lib511.c index d87b4805bc7b..7907f500ee3a 100644 --- a/tests/libtest/lib511.c +++ b/tests/libtest/lib511.c @@ -40,10 +40,10 @@ static CURLcode test_lib511(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FILETIME, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FILETIME, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib513.c b/tests/libtest/lib513.c index 34d505c4e58e..9ed3f1dd424a 100644 --- a/tests/libtest/lib513.c +++ b/tests/libtest/lib513.c @@ -50,25 +50,25 @@ static CURLcode test_lib513(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t513_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t513_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, NULL); + easy_setopt(curl, CURLOPT_READDATA, NULL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib514.c b/tests/libtest/lib514.c index 4afd7cc015bd..1fe9adc8d543 100644 --- a/tests/libtest/lib514.c +++ b/tests/libtest/lib514.c @@ -41,7 +41,7 @@ static CURLcode test_lib514(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Based on a bug report by Niels van Tongeren on June 29, 2004: @@ -52,17 +52,17 @@ static CURLcode test_lib514(const char *URL) */ - test_setopt(curl, CURLOPT_POSTFIELDS, "moo"); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 3L); - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDS, "moo"); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 3L); + easy_setopt(curl, CURLOPT_POST, 1L); /* this is where transfer 1 would take place, but skip that and change options right away instead */ - test_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a fine HEAD request */ diff --git a/tests/libtest/lib515.c b/tests/libtest/lib515.c index 9dcb17271b35..825cbe56cf8e 100644 --- a/tests/libtest/lib515.c +++ b/tests/libtest/lib515.c @@ -41,11 +41,11 @@ static CURLcode test_lib515(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib516.c b/tests/libtest/lib516.c index 21d6d945bc0c..61090736b772 100644 --- a/tests/libtest/lib516.c +++ b/tests/libtest/lib516.c @@ -41,10 +41,10 @@ static CURLcode test_lib516(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPPOST, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPPOST, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index 25e1b5b75eba..bef952bac7f6 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -447,8 +447,8 @@ static CURLcode test_lib518(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib519.c b/tests/libtest/lib519.c index 3ddc93541b46..30aee83ff5b5 100644 --- a/tests/libtest/lib519.c +++ b/tests/libtest/lib519.c @@ -40,17 +40,17 @@ static CURLcode test_lib519(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_USERPWD, "monster:underbed"); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_USERPWD, "monster:underbed"); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* get first page */ result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_USERPWD, "anothermonster:inwardrobe"); + easy_setopt(curl, CURLOPT_USERPWD, "anothermonster:inwardrobe"); /* get second page */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib520.c b/tests/libtest/lib520.c index 8987c9db6030..9da55e02737f 100644 --- a/tests/libtest/lib520.c +++ b/tests/libtest/lib520.c @@ -40,9 +40,9 @@ static CURLcode test_lib520(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FILETIME, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FILETIME, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib521.c b/tests/libtest/lib521.c index 002d07292374..1469d6dc0bad 100644 --- a/tests/libtest/lib521.c +++ b/tests/libtest/lib521.c @@ -44,10 +44,10 @@ static CURLcode test_lib521(const char *URL) return result; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PORT, (long)port); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PORT, (long)port); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib523.c b/tests/libtest/lib523.c index 40a2d3f741a8..ecc3e0ccceea 100644 --- a/tests/libtest/lib523.c +++ b/tests/libtest/lib523.c @@ -40,11 +40,11 @@ static CURLcode test_lib523(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PORT, 19999L); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PORT, 19999L); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib524.c b/tests/libtest/lib524.c index 3d5ac1ff4beb..66ada8b0fa29 100644 --- a/tests/libtest/lib524.c +++ b/tests/libtest/lib524.c @@ -40,9 +40,9 @@ static CURLcode test_lib524(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib536.c b/tests/libtest/lib536.c index e63757ac91d8..e25922062ad4 100644 --- a/tests/libtest/lib536.c +++ b/tests/libtest/lib536.c @@ -56,16 +56,16 @@ static CURLcode test_lib536(const char *URL) if(!host) goto test_cleanup; - test_setopt(curl, CURLOPT_RESOLVE, host); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, url_with_proxy); - test_setopt(curl, CURLOPT_NOPROXY, "goingdirect.test"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, url_with_proxy); + easy_setopt(curl, CURLOPT_NOPROXY, "goingdirect.test"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(!result) { proxystat(curl); - test_setopt(curl, CURLOPT_URL, url_without_proxy); + easy_setopt(curl, CURLOPT_URL, url_without_proxy); result = curl_easy_perform(curl); if(!result) proxystat(curl); diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 84b053e7f885..5f99b4f006f1 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -463,8 +463,8 @@ static CURLcode test_lib537(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib539.c b/tests/libtest/lib539.c index b1d453af4411..b6719ea53a54 100644 --- a/tests/libtest/lib539.c +++ b/tests/libtest/lib539.c @@ -45,9 +45,9 @@ static CURLcode test_lib539(const char *URL) /* * Begin with curl set to use a single CWD to the URL's directory. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result == CURLE_OK) { @@ -67,9 +67,9 @@ static CURLcode test_lib539(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); } diff --git a/tests/libtest/lib541.c b/tests/libtest/lib541.c index 6cd8e3dce2de..35e9d5a0cda3 100644 --- a/tests/libtest/lib541.c +++ b/tests/libtest/lib541.c @@ -82,16 +82,16 @@ static CURLcode test_lib541(const char *URL) } /* enable uploading */ - test_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* now specify which file to upload */ - test_setopt(curl, CURLOPT_READDATA, hd_src); + easy_setopt(curl, CURLOPT_READDATA, hd_src); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib542.c b/tests/libtest/lib542.c index fc71ecfdf074..fe3eb9cd1365 100644 --- a/tests/libtest/lib542.c +++ b/tests/libtest/lib542.c @@ -46,16 +46,16 @@ static CURLcode test_lib542(const char *URL) } /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* enable NOBODY */ - test_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); /* disable HEADER */ - test_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_HEADER, 0L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib544.c b/tests/libtest/lib544.c index 6ceac095d72f..02dc11f39bb3 100644 --- a/tests/libtest/lib544.c +++ b/tests/libtest/lib544.c @@ -52,15 +52,15 @@ static CURLcode test_lib544(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); if(testnum == 545) - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(teststring)); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(teststring)); - test_setopt(curl, CURLOPT_COPYPOSTFIELDS, teststring); + easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, teststring); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Update the original data to detect non-copy. */ curlx_strcopy(teststring, sizeof(teststring), "FAIL", strlen("FAIL")); diff --git a/tests/libtest/lib547.c b/tests/libtest/lib547.c index dd0f787ee654..e44c16b55bb1 100644 --- a/tests/libtest/lib547.c +++ b/tests/libtest/lib547.c @@ -80,28 +80,28 @@ static CURLcode test_lib547(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); if(testnum == 548) { /* set the data to POST with a mere pointer to a null-terminated string */ - test_setopt(curl, CURLOPT_POSTFIELDS, t547_uploadthis); + easy_setopt(curl, CURLOPT_POSTFIELDS, t547_uploadthis); } else { /* 547 style, which means reading the POST data from a callback */ - test_setopt(curl, CURLOPT_IOCTLFUNCTION, t547_ioctl_callback); - test_setopt(curl, CURLOPT_IOCTLDATA, &counter); + easy_setopt(curl, CURLOPT_IOCTLFUNCTION, t547_ioctl_callback); + easy_setopt(curl, CURLOPT_IOCTLDATA, &counter); - test_setopt(curl, CURLOPT_READFUNCTION, t547_read_cb); - test_setopt(curl, CURLOPT_READDATA, &counter); + easy_setopt(curl, CURLOPT_READFUNCTION, t547_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &counter); /* We CANNOT do the POST fine without setting the size (or choose chunked)! */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)t547_datalen); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)t547_datalen); } - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_PROXYUSERPWD, libtest_arg3); - test_setopt(curl, CURLOPT_PROXYAUTH, + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, libtest_arg3); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib549.c b/tests/libtest/lib549.c index e7c5023cb773..fb718988b20f 100644 --- a/tests/libtest/lib549.c +++ b/tests/libtest/lib549.c @@ -45,13 +45,13 @@ static CURLcode test_lib549(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); if(libtest_arg3) { /* enable ASCII/text mode */ - test_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); + easy_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); } result = curl_easy_perform(curl); diff --git a/tests/libtest/lib552.c b/tests/libtest/lib552.c index fda2eee2c17e..69222d217fde 100644 --- a/tests/libtest/lib552.c +++ b/tests/libtest/lib552.c @@ -79,35 +79,35 @@ static CURLcode test_lib552(const char *URL) global_init(CURL_GLOBAL_ALL); easy_init(curl); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); /* the DEBUGFUNCTION has no effect until we enable VERBOSE */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* setup repeated data string */ for(i = 0; i < sizeof(databuf); ++i) databuf[i] = fill[i % sizeof(fill)]; /* Post */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Setup read callback */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(databuf)); - test_setopt(curl, CURLOPT_READFUNCTION, t552_read_cb); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(databuf)); + easy_setopt(curl, CURLOPT_READFUNCTION, t552_read_cb); /* Write callback */ - test_setopt(curl, CURLOPT_WRITEFUNCTION, t552_write_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, t552_write_cb); /* Ioctl function */ - test_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_callback); + easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_callback); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Accept any auth. But for this bug configure proxy with DIGEST, basic might work too, not NTLM */ - test_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib553.c b/tests/libtest/lib553.c index 75844144bd38..dc812aa4a42a 100644 --- a/tests/libtest/lib553.c +++ b/tests/libtest/lib553.c @@ -89,13 +89,13 @@ static CURLcode test_lib553(const char *URL) goto test_cleanup; headerlist = hl; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)POSTLEN); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, myreadfunc); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)POSTLEN); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, myreadfunc); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib554.c b/tests/libtest/lib554.c index 4a4bad94e413..98a7ba7fdcb1 100644 --- a/tests/libtest/lib554.c +++ b/tests/libtest/lib554.c @@ -151,30 +151,30 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ if(testnum == 587) { - test_setopt(curl, CURLOPT_READFUNCTION, t587_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t587_read_cb); } else { - test_setopt(curl, CURLOPT_READFUNCTION, t554_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t554_read_cb); } /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib556.c b/tests/libtest/lib556.c index f71ec5e6adb9..1bc4638b9b29 100644 --- a/tests/libtest/lib556.c +++ b/tests/libtest/lib556.c @@ -41,9 +41,9 @@ static CURLcode test_lib556(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); again: diff --git a/tests/libtest/lib559.c b/tests/libtest/lib559.c index 1f14b7df435a..27a8cb9c22f3 100644 --- a/tests/libtest/lib559.c +++ b/tests/libtest/lib559.c @@ -40,9 +40,9 @@ static CURLcode test_lib559(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_BUFFERSIZE, 1L); /* the smallest! */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_BUFFERSIZE, 1L); /* the smallest! */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib562.c b/tests/libtest/lib562.c index bc3bcad7e144..5cec3f7472dc 100644 --- a/tests/libtest/lib562.c +++ b/tests/libtest/lib562.c @@ -53,13 +53,13 @@ static CURLcode test_lib562(const char *URL) } /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* set port number */ - test_setopt(curl, CURLOPT_PORT, (long)port); + easy_setopt(curl, CURLOPT_PORT, (long)port); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib566.c b/tests/libtest/lib566.c index 3b30092b289e..9042894618a4 100644 --- a/tests/libtest/lib566.c +++ b/tests/libtest/lib566.c @@ -42,8 +42,8 @@ static CURLcode test_lib566(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib567.c b/tests/libtest/lib567.c index ac4516e63ed8..dc17f53b0ba4 100644 --- a/tests/libtest/lib567.c +++ b/tests/libtest/lib567.c @@ -45,17 +45,17 @@ static CURLcode test_lib567(const char *URL) } /* Dump data to stdout for protocol verification */ - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); - test_setopt(curl, CURLOPT_USERAGENT, "test567"); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_USERAGENT, "test567"); custom_headers = curl_slist_append(custom_headers, "Test-Number: 567"); - test_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); + easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib568.c b/tests/libtest/lib568.c index 384aab9254da..654de68461a3 100644 --- a/tests/libtest/lib568.c +++ b/tests/libtest/lib568.c @@ -50,17 +50,17 @@ static CURLcode test_lib568(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -83,19 +83,19 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); - test_setopt(curl, CURLOPT_READDATA, sdpf); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_READDATA, sdpf); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do the ANNOUNCE */ result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_UPLOAD, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 0L); curlx_fclose(sdpf); sdpf = NULL; @@ -105,11 +105,11 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -121,7 +121,7 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -131,17 +131,17 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); - test_setopt(curl, CURLOPT_POSTFIELDS, + easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); + easy_setopt(curl, CURLOPT_POSTFIELDS, "postyfield=postystuff&project=curl\n"); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); - test_setopt(curl, CURLOPT_RTSPHEADER, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_RTSPHEADER, NULL); curl_slist_free_all(custom_headers); custom_headers = NULL; @@ -151,11 +151,11 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib569.c b/tests/libtest/lib569.c index aa4bc20afecd..8f79eeb71b3b 100644 --- a/tests/libtest/lib569.c +++ b/tests/libtest/lib569.c @@ -54,13 +54,13 @@ static CURLcode test_lib569(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result != CURLE_BAD_FUNCTION_ARGUMENT) { curl_mfprintf(stderr, "This should have failed. " @@ -77,12 +77,12 @@ static CURLcode test_lib569(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Fake/NotReal/JustATest;foo=baz"); result = curl_easy_perform(curl); if(result) @@ -97,17 +97,17 @@ static CURLcode test_lib569(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN); result = curl_easy_perform(curl); if(result) goto test_cleanup; /* Clear for the next go-round */ - test_setopt(curl, CURLOPT_RTSP_SESSION_ID, NULL); + easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, NULL); } test_cleanup: diff --git a/tests/libtest/lib570.c b/tests/libtest/lib570.c index 00502e9e6105..998dd68cfa4e 100644 --- a/tests/libtest/lib570.c +++ b/tests/libtest/lib570.c @@ -42,20 +42,20 @@ static CURLcode test_lib570(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -66,17 +66,17 @@ static CURLcode test_lib570(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999L); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, + easy_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999L); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RAW/RAW/UDP;unicast;client_port=3056-3057"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -84,14 +84,14 @@ static CURLcode test_lib570(const char *URL) if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; diff --git a/tests/libtest/lib571.c b/tests/libtest/lib571.c index 739a90cf6dec..37b93e539078 100644 --- a/tests/libtest/lib571.c +++ b/tests/libtest/lib571.c @@ -115,24 +115,24 @@ static CURLcode test_lib571(const char *URL) curl_global_cleanup(); return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write); - test_setopt(curl, CURLOPT_TIMEOUT, 30L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_WRITEDATA, protofile); + easy_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write); + easy_setopt(curl, CURLOPT_TIMEOUT, 30L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_WRITEDATA, protofile); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result) @@ -144,10 +144,10 @@ static CURLcode test_lib571(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); result = curl_easy_perform(curl); if(result) @@ -159,10 +159,10 @@ static CURLcode test_lib571(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result) @@ -173,10 +173,10 @@ static CURLcode test_lib571(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); result = curl_easy_perform(curl); if(result) @@ -187,7 +187,7 @@ static CURLcode test_lib571(const char *URL) /* Use Receive to get the rest of the data */ while(!result && rtp_packet_count < 19) { curl_mfprintf(stderr, "LOOPY LOOP!\n"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); result = curl_easy_perform(curl); } diff --git a/tests/libtest/lib572.c b/tests/libtest/lib572.c index e58c034e8fc3..1ed292761f2f 100644 --- a/tests/libtest/lib572.c +++ b/tests/libtest/lib572.c @@ -50,11 +50,11 @@ static CURLcode test_lib572(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* SETUP */ stream_uri = tutil_suburl(URL, request++); @@ -62,12 +62,12 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Planes/Trains/Automobiles"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Planes/Trains/Automobiles"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -77,7 +77,7 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -101,17 +101,17 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); - test_setopt(curl, CURLOPT_READDATA, paramsf); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_READDATA, paramsf); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_UPLOAD, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 0L); curlx_fclose(paramsf); paramsf = NULL; @@ -121,7 +121,7 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -136,18 +136,18 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); - test_setopt(curl, CURLOPT_POSTFIELDS, "packets_received\njitter\n"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); + easy_setopt(curl, CURLOPT_POSTFIELDS, "packets_received\njitter\n"); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); /* Make sure we can do a normal request now */ stream_uri = tutil_suburl(URL, request++); @@ -155,11 +155,11 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib574.c b/tests/libtest/lib574.c index 0a03d1e954a8..6b5e415fa74b 100644 --- a/tests/libtest/lib574.c +++ b/tests/libtest/lib574.c @@ -48,10 +48,10 @@ static CURLcode test_lib574(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); - test_setopt(curl, CURLOPT_FNMATCH_FUNCTION, new_fnmatch); - test_setopt(curl, CURLOPT_TIMEOUT_MS, (long)TEST_HANG_TIMEOUT); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + easy_setopt(curl, CURLOPT_FNMATCH_FUNCTION, new_fnmatch); + easy_setopt(curl, CURLOPT_TIMEOUT_MS, (long)TEST_HANG_TIMEOUT); result = curl_easy_perform(curl); if(result) { diff --git a/tests/libtest/lib576.c b/tests/libtest/lib576.c index d06661a90a1b..889969d5b852 100644 --- a/tests/libtest/lib576.c +++ b/tests/libtest/lib576.c @@ -106,11 +106,11 @@ static CURLcode test_lib576(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); - test_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn); - test_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end); - test_setopt(curl, CURLOPT_CHUNK_DATA, &chunk_data); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn); + easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end); + easy_setopt(curl, CURLOPT_CHUNK_DATA, &chunk_data); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib578.c b/tests/libtest/lib578.c index 62ccdfedd489..434178552467 100644 --- a/tests/libtest/lib578.c +++ b/tests/libtest/lib578.c @@ -66,24 +66,24 @@ static CURLcode test_lib578(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data_size); - test_setopt(curl, CURLOPT_POSTFIELDS, t578_testdata); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data_size); + easy_setopt(curl, CURLOPT_POSTFIELDS, t578_testdata); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t578_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t578_progress_callback); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib579.c b/tests/libtest/lib579.c index 856688853eb1..7ad133188bc5 100644 --- a/tests/libtest/lib579.c +++ b/tests/libtest/lib579.c @@ -127,32 +127,32 @@ static CURLcode test_lib579(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t579_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t579_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* enforce chunked transfer by setting the header */ - test_setopt(curl, CURLOPT_HTTPHEADER, slist); + easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - test_setopt(curl, CURLOPT_USERPWD, "foo:bar"); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "foo:bar"); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t579_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t579_progress_callback); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib586.c b/tests/libtest/lib586.c index 8cb1652ad78b..29eab1844362 100644 --- a/tests/libtest/lib586.c +++ b/tests/libtest/lib586.c @@ -203,9 +203,9 @@ static CURLcode test_lib586(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("PERFORM\n"); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib589.c b/tests/libtest/lib589.c index cf8565237829..1f4b6cfbe124 100644 --- a/tests/libtest/lib589.c +++ b/tests/libtest/lib589.c @@ -43,9 +43,9 @@ static CURLcode test_lib589(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ if(testnum == 584) { mime = curl_mime_init(curl); @@ -53,14 +53,14 @@ static CURLcode test_lib589(const char *URL) if(mime && part) { curl_mime_name(part, "fake"); curl_mime_data(part, "party", 5); - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); result = curl_easy_perform(curl); } if(result) goto test_cleanup; } - test_setopt(curl, CURLOPT_MIMEPOST, NULL); + easy_setopt(curl, CURLOPT_MIMEPOST, NULL); /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib590.c b/tests/libtest/lib590.c index 1cea00b6e24a..3a83e62701ab 100644 --- a/tests/libtest/lib590.c +++ b/tests/libtest/lib590.c @@ -55,16 +55,16 @@ static CURLcode test_lib590(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXYAUTH, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); /* set in first.c */ + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); /* set in first.c */ /* set the name + password twice to test that the API is fine with it */ - test_setopt(curl, CURLOPT_PROXYUSERNAME, "me"); - test_setopt(curl, CURLOPT_PROXYPASSWORD, "password"); - test_setopt(curl, CURLOPT_PROXYUSERPWD, "me:password"); + easy_setopt(curl, CURLOPT_PROXYUSERNAME, "me"); + easy_setopt(curl, CURLOPT_PROXYPASSWORD, "password"); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, "me:password"); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib598.c b/tests/libtest/lib598.c index dc89a52f004a..c6025c578b5e 100644 --- a/tests/libtest/lib598.c +++ b/tests/libtest/lib598.c @@ -40,12 +40,12 @@ static CURLcode test_lib598(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_REFERER, "http://example.com/the-moo"); - test_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); - test_setopt(curl, CURLOPT_COOKIE, "name=moo"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_REFERER, "http://example.com/the-moo"); + easy_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); + easy_setopt(curl, CURLOPT_COOKIE, "name=moo"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) { @@ -55,9 +55,9 @@ static CURLcode test_lib598(const char *URL) curl_easy_reset(curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib599.c b/tests/libtest/lib599.c index 1d335d40179b..ba8ebca97bbf 100644 --- a/tests/libtest/lib599.c +++ b/tests/libtest/lib599.c @@ -58,20 +58,20 @@ static CURLcode test_lib599(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t599_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t599_progress_callback); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* follow redirects */ - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib643.c b/tests/libtest/lib643.c index 5c4227cb99d4..eaf75e8fe153 100644 --- a/tests/libtest/lib643.c +++ b/tests/libtest/lib643.c @@ -194,16 +194,16 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) curl_mprintf("curl_mime_xxx(5) = %s\n", curl_easy_strerror(result)); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part mimepost */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib650.c b/tests/libtest/lib650.c index b207c4a46109..b0766eee1d0a 100644 --- a/tests/libtest/lib650.c +++ b/tests/libtest/lib650.c @@ -172,19 +172,19 @@ static CURLcode test_lib650(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib651.c b/tests/libtest/lib651.c index a5aa2d3227ec..1c8fca2fd1ec 100644 --- a/tests/libtest/lib651.c +++ b/tests/libtest/lib651.c @@ -64,16 +64,16 @@ static CURLcode test_lib651(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib652.c b/tests/libtest/lib652.c index ad29c5d3e3b9..bf225348ed8c 100644 --- a/tests/libtest/lib652.c +++ b/tests/libtest/lib652.c @@ -94,22 +94,22 @@ static CURLcode test_lib652(const char *URL) } /* First set the URL that is about to receive our mime mail. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Set sender. */ - test_setopt(curl, CURLOPT_MAIL_FROM, "somebody@example.com"); + easy_setopt(curl, CURLOPT_MAIL_FROM, "somebody@example.com"); /* Set recipients. */ - test_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* send a multi-part mail */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Shorten upload buffer. */ - test_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); + easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib654.c b/tests/libtest/lib654.c index 7d8ad2adcb65..0494d580abfc 100644 --- a/tests/libtest/lib654.c +++ b/tests/libtest/lib654.c @@ -82,13 +82,13 @@ static CURLcode test_lib654(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structure. */ pooh.readptr = testdata; @@ -111,7 +111,7 @@ static CURLcode test_lib654(const char *URL) free_callback, &pooh); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Duplicate the handle. */ curl2 = curl_easy_duphandle(curl); diff --git a/tests/libtest/lib655.c b/tests/libtest/lib655.c index 73c634f0be2f..3dd70dfa7351 100644 --- a/tests/libtest/lib655.c +++ b/tests/libtest/lib655.c @@ -78,13 +78,13 @@ static CURLcode test_lib655(const char *URL) } /* Set the URL that is about to receive our first request. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING); - test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail); + easy_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING); + easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail); /* this should fail */ result = curl_easy_perform(curl); @@ -98,12 +98,12 @@ static CURLcode test_lib655(const char *URL) } /* Set the URL that receives our second request. */ - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass); + easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass); /* this should succeed */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib661.c b/tests/libtest/lib661.c index 85fb92f66563..80d46e8738bf 100644 --- a/tests/libtest/lib661.c +++ b/tests/libtest/lib661.c @@ -45,17 +45,17 @@ static CURLcode test_lib661(const char *URL) /* test: CURLFTPMETHOD_SINGLECWD with absolute path should skip CWD to entry path */ newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; curl_free(newURL); newURL = curl_maprintf("%s/folderB/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_URL, newURL); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -72,10 +72,10 @@ static CURLcode test_lib661(const char *URL) curl_free(newURL); newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -83,16 +83,16 @@ static CURLcode test_lib661(const char *URL) /* curve ball: CWD /folderB before reusing connection with _NOCWD */ curl_free(newURL); newURL = curl_maprintf("%s/folderB/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; curl_free(newURL); newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -114,11 +114,11 @@ static CURLcode test_lib661(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -133,11 +133,11 @@ static CURLcode test_lib661(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -146,11 +146,11 @@ static CURLcode test_lib661(const char *URL) not emit CWD for second FTP access when not needed + bonus: see if path buffering survives curl_easy_reset() */ curl_easy_reset(curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib666.c b/tests/libtest/lib666.c index 99a94c1dd759..43ea1eb88e80 100644 --- a/tests/libtest/lib666.c +++ b/tests/libtest/lib666.c @@ -89,19 +89,19 @@ static CURLcode test_lib666(const char *URL) } /* First set the URL that is about to receive our mime mail. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Post form */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Shorten upload buffer. */ - test_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); + easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib667.c b/tests/libtest/lib667.c index 924c6ef41687..28a9cde6d2b3 100644 --- a/tests/libtest/lib667.c +++ b/tests/libtest/lib667.c @@ -72,13 +72,13 @@ static CURLcode test_lib667(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structure. */ pooh.readptr = testdata; @@ -93,7 +93,7 @@ static CURLcode test_lib667(const char *URL) curl_mime_data_cb(part, (curl_off_t)-1, t667_read_cb, NULL, NULL, &pooh); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send data. */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib668.c b/tests/libtest/lib668.c index 20962fac2d65..33cf25e54982 100644 --- a/tests/libtest/lib668.c +++ b/tests/libtest/lib668.c @@ -67,13 +67,13 @@ static CURLcode test_lib668(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structures. */ pooh1.readptr = testdata; @@ -99,7 +99,7 @@ static CURLcode test_lib668(const char *URL) curl_mime_filedata(part, libtest_arg2); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send data. */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib670.c b/tests/libtest/lib670.c index fb46697a4549..2daeb06576a7 100644 --- a/tests/libtest/lib670.c +++ b/tests/libtest/lib670.c @@ -105,13 +105,13 @@ static CURLcode test_lib670(const char *URL) pooh.curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(pooh.curl, CURLOPT_URL, URL); + easy_setopt(pooh.curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(pooh.curl, CURLOPT_VERBOSE, 1L); + easy_setopt(pooh.curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(pooh.curl, CURLOPT_HEADER, 1L); + easy_setopt(pooh.curl, CURLOPT_HEADER, 1L); if(testnum == 670 || testnum == 671) { curl_mimepart *part; @@ -131,7 +131,7 @@ static CURLcode test_lib670(const char *URL) /* Bind mime data to its easy handle. */ if(result == CURLE_OK) - test_setopt(pooh.curl, CURLOPT_MIMEPOST, mime); + easy_setopt(pooh.curl, CURLOPT_MIMEPOST, mime); } else { struct curl_httppost *lastptr = NULL; @@ -148,10 +148,10 @@ static CURLcode test_lib670(const char *URL) } /* We want to use our own read function. */ - test_setopt(pooh.curl, CURLOPT_READFUNCTION, t670_read_cb); + easy_setopt(pooh.curl, CURLOPT_READFUNCTION, t670_read_cb); /* Send a multi-part formpost. */ - test_setopt(pooh.curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(pooh.curl, CURLOPT_HTTPPOST, formpost); } if(testnum == 670 || testnum == 672) { @@ -223,9 +223,9 @@ static CURLcode test_lib670(const char *URL) } else { /* Use the easy interface. */ - test_setopt(pooh.curl, CURLOPT_XFERINFODATA, &pooh); - test_setopt(pooh.curl, CURLOPT_XFERINFOFUNCTION, t670_xferinfo); - test_setopt(pooh.curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(pooh.curl, CURLOPT_XFERINFODATA, &pooh); + easy_setopt(pooh.curl, CURLOPT_XFERINFOFUNCTION, t670_xferinfo); + easy_setopt(pooh.curl, CURLOPT_NOPROGRESS, 0L); result = curl_easy_perform(pooh.curl); } diff --git a/tests/libtest/lib676.c b/tests/libtest/lib676.c index 0f7fc735f9b4..e08f2d4b1230 100644 --- a/tests/libtest/lib676.c +++ b/tests/libtest/lib676.c @@ -40,11 +40,11 @@ static CURLcode test_lib676(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); - test_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); + easy_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) { @@ -53,7 +53,7 @@ static CURLcode test_lib676(const char *URL) } /* now clear the cookies */ - test_setopt(curl, CURLOPT_COOKIEFILE, NULL); + easy_setopt(curl, CURLOPT_COOKIEFILE, NULL); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib694.c b/tests/libtest/lib694.c index 83d5e8f4169f..1b2c6e2d8bb2 100644 --- a/tests/libtest/lib694.c +++ b/tests/libtest/lib694.c @@ -42,12 +42,12 @@ static CURLcode test_lib694(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HTTPAUTH, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); - test_setopt(curl, CURLOPT_USERPWD, "me:password"); + easy_setopt(curl, CURLOPT_USERPWD, "me:password"); do { @@ -63,7 +63,7 @@ static CURLcode test_lib694(const char *URL) } /* set a new URL for the second, so that we do not restart NTLM */ - test_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); } while(!result && ++count < 2); test_cleanup: diff --git a/tests/libtest/lib695.c b/tests/libtest/lib695.c index 48541eaad0cb..de8986d88859 100644 --- a/tests/libtest/lib695.c +++ b/tests/libtest/lib695.c @@ -43,10 +43,10 @@ static CURLcode test_lib695(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do not write anything. */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); diff --git a/tests/libtest/lib757.c b/tests/libtest/lib757.c index 6c9f432794f2..325f3b7b6b96 100644 --- a/tests/libtest/lib757.c +++ b/tests/libtest/lib757.c @@ -64,10 +64,10 @@ static CURLcode test_lib757(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do not write anything. */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index c6cd49d8531b..2fa8ef3ab110 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -352,7 +352,7 @@ static CURLcode t758_one(const char *URL, int timer_fail_at, easy_init(curl); debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); From 8c3ef95adf33efedc8fd338a6307015b898a273b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 17 Jun 2026 11:50:35 +0200 Subject: [PATCH 0472/1062] dns-httpsrr-lookup: use origin, not peer Origin is the correct peer for lookup of HTTPS-RR records. Closes #22059 --- lib/vtls/openssl.c | 2 +- lib/vtls/rustls.c | 2 +- lib/vtls/wolfssl.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index eb6839cfba4b..4689d7a2fc93 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3502,7 +3502,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex, peer->peer); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 5183844a6fbd..950f17021239 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -983,7 +983,7 @@ init_config_builder_ech(struct Curl_easy *data, else { const struct ssl_connect_data *connssl = cf->ctx; const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.peer); + Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.origin); if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index c55490eb833f..92eaa7a75100 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1248,7 +1248,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex, peer->peer); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; From d2886c5ac47bf5e08f74ee2c027ab16b23a64587 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 17 Jun 2026 12:06:29 +0200 Subject: [PATCH 0473/1062] http: for basic+digest auth, do not engage on empty user+passwd Since we have the quirky of empty credentials (the empty string for username and password) for Negotiate reactivated, we need to check for this when considering Basic and Digest auth. Verify a redirect to blank user+password in test 2208 Closes #22060 --- lib/creds.h | 2 + lib/http.c | 19 +++++---- tests/data/Makefile.am | 1 + tests/data/test2208 | 90 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 tests/data/test2208 diff --git a/lib/creds.h b/lib/creds.h index 36deff323e96..0664d08a4f4f 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -73,6 +73,8 @@ bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2); /* Provides properties for creds or, if creds is NULL, the empty string */ #define Curl_creds_has_user(c) ((c) && (c)->user[0]) #define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) +#define Curl_creds_has_user_or_pass(c) \ + ((c) && ((c)->user[0] || (c)->passwd[0])) #define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) #define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) #define Curl_creds_user(c) ((c) ? (c)->user : "") diff --git a/lib/http.c b/lib/http.c index e3697b1fa9f0..83d0e011528e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -347,8 +347,10 @@ static CURLcode http_output_bearer(struct Curl_easy *data) * * return TRUE if one was picked */ -static bool pickoneauth(struct auth *pick, unsigned long mask) +static bool pickoneauth(struct auth *pick, unsigned long mask, + struct Curl_creds *creds) { + bool have_user_pass = Curl_creds_has_user_or_pass(creds); bool picked; /* only deal with authentication we want */ unsigned long avail = pick->avail & pick->want & mask; @@ -356,20 +358,20 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) /* The order of these checks is highly relevant, as this will be the order of preference in case of the existence of multiple accepted types. */ - if(avail & CURLAUTH_NEGOTIATE) + if(avail & CURLAUTH_NEGOTIATE) /* available on empty creds */ pick->picked = CURLAUTH_NEGOTIATE; #ifndef CURL_DISABLE_BEARER_AUTH - else if(avail & CURLAUTH_BEARER) + else if((avail & CURLAUTH_BEARER) && Curl_creds_has_oauth_bearer(creds)) pick->picked = CURLAUTH_BEARER; #endif #ifndef CURL_DISABLE_DIGEST_AUTH - else if(avail & CURLAUTH_DIGEST) + else if((avail & CURLAUTH_DIGEST) && have_user_pass) pick->picked = CURLAUTH_DIGEST; #endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; #ifndef CURL_DISABLE_BASIC_AUTH - else if(avail & CURLAUTH_BASIC) + else if((avail & CURLAUTH_BASIC) && have_user_pass) pick->picked = CURLAUTH_BASIC; #endif #ifndef CURL_DISABLE_AWS @@ -568,7 +570,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.creds && ((data->req.httpcode == 401) || (data->req.authneg && data->req.httpcode < 300))) { - pickhost = pickoneauth(&data->state.authhost, authmask); + pickhost = pickoneauth(&data->state.authhost, authmask, data->state.creds); if(!pickhost) data->state.authproblem = TRUE; else @@ -586,7 +588,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) ((data->req.httpcode == 407) || (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, - authmask & ~CURLAUTH_BEARER); + authmask & ~CURLAUTH_BEARER, + conn->http_proxy.creds); if(!pickproxy) data->state.authproblem = TRUE; else @@ -699,10 +702,12 @@ static CURLcode output_auth_headers(struct Curl_easy *data, if( #ifndef CURL_DISABLE_PROXY (proxy && conn->http_proxy.creds && + Curl_creds_has_user_or_pass(conn->http_proxy.creds) && !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif (!proxy && data->state.creds && + Curl_creds_has_user_or_pass(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; result = http_output_basic(data, conn, proxy); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 0d5277d55bce..a1d75ab3051f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -256,6 +256,7 @@ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ +test2208 \ \ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 test2310 \ diff --git a/tests/data/test2208 b/tests/data/test2208 new file mode 100644 index 000000000000..354d1a2a7e5d --- /dev/null +++ b/tests/data/test2208 @@ -0,0 +1,90 @@ + + + + +HTTP +HTTP proxy +--location +HTTP Basic auth + + + +# Server-side + + +HTTP/1.1 301 redirect +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 +Connection: close +Content-Type: text/html +Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 4 +Connection: close +Content-Type: text/html + +hey + + + +HTTP/1.1 301 redirect +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 +Connection: close +Content-Type: text/html +Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002 + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 4 +Connection: close +Content-Type: text/html + +hey + + + + +# Client-side + + +proxy + + +http + + +HTTP auth on redirect with empty URL userinfo + + +-x http://%HOSTIP:%HTTPPORT http://firsthost.com -L -u joe:secret + + + +# Verify data after the test has been "shot" + + +GET http://firsthost.com/ HTTP/1.1 +Host: firsthost.com +Authorization: Basic %b64[joe:secret]b64% +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://firsthost.com:9999/a/path/%TESTNUMBER0002 HTTP/1.1 +Host: firsthost.com:9999 +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + From 60381b2046af3e863ede3362dffcf5df99cc60e0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 14:37:14 +0200 Subject: [PATCH 0474/1062] first.h reflow --- tests/libtest/first.h | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 8a31aa950e10..fda2fd9f1ff4 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -184,15 +184,14 @@ void ws_close(CURL *curl); /* close the connection */ /* ---------------------------------------------------------------- */ -#define exe_easy_setopt(A, B, C, Y, Z) \ - do { \ - result = curl_easy_setopt(A, B, C); \ - if(result) \ - curl_mfprintf(stderr, \ - "%s:%d curl_easy_setopt() failed, " \ - "with code %d (%s)\n", \ - Y, Z, (int)result, \ - curl_easy_strerror(result)); \ +#define exe_easy_setopt(A, B, C, Y, Z) \ + do { \ + result = curl_easy_setopt(A, B, C); \ + if(result) \ + curl_mfprintf(stderr, \ + "%s:%d curl_easy_setopt() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)result, curl_easy_strerror(result)); \ } while(0) #define res_easy_setopt(A, B, C) \ @@ -509,15 +508,14 @@ void ws_close(CURL *curl); /* close the connection */ /* ---------------------------------------------------------------- */ -#define exe_global_init(A, Y, Z) \ - do { \ - result = curl_global_init(A); \ - if(result) \ - curl_mfprintf(stderr, \ - "%s:%d curl_global_init() failed, " \ - "with code %d (%s)\n", \ - Y, Z, (int)result, \ - curl_easy_strerror(result)); \ +#define exe_global_init(A, Y, Z) \ + do { \ + result = curl_global_init(A); \ + if(result) \ + curl_mfprintf(stderr, \ + "%s:%d curl_global_init() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)result, curl_easy_strerror(result)); \ } while(0) #define chk_global_init(A, Y, Z) \ From 4abe47e1f53ccc69218bad0453ec07c28a5b5726 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 12:35:20 +0200 Subject: [PATCH 0475/1062] src: sync nghttp2 versions checks with current requirements Also: - drop a redundant check. - make the in-source error informative. Follow-up to 2900c29218d2d24ab519853589da84caa850e8c7 #11473 Closes #22061 --- lib/cf-h2-proxy.c | 3 +-- lib/http2.c | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 938b00402567..be303ffd3ea3 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -264,8 +264,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); diff --git a/lib/http2.c b/lib/http2.c index 2133c5a6e0bc..a820439ef45a 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -47,14 +47,11 @@ #include "curlx/dynbuf.h" #include "headers.h" -#if NGHTTP2_VERSION_NUM < 0x010c00 -#error too old nghttp2 version, upgrade! +#if NGHTTP2_VERSION_NUM < 0x010f00 +#error "nghttp2 1.15.0 or greater required" #endif -#if NGHTTP2_VERSION_NUM >= 0x010c00 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 -#endif - /* buffer dimensioning: * use 16K as chunk size, as that fits H2 DATA frames well */ @@ -463,8 +460,7 @@ static int h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); From bdb1773536d76562a9e0e8f2fdf60bedda2f1910 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 12:42:12 +0200 Subject: [PATCH 0476/1062] INTERNALS.md: document minimum nghttp3 and ngtcp2 versions Follow-up to 5eefdd71a394d135c0ffb56fb8ec117c87dbe4f0 #17027 Follow-up to 915f3981c93cba568806b16a5719ff444b62e365 #16320 Closes #22062 --- docs/INTERNALS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index 73d4d7b9b8f9..ff59f733e048 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -38,6 +38,8 @@ We aim to support these or later versions: - mbedTLS 3.2.0 (2022-07-11) - MIT Kerberos 1.3 (2003-07-31) - nghttp2 1.15.0 (2016-09-25) +- nghttp3 1.0.0 (2023-10-15) +- ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) - OpenLDAP 2.0 (2000-08-01) - OpenSSL 3.0.0 (2021-09-07) - Windows Vista 6.0 (2006-11-08 - 2012-04-10) From 0ffd2e7fdebb19186879c513d0eeceb7f27c6d22 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 14:09:49 +0200 Subject: [PATCH 0477/1062] GHA/windows: do `apt-get update` in clang-tidy cross-build job again Syncing with most similar uses in other workflows. Fixing, e.g.: ``` E: Failed to fetch http://azure.archive.ubuntu.com/ubuntu/pool/universe/l/ llvm-toolchain-20/llvm-20-linker-tools_20.1.2-0ubuntu1%7e24.04.2_amd64.deb 404 Not Found [IP: 172.66.152.176 443] ``` Ref: https://github.com/curl/curl/actions/runs/27682974841/job/81877061033?pr=22061 Follow-up to 1b8449674adb57ee0f60e761d654c69b20ee8fcf #14992 Closes #22064 --- .github/workflows/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 8d7cd2b4cfb3..bbf4bd709155 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -789,6 +789,7 @@ jobs: MATRIX_INSTALL_PACKAGES: '${{ matrix.install_packages }}' run: | sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 4e07b374dd6ff0831dea1b95a0491e8426b192f0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 14:15:01 +0200 Subject: [PATCH 0478/1062] GHA/linux: allow more time for `apt.repos.intel.com` install Whether the install is extreme slow and will fail anyway, or only slower sometimes, and this fixes, we will see. Example: ``` Need to get 1159 MB of archives. After this operation, 4463 MB of additional disk space will be used. Get:1 https://apt.repos.intel.com/oneapi all/main all intel-oneapi-common-licensing-2023.2.0 all 2023.2.0-49462 [30.4 kB] Get:2 https://apt.repos.intel.com/oneapi all/main all intel-oneapi-common-licensing-2026.0 all 2026.0.0-235 [30.7 kB] [...] Get:21 https://apt.repos.intel.com/oneapi all/main amd64 intel-oneapi-dpcpp-debugger-2023.2.0 amd64 2023.2.0-49330 [201 MB] Error: The action 'install Intel compilers' has timed out after 2 minutes. ``` Ref: https://github.com/curl/curl/actions/runs/27683923870/job/81877924590 Follow-up to 50ff4f2927e3e319d39ba86bbcac3f57e5c89984 #21414 Closes #22065 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f5c6f999bd97..96901b158ca3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -829,7 +829,7 @@ jobs: - name: 'install Intel compilers' if: ${{ contains(matrix.build.install_steps, 'intelc') }} - timeout-minutes: 2 + timeout-minutes: 4 run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --compressed https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | \ From 766969be39bbc9f0172ece496965fd2a6f30f06b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 14:53:17 +0200 Subject: [PATCH 0479/1062] GHA: sync apt-get code between workflows/jobs - delete 3rd-party apt sources, where missing. - do `apt-get update`, where missing. Closes #22067 --- .github/workflows/configure-vs-cmake.yml | 2 ++ .github/workflows/distcheck.yml | 2 ++ .github/workflows/linux.yml | 1 + .github/workflows/non-native.yml | 2 ++ .github/workflows/windows.yml | 2 ++ 5 files changed, 9 insertions(+) diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 93bbb7ff954a..bcab34395f59 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -149,7 +149,9 @@ jobs: - name: 'install packages' timeout-minutes: 1 run: | + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index c9682d92bc51..6aa969cd0f4f 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -289,7 +289,9 @@ jobs: sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OLD_CMAKE_SHA256_WIN_INTEL}" && unzip -q pkg.bin && rm -f pkg.bin printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-win64-x64/bin/cmake.exe > ~/old-cmake-path.txt elif [[ "${MATRIX_IMAGE}" = *'ubuntu'* ]]; then + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install libpsl-dev libssl-dev cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 96901b158ca3..9eddb0c17499 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -834,6 +834,7 @@ jobs: curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --compressed https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | \ sudo tee /etc/apt/trusted.gpg.d/intel-sw.asc >/dev/null + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo add-apt-repository 'deb https://apt.repos.intel.com/oneapi all main' sudo apt-get -o Dpkg::Use-Pty=0 install intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c6ba80c70366..50ebe3768cbb 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -418,7 +418,9 @@ jobs: - name: 'install packages' timeout-minutes: 2 run: | + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install libfl2 - name: 'cache compiler (djgpp)' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bbf4bd709155..0f95d3e65e9e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -788,6 +788,8 @@ jobs: env: MATRIX_INSTALL_PACKAGES: '${{ matrix.install_packages }}' run: | + ls -l /etc/apt/sources.list.d + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} From e8b76773af5bb14338033df8d4cf179732233cd4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 14:56:45 +0200 Subject: [PATCH 0480/1062] GHA/linux: give more time for `apt-get install` 3 minutes (was: 2). IIn the hope it fixes timeouts, assuming the Ubuntu mirrors are only somewhat slower sometimes (and not completely stalled). Closes #22068 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9eddb0c17499..60553f796a78 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -455,7 +455,7 @@ jobs: steps: - name: 'install prereqs' if: ${{ matrix.build.container == null && !contains(matrix.build.name, 'i686') }} - timeout-minutes: 2 + timeout-minutes: 3 env: INSTALL_PACKAGES_BREW: '${{ matrix.build.install_steps_brew }}' INSTALL_PACKAGES: >- From 174cafb3e6075e7c82d270dfe7a0011809422178 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 13:50:52 +0200 Subject: [PATCH 0481/1062] cmake: simplify `LINK_ONLY` imported target extraction Suggested-by: Kai Pastor Ref: https://github.com/curl/curl/pull/21654#discussion_r3425296606 Follow-up to 3c597ced16e1f3aa7bfe08609add0feaf5c8d90d #21654 Closes #22063 --- CMake/Macros.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index f0968736b067..953c1c6ee6dd 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -267,10 +267,8 @@ macro(curl_collect_target_link_options _target) get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) if(_val) foreach(_lib IN LISTS _val) - # E.g. via libssh2: "$" - if(_lib MATCHES "LINK_ONLY:") - string(REGEX MATCH "([A-Za-z0-9_-]+::[A-Za-z0-9_-]+)" _lib "${_lib}") # Extract imported target name - endif() + # Extract imported target name from e.g. "$" set by libssh2 + string(REGEX REPLACE "^\\\$\$" "\\1" _lib "${_lib}") if(TARGET "${_lib}") curl_collect_target_link_options(${_lib}) else() From a36e979284219a0a15c758178053afecebc0af1b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 16:22:52 +0200 Subject: [PATCH 0482/1062] GHA/macos: enable krb5 in an autotools job Cherry-picked from #22052 Closes #22069 --- .github/workflows/macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4a50877d7f3c..685a533c507f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -277,11 +277,11 @@ jobs: -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_USE_LIBSSH2=OFF -DCMAKE_C_STANDARD=90 -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - - name: 'OpenSSL SecTrust' + - name: 'OpenSSL SecTrust krb5' compiler: clang install: libnghttp3 libngtcp2 install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 + configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi - name: 'OpenSSL event-based' compiler: clang From 6db0ba2a381648a27e90dd9151ea2c255f869534 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 17:38:32 +0200 Subject: [PATCH 0483/1062] cmake/FindGSS: drop "MIT Unknown" version value, related tidy ups After this patch the `GSS_VERSION` value is left empty in all cases when there is known version number (potentially on Windows). Also: - sync `GSS_FOUND` comment with other Find modules. - sync `GSS_VERSION` comment with other Find modules, drop the promise of returning "unknown", which was not true and also not done by other Find modules. - tidy up Windows-registry-based MIT `GSS_VERSION` detection, by guarding the whole block for `WIN32`. - drop fallback version value `MIT Unknown` used for MIT. - fix vertical alignment in comment block. Changing CMake log output like so (in affected config): ``` -- Found GSS: MIT (found version "MIT Unknown") ``` -> ``` -- Found GSS: MIT ``` Spotted by Copilot Bug: https://github.com/curl/curl/pull/22052#discussion_r3429273238 Follow-up to 558814e16d84aa202c5ccc0c8108a9d728e77a58 Closes #22071 --- CMake/FindGSS.cmake | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 93efb497864b..e94067a84427 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -25,14 +25,13 @@ # # Input variables: # -# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) +# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) # # Defines: # -# - `GSS_FOUND`: System has a GSS library. -# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. -# In case the library is found but no version info available it is set to "unknown" -# - `CURL::gss`: GSS library target. +# - `GSS_FOUND`: System has GSS. +# - `GSS_VERSION`: Version of GSS. +# - `CURL::gss`: GSS library target. # - `INTERFACE_CURL_GSS_FLAVOR`: Custom property. "GNU" or "MIT" if detected. set(_gnu_modname "gss") @@ -210,7 +209,7 @@ endif() set(GSS_VERSION ${_gss_version}) if(NOT GSS_VERSION) - if(_gss_flavor STREQUAL "MIT") + if(_gss_flavor STREQUAL "MIT" AND WIN32) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) cmake_host_system_information(RESULT _mit_version QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/MIT/Kerberos/SDK/CurrentVersion" VALUE "VersionString") @@ -218,12 +217,8 @@ if(NOT GSS_VERSION) get_filename_component(_mit_version "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) endif() - if(WIN32 AND _mit_version) - set(GSS_VERSION "${_mit_version}") - else() - set(GSS_VERSION "MIT Unknown") - endif() - else() # GNU + set(GSS_VERSION "${_mit_version}") + elseif(_gss_flavor STREQUAL "GNU") if(_gss_INCLUDE_DIRS AND EXISTS "${_gss_INCLUDE_DIRS}/gss.h") set(_version_regex "#[\t ]*define[\t ]+GSS_VERSION[\t ]+\"([^\"]*)\"") file(STRINGS "${_gss_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}") From e1366945251ae3107194acb970ec4e40b1da5682 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 17 Jun 2026 18:27:13 +0200 Subject: [PATCH 0484/1062] cmake/FindGSS: drop CMake <3.16 compatibility logic Redundant since bumping minimum to 3.18. Follow-up to 89043ba90689418a115e967633e261139b48ce23 #20407 Follow-up to 1f112242323848d0ebfc88ae97b139d18e7987f6 #18950 Closes #22072 --- CMake/FindGSS.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index e94067a84427..6f352b2e4f67 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -192,12 +192,10 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr message(FATAL_ERROR "GNU or MIT GSS is required") endif() else() - # _gss_MODULE_NAME set since CMake 3.16. - # _pkg_check_modules_pkg_name is undocumented and used as a fallback for CMake <3.16 versions. - if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _pkg_check_modules_pkg_name STREQUAL _gnu_modname) + if(_gss_MODULE_NAME STREQUAL _gnu_modname) set(_gss_flavor "GNU") set(_gss_pc_requires ${_gnu_modname}) - elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _pkg_check_modules_pkg_name STREQUAL _mit_modname) + elseif(_gss_MODULE_NAME STREQUAL _mit_modname) set(_gss_flavor "MIT") set(_gss_pc_requires ${_mit_modname}) else() From 9ccc80b192bbefd7ac221197ac1c7d1ef9da168b Mon Sep 17 00:00:00 2001 From: Yedaya Katsman Date: Wed, 17 Jun 2026 22:46:14 +0300 Subject: [PATCH 0485/1062] url: don't log bits.close state This doesn't seem useful to users, and there doesn't seem to be a scenario where bits.close is set to 1 during this logging anyway. Closes #22073 --- lib/url.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/url.c b/lib/url.c index 3018dc438b8a..3ace5ca23707 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1317,7 +1317,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) conn->gssapi_delegation = data->set.gssapi_delegation; #endif - DEBUGF(infof(data, "alloc connection, bits.close=%d", conn->bits.close)); return conn; error: @@ -1677,13 +1676,11 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, struct Curl_peer *peer = NULL; CURLcode result; - DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); if(conn->scheme->run->setup_connection) { result = conn->scheme->run->setup_connection(data, conn); if(result) return result; } - DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); /* Now create the destination name */ peer = Curl_conn_get_destination(conn, FIRSTSOCKET); @@ -2423,7 +2420,6 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) /* We have decided that we want a new connection. We may not be able to do that if we have reached the limit of how many connections we are allowed to open. */ - DEBUGF(infof(data, "new connection, bits.close=%d", needle->bits.close)); if(waitpipe) { /* There is a connection that *might* become usable for multiplexing From 4a86af99401fb48d58a0a550117e6516ca5a76bd Mon Sep 17 00:00:00 2001 From: alhudz Date: Wed, 17 Jun 2026 20:15:11 +0530 Subject: [PATCH 0486/1062] cookie: reject control octets in file-loaded cookies Verified by test 2311 Closes #22070 --- lib/cookie.c | 8 ++++++ tests/data/Makefile.am | 2 +- tests/data/test2311 | 56 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2311 diff --git a/lib/cookie.c b/lib/cookie.c index e6a147705886..99f2910632ad 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -786,6 +786,14 @@ static CURLcode parse_netscape(struct Cookie *co, /* we did not find the sufficient number of fields */ return CURLE_OK; + /* Reject control octets in the name or value, matching the filtering done + for cookies set over HTTP. A cookie loaded from a file is later sent in + request headers, so the same bytes that make a server reject a request + must not slip in through the file. */ + if(invalid_octets(co->name, strlen(co->name)) || + invalid_octets(co->value, strlen(co->value))) + return CURLE_OK; + *okay = TRUE; return CURLE_OK; } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a1d75ab3051f..f537ba181b9e 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -259,7 +259,7 @@ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ \ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ -test2309 test2310 \ +test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ test2408 test2409 test2410 test2411 \ diff --git a/tests/data/test2311 b/tests/data/test2311 new file mode 100644 index 000000000000..92453a75e3bf --- /dev/null +++ b/tests/data/test2311 @@ -0,0 +1,56 @@ + + + + +HTTP +HTTP GET +HTTP proxy +cookies + + + +# Server-side + + +HTTP/1.1 200 OK +Server: test-server/fake +Content-Length: 21 + +This server says moo + + + +# Client-side + + +http + + +Cookie from file with control octet in value is rejected + + +http://example.fake/%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER -x %HOSTIP:%HTTPPORT + + +example.fake FALSE / FALSE 0 clean good +example.fake FALSE / FALSE 0 bad %hex[ba%07d]hex% + + +cookies +proxy + + + +# Verify data after the test has been "shot" + + +GET http://example.fake/%TESTNUMBER HTTP/1.1 +Host: example.fake +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Cookie: clean=good + + + + From abad1c9e4895cc89edddd668b6bee36fb03d11c8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 18 Jun 2026 08:31:39 +0200 Subject: [PATCH 0487/1062] RELEASE-PROCEDURE.md: update coming relese dates Adjusted for the summer of bliss 2026 --- docs/RELEASE-PROCEDURE.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index 36c786cbfe90..e5543b54db79 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -125,11 +125,9 @@ push for it. Based on the description above, here are some planned future release dates: -- March 11, 2026 -- April 29, 2026 - June 24, 2026 -- August 19, 2026 -- October 14, 2026 -- December 9, 2026 -- February 3, 2027 -- March 31, 2027 +- September 2, 2026 +- October 28, 2026 +- December 23, 2026 +- February 17, 2027 +- April 14, 2027 From f0be41763542f68dce344beee8a5c5e5b858e6d1 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 17 Jun 2026 14:20:02 +0200 Subject: [PATCH 0488/1062] multi: xfers_really_alive Yes, we were counting the "live" transfers before, but were they *really* alive? When determining to add the wakeup socket to fdset/waitfds etc, we should only do that when the multi handle is actually processing transfers. Other wise, the application could wait on the wakeup socket forever. For this, we counted `multi->xfers_alive` (e.g. the "running" number returned by `curl_multi_perform()`). This was almost correct. The problem is that added easy handles are counted as "alive" right away on the addition. But the processing has not started yet. They did not trigger any DNS resolves or opened any sockets yet. Add two fields in multi and easy handle: * `multi->xfers_really_alive`: counts the "alive" transfers that have passed `MSTATE_INIT` (at least once) * `data->state.really_alive`: to track if the transfer has been counted Add test 2412 to check that adding transfers without perform will not trigger the wakeup socket to be added. Fixes #22050 Reported-by: Bryan Henderson Closes #22066 --- lib/multi.c | 36 ++++++++++++--- lib/multihandle.h | 2 + lib/urldata.h | 6 +-- tests/data/Makefile.am | 2 +- tests/data/test2412 | 50 ++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib2412.c | 95 ++++++++++++++++++++++++++++++++++++++ tests/libtest/lib530.c | 3 ++ 8 files changed, 182 insertions(+), 13 deletions(-) create mode 100644 tests/data/test2412 create mode 100644 tests/libtest/lib2412.c diff --git a/lib/multi.c b/lib/multi.c index d0fa68ab4c45..d6ae111d8ee3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -531,6 +531,8 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) /* set the easy handle */ multistate(data, MSTATE_INIT); + /* not yet passed INIT state */ + data->state.really_alive = FALSE; #ifdef USE_LIBPSL /* Do the same for PSL. */ @@ -570,12 +572,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; - mresult = multi_assess_wakeup(multi); - if(mresult) { - failf(data, "error enabling wakeup listening: %d", mresult); - return mresult; - } - CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -851,6 +847,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } Curl_wildcard_dtor(&data->wildcard); @@ -1151,7 +1153,9 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, /* The admin handle always listens on the wakeup socket when there * are transfers alive. */ if(data->multi && (data == data->multi->admin) && - data->multi->xfers_alive) { + data->multi->xfers_really_alive) { + CURL_TRC_M(data, "adding wakeup, %u xfers really alive", + data->multi->xfers_really_alive); result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); } #endif @@ -2459,6 +2463,12 @@ static void handle_completed(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); Curl_uint32_bset_remove(&multi->pending, data->mid); Curl_uint32_bset_add(&multi->msgsent, data->mid); + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } --multi->xfers_alive; if(!multi->xfers_alive) multi_assess_wakeup(multi); @@ -2466,6 +2476,18 @@ static void handle_completed(struct Curl_multi *multi, static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) { + if(!data->state.really_alive) { + data->state.really_alive = TRUE; + ++data->multi->xfers_really_alive; + if(data->multi->xfers_really_alive == 1) { + CURLMcode mresult = multi_assess_wakeup(data->multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + } + } + *result = Curl_pretransfer(data); if(*result) return CURLM_OK; diff --git a/lib/multihandle.h b/lib/multihandle.h index c5cdfbe82e42..19dd2ffcdf16 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -85,6 +85,8 @@ struct Curl_multi { unsigned int xfers_alive; /* amount of added transfers that have not yet reached COMPLETE state */ + unsigned int xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ diff --git a/lib/urldata.h b/lib/urldata.h index 232364fcf3a7..d4d336d8dbdb 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -704,11 +704,7 @@ struct UrlState { uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - /* when curl_easy_perform() is called, the multi handle is "owned" by - the easy handle so curl_easy_cleanup() on such an easy handle will - also close the multi handle! */ - BIT(multi_owned_by_easy); - + BIT(really_alive); /* transfer is really alive in multi, passed INIT */ BIT(this_is_a_follow); /* this is a followed Location: request */ BIT(refused_stream); /* this was refused, try again */ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f537ba181b9e..705b8be4cdd0 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -262,7 +262,7 @@ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ -test2408 test2409 test2410 test2411 \ +test2408 test2409 test2410 test2411 test2412 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ diff --git a/tests/data/test2412 b/tests/data/test2412 new file mode 100644 index 000000000000..e0320e2ce4b6 --- /dev/null +++ b/tests/data/test2412 @@ -0,0 +1,50 @@ + + + + +multi + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6007 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- +%repeat[1000 x foobar]% + + + +# Client-side + + +wakeup + + +http + + +lib%TESTNUMBER + + +checking curl_multi_fdset on nothing to do + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 98c99399944e..bec648542b4f 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -115,6 +115,7 @@ TESTS_C = \ lib2023.c lib2032.c lib2082.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ lib2402.c lib2404.c lib2405.c \ + lib2412.c \ lib2502.c lib2504.c lib2505.c lib2506.c \ lib2700.c \ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ diff --git a/tests/libtest/lib2412.c b/tests/libtest/lib2412.c new file mode 100644 index 000000000000..79d49a2d7680 --- /dev/null +++ b/tests/libtest/lib2412.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Dmitry Karpov + * + * 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 "first.h" +#include "testtrace.h" + +static CURLcode test_lib2412(const char *URL) +{ + CURLcode result = CURLE_OK; + CURLM *multi = NULL; + CURL *easy = NULL; + CURLMcode rc; + fd_set readFdSet, writeFdSet, exceptFdSet; + int maxFd; + + (void)URL; + global_init(CURL_GLOBAL_ALL); + + multi = curl_multi_init(); + if(!multi) { + curl_mfprintf(stderr, "curl_multi_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + easy = curl_easy_init(); + if(!easy) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + debug_config.nohex = TRUE; + debug_config.tracetime = TRUE; + easy_setopt(easy, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(easy, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(easy, CURLOPT_VERBOSE, 1L); + + rc = curl_multi_add_handle(multi, easy); + if(rc) { + curl_mfprintf(stderr, "curl_multi_add_handle() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + FD_ZERO(&readFdSet); + FD_ZERO(&writeFdSet); + FD_ZERO(&exceptFdSet); + maxFd = -1; + rc = curl_multi_fdset(multi, &readFdSet, &writeFdSet, &exceptFdSet, + &maxFd); + if(rc) { + curl_mfprintf(stderr, "curl_multi_fdset() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + if(maxFd == -1) + curl_mfprintf(stderr, "There are no file descriptors to wait for\n"); + else { + curl_mfprintf(stderr, "libcurl supplied a file descriptor to " + "wait for (maxFd=%d). Waiting now ...\n", maxFd); + result = TEST_ERR_FAILURE; + } + +test_cleanup: + if(easy) { + curl_multi_remove_handle(multi, easy); + curl_easy_cleanup(easy); + } + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index d4c894d1d083..bddb857be0f6 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -29,6 +29,7 @@ */ #include "first.h" +#include "testtrace.h" static struct t530_ctx { int socket_calls; @@ -300,6 +301,8 @@ static CURLcode testone(const char *URL, int timer_fail_at, int socket_fail_at) easy_setopt(curl, CURLOPT_URL, URL); /* go verbose */ + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); multi_init(multi); From af94731a432b0c512f93cfcd8d33aa74eb9f85ce Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 10:18:29 +0200 Subject: [PATCH 0489/1062] GHA/linux: drop arm runner home attribute workaround (fixed upstream) Issue had been fixed in the ubuntu-24.04-arm runner image upstream. now: ``` $ ls -l /home # on arm drwxr-x--- 11 runner runner 4096 Jun 18 08:19 runner $ ls -l /home # on intel drwxr-x--- 11 runner runner 4096 Jun 18 08:19 runner ``` Follow-up to 2b0d8dcc16c531d3154ab54347a3eaabf9bd2c7d #20231 Closes #22076 --- .github/workflows/linux.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 60553f796a78..2e990123f7a2 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -475,12 +475,6 @@ jobs: if [ -n "${INSTALL_PACKAGES_BREW}" ]; then /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} fi - # Workaround for ubuntu-24.04-arm images having 0777 for /home/runner, - # which breaks the test sshd server used in pytest. - if [[ "$(uname -m)" = *'aarch64'* ]]; then - ls -l /home - chmod 0755 /home/runner - fi - name: 'install prereqs (i686)' if: ${{ contains(matrix.build.name, 'i686') }} From 6079ff314b059c48f560065cdbe6da8334961e2b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 03:06:30 +0200 Subject: [PATCH 0490/1062] GHA/http3-linux: simplify setting `CC`/`CXX` envs Replace `GITHUB_ENV` method by defining these envs at workflow-level. Follow-up to a8174176b5425c5692b55b78e40aef3a2331155f #13841 Closes #22075 --- .github/workflows/http3-linux.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 40c91a74a708..a1b15e1b87df 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -35,6 +35,8 @@ env: MAKEFLAGS: -j 5 CURL_CI: github CURL_TEST_MIN: 1850 + CC: gcc-12 + CXX: g++-12 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.0.0 @@ -232,8 +234,6 @@ jobs: libuv1-dev \ libc-ares-dev \ libp11-kit-dev autopoint bison gperf gtk-doc-tools libtasn1-bin # for GnuTLS - echo 'CC=gcc-12' >> "$GITHUB_ENV" - echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'build awslc' if: ${{ !steps.cache-awslc.outputs.cache-hit }} @@ -593,8 +593,6 @@ jobs: libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libidn2-0-dev libldap-dev libuv1-dev valgrind \ ${INSTALL_PACKAGES} \ ${MATRIX_INSTALL_PACKAGES} - echo 'CC=gcc-12' >> "$GITHUB_ENV" - echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'cache awslc' if: ${{ contains(matrix.build.name, 'awslc') }} From 2d70c815e4e22886875dff52415eb662a515142f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 10:44:03 +0200 Subject: [PATCH 0491/1062] GHA/linux: use default GCC compiler, drop `CC`/`CXX` envs At the time of the original commit, the runner was ubuntu-22.04 with a default GCC 11. It made sense to bump to 12 manually. Since 2025, the default is ubuntu-24.04 with GCC 13, when this became a downgrade. Drop manual envs and bump to GCC 13 with it. Other options available are 14, 15 and 16. Refs: https://packages.ubuntu.com/jammy/gcc (ubuntu-22.04) https://packages.ubuntu.com/noble/gcc (ubuntu-24.04) Follow-up to 6079ff314b059c48f560065cdbe6da8334961e2b #22075 Follow-up to a8174176b5425c5692b55b78e40aef3a2331155f #13841 Closes #22077 --- .github/workflows/http3-linux.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a1b15e1b87df..0ee95f16b383 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -35,8 +35,6 @@ env: MAKEFLAGS: -j 5 CURL_CI: github CURL_TEST_MIN: 1850 - CC: gcc-12 - CXX: g++-12 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.0.0 From a7e35c9194f3c6cc6bcfaa5bc559b02bcc83367a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 18 Jun 2026 12:34:13 +0200 Subject: [PATCH 0492/1062] docs/VERIFY: expand with more things we do Closes #22080 --- .github/scripts/pyspelling.words | 1 + docs/VERIFY.md | 41 +++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 63e5143191b3..1c0fb96d9b05 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -1014,6 +1014,7 @@ Youtube YYYY YYYYMMDD Zakrzewski +Zeropath Zitzmann zlib zsh diff --git a/docs/VERIFY.md b/docs/VERIFY.md index 0613803acf9e..c18b65a6612c 100644 --- a/docs/VERIFY.md +++ b/docs/VERIFY.md @@ -79,15 +79,21 @@ gain trust is to verify and review our testing procedures. - we have a ceiling for complexity in functions to keep them easy to follow, read and understand (failing to do so causes errors) -- we review all pull requests before merging, both with humans and with bots. We - link back commits to their origin pull requests in commit messages. +- we review all pull requests before merging, both with humans and with bots. + We link back commits to their origin pull requests in commit messages. - we ban use of "binary blobs" in git to not provide means for malicious actors to bundle encrypted payloads (trying to include a blob causes errors) +- every single file in the git repository has a clear copyright and license + statement. Complete knowledge and tracking of provenience. + - we actively avoid base64 encoded chunks as they too could function as ways to obfuscate malicious contents +- we forbid and prevent git force push on the master branch. History cannot be + rewritten. + - we ban most uses of UTF-8 in code and documentation to avoid easily mixed up Unicode characters that look like other characters. (adding Unicode characters causes errors) @@ -106,6 +112,10 @@ gain trust is to verify and review our testing procedures. every commit and every PR. We do not merge commits that have unexplained test failures. +- we run all tests as "torture tests", where each test case is rerun to have + every invoked fallible function call fail once each, to make sure curl + never leaks memory or crashes due to this. + - we build curl in CI with the most picky compiler options enabled and we never allow compiler warnings to linger. We always use `-Werror` that converts warnings to errors and fail the builds. @@ -114,9 +124,9 @@ gain trust is to verify and review our testing procedures. find and reduce the risk for memory problems, undefined behavior and similar -- we run all tests as "torture tests", where each test case is rerun to have - every invoked fallible function call fail once each, to make sure curl - never leaks memory or crashes due to this. +- we keep running static code analyzers on the code, both traditional ones + (clang-tidy, CodeSonar, Coverity) but also new generation AI powered ones + like Zeropath and Codex Security. - we run fuzzing on curl: non-stop as part of Google's OSS-Fuzz project, but also briefly as part of the CI setup for every commit and PR @@ -128,6 +138,14 @@ gain trust is to verify and review our testing procedures. - we run `zizmor` and other code analyzer tools on the CI job config scripts to reduce the risk of us running or using insecure CI jobs. +- we do reproducible releases to allow anyone to verify that the contents is + untainted + +- we digitally sign releases, git tags and git commits + +- there is a git backup on [codeberg](https://codeberg.org/curl/) for enhanced + resilience to infrastructure disturbance + - we are committed to always fix reported vulnerabilities in the following release. Security problems never linger around once they have been reported. @@ -135,11 +153,16 @@ gain trust is to verify and review our testing procedures. - we document everything and every detail about all curl vulnerabilities ever reported +- our code has been audited several times by external security experts, and + the few issues that have been detected in those were immediately addressed + +- Strong two-factor authentication on GitHub is mandatory for all committers + - our commitment to never breaking ABI or API allows all users to easily upgrade to new releases. This enables users to run recent security-fixed versions instead of legacy insecure versions. -- our code has been audited several times by external security experts, and - the few issues that have been detected in those were immediately addressed - -- Two-factor authentication on GitHub is mandatory for all committers +- we have a vulnerability disclosure program that allows researchers to submit + suspected vulnerabilities in a private and secure fashion, so that we can + work on fixing curl and announcing the flaw in a responsible manner to + minimize risks for users. From 2f8fb98c5f0c4ff490a6c836634b69034d2a7cd9 Mon Sep 17 00:00:00 2001 From: alhudz Date: Sat, 13 Jun 2026 13:34:51 +0530 Subject: [PATCH 0493/1062] pingpong: reject nul byte in server response line Add test 2108 covering the rejection over FTP. Drop the now-vestigial nul bytes from test 1282; they exercised the removed Kerberos FTP security buffer check and now trip this rejection before the 633 login-denied path is reached. Closes #21996 --- lib/pingpong.c | 7 +++++++ tests/data/Makefile.am | 1 + tests/data/test1282 | 2 +- tests/data/test2108 | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2108 diff --git a/lib/pingpong.c b/lib/pingpong.c index ae3f7faa30e0..b40d968b3f48 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -292,6 +292,13 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, the line is not really terminated until the LF comes */ size_t length = nl - line + 1; + if(memchr(line, 0, length)) { + /* The response line is passed on as a "header" below, so reject an + embedded nul the same way verify_header() does for HTTP. */ + failf(data, "Nul byte in server response line"); + return CURLE_WEIRD_SERVER_REPLY; + } + /* output debug output if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, line, length); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 705b8be4cdd0..6e8eca22bd5f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,6 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ +test2108 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test1282 b/tests/data/test1282 index 06cf5170edc6..774f54cffaee 100644 --- a/tests/data/test1282 +++ b/tests/data/test1282 @@ -10,7 +10,7 @@ RETR # Server-side -REPLY PASS 633 XXXXXXXX\x00\x00XXXXXXXX +REPLY PASS 633 XXXXXXXXXXXXXXXX diff --git a/tests/data/test2108 b/tests/data/test2108 new file mode 100644 index 000000000000..481d09357e73 --- /dev/null +++ b/tests/data/test2108 @@ -0,0 +1,41 @@ + + + + +FTP + + +# Server-side + + +REPLY PASS 230 logged\x00 in + + + +# Client-side + + +ftp + + +FTP rejects a nul byte in a server response line + + +ftp://%HOSTIP:%FTPPORT/%TESTNUMBER + + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com + + +# 8 == CURLE_WEIRD_SERVER_REPLY + +8 + + + From 5a2af800dea30b8ca5e6fa064ce2b8f2b9f0eb44 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 16:27:52 +0200 Subject: [PATCH 0494/1062] GHA/linux: bump analyzer job to gcc-16, and ubuntu-26.04 To fix false positive gcc analyzer warning `-Wanalyzer-deref-before-check`, seen with gcc-15 and lower. Also bump its pair job. Tests with #22082 applied: gcc-13: https://github.com/curl/curl/actions/runs/27761999978/job/82138558662 (warning) gcc-15: https://github.com/curl/curl/actions/runs/27767571050/job/82158465527 (warning) gcc-16: https://github.com/curl/curl/actions/runs/27767332723/job/82157636394 (OK) Ref: #22082 Fixes #22083 Closes #22084 --- .github/workflows/linux.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2e990123f7a2..6df5a65c2842 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -215,14 +215,16 @@ jobs: configure: --with-openssl --enable-debug --disable-unity - name: 'openssl libssh2 sync-resolver valgrind 1 +analyzer' - image: ubuntu-24.04-arm - install_packages: libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + image: ubuntu-26.04-arm + install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + CC: gcc-16 tflags: '--min=965 1 to 1000' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_GCC_ANALYZER=ON -DCURL_ENABLE_NTLM=ON - name: 'openssl libssh2 sync-resolver valgrind 2' - image: ubuntu-24.04-arm - install_packages: libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + image: ubuntu-26.04-arm + install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + CC: gcc-16 tflags: '--min=920 1001 to 9999' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON From b71d3d9aeaacdd32bb4ae93b1dd647f66c6c437e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 18 Jun 2026 14:43:08 +0200 Subject: [PATCH 0495/1062] CURLMOPT_SOCKETFUNCTION.md: this sends *all* file descriptors Since libcurl has more than just the main tranfer sockets to worry about. Closes #22081 --- docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md | 56 ++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md index 411b856e9328..2774ccbfc7f4 100644 --- a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md @@ -22,13 +22,14 @@ CURLMOPT_SOCKETFUNCTION - callback informed about what to wait for ~~~c #include -int socket_callback(CURL *easy, /* easy handle */ - curl_socket_t s, /* socket */ - int what, /* describes the socket */ - void *clientp, /* private callback pointer */ - void *socketp); /* private socket pointer */ - -CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, socket_callback); +int socket_callback(CURL *easy, + curl_socket_t socket, + int what, + void *clientp, + void *socketp); + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, + socket_callback); ~~~ # DESCRIPTION @@ -36,12 +37,12 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, socket_callb Pass a pointer to your callback function, which should match the prototype shown above. -When the curl_multi_socket_action(3) function is called, it uses this -callback to inform the application about updates in the socket (file -descriptor) status by doing none, one, or multiple calls to the -**socket_callback**. The callback function gets status updates with changes -since the previous time the callback was called. If the given callback pointer -is set to NULL, no callback is called. +When the curl_multi_socket_action(3) function is called, it uses this callback +to inform the application about updates in the socket (file descriptor) status +by doing none, one, or multiple calls to the **socket_callback**. The callback +function gets status updates with changes since the previous time the callback +was called. If the given callback pointer is set to NULL, no callback is +called. libcurl then expects the application to monitor the sockets for the specific activities and tell libcurl again when something happens on one of them. Tell @@ -52,18 +53,23 @@ This may even happen after all transfers are done and is *likely* to happen *during* a call to curl_multi_cleanup(3) when cached connections are shut down. +libcurl may use a number of internal file descriptors for name resolving, +Happy Eyeballs racing, internal communication and more, in addition to the +main sockets used for network transfers. All of those file descriptors might +get passed to this callback as "sockets". + # CALLBACK ARGUMENTS -*easy* identifies the specific transfer for which this update is related. +**easy** identifies the specific transfer for which this update is related. Since this callback manages a whole multi handle, an application should not make assumptions about which particular handle that is passed here. It might even be an internal easy handle that the application did not add itself. -*s* is the specific socket this function invocation concerns. If the -**what** argument is not CURL_POLL_REMOVE then it holds information about -what activity on this socket the application is supposed to -monitor. Subsequent calls to this callback might update the **what** bits -for a socket that is already monitored. +**socket** is the specific socket this function invocation concerns. If the +**what** argument is not CURL_POLL_REMOVE then it holds information about what +activity on this socket the application is supposed to monitor. Subsequent +calls to this callback might update the **what** bits for a socket that is +already monitored. The socket callback should return 0 on success, and -1 on error. If this callback returns error, **all** transfers currently in progress in this @@ -73,8 +79,8 @@ multi handle are aborted and made to fail. **socketp** is set with curl_multi_assign(3) or NULL. -The **what** parameter informs the callback on the status of the given -socket. It can hold one of these values: +The **what** parameter informs the callback on the status of the given socket. +It can hold one of these values: ## CURL_POLL_IN @@ -94,6 +100,14 @@ writable. The specified socket/file descriptor is no longer used by libcurl for any active transfer. It might soon be added again. +When a socket is given a CURL_POLL_REMOVE value, it might be because libcurl +is going to close it, but it might also mean that it does not need any more +monitoring for the moment. An application cannot assume either. The same +socket might appear soon in a call asking for monitoring again. + +A socket that is removed like this loses its assigned pointer as set with +curl_multi_assign(3). + # DEFAULT NULL (no callback) From 39628c50844eb8dd6f7416653a836fa0ec4d73eb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 17:12:03 +0200 Subject: [PATCH 0496/1062] openssl: do not mix OpenSSL int result with `CURLcode` variable Seen with clang-22: ``` lib/vtls/openssl.c:3538:14: error: implicit conversion from 'int' to enumeration type 'CURLcode' is invalid in C++ [-Werror,-Wimplicit-int-enum-cast] 3538 | result = SSL_ech_set1_server_names(octx->ssl, | ~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3539 | peer->origin->hostname, outername, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3540 | 0 /* do send outer */); | ~~~~~~~~~~~~~~~~~~~~~~ 1 error generated. ``` Ref: https://github.com/curl/curl/actions/runs/27769068896/job/82163712258#step:42:43 Cherry-picked from #22086 Closes #22087 --- lib/vtls/openssl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4689d7a2fc93..010bbb9825e4 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3445,7 +3445,6 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, size_t ech_config_len = 0; char *outername = data->set.str[STRING_ECH_PUBLIC]; int trying_ech_now = 0; - CURLcode result = CURLE_OK; if(!CURLECH_ENABLED(data)) return CURLE_OK; @@ -3462,6 +3461,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, #ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; + CURLcode result; if(!b64) { infof(data, "ECH: ECHConfig from command line empty"); @@ -3533,14 +3533,14 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } #else if(trying_ech_now && outername) { + int ret; infof(data, "ECH: inner: '%s', outer: '%s'", peer->origin->hostname ? peer->origin->hostname : "NULL", outername); - result = SSL_ech_set1_server_names(octx->ssl, - peer->origin->hostname, outername, - 0 /* do send outer */); - if(result != 1) { - infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", - (int)result); + ret = SSL_ech_set1_server_names(octx->ssl, + peer->origin->hostname, outername, + 0 /* do send outer */); + if(ret != 1) { + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", ret); return CURLE_SSL_CONNECT_ERROR; } } From 595d052923629e44009d106c1804334012c48d0e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 18 Jun 2026 17:34:02 +0200 Subject: [PATCH 0497/1062] curl_multi_assign.md: clarify lifetime Closes #22088 --- docs/libcurl/curl_multi_assign.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/libcurl/curl_multi_assign.md b/docs/libcurl/curl_multi_assign.md index 279965f53436..784f5ad9a9cc 100644 --- a/docs/libcurl/curl_multi_assign.md +++ b/docs/libcurl/curl_multi_assign.md @@ -7,6 +7,7 @@ Source: libcurl See-also: - curl_multi_setopt (3) - curl_multi_socket_action (3) + - CURLMOPT_SOCKETFUNCTION (3) Protocol: - All Added-in: 7.15.5 @@ -31,11 +32,12 @@ This function creates an association in the multi handle between the given socket and a private pointer of the application. This is designed for curl_multi_socket_action(3) uses. -When set, the *sockptr* pointer is passed to all future socket callbacks -for the specific *sockfd* socket. +When set, the *sockptr* pointer is passed to all future socket callbacks for +the specific *sockfd* socket, until the socket stops being monitored +(CURL_POLL_REMOVE is sent to the CURLMOPT_SOCKETFUNCTION(3) callback). -If the given *sockfd* is not already in use by libcurl, this function -returns an error. +If the given *sockfd* is not already in use by libcurl, this function returns +an error. libcurl only keeps one single pointer associated with a socket, so calling this function several times for the same socket makes the last set pointer get From adb4edd177402011c9649047f7ecbde9fda4080b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 17:04:58 +0200 Subject: [PATCH 0498/1062] GHA: bump analyzer/sanitizer jobs to clang-22, and ubuntu-26.04 On Linux, and Windows cross-builds. clang-tidy jobs look significantly faster. Other jobs remain around the same (this feels nice after seeing the significant slowdowns in Windows-2025, FreeBSD 15.) Before: https://github.com/curl/curl/actions/runs/27770630688 After: https://github.com/curl/curl/actions/runs/27770913426?pr=22086 gcc-analyzer also got faster: Before: https://github.com/curl/curl/actions/runs/27758865007/job/82127670883 After: https://github.com/curl/curl/actions/runs/27768696084/job/82162385765 Also: - work around actionlint 1.7.12 not yet being aware of ubuntu-26.04: ``` windows.yml:770:14: label "ubuntu-26.04" is unknown. available labels are [...] ``` Ref: https://github.com/curl/curl/actions/runs/27769065782/job/82163700294#step:6:13 Ref: https://github.com/rhysd/actionlint/issues/682 Ref: https://github.com/rhysd/actionlint/pull/683 Follow-up to 5a2af800dea30b8ca5e6fa064ce2b8f2b9f0eb44 #22084 Closes #22086 --- .github/workflows/checksrc.yml | 2 +- .github/workflows/linux.yml | 34 ++++++++++++++++++++-------------- .github/workflows/windows.yml | 10 +++++----- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 09aa7597ed8a..dad8ed48f5f4 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -174,7 +174,7 @@ jobs: eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" export SHELLCHECK_OPTS='--exclude=1090,1091,2086,2153 --enable=avoid-nullary-conditions,deprecate-which' actionlint --version - actionlint --ignore matrix .github/workflows/*.yml + actionlint --ignore matrix --ignore ubuntu-26.04 .github/workflows/*.yml - name: 'shellcheck CI' run: | diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6df5a65c2842..8cb02dec2464 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -319,10 +319,11 @@ jobs: configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity - name: 'clang-tidy' - install_packages: clang-20 clang-tidy-20 libssl-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev + image: ubuntu-26.04 + install_packages: clang-22 clang-tidy-22 libssl-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev install_steps: skiprun mbedtls-latest-intel rustls wolfssl-opensslextra-intel install_steps_brew: openssl@4 gsasl - CC: clang-20 + CC: clang-22 CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib @@ -341,13 +342,14 @@ jobs: -DCURL_USE_WOLFSSL=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_RUSTLS=ON -DCURL_USE_GSASL=ON -DUSE_ECH=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON - -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22 - name: 'clang-tidy H3 c-ares !examples' - install_packages: clang-20 clang-tidy-20 libidn2-dev libssh-dev libnghttp2-dev + image: ubuntu-26.04 + install_packages: clang-22 clang-tidy-22 libidn2-dev libssh-dev libnghttp2-dev install_steps: skiprun install_steps_brew: libngtcp2 libnghttp3 c-ares - CC: clang-20 + CC: clang-22 CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/openssl/lib @@ -363,13 +365,14 @@ jobs: -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_HTTPSRR=ON -DENABLE_ARES=ON -DUSE_PROXY_HTTP3=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON - -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22 - name: 'address-sanitizer' - install_packages: clang-20 libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 install_steps: pytest randcurl install_steps_brew: openssl@4 - CC: clang-20 + CC: clang-22 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined @@ -382,10 +385,11 @@ jobs: generate: -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_USE_LIBSSH=ON - name: 'address-sanitizer H3 c-ares' - install_packages: clang-20 libubsan1 libasan8 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libubsan1 libasan8 libtsan2 install_steps: pytest install_steps_brew: openssl libssh2 libngtcp2 libnghttp3 c-ares - CC: clang-20 + CC: clang-22 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined @@ -405,17 +409,19 @@ jobs: -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON -DUSE_PROXY_HTTP3=ON - name: 'thread-sanitizer' - install_packages: clang-20 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libtsan2 install_steps: pytest openssl-tsan - CC: clang-20 + CC: clang-22 CFLAGS: -fsanitize=thread -g LDFLAGS: -fsanitize=thread generate: -DOPENSSL_ROOT_DIR=/home/runner/openssl -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'memory-sanitizer' - install_packages: clang-20 + image: ubuntu-26.04 + install_packages: clang-22 install_steps: randcurl - CC: clang-20 + CC: clang-22 CFLAGS: -fsanitize=memory -Wformat -Werror=format-security -Werror=array-bounds -g LDFLAGS: -fsanitize=memory LIBS: -ldl diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0f95d3e65e9e..9c3acbdf710e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -767,7 +767,7 @@ jobs: linux-cross-mingw-w64: name: "linux-mingw, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }}" - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 env: LDFLAGS: -s @@ -781,7 +781,7 @@ jobs: include: - { build: 'autotools', compiler: 'gcc' } - { build: 'cmake' , compiler: 'gcc' } - - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-20 clang-tidy-20', CFLAGS: '-Wunused-macros' } + - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-22 clang-tidy-22', CFLAGS: '-Wunused-macros' } steps: - name: 'install packages' timeout-minutes: 2 @@ -808,10 +808,10 @@ jobs: run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then if [ "${MATRIX_COMPILER}" = 'clang-tidy' ]; then - options+=' -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20' + options+=' -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22' options+=' -DENABLE_UNICODE=ON -DUSE_SSLS_EXPORT=ON' - options+=' -DCMAKE_C_COMPILER=clang-20' - options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang-20 -dumpversion | cut -d '.' -f 1)" + options+=' -DCMAKE_C_COMPILER=clang-22' + options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang-22 -dumpversion | cut -d '.' -f 1)" else options+=" -DCMAKE_C_COMPILER=${TRIPLET}-gcc" fi From c972583f6c57d15437e36c63cb0bc19790a7f2b8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 19:00:48 +0200 Subject: [PATCH 0499/1062] MANUAL.md: update `apt-key` example To use `tee` instead, due to `apt-key` being deprecated, and missing from recent distros. Also lowercase `stdin` to match rest of the file. Ref: https://documentation.ubuntu.com/release-notes/26.04/summary-for-lts-users/#package-management-apt-3 Follow-up to b13e9066b3dfd65ba8aadc336232ae7832ac687a #16127 Follow-up to 54130a6cad4e044a199f40e857c300a139818b9b #10170 Closes #22090 --- docs/MANUAL.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 1032098dcba6..fb62f64ba24c 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -188,13 +188,13 @@ transfers, and curl's `-v` option to see exactly what curl is sending. ## Piping -Get a key file and add it with `apt-key` (when on a system that uses `apt` for -package management): +Get a key file and install it as a trusted one (when on a system that uses +`apt` for package management): - curl -L https://apt.example.org/llvm-snapshot.gpg.key | sudo apt-key add - + curl -L https://apt.example.org/llvm-snapshot.gpg.key | sudo tee + /etc/apt/trusted.gpg.d/llvm-snapshot.asc >/dev/null -The '|' pipes the output to STDIN. `-` tells `apt-key` that the key file -should be read from STDIN. +The '|' pipes the output to stdin. `tee` reads from stdin. ## Ranges From 950a30d762d86e3a10154e480a9e7a5a6d7cbabe Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 17:57:51 +0200 Subject: [PATCH 0500/1062] GHA/http3-linux: bump to ubuntu-26.04 Before: https://github.com/curl/curl/actions/runs/27772068909 After: https://github.com/curl/curl/actions/runs/27772321661?pr=22089 Closes #22089 --- .github/workflows/http3-linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 0ee95f16b383..2909203e16a7 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -68,7 +68,7 @@ env: jobs: build-cache: name: 'Build caches' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 steps: - name: 'cache awslc' @@ -426,7 +426,7 @@ jobs: linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} needs: build-cache - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 env: CURL_TRACE_PKG_CONFIG: '1' From e44f1a1446f8e72573e5a1026807e71a0495f88d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 20:04:51 +0200 Subject: [PATCH 0501/1062] smb: constify `strchr()` result variable Fixing (as seen with gcc-15 on Ubuntu 26.04): ``` lib/smb.c: In function 'smb_connect': lib/smb.c:491:9: error: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] 491 | slash = strchr(user, '/'); | ^ lib/smb.c:493:11: error: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] 493 | slash = strchr(user, '\\'); | ^ ``` Ref: https://github.com/curl/curl/actions/runs/27778098314/job/82195462418?pr=22092 Follow-up to 4e5908306ad5febee88f7eae8ea3b0c41a6b7d84 #20428 Follow-up to 7dc60bdb90c710c2e36b2d05aa3686ff491a9bbe #20425 Follow-up to 0e2507a3c65376d6bda860ff20bd94ada9bbb9fd #20421 Cherry-picked from #22092 Closes #22094 --- lib/smb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/smb.c b/lib/smb.c index c06ca14fb3dc..70af8d597090 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -467,7 +467,7 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN); - char *slash; + const char *slash; const char *user = Curl_creds_user(conn->creds); (void)done; From fdd6ba3580f877a71394e106bd66f8d81fcb5771 Mon Sep 17 00:00:00 2001 From: alhudz Date: Thu, 18 Jun 2026 20:01:20 +0530 Subject: [PATCH 0502/1062] cookie: check __Secure- and __Host- case sensitively when read from file The header path matches these prefixes case sensitively, as 5af0165562 made it for cookie spec reasons, but the Netscape cookie-file path still used a case-insensitive match. Align the file path so a differently cased name like __secure-x is treated as an ordinary cookie instead of being put through the prefix integrity checks. Extended test 2311 to cover it. Closes #22085 --- lib/cookie.c | 8 +++++--- tests/data/test2311 | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/cookie.c b/lib/cookie.c index 99f2910632ad..91dc8d5fd0d2 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -759,10 +759,12 @@ static CURLcode parse_netscape(struct Cookie *co, if(!co->name) return CURLE_OUT_OF_MEMORY; else { - /* For Netscape file format cookies we check prefix on the name */ - if(curl_strnequal("__Secure-", co->name, 9)) + /* For Netscape file format cookies we check prefix on the name. + These prefixes are matched case sensitively, same as on the + header path and as the 6265bis document specifies. */ + if(!strncmp("__Secure-", co->name, 9)) co->prefix_secure = TRUE; - else if(curl_strnequal("__Host-", co->name, 7)) + else if(!strncmp("__Host-", co->name, 7)) co->prefix_host = TRUE; } break; diff --git a/tests/data/test2311 b/tests/data/test2311 index 92453a75e3bf..91032b81d241 100644 --- a/tests/data/test2311 +++ b/tests/data/test2311 @@ -26,7 +26,7 @@ This server says moo http -Cookie from file with control octet in value is rejected +Cookie from file: control octet rejected, prefixes matched case sensitively http://example.fake/%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER -x %HOSTIP:%HTTPPORT @@ -34,6 +34,8 @@ http://example.fake/%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER -x %HOSTIP:%HTTPPORT example.fake FALSE / FALSE 0 clean good example.fake FALSE / FALSE 0 bad %hex[ba%07d]hex% +example.fake FALSE / FALSE 0 __secure-x yes +example.fake FALSE / FALSE 0 __Secure-y no cookies @@ -49,7 +51,7 @@ Host: example.fake User-Agent: curl/%VERSION Accept: */* Proxy-Connection: Keep-Alive -Cookie: clean=good +Cookie: __secure-x=yes; clean=good From 139ce4d37cfdc3126179bdb166ec61a095360c62 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 23:48:23 +0200 Subject: [PATCH 0503/1062] GHA: separate pytype from other checkers and pips pytype is discontinued, does not receive further updates, and it requires older python, offered by Ubuntu 24.04 or older. Move it to its own GHA job to allow bumping the rest of checkers to. newer runner images. Also move it out from the shared `requirements.txt` and install directly from its separate GHA job, to avoid installing it unnecessarily from others. Since it does not receive update, it's fine to move out from Dependabot's view. Ref: https://pypi.org/project/pytype/ Cherry-picked from #22092 Closes #22096 --- .github/scripts/requirements.txt | 1 - .github/workflows/checksrc.yml | 24 ++++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 5f50cb4b0259..adf6d03ba456 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -4,6 +4,5 @@ cmakelang==0.6.13 codespell==2.4.2 -pytype==2024.10.11 reuse==6.2.0 ruff==0.15.16 diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index dad8ed48f5f4..83f5afe7a1aa 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -88,15 +88,31 @@ jobs: run: | scripts/perlcheck.sh - - name: 'pytype' + - name: 'ruff' run: | source ~/venv/bin/activate - find . -name '*.py' -exec pytype -j auto -k -- {} + + scripts/pythonlint.sh - - name: 'ruff' + pytype: + name: 'pytype' + runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 'install prereqs' + run: | + python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary pytype==2024.10.11 \ + -r .github/scripts/requirements.txt \ + -r tests/http/requirements.txt \ + -r tests/requirements.txt + + - name: 'check' run: | source ~/venv/bin/activate - scripts/pythonlint.sh + find . -name '*.py' -exec pytype -j auto -k -- {} + complexity: name: 'complexity and function sizes' From 8f5e4f020e79ec1fd1e8540bdea3c1c20030405b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 00:17:50 +0200 Subject: [PATCH 0504/1062] GHA: fix Linux triplet passed to `CMAKE_C_COMPILER_TARGET` Before this patch it broke clang 20/21 cmake builds on ubuntu-26.04-arm runner, failing at the beginning of the configure stage while probing the compiler. Seen in the 'CM openssl clang krb5 LTO' job: ``` : && /usr/bin/clang --target=aarch64-pc-linux-gnu CMakeFiles/cmTC_3d9ae.dir/testCCompiler.c.o -o cmTC_3d9ae && : /usr/bin/aarch64-linux-gnu-ld.bfd: cannot find crtbeginS.o: No such file or directory /usr/bin/aarch64-linux-gnu-ld.bfd: cannot find -lgcc: No such file or directory /usr/bin/aarch64-linux-gnu-ld.bfd: cannot find -lgcc_s: No such file or directory ``` Ref: https://github.com/curl/curl/actions/runs/27778098314/job/82195462687#step:38:66 Follow-up to 36bd8074758a0b3a784403eb3d2cc31d240de896 #15242 Follow-up to 232302f88a152a1d1722da9f69c383a766528918 #14382 Cherry-picked from #22092 Closes #22097 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 2909203e16a7..a95a11316e0d 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -789,7 +789,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then [[ "${MATRIX_GENERATE}" = *'boringssl'* ]] && options=" -DBORINGSSL_VERSION=${BORINGSSL_VERSION}" cmake -B bld -G Ninja \ - -DCMAKE_C_COMPILER_TARGET="$(uname -m)-pc-linux-gnu" -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ -DCURL_USE_LIBUV=ON -DCURL_ENABLE_NTLM=ON \ -DTEST_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8cb02dec2464..6b520ec41f44 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -866,7 +866,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake -B bld -G Ninja \ -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_C_COMPILER_TARGET="$(uname -m)-pc-linux-gnu" -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ ${MATRIX_GENERATE} else From 2a993e2a4a3d057b277e0e2dd490c5c9466b6d94 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 01:06:39 +0200 Subject: [PATCH 0505/1062] GHA: re-sync Linux CMake triplet with autotools builds Follow-up to 8f5e4f020e79ec1fd1e8540bdea3c1c20030405b #22097 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a95a11316e0d..c766ce94f806 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -789,7 +789,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then [[ "${MATRIX_GENERATE}" = *'boringssl'* ]] && options=" -DBORINGSSL_VERSION=${BORINGSSL_VERSION}" cmake -B bld -G Ninja \ - -DCMAKE_C_COMPILER_TARGET="$(uname -m)-linux-gnu" -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-unknown-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ -DCURL_USE_LIBUV=ON -DCURL_ENABLE_NTLM=ON \ -DTEST_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6b520ec41f44..1d0043c29fef 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -866,7 +866,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake -B bld -G Ninja \ -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_C_COMPILER_TARGET="$(uname -m)-linux-gnu" -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-unknown-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ ${MATRIX_GENERATE} else From 04305a3e40989d3731e97bd0ef41bbd55c680a3f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 10:34:40 +0200 Subject: [PATCH 0506/1062] runtests: drop orphaned, no-op `-k` option And the corresponding internal variable. This option became the always-enabled default earlier, via #4035. Reported-by: Zartaj Majeed Ref: #22098 Follow-up to 6617db6a7ed322d28322896aa20bcabf3a479e7c #4035 Closes #22100 --- docs/runtests.md | 5 ----- tests/runtests.pl | 6 ------ 2 files changed, 11 deletions(-) diff --git a/docs/runtests.md b/docs/runtests.md index 79066d8ffd0b..a1190004afc7 100644 --- a/docs/runtests.md +++ b/docs/runtests.md @@ -164,11 +164,6 @@ CPU cores is a good figure to start with, or 1.3 times if Valgrind is in use, or 5 times for torture tests. Enabling parallel tests is not recommended in conjunction with the -g option. -## `-k` - -Keep output and log files in log/ after a test run, even if no error was -detected. Useful for debugging. - ## `-L \` Load and execute the specified file which should contain perl code. This diff --git a/tests/runtests.pl b/tests/runtests.pl index 35606c740e5c..61949614b970 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -182,7 +182,6 @@ BEGIN # my $short; my $no_debuginfod; -my $keepoutfiles; # keep stdout and stderr files after tests my $postmortem; # display detailed info about failed tests my $run_disabled; # run the specific tests even if listed in DISABLED my $scrambleorder; @@ -2540,10 +2539,6 @@ sub pickrunner { $jobs = $1; } } - elsif($ARGV[0] eq "-k") { - # keep stdout and stderr files after tests - $keepoutfiles = 1; - } elsif($ARGV[0] eq "-r") { # run time statistics needs Time::HiRes if($Time::HiRes::VERSION) { @@ -2596,7 +2591,6 @@ sub pickrunner { -gw run the test case with gdb as a windowed application -h this help text -j[N] spawn this number of processes to run tests (default 0) - -k keep stdout and stderr files present after tests -L path require an additional perl library file to replace certain functions -l list all test case names/descriptions -m=[seconds] set timeout for curl commands in tests From 93e2341e87564d5f6f69a061b0280284d04f65da Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 14:11:50 +0200 Subject: [PATCH 0507/1062] vquic: fix `-Wunused-parameter` with proxies disabled Fixing: ``` lib/vquic/vquic.c:864:56: error: unused parameter 'conn' [-Werror,-Wunused-parameter] 864 | const struct connectdata *conn, | ^ ``` Closes #22104 --- lib/vquic/vquic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 8cd006bcb5de..dfb8346c1ad3 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -877,6 +877,8 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, failf(data, "HTTP/3 is not supported over a SOCKS proxy"); return CURLE_URL_MALFORMAT; } +#else + (void)conn; #endif return CURLE_OK; From 94962a9b82b5d6a13f5f75ae41e6cb3b58e25601 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:45:46 +0000 Subject: [PATCH 0508/1062] GHA: update dependency cloudflare/quiche to v0.29.2 Closes #22106 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index c766ce94f806..2be23997447c 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -52,7 +52,7 @@ env: OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.29.1 + QUICHE_VERSION: 0.29.2 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com From acdeb7f50ba281a5fd12a4df5e633b7c7d9a90e6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 15:12:15 +0200 Subject: [PATCH 0509/1062] KNOWN_BUGS.md: drop outdated CMake issues - "cmake outputs: no version information available" Ref: #11158 Seems to be about missing support for autotools `--enable-versioned-symbols`. This was implemented for CMake in: 14d4712db7e34fda25a11fd5f23f0f57a6aea67f #17039 7100c5bc9b8c9c49a44c47d64b42e98b4de2465b #14818 7b1444979094a365c82c665cce0e2ebc6b69467b #14378 - "generated `.pc` file contains strange entries" Ref: #6167 Fixed in: 9f56bb608ecfbb8978c6cb72a04d9e8b23162d82 #14681 - "CMake build with MIT Kerberos does not work" Ref: #6904 The FindGSS module responsible for MIT Kerberos detection has seen 50 updates since this report. In the last years I made many local tests with it, and it's also extensively CI-tested since (including Windows for a 1-year period), with no known issues. If you see problems remaining, let us know in a new issue. Closes #22108 --- docs/KNOWN_BUGS.md | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index 8bf35114c084..359c2fab9955 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -492,32 +492,6 @@ then subsequently fails anyway if that was actually in use. [curl issue 8112](https://github.com/curl/curl/issues/8112) -# CMake - -## cmake outputs: no version information available - -Something in the SONAME generation seems to be wrong in the cmake build. - -[curl issue 11158](https://github.com/curl/curl/issues/11158) - -## generated `.pc` file contains strange entries - -The `Libs.private` field of the generated `.pc` file contains `-lgcc -lgcc_s --lc -lgcc -lgcc_s`. - -See [curl issue 6167](https://github.com/curl/curl/issues/6167) - -## CMake build with MIT Kerberos does not work - -Minimum CMake version was bumped in curl 7.71.0 (#5358) Since CMake 3.2 -try_compile started respecting the `CMAKE_EXE_FLAGS`. The code dealing with -MIT Kerberos detection sets few variables to potentially weird mix of space, -and ;-separated flags. It had to blow up at some point. All the CMake checks -that involve compilation are doomed from that point, the configured tree -cannot be built. - -[curl issue 6904](https://github.com/curl/curl/issues/6904) - # HTTP/2 ## HTTP/2 prior knowledge over proxy From 52fa8d994dd69772701d3c59c2e127bd96c76927 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 18 Jun 2026 18:21:52 +0200 Subject: [PATCH 0510/1062] setopt: refactor setopt_cptr into smaller helper functions This takes down the longest function to sub 500 lines Closes #22095 --- lib/setopt.c | 612 ++++++++++++++++++++++----------------------- scripts/top-length | 1 - 2 files changed, 303 insertions(+), 310 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 00dda4ea2981..d07a794135fc 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1632,7 +1632,7 @@ static CURLcode setproxy(struct Curl_easy *data, const char *proxy) } static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, - const char *ptr) + char *ptr) { CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; @@ -1884,17 +1884,12 @@ static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) #define setopt_ech(x,y) CURLE_NOT_BUILT_IN #endif -static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, - char *ptr) +#ifdef USE_SSL +static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, + char *ptr) { - CURLcode result; + CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; -#ifndef CURL_DISABLE_PROXY - result = setopt_cptr_proxy(data, option, ptr); - if(result != CURLE_UNKNOWN_OPTION) - return result; -#endif - result = CURLE_OK; switch(option) { case CURLOPT_CAINFO: @@ -1909,14 +1904,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * Set CA path info for SSL connection. Specify directory name of the CA * certificates which have been prepared using openssl c_rehash utility. */ -#ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ result = Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); s->ssl.custom_capath = !!s->str[STRING_SSL_CAPATH]; return result; } -#endif return CURLE_NOT_BUILT_IN; case CURLOPT_CRLFILE: /* @@ -1933,27 +1926,110 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, else return CURLE_NOT_BUILT_IN; case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) /* set preferred list of TLS 1.3 cipher suites */ return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST], ptr); - } else return CURLE_NOT_BUILT_IN; case CURLOPT_RANDOM_FILE: break; case CURLOPT_EGDSOCKET: break; - case CURLOPT_REQUEST_TARGET: - return Curl_setstropt(&s->str[STRING_TARGET], ptr); -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC_FILE: + case CURLOPT_SSL_CTX_DATA: /* - * Use this file instead of the $HOME/.netrc file + * Set an SSL_CTX callback parameter pointer */ - return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { + s->ssl.fsslctxp = ptr; + break; + } + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSLCERT: + /* + * String that holds filename of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT], ptr); + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); + case CURLOPT_SSLKEY: + /* + * String that holds filename of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY], ptr); + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ + if(ptr && ptr[0]) { + result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); + if(!result) { + result = Curl_ssl_set_engine(data, ptr); + } + } + break; + case CURLOPT_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_EC_CURVES: + /* + * Set accepted curves in SSL connection setup. + * Specify colon-delimited list of curve algorithm names. + */ + if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) + return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_SIGNATURE_ALGORITHMS: + /* + * Set accepted signature algorithms. + * Specify colon-delimited list of signature scheme names. + */ + if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) + return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_ECH: + return setopt_ech(data, ptr); + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} #endif #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) +static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, + CURLoption option, char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { case CURLOPT_COPYPOSTFIELDS: return setopt_copypostfields(ptr, s); @@ -1966,7 +2042,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; -#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ #ifndef CURL_DISABLE_HTTP case CURLOPT_TRAILERDATA: @@ -2056,115 +2131,74 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #endif /* !CURL_DISABLE_COOKIES */ #endif /* !CURL_DISABLE_HTTP */ + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); - - /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we - were using the already set type and this changes the actual request - keyword */ - case CURLOPT_SERVICE_NAME: - /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO - */ - return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); - - case CURLOPT_HEADERDATA: - /* - * Custom pointer to pass the header write callback function - */ - s->writeheader = ptr; - break; - case CURLOPT_READDATA: - /* - * FILE pointer to read the file to be uploaded from. Or possibly used as - * argument to the read callback. - */ - s->in_set = ptr; - break; - case CURLOPT_WRITEDATA: - /* - * FILE pointer to write to. Or possibly used as argument to the write - * callback. - */ - s->out = ptr; - break; - case CURLOPT_DEBUGDATA: - /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. - */ - s->debugdata = ptr; - break; - case CURLOPT_PROGRESSDATA: - /* - * Custom client data to pass to the progress callback - */ - s->progress_client = ptr; - break; - case CURLOPT_SEEKDATA: - /* - * Seek control callback. Might be NULL. - */ - s->seek_client = ptr; - break; - case CURLOPT_IOCTLDATA: +#ifdef USE_SSH +static CURLcode setopt_cptr_ssh(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_SSH_PUBLIC_KEYFILE: /* - * I/O control data pointer. Might be NULL. + * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - s->ioctl_client = ptr; - break; - case CURLOPT_SSL_CTX_DATA: + return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); + case CURLOPT_SSH_PRIVATE_KEYFILE: /* - * Set an SSL_CTX callback parameter pointer + * Use this file instead of the $HOME/.ssh/id_dsa file */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { - s->ssl.fsslctxp = ptr; - break; - } - else -#endif - return CURLE_NOT_BUILT_IN; - case CURLOPT_SOCKOPTDATA: + return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); + case CURLOPT_SSH_KEYDATA: /* - * socket callback data pointer. Might be NULL. + * Custom client data to pass to the SSH keyfunc callback */ - s->sockopt_client = ptr; + s->ssh_keyfunc_userp = ptr; break; - case CURLOPT_OPENSOCKETDATA: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* - * socket callback data pointer. Might be NULL. + * Option to allow for the MD5 of the host public key to be checked + * for validation purposes. */ - s->opensocket_client = ptr; - break; - case CURLOPT_RESOLVER_START_DATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* - * resolver start callback data pointer. Might be NULL. + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. */ - s->resolver_start_client = ptr; - break; - case CURLOPT_CLOSESOCKETDATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); + case CURLOPT_SSH_KNOWNHOSTS: /* - * socket callback data pointer. Might be NULL. + * Store the filename to read known hosts from. */ - s->closesocket_client = ptr; - break; - case CURLOPT_PREREQDATA: - s->prereq_userp = ptr; - break; - case CURLOPT_ERRORBUFFER: + return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); +#ifdef USE_LIBSSH2 + case CURLOPT_SSH_HOSTKEYDATA: /* - * Error buffer provided by the caller to get the human readable error - * string in. + * Custom client data to pass to the SSH keyfunc callback */ - s->errorbuffer = ptr; + s->ssh_hostkeyfunc_userp = ptr; break; +#endif /* USE_LIBSSH2 */ + default: + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +} +#endif /* USE_SSH */ #ifndef CURL_DISABLE_FTP +static CURLcode setopt_cptr_ftp(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { case CURLOPT_FTPPORT: /* * Use FTP PORT, this also specifies which IP address to use @@ -2187,180 +2221,148 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_FNMATCH_DATA: s->fnmatch_data = ptr; break; -#endif - case CURLOPT_URL: - /* - * The URL to fetch. - */ - result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); - Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); - break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_FTP */ - case CURLOPT_USERPWD: +static CURLcode setopt_cptr_net(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_INTERFACE: /* - * user:password to use in the operation + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. */ - return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], - &s->str[STRING_PASSWORD]); + return setstropt_interface(ptr, + &s->str[STRING_DEVICE], + &s->str[STRING_INTERFACE], + &s->str[STRING_BINDHOST]); +#ifdef USE_RESOLV_ARES + case CURLOPT_DNS_SERVERS: + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - case CURLOPT_USERNAME: - /* - * authentication username to use in the operation - */ - return Curl_setstropt(&s->str[STRING_USERNAME], ptr); + case CURLOPT_DNS_INTERFACE: + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); + + case CURLOPT_DNS_LOCAL_IP4: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); + + case CURLOPT_DNS_LOCAL_IP6: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); +#endif +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + s->abstract_unix_socket = FALSE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); + + case CURLOPT_ABSTRACT_UNIX_SOCKET: + s->abstract_unix_socket = TRUE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); +#endif +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_URL: + { + CURLcode result = Curl_setstropt(&s->str[STRING_DOH], ptr); + s->doh = !!(s->str[STRING_DOH]); + return result; + } +#endif + default: + return CURLE_UNKNOWN_OPTION; + } +} + +static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_REQUEST_TARGET: + return Curl_setstropt(&s->str[STRING_TARGET], ptr); +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC_FILE: + return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); +#endif + case CURLOPT_CUSTOMREQUEST: + return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); + + /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we + were using the already set type and this changes the actual request + keyword */ + case CURLOPT_SERVICE_NAME: + return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); + + case CURLOPT_HEADERDATA: + s->writeheader = ptr; + break; + case CURLOPT_READDATA: + s->in_set = ptr; + break; + case CURLOPT_WRITEDATA: + s->out = ptr; + break; + case CURLOPT_DEBUGDATA: + s->debugdata = ptr; + break; + case CURLOPT_PROGRESSDATA: + s->progress_client = ptr; + break; + case CURLOPT_SEEKDATA: + s->seek_client = ptr; + break; + case CURLOPT_IOCTLDATA: + s->ioctl_client = ptr; + break; + case CURLOPT_SOCKOPTDATA: + s->sockopt_client = ptr; + break; + case CURLOPT_OPENSOCKETDATA: + s->opensocket_client = ptr; + break; + case CURLOPT_RESOLVER_START_DATA: + s->resolver_start_client = ptr; + break; + case CURLOPT_CLOSESOCKETDATA: + s->closesocket_client = ptr; + break; + case CURLOPT_PREREQDATA: + s->prereq_userp = ptr; + break; + case CURLOPT_ERRORBUFFER: + s->errorbuffer = ptr; + break; + case CURLOPT_URL: + result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); + Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); + break; + + case CURLOPT_USERPWD: + return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], + &s->str[STRING_PASSWORD]); + + case CURLOPT_USERNAME: + return Curl_setstropt(&s->str[STRING_USERNAME], ptr); case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ return Curl_setstropt(&s->str[STRING_BEARER], ptr); case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - case CURLOPT_SSLCERT: - /* - * String that holds filename of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT], ptr); - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); - case CURLOPT_SSLKEY: - /* - * String that holds filename of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY], ptr); - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - if(ptr && ptr[0]) { - result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); - if(!result) { - result = Curl_ssl_set_engine(data, ptr); - } - } - break; - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - return setstropt_interface(ptr, - &s->str[STRING_DEVICE], - &s->str[STRING_INTERFACE], - &s->str[STRING_BINDHOST]); - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); - return CURLE_NOT_BUILT_IN; case CURLOPT_PRIVATE: - /* - * Set private data pointer. - */ s->private_data = ptr; break; -#ifdef USE_SSL - case CURLOPT_SSL_EC_CURVES: - /* - * Set accepted curves in SSL connection setup. - * Specify colon-delimited list of curve algorithm names. - */ - if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) - return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); - return CURLE_NOT_BUILT_IN; - case CURLOPT_SSL_SIGNATURE_ALGORITHMS: - /* - * Set accepted signature algorithms. - * Specify colon-delimited list of signature scheme names. - */ - if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) - return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); - return CURLE_NOT_BUILT_IN; - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); - return CURLE_NOT_BUILT_IN; -#endif -#ifdef USE_SSH - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa.pub file - */ - return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); - case CURLOPT_SSH_PRIVATE_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa file - */ - return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_keyfunc_userp = ptr; - break; -#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - /* - * Option to allow for the MD5 of the host public key to be checked - * for validation purposes. - */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); - case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: - /* - * Option to allow for the SHA256 of the host public key to be checked - * for validation purposes. - */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the filename to read known hosts from. - */ - return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); -#endif -#ifdef USE_LIBSSH2 - case CURLOPT_SSH_HOSTKEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_hostkeyfunc_userp = ptr; - break; -#endif /* USE_LIBSSH2 */ -#endif /* USE_SSH */ case CURLOPT_PROTOCOLS_STR: if(ptr) { curl_prot_t protos; @@ -2399,21 +2401,10 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * resuming a previously established RTSP session - */ return Curl_setstropt(&s->str[STRING_RTSP_SESSION_ID], ptr); case CURLOPT_RTSP_STREAM_URI: - /* - * Set the Stream URI for the RTSP request. Unless the request is - * for generic server options, the application will need to set this. - */ return Curl_setstropt(&s->str[STRING_RTSP_STREAM_URI], ptr); case CURLOPT_RTSP_TRANSPORT: - /* - * The content of the Transport: header for the RTSP request - */ return Curl_setstropt(&s->str[STRING_RTSP_TRANSPORT], ptr); case CURLOPT_INTERLEAVEDATA: s->rtp_out = ptr; @@ -2439,37 +2430,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, break; #endif #endif -#ifdef USE_RESOLV_ARES - case CURLOPT_DNS_SERVERS: - return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - - case CURLOPT_DNS_INTERFACE: - return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - - case CURLOPT_DNS_LOCAL_IP4: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - - case CURLOPT_DNS_LOCAL_IP6: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - -#endif -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - s->abstract_unix_socket = FALSE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - - case CURLOPT_ABSTRACT_UNIX_SOCKET: - s->abstract_unix_socket = TRUE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - -#endif - -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_URL: - result = Curl_setstropt(&s->str[STRING_DOH], ptr); - s->doh = !!(s->str[STRING_DOH]); - break; -#endif #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTSREADDATA: s->hsts_read_userp = ptr; @@ -2532,6 +2492,40 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return result; } +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + typedef CURLcode (*ptrfunc)(struct Curl_easy *data, CURLoption option, + char *ptr); + static const ptrfunc setopt_call[] = { +#ifndef CURL_DISABLE_PROXY + setopt_cptr_proxy, +#endif +#ifdef USE_SSL + setopt_cptr_ssl, +#endif +#ifdef USE_SSH + setopt_cptr_ssh, +#endif +#ifndef CURL_DISABLE_FTP + setopt_cptr_ftp, +#endif +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) + setopt_cptr_http_mqtt, +#endif + setopt_cptr_net, + setopt_cptr_misc, + }; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(setopt_call); i++) { + CURLcode result = setopt_call[i](data, option, ptr); + if(result != CURLE_UNKNOWN_OPTION) + return result; + } + return CURLE_UNKNOWN_OPTION; +} + static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, va_list param) { diff --git a/scripts/top-length b/scripts/top-length index 221c48d0b1ca..7bed39b8db24 100755 --- a/scripts/top-length +++ b/scripts/top-length @@ -77,7 +77,6 @@ close($pmc); # these functions can be this long, but not longer my %whitelist = ( - 'setopt_cptr' => 674, ); # function length above this level is treated as an error and contributes to From 372401f9695d2a6cb831e5110f046138db72d1c4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 20 Jun 2026 09:29:21 +0200 Subject: [PATCH 0511/1062] INSTALL-CMAKE.md: document CMake environment variables Closes #22114 --- docs/INSTALL-CMAKE.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 1d8cd15a2936..64975ab63073 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -313,6 +313,17 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_BUILDINFO`: Print `buildinfo.txt` if set. - `CURL_CI`: Assume running under CI if set. +## Environment (via CMake) + +- `CC`: Set C compiler. Alternative to `CMAKE_C_COMPILER` option. +- `CFLAGS`: Pass custom C compiler flags. Alternative to `CMAKE_C_FLAGS` option. +- `CMAKE_GENERATOR`: Alternative to `-G` command-line option. +- `DESTDIR`: Set install destination directory. +- `LDFLAGS`: Pass custom linker flags. + +Details via CMake +[envvars](https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html). + ## CMake options - `CMAKE_BUILD_TYPE`: (see CMake) From 0d6e5944bc098a64380e9f22d1c41274335f869f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 21 Jun 2026 23:18:13 +0200 Subject: [PATCH 0512/1062] setopt: make CURLOPT_KEYPASSWD work for SSH-only builds This option is used for both TLS and SSH so it needs to be handled even in TLS-disabled builds Mention this in the man page as well. Follow-up to 52fa8d9 Pointed out by Codex Security Closes #22121 --- docs/libcurl/opts/CURLOPT_KEYPASSWD.md | 2 ++ lib/setopt.c | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md index d97a94716436..38f5120cae8c 100644 --- a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md +++ b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md @@ -9,6 +9,8 @@ See-also: - CURLOPT_SSLKEY (3) Protocol: - TLS + - SFTP + - SCP TLS-backend: - OpenSSL - mbedTLS diff --git a/lib/setopt.c b/lib/setopt.c index d07a794135fc..0c0d47a68025 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1884,7 +1884,8 @@ static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) #define setopt_ech(x,y) CURLE_NOT_BUILT_IN #endif -#ifdef USE_SSL +#if defined(USE_SSL) || defined(USE_SSH) +/* One of the options is used for both TLS and SSH */ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, char *ptr) { @@ -1892,6 +1893,13 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + result = Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); + break; +#ifdef USE_SSL case CURLOPT_CAINFO: /* * Set CA info for SSL connection. Specify filename of the CA certificate @@ -1965,11 +1973,6 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * String that holds file type of the SSL key to use */ return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); case CURLOPT_SSLENGINE: /* * String that holds the SSL crypto engine. @@ -2015,6 +2018,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, return CURLE_NOT_BUILT_IN; case CURLOPT_ECH: return setopt_ech(data, ptr); +#endif default: return CURLE_UNKNOWN_OPTION; } @@ -2501,7 +2505,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #ifndef CURL_DISABLE_PROXY setopt_cptr_proxy, #endif -#ifdef USE_SSL +#if defined(USE_SSL) || defined(USE_SSH) setopt_cptr_ssl, #endif #ifdef USE_SSH From cb7efe1c3574d9de639eb987da72463be4514841 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 22 Jun 2026 00:27:22 +0200 Subject: [PATCH 0513/1062] cd2nroff: handle TLS together with other protocols To render CURLOPT_KEYPASSWD properly Closes #22123 --- scripts/cd2nroff | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 99c2e8a576bf..27717cfbe31b 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -100,27 +100,33 @@ sub outprotocols { my (@p) = @_; my $comma = 0; my @o; + my $tls = 0; push @o, ".SH PROTOCOLS\n"; - if($p[0] eq "TLS") { - push @o, "This functionality affects all TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc."; - } - else { - my @s = sort @p; - push @o, "This functionality affects "; - for my $e (sort @s) { + my @s = sort @p; + push @o, "This functionality affects "; + for my $e (sort @s) { + if($e eq "TLS") { + $tls = 1; + } + else { push @o, sprintf "%s%s", $comma ? (($e eq $s[-1]) ? " and " : ", "): "", lc($e); $comma = 1; } - if($#s == 0) { - if($s[0] eq "All") { - push @o, " supported protocols"; - } - else { - push @o, " only"; - } + } + if($tls) { + push @o, sprintf + "%sall TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.", + $comma ? " and ": " "; + } + if($#s == 0) { + if($s[0] eq "All") { + push @o, " supported protocols"; + } + else { + push @o, " only"; } } push @o, "\n"; From 810d9535e17b9e1468013493ddd65bd37d6a967a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Fri, 19 Jun 2026 16:16:55 +0200 Subject: [PATCH 0514/1062] proxy: fix automatic tunnel mode with "connect to host" Fix a regression in curl 8.21.0-rc3: Check whether the host and the "connect to host" are equal before initializing the proxy. If they are equal, switching to tunnel mode is not necessary. Follow-up to 73daec6 Closes #22110 --- lib/url.c | 19 ++++++++++--------- tests/data/test2050 | 11 +++++++++-- tests/data/test2055 | 11 +++++++++-- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/url.c b/lib/url.c index 3ace5ca23707..505e08a7e10a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2100,7 +2100,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); /************************************************************* - * Determine `conn->origin` and propulate `data->state.up` and + * Determine `conn->origin` and populate `data->state.up` and * other URL related properties. *************************************************************/ result = url_set_conn_origin_etc(data, needle); @@ -2135,6 +2135,15 @@ static CURLcode url_create_needle(struct Curl_easy *data, goto out; } + /************************************************************* + * Check whether the host and the "connect to host" are equal. + * Do this after the hostnames have been IDN-converted and + * before initializing the proxy. + *************************************************************/ + if(Curl_peer_equal(needle->origin, needle->via_peer)) { + Curl_peer_unlink(&needle->via_peer); + } + #ifndef CURL_DISABLE_PROXY /* Going via a unix socket ignores any proxy settings */ if(network_scheme && @@ -2149,14 +2158,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, if(result) goto out; - /************************************************************* - * Check whether the host and the "connect to host" are equal. - * Do this after the hostnames have been IDN-converted. - *************************************************************/ - if(Curl_peer_equal(needle->origin, needle->via_peer)) { - Curl_peer_unlink(&needle->via_peer); - } - /************************************************************* * Setup internals depending on protocol. Needs to be done after * we figured out what/if proxy to use. diff --git a/tests/data/test2050 b/tests/data/test2050 index ef20c69b828e..3c2a9b40f5f7 100644 --- a/tests/data/test2050 +++ b/tests/data/test2050 @@ -50,7 +50,8 @@ http-proxy -http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT +http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --next +http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER --connect-to ::www.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT proxy @@ -59,12 +60,18 @@ proxy # Verify data after the test has been "shot" - + CONNECT connect.example.com.%TESTNUMBER:%HTTPPORT HTTP/1.1 Host: connect.example.com.%TESTNUMBER:%HTTPPORT User-Agent: curl/%VERSION Proxy-Connection: Keep-Alive +GET http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER HTTP/1.1 +Host: www.example.com.%TESTNUMBER:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + GET /%TESTNUMBER HTTP/1.1 diff --git a/tests/data/test2055 b/tests/data/test2055 index 608facb86e45..6a7a8d34ac61 100644 --- a/tests/data/test2055 +++ b/tests/data/test2055 @@ -54,18 +54,25 @@ socks5 proxy -http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT +http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT --next +http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER --connect-to ::www.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT # Verify data after the test has been "shot" - + CONNECT connect.example.com.%TESTNUMBER:%HTTPPORT HTTP/1.1 Host: connect.example.com.%TESTNUMBER:%HTTPPORT User-Agent: curl/%VERSION Proxy-Connection: Keep-Alive +GET http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER HTTP/1.1 +Host: www.example.com.%TESTNUMBER:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + GET /%TESTNUMBER HTTP/1.1 From 8ed285f06deedf02fc407f6eed1892301e43004a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 19 Jun 2026 17:01:00 +0200 Subject: [PATCH 0515/1062] websockets: buffer ugprade data at connection level When the HTTP Upgrade to websockets already carries ws frame data, buffer that data at connection level and not in the ws decoder. Adding new cfilter `cf_recvbuf` to buffer a fixed amont of data to be received later. When the data is received, the filter passes further recv call through to its subfilter. Fixes #22107 Reported-by: sideshowbarker on github Closes #22111 --- lib/Makefile.inc | 2 + lib/cf-recvbuf.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ lib/cf-recvbuf.h | 40 ++++++++++++ lib/cfilters.c | 21 +++++++ lib/cfilters.h | 2 + lib/curl_trc.c | 4 ++ lib/ws.c | 19 +++--- 7 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 lib/cf-recvbuf.c create mode 100644 lib/cf-recvbuf.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 061a317b8d65..266ba52af377 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -165,6 +165,7 @@ LIB_CFILES = \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ + cf-recvbuf.c \ cf-setup.c \ cf-socket.c \ cfilters.c \ @@ -298,6 +299,7 @@ LIB_HFILES = \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ + cf-recvbuf.h \ cf-setup.h \ cf-socket.h \ cfilters.h \ diff --git a/lib/cf-recvbuf.c b/lib/cf-recvbuf.c new file mode 100644 index 000000000000..8ccc148bd3d9 --- /dev/null +++ b/lib/cf-recvbuf.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ + +#include "urldata.h" +#include "bufq.h" +#include "cfilters.h" +#include "cf-recvbuf.h" +#include "curl_trc.h" + +#define CURL_CF_RECVBUF_CHUNK (16 * 1024) + +struct cf_recvbuf_ctx { + struct bufq recvbuf; +}; + +static void cf_recvbuf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } +} + +static CURLcode cf_recvbuf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) { + return Curl_bufq_cread(&ctx->recvbuf, buf, len, pnread); + } + + if(cf->next) + return cf->next->cft->do_recv(cf->next, data, buf, len, pnread); + *pnread = 0; + return CURLE_RECV_ERROR; +} + +static bool cf_recvbuf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +struct Curl_cftype Curl_cft_recvbuf = { + "RECVBUF", + 0, + CURL_LOG_LVL_NONE, + cf_recvbuf_destroy, + Curl_cf_def_connect, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + cf_recvbuf_data_pending, + Curl_cf_def_send, + cf_recvbuf_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf = NULL; + struct cf_recvbuf_ctx *ctx; + CURLcode result = CURLE_OK; + size_t nwritten = 0; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_bufq_init2(&ctx->recvbuf, CURL_CF_RECVBUF_CHUNK, + (blen / CURL_CF_RECVBUF_CHUNK) + 1, + (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES)); + result = Curl_bufq_write(&ctx->recvbuf, buf, blen, &nwritten); + if(result) + goto out; + if(nwritten != blen) { + result = CURLE_FAILED_INIT; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_recvbuf, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result ? NULL : cf; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_recvbuf_create(&cf, data, buf, blen); + if(result) + goto out; + + cf->connected = Curl_conn_is_connected(conn, sockindex); + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +#endif /* !CURL_DISABLE_WEBSOCKETS */ diff --git a/lib/cf-recvbuf.h b/lib/cf-recvbuf.h new file mode 100644 index 000000000000..1b08fcc3be66 --- /dev/null +++ b/lib/cf-recvbuf.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_CF_RECVBUF_H +#define HEADER_CURL_CF_RECVBUF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ + +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen); + +extern struct Curl_cftype Curl_cft_recvbuf; + +#endif /* !CURL_DISABLE_WEBSOCKETS */ + +#endif /* HEADER_CURL_CF_RECVBUF_H */ diff --git a/lib/cfilters.c b/lib/cfilters.c index b6278eff12c9..fb1914d133e8 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -33,6 +33,27 @@ #include "select.h" #include "curlx/strparse.h" +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; +} + CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { diff --git a/lib/cfilters.h b/lib/cfilters.h index a9e00d489904..b1aad029e070 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -265,6 +265,8 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, int query, int *pres1, void *pres2); CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); /** * Create a new filter instance, unattached to the filter chain. diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 127bc8c5ccd7..f8287e420fd5 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -29,6 +29,7 @@ #include "multiif.h" #include "cf-dns.h" +#include "cf-recvbuf.h" #include "cf-socket.h" #include "cf-setup.h" #include "http2.h" @@ -563,6 +564,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_unix, TRC_CT_NETWORK }, { &Curl_cft_tcp_accept, TRC_CT_NETWORK }, { &Curl_cft_ip_happy, TRC_CT_NETWORK }, +#ifndef CURL_DISABLE_WEBSOCKETS + { &Curl_cft_recvbuf, TRC_CT_PROTOCOL }, +#endif { &Curl_cft_setup, TRC_CT_PROTOCOL }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) { &Curl_cft_nghttp2, TRC_CT_PROTOCOL }, diff --git a/lib/ws.c b/lib/ws.c index 3f3a07466d42..9820c3e4bd26 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -32,6 +32,7 @@ #include "curlx/dynbuf.h" #include "rand.h" #include "curlx/base64.h" +#include "cf-recvbuf.h" #include "connect.h" #include "sendf.h" #include "curl_trc.h" @@ -1392,15 +1393,17 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, k->header = FALSE; /* we will not get more response headers */ if(data->set.connect_only) { - size_t nwritten; /* In CONNECT_ONLY setup, the payloads from `mem` need to be received - * when using `curl_ws_recv` later on after this transfer is already - * marked as DONE. */ - result = Curl_bufq_write(&ws->recvbuf, (const uint8_t *)mem, - nread, &nwritten); - if(result) - goto out; - DEBUGASSERT(nread == nwritten); + * when using `curl_ws_recv/curl_easy_recv` later on, after this transfer + * is already marked as DONE. + * Since `curl_easy_recv()` is also supposed to work, we need + * to buffer the data at connection level. See #22107 */ + if(nread) { + result = Curl_cf_recvbuf_add(data, data->conn, FIRSTSOCKET, + (const uint8_t *)mem, nread); + if(result) + goto out; + } CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ From e8e3af2abb56bdafba55fbbb3fa15a449b91893c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 21 Jun 2026 23:32:09 +0200 Subject: [PATCH 0516/1062] doh: cap the maximum TTL to 24 hours To avoid mistakes or abuse to cause problems. Many public DNS providers cap their cache times to this. Verify in test 1650 Reported-by: netspacer.research Closes #22122 --- lib/doh.c | 6 +++++- tests/unit/unit1650.c | 17 +++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/doh.c b/lib/doh.c index af3d3ad97265..5616dc948acc 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -725,7 +725,9 @@ UNITTEST void de_init(struct dohentry *de) curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } -/* @unittest 1655 */ +/* TTL value cap */ +#define MAX_DNS_TTL 86400U /* 24 hours */ +/* @unittest 1650 */ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -795,6 +797,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, return DOH_DNS_OUT_OF_RANGE; ttl = doh_get32bit(doh, index); + if(ttl > MAX_DNS_TTL) + ttl = MAX_DNS_TTL; if(ttl < d->ttl) d->ttl = ttl; index += 4; diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index 4c58d8330c37..cdc3375d3cd8 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -102,16 +102,16 @@ static CURLcode test_unit1650(const char *arg) "\x6c\x04\x63\x75\x72\x6c\x00\x00\x05\x00\x01\xc0\x0c\x00\x05\x00" "\x01\x00\x00\x00\x37\x00\x11\x08\x61\x6e\x79\x77\x68\x65\x72\x65" "\x06\x72\x65\x61\x6c\x6c\x79\x00", 56, - CURL_DNS_TYPE_A, DOH_OK, "anywhere.really "}, + CURL_DNS_TYPE_A, DOH_OK, "anywhere.really (55)"}, - {DNS_FOO_EXAMPLE_COM, 49, CURL_DNS_TYPE_A, DOH_OK, "127.0.0.1 "}, + {DNS_FOO_EXAMPLE_COM, 49, CURL_DNS_TYPE_A, DOH_OK, "127.0.0.1 (55)"}, {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x00\x00\x00\x04\x61\x61\x61" "\x61\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c" - "\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x00\x37\x00\x10\x20\x20" + "\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x01\x37\x00\x10\x20\x20" "\x20\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x20", 62, CURL_DNS_TYPE_AAAA, DOH_OK, - "2020:2020:0000:0000:0000:0000:0000:2020 " }, + "2020:2020:0000:0000:0000:0000:0000:2020 (311)" }, {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x00\x00\x00\x04\x63\x75\x72" "\x6c\x04\x63\x75\x72\x6c\x00\x00\x05\x00\x01\xc0\x0c\x00\x05\x00" @@ -129,19 +129,19 @@ static CURLcode test_unit1650(const char *arg) 62 + 30, CURL_DNS_TYPE_AAAA, DOH_OK, - "2020:2020:0000:0000:0000:0000:0000:2020 " }, + "2020:2020:0000:0000:0000:0000:0000:2020 (55)" }, - /* packet with ARCOUNT == 1 */ + /* packet with ARCOUNT == 1, and a capped TTL */ {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x00\x00\x01\x04\x61\x61\x61" "\x61\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c" - "\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x00\x37\x00\x10\x20\x20" + "\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\xff\xff\x37\x00\x10\x20\x20" "\x20\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x20" LABEL_TEST LABEL_HOST LABEL_NAME DNSAAAA_EPILOGUE "\x00\x00\x00\x01" "\00\x04\x01\x01\x01\x01", /* RDDATA */ 62 + 30, CURL_DNS_TYPE_AAAA, DOH_OK, - "2020:2020:0000:0000:0000:0000:0000:2020 " }, + "2020:2020:0000:0000:0000:0000:0000:2020 (86400)" }, }; @@ -222,6 +222,7 @@ static CURLcode test_unit1650(const char *arg) len -= o; ptr += o; } + curl_msnprintf(ptr, len, "(%u)", d.ttl); de_cleanup(&d); if(resp[i].out && strcmp((const char *)buffer, resp[i].out)) { curl_mfprintf(stderr, "resp %zu: Expected %s got %s\n", i, From ab779d4e4a30506acc9221ce4fdbd7c4a250d2ad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 22 Jun 2026 09:10:15 +0200 Subject: [PATCH 0517/1062] doh: stricter HTTPS RNAME parsing If any sublabel is longer than 63 octets, abort. This then also catches compression attempts. Verified in test 1658 Reported-by: netspacer.research Closes #22124 --- lib/doh.c | 11 ++++++++++- lib/httpsrr.h | 2 +- tests/unit/unit1658.c | 21 ++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/doh.c b/lib/doh.c index 5616dc948acc..331bce46b678 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -1092,10 +1092,14 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, DEBUGASSERT(buf && remaining && dnsname); if(!buf || !remaining || !dnsname || !*remaining) return CURLE_OUT_OF_MEMORY; - curlx_dyn_init(&thename, CURL_MAXLEN_host_name); + curlx_dyn_init(&thename, CURL_MAXLEN_HOST_NAME); rem = *remaining; cp = *buf; clen = *cp++; + /* RFC 9460 says it must be uncompressed */ + if(clen > 63) + return CURLE_WEIRD_SERVER_REPLY; + if(clen == 0) { /* special case - return "." as name */ if(curlx_dyn_addn(&thename, ".", 1)) @@ -1117,6 +1121,11 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, return CURLE_OUT_OF_MEMORY; } clen = *cp++; + if(clen > 63) { + /* invalid format */ + curlx_dyn_free(&thename); + return CURLE_WEIRD_SERVER_REPLY; + } } *buf = cp; *remaining = rem - 1; diff --git a/lib/httpsrr.h b/lib/httpsrr.h index 28a790d17f07..2ee1beab3e76 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -31,7 +31,7 @@ #ifdef USE_HTTPSRR -#define CURL_MAXLEN_host_name 253 +#define CURL_MAXLEN_HOST_NAME 253 #define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; diff --git a/tests/unit/unit1658.c b/tests/unit/unit1658.c index 1c59c09c451a..1491e9338f96 100644 --- a/tests/unit/unit1658.c +++ b/tests/unit/unit1658.c @@ -493,7 +493,26 @@ static CURLcode test_unit1658(const char *arg) "ech:fe80dabbc1ff7eb38a22123456789123|" "ipv6:fe80:dabb:c1ff:fea3:8a22:1234:5678:9123|" "ipv6:ee80:dabb:c1ff:fea3:8a22:1234:5678:9125|" - } + }, + { + "rname too long label", + (const unsigned char *)"\x00\x00" /* 16-bit prio */ + "\x40" + "0123456789012345678901234567890123456789012345678901234567890123" + "\x04some\x00", /* RNAME */ + 73, + "r:27|", + }, + { + "rname long label", + (const unsigned char *)"\x00\x00" /* 16-bit prio */ + "\x3f" + "012345678901234567890123456789012345678901234567890123456789012" + "\x04some\x00", /* RNAME */ + 72, + "r:0|p:0|" + "012345678901234567890123456789012345678901234567890123456789012.some.|", + }, }; CURLcode result = CURLE_OUT_OF_MEMORY; From d638eac18967d8afef230ced3daddc47cebd9737 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 19 Jun 2026 10:59:05 +0200 Subject: [PATCH 0518/1062] libssh2: code and infof/trace cleanups Give the libssh2 infof() messages a common format, add/clarify some to make the connect/authentication flow more clear. Closes #22101 --- lib/vssh/libssh2.c | 396 +++++++++++++++++++++++---------------------- 1 file changed, 202 insertions(+), 194 deletions(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 7868c385f486..1dd4a9107b84 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -307,166 +307,173 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) static CURLcode ssh_knownhost(struct Curl_easy *data, struct ssh_conn *sshc) { + struct connectdata *conn = data->conn; + struct libssh2_knownhost *host = NULL; + const char *remotekey = NULL; + int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; + int keybit = 0; int sshkeytype = 0; size_t keylen = 0; int rc = 0; CURLcode result = CURLE_OK; - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we are asked to verify the host against a file */ - struct connectdata *conn = data->conn; - struct libssh2_knownhost *host = NULL; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &sshkeytype); - int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; - int keybit = 0; + if(!data->set.str[STRING_SSH_KNOWNHOSTS]) { + infof(data, "SSH: no knownhosts file configured"); + return CURLE_OK; + } - if(remotekey) { - /* - * A subject to figure out is what hostname we need to pass in here. - * What hostname does OpenSSH store in its file if an IDN name is - * used? - */ - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - switch(sshkeytype) { - case LIBSSH2_HOSTKEY_TYPE_RSA: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; - break; + remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &sshkeytype); + if(remotekey) { + /* + * A subject to figure out is what hostname we need to pass in here. + * What hostname does OpenSSH store in its file if an IDN name is + * used? + */ + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + switch(sshkeytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; #ifdef LIBSSH2_HOSTKEY_TYPE_DSS - case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ - keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; - break; + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; #endif - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; - break; - case LIBSSH2_HOSTKEY_TYPE_ED25519: - keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; - break; - default: - infof(data, "unsupported key type, cannot check knownhosts"); - keybit = 0; - break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + break; + default: + infof(data, "SSH: unsupported host key type for knownhosts check"); + keybit = 0; + break; + } + if(!keybit) + /* no check means failure! */ + rc = CURLKHSTAT_REJECT; + else { + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->origin->hostname, + (conn->origin->port != PORT_SSH) ? + conn->origin->port : -1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); + + infof(data, "SSH: host check %d, key: %s", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? + host->key : ""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = convert_ssh2_keytype(sshkeytype); + knownkeyp = &knownkey; } - if(!keybit) - /* no check means failure! */ - rc = CURLKHSTAT_REJECT; - else { - keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->origin->hostname, - (conn->origin->port != PORT_SSH) ? - conn->origin->port : -1, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); - - infof(data, "SSH host check: %d, key: %s", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? - host->key : ""); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = convert_ssh2_keytype(sshkeytype); - knownkeyp = &knownkey; - } - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = convert_ssh2_keytype(sshkeytype); + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = convert_ssh2_keytype(sshkeytype); - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; - /* Ask the callback how to behave */ - Curl_set_in_callback(data, TRUE); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); - } + /* Ask the callback how to behave */ + Curl_set_in_callback(data, TRUE); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, FALSE); } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; + } + else { + /* no remotekey means failure! */ + infof(data, "SSH: host offers no public key"); + rc = CURLKHSTAT_REJECT; + } - switch(rc) { - default: /* unknown return codes is the same as reject */ - case CURLKHSTAT_REJECT: - myssh_to(data, sshc, SSH_SESSION_FREE); - FALLTHROUGH(); - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE_REPLACE: - /* remove old host+key that does not match */ - if(host) - libssh2_knownhost_del(sshc->kh, host); - FALLTHROUGH(); - case CURLKHSTAT_FINE: - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - int addrc; - const char *hostbuf; - char *hostport = NULL; - if(conn->origin->port != PORT_SSH) { - hostbuf = hostport = curl_maprintf("[%s]:%u", conn->origin->hostname, - conn->origin->port); - if(!hostbuf) - infof(data, "WARNING: failed allocating buffer for [host]:port"); - } - else - hostbuf = conn->origin->hostname; - if(hostbuf) { - /* the found host+key did not match but has been told to be fine - anyway so we add it in memory */ - addrc = libssh2_knownhost_addc(sshc->kh, hostbuf, NULL, - remotekey, keylen, NULL, 0, - LIBSSH2_KNOWNHOST_TYPE_PLAIN | - LIBSSH2_KNOWNHOST_KEYENC_RAW | - keybit, NULL); - if(addrc) - infof(data, "WARNING: adding the known host %s failed", hostbuf); - else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || - rc == CURLKHSTAT_FINE_REPLACE) { - /* now we write the entire in-memory list of known hosts to the - known_hosts file */ - int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(wrc) { - infof(data, "WARNING: writing %s failed", - data->set.str[STRING_SSH_KNOWNHOSTS]); - } + switch(rc) { + default: /* unknown return codes is the same as reject */ + case CURLKHSTAT_REJECT: + infof(data, "SSH: knownhost check failed"); + myssh_to(data, sshc, SSH_SESSION_FREE); + FALLTHROUGH(); + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE_REPLACE: + /* remove old host+key that does not match */ + if(host) + libssh2_knownhost_del(sshc->kh, host); + FALLTHROUGH(); + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + int addrc; + const char *hostbuf; + char *hostport = NULL; + if(conn->origin->port != PORT_SSH) { + hostbuf = hostport = curl_maprintf("[%s]:%u", conn->origin->hostname, + conn->origin->port); + if(!hostbuf) + infof(data, "WARNING: failed allocating buffer for [host]:port"); + } + else + hostbuf = conn->origin->hostname; + if(hostbuf) { + /* the found host+key did not match but has been told to be fine + anyway so we add it in memory */ + addrc = libssh2_knownhost_addc(sshc->kh, hostbuf, NULL, + remotekey, keylen, NULL, 0, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, NULL); + if(addrc) + infof(data, "WARNING: adding the known host %s failed", hostbuf); + else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || + rc == CURLKHSTAT_FINE_REPLACE) { + /* now we write the entire in-memory list of known hosts to the + known_hosts file */ + int wrc = + libssh2_knownhost_writefile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(wrc) { + infof(data, "WARNING: writing %s failed", + data->set.str[STRING_SSH_KNOWNHOSTS]); } } - curlx_free(hostport); } - break; + curlx_free(hostport); } + else + infof(data, "SSH: knownhost entry matches host key"); + break; } return result; } @@ -477,11 +484,6 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; - infof(data, "SSH MD5 public key: %s", - pubkey_md5 ? pubkey_md5 : "NULL"); - infof(data, "SSH SHA256 public key: %s", - pubkey_sha256 ? pubkey_sha256 : "NULL"); - if(pubkey_sha256) { const char *fingerprint = NULL; char *fingerprint_b64 = NULL; @@ -489,6 +491,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, size_t pub_pos = 0; size_t b64_pos = 0; + infof(data, "SSH: SHA256 public key '%s'", pubkey_sha256); /* The fingerprint points to static storage (!), do not free() it. */ fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_SHA256); @@ -514,7 +517,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + infof(data, "SSH: SHA256 fingerprint '%s'", fingerprint_b64); /* Find the position of any = padding characters in the public key */ while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { @@ -542,13 +545,14 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, curlx_free(fingerprint_b64); - infof(data, "SHA256 checksum match"); + infof(data, "SSH: SHA256 checksum match"); } if(pubkey_md5) { char md5buffer[33]; const char *fingerprint; + infof(data, "SSH: MD5 public key '%s'", pubkey_md5); fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_MD5); @@ -560,7 +564,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, (unsigned char)fingerprint[i]); } - infof(data, "SSH MD5 fingerprint: %s", md5buffer); + infof(data, "SSH: MD5 fingerprint '%s'", md5buffer); } /* This does NOT verify the length of 'pubkey_md5' separately, which @@ -579,7 +583,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "MD5 checksum match"); + infof(data, "SSH: MD5 checksum match"); } if(!pubkey_md5 && !pubkey_sha256) { @@ -598,8 +602,10 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); if(rc != CURLKHMATCH_OK) { myssh_to(data, sshc, SSH_SESSION_FREE); + failf(data, "SSH: callback failed host public key verification"); return CURLE_PEER_FAILED_VERIFICATION; } + infof(data, "SSH: verified public key via callback"); } else { myssh_to(data, sshc, SSH_SESSION_FREE); @@ -608,6 +614,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_OK; } else { + CURL_TRC_SSH(data, "no host key checksum given, checking knownhosts"); return ssh_knownhost(data, sshc); } } @@ -654,7 +661,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, const char *p; const char *kh_name_end = strstr(store->name, "]:"); if(!kh_name_end) { - infof(data, "Invalid host pattern %s in %s", + infof(data, "SSH: invalid host pattern %s in %s", store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); continue; } @@ -684,7 +691,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, if(found) { int rc; const char *hostkey_method = NULL; - infof(data, "Found host %s in %s", + infof(data, "SSH: found host '%s' in '%s'", conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { @@ -717,7 +724,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, return CURLE_SSH; } - infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); + infof(data, "SSH: set '%s' as hostkey type", hostkey_method); rc = libssh2_session_method_pref(sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if(rc) { @@ -729,7 +736,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } } else { - infof(data, "Did not find host %s in %s", + infof(data, "SSH: did not find host '%s' in '%s'", conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); } } @@ -1171,8 +1178,8 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, sshc->passphrase = ""; if(sshc->rsa_pub) - infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); - infof(data, "Using SSH private key file '%s'", sshc->rsa); + infof(data, "SSH: trying public key file '%s'", sshc->rsa_pub); + infof(data, "SSH: trying private key file '%s'", sshc->rsa); myssh_to(data, sshc, SSH_AUTH_PKEY); } @@ -1374,7 +1381,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, if(data->req.size == 0) { /* no data to transfer */ Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); + infof(data, "SSH: file already completely downloaded"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1524,7 +1531,7 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, int rc; if(libssh2_userauth_authenticated(sshc->ssh_session)) { sshc->authed = TRUE; - infof(data, "SSH user accepted with no authentication"); + infof(data, "SSH: user accepted with no authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1535,7 +1542,7 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, myssh_to(data, sshc, SSH_SESSION_FREE); return libssh2_session_error_to_CURLE(rc); } - infof(data, "SSH authentication methods available: %s", sshc->authlist); + infof(data, "SSH: host offers authentication via: %s", sshc->authlist); myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); return CURLE_OK; @@ -1562,7 +1569,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized SSH public key authentication"); + infof(data, "SSH: authenticated via publickey"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1576,7 +1583,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, else { (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); } - infof(data, "SSH public key authentication failed: %s", err_msg); + infof(data, "SSH: publickey authentication denied: %s", err_msg); myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return CURLE_OK; @@ -1612,7 +1619,7 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, } if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized password authentication"); + infof(data, "SSH: initialized password authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1641,13 +1648,14 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) && strstr(sshc->authlist, "publickey")) { + infof(data, "SSH: trying publickey authentication via agent"); /* Connect to the ssh-agent */ /* The agent could be shared by a curl thread i believe but nothing obvious as keys can be added/removed at any time */ if(!sshc->ssh_agent) { sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); if(!sshc->ssh_agent) { - infof(data, "Could not create agent object"); + infof(data, "SSH: could not create agent object"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; @@ -1658,7 +1666,7 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure connecting to agent"); + infof(data, "SSH: failure connecting to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1678,7 +1686,7 @@ static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1701,8 +1709,11 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, return CURLE_AGAIN; if(rc == 0) { - struct connectdata *conn = data->conn; - rc = libssh2_agent_userauth(sshc->ssh_agent, Curl_creds_user(conn->creds), + CURL_TRC_SSH(data, "[SSH_AUTH_AGENT_LIST] auth user '%s' for key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); + rc = libssh2_agent_userauth(sshc->ssh_agent, + Curl_creds_user(data->conn->creds), sshc->sshagent_identity); if(rc < 0) { @@ -1716,13 +1727,15 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, } if(rc < 0) - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); else if(rc == 1) - infof(data, "No identity would match"); + infof(data, "SSH: no agent identity would match"); if(rc == LIBSSH2_ERROR_NONE) { sshc->authed = TRUE; - infof(data, "Agent based authentication successful"); + infof(data, "SSH: agent authenticated user '%s' with key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1759,7 +1772,7 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized keyboard interactive authentication"); + infof(data, "SSH: initialized keyboard interactive authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1779,7 +1792,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, /* * At this point we have an authenticated ssh session. */ - infof(data, "Authentication complete"); + infof(data, "SSH: authentication complete"); Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ @@ -1790,7 +1803,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, myssh_to(data, sshc, SSH_SFTP_INIT); return CURLE_OK; } - infof(data, "SSH CONNECT phase done"); + infof(data, "SSH: connection established"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1884,7 +1897,7 @@ static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data, } if(data->set.quote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.quote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -1898,7 +1911,7 @@ static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data, struct ssh_conn *sshc) { if(data->set.postquote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.postquote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -2713,7 +2726,7 @@ static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, sshc->slash_pos = strchr(sshc->slash_pos, '/'); if(sshc->slash_pos) { *sshc->slash_pos = 0; - infof(data, "Creating directory '%s'", sshp->path); + infof(data, "SFTP: creating directory '%s'", sshp->path); myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); return CURLE_OK; } @@ -2838,8 +2851,7 @@ static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, - "Failed to send libssh2 channel EOF: %d %s", + infof(data, "Failed to send libssh2 channel EOF: %d %s", rc, err_msg); } } @@ -2858,8 +2870,7 @@ static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", - rc, err_msg); + infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); @@ -2878,8 +2889,7 @@ static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", - rc, err_msg); + infof(data, "Channel failed to close: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); @@ -2898,9 +2908,7 @@ static CURLcode ssh_state_scp_channel_free( char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, - "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); } sshc->ssh_channel = NULL; } @@ -3466,16 +3474,16 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) break; } if(crypto_str) - infof(data, "libssh2 cryptography backend: %s", crypto_str); + infof(data, "SSH: libssh2 cryptography backend: %s", crypto_str); } #endif if(!sshc) return CURLE_FAILED_INIT; - infof(data, "User: '%s'", Curl_creds_user(conn->creds)); + infof(data, "SSH: user '%s'", Curl_creds_user(conn->creds)); #ifdef CURL_LIBSSH2_DEBUG - infof(data, "Password: %s", Curl_creds_passwd(conn->creds)); + infof(data, "SSH: password %s", Curl_creds_passwd(conn->creds)); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ @@ -3513,7 +3521,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) */ #if LIBSSH2_VERSION_NUM >= 0x010b01 - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); #if defined(__clang__) && __clang_major__ >= 16 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-function-type-strict" @@ -3547,7 +3555,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sshrecv.recvptr = ssh_tls_recv; sshsend.sendptr = ssh_tls_send; - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); libssh2_session_callback_set(sshc->ssh_session, LIBSSH2_CALLBACK_RECV, sshrecv.recvp); libssh2_session_callback_set(sshc->ssh_session, @@ -3572,7 +3580,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(data->set.ssh_compression && libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) { - infof(data, "Failed to enable compression for ssh session"); + infof(data, "SSH: failed to enable compression for session"); } if(data->set.str[STRING_SSH_KNOWNHOSTS]) { @@ -3589,13 +3597,13 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) data->set.str[STRING_SSH_KNOWNHOSTS], LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(rc < 0) - infof(data, "Failed to read known hosts from %s", + infof(data, "SSH: failed to read known hosts from %s", data->set.str[STRING_SSH_KNOWNHOSTS]); } #ifdef CURL_LIBSSH2_DEBUG libssh2_trace(sshc->ssh_session, ~0); - infof(data, "SSH socket: %d", (int)sock); + infof(data, "SSH: socket %d", (int)sock); #endif myssh_to(data, sshc, SSH_INIT); From c5d0e93879d94c8ab8034b35a26cbdfa93a2dac8 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 19 Jun 2026 15:44:33 +0200 Subject: [PATCH 0519/1062] HTTP3.md: update quiche build Fixes #22105 Reported-by: av223119 on github Closes #22109 --- docs/HTTP3.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/HTTP3.md b/docs/HTTP3.md index 77fa9664278c..74254233b21b 100644 --- a/docs/HTTP3.md +++ b/docs/HTTP3.md @@ -215,14 +215,16 @@ but in case of problems, we recommend their latest release tag. ## Build -Build quiche and BoringSSL: +Build quiche and BoringSSL (described here for quiche v0.29.1, the locations +where BoringSSL is to be found vary with version): - % git clone --depth 1 --branch 0.24.7 --recursive https://github.com/cloudflare/quiche + % git clone --depth 1 --branch 0.29.1 --recursive https://github.com/cloudflare/quiche % cd quiche % cargo build --package quiche --release --features ffi,pkg-config-meta,qlog % ln -s libquiche.so target/release/libquiche.so.0 - % mkdir quiche/deps/boringssl/src/lib - % ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ + % mkdir -p boringssl/lib + % find target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' boringssl/lib \; + % find target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../{}' boringssl \; Build curl: @@ -230,8 +232,8 @@ Build curl: % git clone --depth 1 https://github.com/curl/curl % cd curl % autoreconf -fi - % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" \ - --with-openssl=$PWD/../quiche/quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release + % ./configure --with-openssl=$PWD/../quiche/boringssl \ + --with-quiche=$PWD/../quiche/target/release % make % make install From c35e2cb29f3a20fa6fb330027c77216e0bc6297f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 22 Jun 2026 10:43:53 +0200 Subject: [PATCH 0520/1062] setopt: cleanup the length check for COPYPOSTFIELDS handling Make it more straight-forward Closes #22127 --- lib/setopt.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 0c0d47a68025..df054469d4a4 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1814,14 +1814,12 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) { CURLcode result = CURLE_OK; + if(s->postfieldsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; if(!ptr || s->postfieldsize == -1) result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); else { - size_t pflen; - - if(s->postfieldsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); + size_t pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); if(pflen == SIZE_MAX) return CURLE_OUT_OF_MEMORY; else { From 2735ef3baeaf8ee7a6019e224d83a2501ea1d437 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 10:26:36 +0200 Subject: [PATCH 0521/1062] libssh2: fix to return error code on missing parameter Reported by GitHub Code Quality Follow-up to 0095f98464d85a3b2863d1c9ef7c5a71c9739450 #15250 Closes #22125 --- lib/vssh/libssh2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 1dd4a9107b84..371667d44b2c 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -805,7 +805,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, cp = strchr(cmd, ' '); if(!cp) { failf(data, "Syntax error command '%s', missing parameter", cmd); - return result; + return CURLE_QUOTE_ERROR; } /* From b3675fe80a3393264d58d0359b3c5ab332f3a9f9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 10:34:38 +0200 Subject: [PATCH 0522/1062] libssh2: drop stray double-negative from `strncmp()` result Just a tidy-up. Logic remains identical. Spotted by GitHub Code Quality Follow-up to a867314f4fba0f05201226093335d75f3dbd0f3f #16382 Closes #22126 --- lib/vssh/libssh2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 371667d44b2c..b12d69f3177a 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1207,7 +1207,7 @@ static CURLcode sftp_quote_stat(struct Curl_easy *data, sshc->acceptfail = TRUE; } - if(!!strncmp(cmd, "chmod", 5)) { + if(strncmp(cmd, "chmod", 5)) { /* Since chown and chgrp only set owner OR group but libssh2 wants to set * them both at once, we need to obtain the current ownership first. This * takes an extra protocol round trip. From f7d4e11f4b423a6db61ca86cf59483fad9f4bc13 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 11:34:10 +0200 Subject: [PATCH 0523/1062] setopt: return error if received `curl_blob->data` is NULL To avoid dereferencing in the function if `CURL_BLOB_COPY` is set, or outside of the function if unset. Reported-by: netspacer.research Closes #22129 --- lib/setopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/setopt.c b/lib/setopt.c index df054469d4a4..eb9ff2e39679 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -108,7 +108,7 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, if(blob) { struct curl_blob *nblob; - if(!blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) + if(!blob->data || !blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; nblob = (struct curl_blob *) curlx_malloc(sizeof(struct curl_blob) + From d824266425d000d4b7c8ca8756dbff83bfa4eb02 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 12:45:44 +0200 Subject: [PATCH 0524/1062] appveyor: pass `--proto-redir =https` option Follow-up to 500820682ce570f21586f567ddec4dbea4e6dad5 #21757 Closes #22130 --- appveyor.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.sh b/appveyor.sh index eed6295349f8..8852caec9b46 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -59,7 +59,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then fn="cmake-${CMAKE_VERSION}-win64-x64" fi curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${fn}.zip" --output pkg.bin + --location --proto-redir =https "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${fn}.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${CMAKE_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin PATH="$PWD/${fn}/bin:$PATH" fi From be1d976a2ab998aaaa824e4396e43c647a17cdb2 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 22 Jun 2026 14:45:25 +0200 Subject: [PATCH 0525/1062] peer: fix ipv6 detection When trying to detect ipv6 addresses, ipv4 addresses were also flagged as ipv6. Add test2413 to check. Closes #22134 --- lib/peer.c | 9 +++- tests/data/Makefile.am | 2 +- tests/data/test2413 | 19 +++++++ tests/unit/Makefile.inc | 1 + tests/unit/unit2413.c | 112 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2413 create mode 100644 tests/unit/unit2413.c diff --git a/lib/peer.c b/lib/peer.c index d559d1dee316..44bcb52f2314 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -222,7 +222,14 @@ static CURLcode peer_parse_host(struct Curl_easy *data, if(scan_for_ipv6 && Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, &pp->host, &pp->zoneid)) { - pp->ipv6 = TRUE; + if(pp->host_user.len < MAX_IPADR_LEN) { + char tmp[MAX_IPADR_LEN]; + memcpy(tmp, pp->host_user.str, pp->host_user.len); + tmp[pp->host_user.len] = 0; + pp->ipv6 = !Curl_is_ipv4addr(tmp); + } + else + pp->ipv6 = TRUE; } else pp->host = pp->host_user; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 6e8eca22bd5f..9fb1720f7c9d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -263,7 +263,7 @@ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ -test2408 test2409 test2410 test2411 test2412 \ +test2408 test2409 test2410 test2411 test2412 test2413 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ diff --git a/tests/data/test2413 b/tests/data/test2413 new file mode 100644 index 000000000000..0b0e8a08aacc --- /dev/null +++ b/tests/data/test2413 @@ -0,0 +1,19 @@ + + + + +unittest +Curl_peer + + + +# Client-side + + +unittest + + +Curl_peer unit tests + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index c6c75c781d00..18d97b051ccc 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -44,6 +44,7 @@ TESTS_C = \ unit1666.c unit1667.c unit1668.c unit1669.c \ unit1674.c unit1675.c unit1676.c \ unit1979.c unit1980.c \ + unit2413.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ diff --git a/tests/unit/unit2413.c b/tests/unit/unit2413.c new file mode 100644 index 000000000000..a800971ae1a0 --- /dev/null +++ b/tests/unit/unit2413.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" +#include "urldata.h" + +static CURLcode test_create2413(const char *name, + CURL *curl, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + const char *exp_hostname, + bool exp_ipv6, + const char *exp_zoneid) +{ + struct Curl_peer *peer = NULL; + CURLcode result; + + result = Curl_peer_create((struct Curl_easy *)curl, + scheme, hostname, port, &peer); + if(result) { + curl_mfprintf(stderr, "%s: create failed %d", name, (int)result); + goto out; + } + + result = CURLE_FAILED_INIT; + if(peer->scheme != scheme) { + curl_mfprintf(stderr, "%s: has wrong scheme", name); + } + else if(!curl_strequal(peer->user_hostname, hostname)) { + curl_mfprintf(stderr, "%s: user_hostname=%s, expected %s", name, + peer->user_hostname, exp_hostname); + } + else if(exp_hostname && !curl_strequal(peer->hostname, exp_hostname)) + curl_mfprintf(stderr, "%s: hostname=%s, expected %s", name, + peer->hostname, exp_hostname); + else if(peer->port != port) + curl_mfprintf(stderr, "%s: port=%u, expected %u", name, + peer->port, port); + else if((bool)peer->ipv6 != exp_ipv6) + curl_mfprintf(stderr, "%s: ipv6=%d, expected %d", name, + peer->ipv6, exp_ipv6); + else if(exp_zoneid && + (!peer->zoneid || !curl_strequal(exp_zoneid, peer->zoneid))) + curl_mfprintf(stderr, "%s: zoneid=%s, expected %s", name, + peer->zoneid, exp_zoneid); + else if(!exp_zoneid && peer->zoneid) + curl_mfprintf(stderr, "%s: zoneid=%s, expected nothing", name, + peer->zoneid); + else + result = CURLE_OK; + +out: + Curl_peer_unlink(&peer); + fail_unless(!result, "check failed"); + return result; +} + +static CURLcode test_unit2413(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + CURL *curl; + struct Curl_peer *peer = NULL; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if(!curl) { + curl_global_cleanup(); + goto unit_test_abort; + } + + test_create2413("peer1", curl, &Curl_scheme_https, "test.curl.se", 1234, + "test.curl.se", FALSE, NULL); + test_create2413("peer2", curl, &Curl_scheme_https, "127.0.0.1", 1234, + "127.0.0.1", FALSE, NULL); + test_create2413("peer3", curl, &Curl_scheme_https, "::1", 1234, + "::1", TRUE, NULL); + test_create2413("peer3", curl, &Curl_scheme_https, "[::1]", 1234, + "::1", TRUE, NULL); + test_create2413("peer4", curl, &Curl_scheme_https, "test.curl.se.", 1234, + "test.curl.se.", FALSE, NULL); + test_create2413("peer5", curl, &Curl_scheme_https, "[::1%tada]", 1234, + "::1", TRUE, "tada"); + test_create2413("peer6", curl, &Curl_scheme_https, "::1%tada", 1234, + "::1", TRUE, "tada"); + + curl_easy_cleanup(curl); + Curl_peer_unlink(&peer); + curl_global_cleanup(); + + UNITTEST_END_SIMPLE +} From 57355408352585c784e58862cb57a0fd3d14c0b3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 13:58:44 +0200 Subject: [PATCH 0526/1062] CURLOPT_SSLVERSION.md: drop stray space from 'SSLv*', 'TLSv*' To match rest of codebase. Closes #22131 --- docs/libcurl/opts/CURLOPT_SSLVERSION.md | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_SSLVERSION.md b/docs/libcurl/opts/CURLOPT_SSLVERSION.md index 411da989efbf..cbe7a89c30c9 100644 --- a/docs/libcurl/opts/CURLOPT_SSLVERSION.md +++ b/docs/libcurl/opts/CURLOPT_SSLVERSION.md @@ -34,43 +34,43 @@ Pass a long as parameter to control which version range of SSL/TLS versions to use. The SSL and TLS versions have typically developed from the most insecure -version to be more and more secure in this order through history: SSL v2, -SSLv3, TLS v1.0, TLS v1.1, TLS v1.2 and the most recent TLS v1.3. +version to be more and more secure in this order through history: SSLv2, +SSLv3, TLSv1.0, TLSv1.1, TLSv1.2 and the most recent TLSv1.3. Use one of the available defines for this purpose. The available options are: ## CURL_SSLVERSION_DEFAULT The default acceptable version range. The minimum acceptable version is by -default TLS v1.2 since 8.16.0 (unless the TLS library has a stricter rule). +default TLSv1.2 since 8.16.0 (unless the TLS library has a stricter rule). ## CURL_SSLVERSION_TLSv1 -TLS v1.0 or later +TLSv1.0 or later ## CURL_SSLVERSION_SSLv2 -SSL v2 - refused +SSLv2 - refused ## CURL_SSLVERSION_SSLv3 -SSL v3 - refused +SSLv3 - refused ## CURL_SSLVERSION_TLSv1_0 -TLS v1.0 or later +TLSv1.0 or later ## CURL_SSLVERSION_TLSv1_1 -TLS v1.1 or later +TLSv1.1 or later ## CURL_SSLVERSION_TLSv1_2 -TLS v1.2 or later +TLSv1.2 or later ## CURL_SSLVERSION_TLSv1_3 -TLS v1.3 or later +TLSv1.3 or later ## @@ -82,24 +82,24 @@ with *one* of the CURL_SSLVERSION_MAX_ macros. The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL library is used. libcurl uses a sensible default maximum, -which was TLS v1.2 up to before 7.61.0 and is TLS v1.3 since then - assuming +which was TLSv1.2 up to before 7.61.0 and is TLSv1.3 since then - assuming the TLS library support it. ## CURL_SSLVERSION_MAX_TLSv1_0 -The flag defines maximum supported TLS version as TLS v1.0. +The flag defines maximum supported TLS version as TLSv1.0. ## CURL_SSLVERSION_MAX_TLSv1_1 -The flag defines maximum supported TLS version as TLS v1.1. +The flag defines maximum supported TLS version as TLSv1.1. ## CURL_SSLVERSION_MAX_TLSv1_2 -The flag defines maximum supported TLS version as TLS v1.2. +The flag defines maximum supported TLS version as TLSv1.2. ## CURL_SSLVERSION_MAX_TLSv1_3 -The flag defines maximum supported TLS version as TLS v1.3. +The flag defines maximum supported TLS version as TLSv1.3. # DEFAULT From b0d0f16d20d8f2c3d14c9f9d25699ba883c6d95a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 15:52:23 +0200 Subject: [PATCH 0527/1062] tidy-up: make 'CA' uppercase, where missing ```sh git grep -w ca | grep -v -E -i 'ca[;"=/()%_.-]' | grep -v -E -i '[*$"=/()%_.-]ca' ``` Closes #22135 --- acinclude.m4 | 8 ++++---- configure.ac | 10 +++++----- docs/FAQ.md | 6 +++--- docs/TheArtOfHttpScripting.md | 2 +- docs/examples/cacertinmem.c | 2 +- docs/libcurl/libcurl-tutorial.md | 2 +- docs/libcurl/opts/CURLINFO_CAINFO.md | 2 +- docs/libcurl/opts/CURLINFO_CAPATH.md | 2 +- lib/config-os400.h | 2 +- lib/curl_config-cmake.h.in | 6 +++--- lib/vtls/vtls_config.c | 2 +- m4/curl-amissl.m4 | 2 +- projects/vms/generate_config_vms_h_curl.com | 2 +- scripts/firefox-db2pem.sh | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 11fe68c73729..ed8b0499dd6a 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1127,7 +1127,7 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), capath="$want_capath" ca="no" elif test "$ca_native" != "no"; then - dnl native ca configured, do not look further + dnl native CA configured, do not look further ca="no" capath="no" else @@ -1161,7 +1161,7 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), fi done fi - AC_MSG_NOTICE([want $want_capath ca $ca]) + AC_MSG_NOTICE([want $want_capath CA $ca]) if test "x$want_capath" = "xunset"; then check_capath="/etc/ssl/certs" fi @@ -1197,13 +1197,13 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), if test "x$ca" != "xno"; then CURL_CA_BUNDLE="$ca" - AC_DEFINE_UNQUOTED(CURL_CA_BUNDLE, "$ca", [Location of default ca bundle]) + AC_DEFINE_UNQUOTED(CURL_CA_BUNDLE, "$ca", [Location of default CA bundle]) AC_SUBST(CURL_CA_BUNDLE) AC_MSG_RESULT([$ca]) fi if test "x$capath" != "xno"; then CURL_CA_PATH="\"$capath\"" - AC_DEFINE_UNQUOTED(CURL_CA_PATH, "$capath", [Location of default ca path]) + AC_DEFINE_UNQUOTED(CURL_CA_PATH, "$capath", [Location of default CA path]) AC_MSG_RESULT([$capath (capath)]) fi if test "x$ca" = "xno" && test "x$capath" = "xno"; then diff --git a/configure.ac b/configure.ac index 1752bb10a278..a21847ea6663 100644 --- a/configure.ac +++ b/configure.ac @@ -5535,11 +5535,11 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: Verbose errors: ${curl_verbose_msg} Code coverage: ${curl_coverage_msg} SSPI: ${curl_sspi_msg} - ca native: ${ca_native} - ca cert bundle: ${ca}${ca_warning} - ca cert path: ${capath}${capath_warning} - ca cert embed: ${CURL_CA_EMBED_msg} - ca fallback: ${with_ca_fallback} + CA native: ${ca_native} + CA cert bundle: ${ca}${ca_warning} + CA cert path: ${capath}${capath_warning} + CA cert embed: ${CURL_CA_EMBED_msg} + CA fallback: ${with_ca_fallback} LDAP: ${curl_ldap_msg} LDAPS: ${curl_ldaps_msg} IPFS/IPNS: ${curl_ipfs_msg} diff --git a/docs/FAQ.md b/docs/FAQ.md index e6d7c43cdc1e..6cdf97d32725 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -204,16 +204,16 @@ world wide. ## Why do you not update ca-bundle.crt In the curl project we have decided not to attempt to keep this file updated -(or even present) since deciding what to add to a ca cert bundle is an +(or even present) since deciding what to add to a CA cert bundle is an undertaking we have not been ready to accept, and the one we can get from Mozilla is perfectly fine so there is no need to duplicate that work. Today, with many services performed over HTTPS, every operating system should -come with a default ca cert bundle that can be deemed somewhat trustworthy and +come with a default CA cert bundle that can be deemed somewhat trustworthy and that collection (if reasonably updated) should be deemed to be a lot better than a private curl version. -If you want the most recent collection of ca certs that Mozilla Firefox uses, +If you want the most recent collection of CA certs that Mozilla Firefox uses, we recommend using our online [CA certificate service](https://curl.se/docs/caextract.html) setup for this purpose. diff --git a/docs/TheArtOfHttpScripting.md b/docs/TheArtOfHttpScripting.md index 7f300f070395..541cb4c2ff71 100644 --- a/docs/TheArtOfHttpScripting.md +++ b/docs/TheArtOfHttpScripting.md @@ -591,7 +591,7 @@ Failing the verification causes curl to deny the connection. You must then use [`--insecure`](https://curl.se/docs/manpage.html#-k) (`-k`) in case you want to tell curl to ignore that the server cannot be verified. -More about server certificate verification and ca cert bundles can be read in +More about server certificate verification and CA cert bundles can be read in the [`SSLCERTS` document](https://curl.se/docs/sslcerts.html). At times you may end up with your own CA cert store and then you can tell diff --git a/docs/examples/cacertinmem.c b/docs/examples/cacertinmem.c index 06d088c61ad0..2e43a783f5dc 100644 --- a/docs/examples/cacertinmem.c +++ b/docs/examples/cacertinmem.c @@ -155,7 +155,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CAINFO, NULL); curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); - /* first try: retrieve page without ca certificates -> should fail + /* first try: retrieve page without CA certificates -> should fail * unless libcurl was built --with-ca-fallback enabled at build-time */ result = curl_easy_perform(curl); diff --git a/docs/libcurl/libcurl-tutorial.md b/docs/libcurl/libcurl-tutorial.md index b5cfcf2922db..befcf7739968 100644 --- a/docs/libcurl/libcurl-tutorial.md +++ b/docs/libcurl/libcurl-tutorial.md @@ -1420,7 +1420,7 @@ to figure out success on each individual transfer. # SSL, Certificates and Other Tricks -[ seeding, passwords, keys, certificates, ENGINE, ca certs ] +[ seeding, passwords, keys, certificates, ENGINE, CA certs ] # Sharing Data Between Easy Handles diff --git a/docs/libcurl/opts/CURLINFO_CAINFO.md b/docs/libcurl/opts/CURLINFO_CAINFO.md index 23502d22966a..626abca19e3f 100644 --- a/docs/libcurl/opts/CURLINFO_CAINFO.md +++ b/docs/libcurl/opts/CURLINFO_CAINFO.md @@ -53,7 +53,7 @@ int main(void) char *cainfo = NULL; curl_easy_getinfo(curl, CURLINFO_CAINFO, &cainfo); if(cainfo) { - printf("default ca info path: %s\n", cainfo); + printf("default CA info path: %s\n", cainfo); } curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_CAPATH.md b/docs/libcurl/opts/CURLINFO_CAPATH.md index c58930e0e9bc..603634da8a4d 100644 --- a/docs/libcurl/opts/CURLINFO_CAPATH.md +++ b/docs/libcurl/opts/CURLINFO_CAPATH.md @@ -56,7 +56,7 @@ int main(void) char *capath = NULL; curl_easy_getinfo(curl, CURLINFO_CAPATH, &capath); if(capath) { - printf("default ca path: %s\n", capath); + printf("default CA path: %s\n", capath); } curl_easy_cleanup(curl); } diff --git a/lib/config-os400.h b/lib/config-os400.h index eacd6e8942c8..76841af202ec 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -34,7 +34,7 @@ /* Global configuration parameters: normally generated by autoconf. */ /* ---------------------------------------------------------------- */ -/* Location of default ca bundle */ +/* Location of default CA bundle */ /* Use the system keyring as the default CA bundle. */ #define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 6db19c22ebad..c38caa2a03fd 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -22,13 +22,13 @@ * ***************************************************************************/ -/* Location of default ca bundle */ +/* Location of default CA bundle */ #cmakedefine CURL_CA_BUNDLE "${CURL_CA_BUNDLE}" -/* define "1" to use built-in ca store of TLS backend */ +/* define "1" to use built-in CA store of TLS backend */ #cmakedefine CURL_CA_FALLBACK 1 -/* Location of default ca path */ +/* Location of default CA path */ #cmakedefine CURL_CA_PATH "${CURL_CA_PATH}" /* Default SSL backend */ diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index 0d294da83a91..4a9b69654b3c 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -241,7 +241,7 @@ static void ssl_easy_config_compl_options(struct Curl_peer *origin, uint8_t options = sslc->primary.ssl_options; /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it. * If not, we switch it on for supported backends if no custom - * ca settings exist. */ + * CA settings exist. */ sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST); sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN); diff --git a/m4/curl-amissl.m4 b/m4/curl-amissl.m4 index 4048037fd016..e32861e9ef12 100644 --- a/m4/curl-amissl.m4 +++ b/m4/curl-amissl.m4 @@ -45,7 +45,7 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then test "amissl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes AMISSL_ENABLED=1 OPENSSL_ENABLED=1 - dnl Use AmiSSL's built-in ca bundle + dnl Use AmiSSL's built-in CA bundle check_for_ca_bundle=1 with_ca_fallback=yes LIBS="-lamisslstubs -lamisslauto $LIBS" diff --git a/projects/vms/generate_config_vms_h_curl.com b/projects/vms/generate_config_vms_h_curl.com index 0a651b1a99d4..6e78b801f243 100644 --- a/projects/vms/generate_config_vms_h_curl.com +++ b/projects/vms/generate_config_vms_h_curl.com @@ -215,7 +215,7 @@ $write cvh "" $! $! We are now setting this on the GNV build, so also do this $! for compatibility. -$write cvh "/* Location of default ca path */" +$write cvh "/* Location of default CA path */" $write cvh "#define curl_ca_path ""gnv$curl_ca_path""" $! $! The config_h.com finds a bunch of default disable commands in diff --git a/scripts/firefox-db2pem.sh b/scripts/firefox-db2pem.sh index 7d31b1288693..b763ef10a26a 100755 --- a/scripts/firefox-db2pem.sh +++ b/scripts/firefox-db2pem.sh @@ -23,7 +23,7 @@ # * # *************************************************************************** # This shell script creates a fresh ca-bundle.crt file for use with libcurl. -# It extracts all ca certs it finds in the local Firefox database and converts +# It extracts all CA certs it finds in the local Firefox database and converts # them all into PEM format. # # It uses the "certutil" command line tool from the NSS project to perform the From 560dc2985ac4909853b295988292f96467fad026 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 14:36:34 +0200 Subject: [PATCH 0528/1062] doh: drop redundant `curlx_dyn_free()` call in `doh_probe_done()` The buffer is freed on the next instruction via `Curl_meta_remove()`'s destructor. Reported-by: netspacer.research Follow-up to 1ebd92d0fdbb1693c926f7190442dff00226fbf3 #16384 Closes #22133 --- lib/doh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/doh.c b/lib/doh.c index 331bce46b678..8b643aa0b428 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -259,7 +259,6 @@ static void doh_probe_done(struct Curl_easy *data, result = curlx_dyn_addn(&dohp->probe_resp[i].body, curlx_dyn_ptr(&doh_req->resp_body), curlx_dyn_len(&doh_req->resp_body)); - curlx_dyn_free(&doh_req->resp_body); } Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); From 364d6c18f717aef221e62a6e7ffeeaf902a588d2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 21:17:24 +0200 Subject: [PATCH 0529/1062] cmake: add pre-fills for DragonFly BSD and MidnightBSD Based on CI runs: DragonFlyBSD: https://github.com/curl/curl/actions/runs/27978506617/job/82802332910 (autotools) MidnightBSD: https://github.com/curl/curl/actions/runs/27977103321/job/82797523470 (cmake) Also readd DragonFly BSD to GHA/non-native, but keep it commented. Closes #22138 --- .github/workflows/non-native.yml | 6 +++++- CMake/unix-cache.cmake | 31 +++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 50ebe3768cbb..420c976d9c38 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -54,6 +54,8 @@ jobs: strategy: matrix: include: + # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl !runtests', + # options: '--with-openssl' } - { os: 'freebsd' , version: '15.0', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', options: '--with-openssl --with-gssapi' } - { os: 'freebsd' , version: '15.0', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity !runtests !examples', @@ -83,7 +85,9 @@ jobs: - name: 'install prereqs' run: | - if [ "${MATRIX_OS}" = 'freebsd' ]; then + if [ "${MATRIX_OS}" = 'dragonflybsd' ]; then + sudo pkg install -y autoconf automake libtool perl5 pkgconf brotli openldap26-client libidn2 libnghttp2 + elif [ "${MATRIX_OS}" = 'freebsd' ]; then # https://ports.freebsd.org/ if [ "${MATRIX_BUILD}" = 'cmake' ]; then tools='cmake-core ninja perl5' diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index e69ea5f6088e..78816499ca01 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -21,7 +21,7 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Based on CI runs for Cygwin/MSYS2, Linux, macOS, FreeBSD, NetBSD, OpenBSD +# Based on CI runs for Cygwin/MSYS2, Linux, macOS/iOS, DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD if(NOT UNIX) message(FATAL_ERROR "This file should be included on Unix platforms only") endif() @@ -30,7 +30,9 @@ if(APPLE OR CYGWIN) set(HAVE_ACCEPT4 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_ACCEPT4 1) @@ -58,10 +60,12 @@ set(HAVE_DECL_FSEEKO 1) set(HAVE_DIRENT_H 1) if(APPLE OR CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_EVENTFD 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_EVENTFD 1) endif() @@ -70,7 +74,8 @@ if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34) endif() if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+ - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") # v11.2+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # v11.2+ + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") # v1.3+ set(HAVE_MEMSET_S 1) elseif(NOT APPLE) set(HAVE_MEMSET_S 0) @@ -86,7 +91,9 @@ if(APPLE) set(HAVE_FSETXATTR 1) set(HAVE_FSETXATTR_5 0) set(HAVE_FSETXATTR_6 1) -elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR +elseif(CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_FSETXATTR 0) set(HAVE_FSETXATTR_5 0) @@ -103,7 +110,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETADDRINFO_THREADSAFE 0) elseif(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_GETADDRINFO_THREADSAFE 1) endif() @@ -114,14 +123,18 @@ if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETHOSTBYNAME_R 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(HAVE_GETHOSTBYNAME_R 1) endif() set(HAVE_GETHOSTBYNAME_R_3 0) set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) set(HAVE_GETHOSTBYNAME_R_5 0) set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(HAVE_GETHOSTBYNAME_R_6 1) set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 1) else() @@ -137,7 +150,9 @@ endif() if(APPLE OR CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETPASS_R 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") @@ -207,6 +222,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR BSD OR CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS") @@ -227,7 +243,8 @@ set(HAVE_SCHED_YIELD 1) set(HAVE_SELECT 1) set(HAVE_SEND 1) if(APPLE OR - CYGWIN) + CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD") set(HAVE_SENDMMSG 0) else() set(HAVE_SENDMMSG 1) @@ -265,10 +282,12 @@ if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") endif() if(APPLE OR CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_SYS_EVENTFD_H 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_SYS_EVENTFD_H 1) endif() From 621176cbd532ef230f9a8962c5c1f5378b14912c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 23:58:49 +0200 Subject: [PATCH 0530/1062] GHA/windows: settle on windows-2025 image name It's the final/stable image name and it's shipping with VS2026 now. Ref: https://github.com/actions/runner-images/issues/14017 Follow-up to b0239417b34238121165dee465afb944cbad17ec #21713 Closes #22139 --- .github/workflows/windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9c3acbdf710e..604bcbbdc3fb 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -311,7 +311,7 @@ jobs: # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , # config: '--without-debug --with-schannel --disable-static', # install: 'mingw-w64-ucrt-x86_64-libssh2' } - - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025-vs2026', + - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2' } @@ -902,7 +902,7 @@ jobs: env: 'ucrt-x86_64' plat: 'uwp' type: 'Debug' - image: 'windows-2025-vs2026' + image: 'windows-2025' tflags: 'skiprun' config: >- -DENABLE_DEBUG=ON @@ -924,7 +924,7 @@ jobs: env: 'ucrt-x86_64' plat: 'windows' type: 'Debug' - image: 'windows-2025-vs2026' + image: 'windows-2025' chkprefill: '_chkprefill' tflags: '--min=1850' config: >- @@ -1032,7 +1032,7 @@ jobs: # VS2022). Since it integrates badly with CI steps and shell scripts, # reproduce the necessary build configuration manually, without envs. MSVC_EDITION='2022/Enterprise/vc/tools/msvc' - [[ "${MATRIX_IMAGE}" = *'vs2026'* ]] && MSVC_EDITION='18/Enterprise/vc/tools/msvc' + [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]] && MSVC_EDITION='18/Enterprise/vc/tools/msvc' [[ "$(uname -s)" = *'ARM64'* ]] && MSVC_HOST='arm64' || MSVC_HOST='x64' # x86 MSVC_ROOTD="$(cygpath --mixed --short-name "$PROGRAMFILES/Microsoft Visual Studio")" # to avoid spaces in directory names MSVC_ROOTU="$(/usr/bin/find "$(cygpath --unix "$MSVC_ROOTD/$MSVC_EDITION")" -mindepth 1 -maxdepth 1 -type d -name '*.*' | sort | tail -n 1)" From 7e37510c93d156014f95e5d828b2a3a4c0c354b7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 23 Jun 2026 10:05:41 +0200 Subject: [PATCH 0531/1062] RELEASE-PROCEDURE.md: typo --- docs/RELEASE-PROCEDURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index e5543b54db79..e62a90cbd743 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -97,7 +97,7 @@ pending release: - Release candidate two (**rc2**) ships nine days later, sixteen days before the release. On a Monday. Tagged like `rc-7_34_0-2`. -- Release candidate tree (**rc3**) ships nine days later, seven days before +- Release candidate three (**rc3**) ships nine days later, seven days before the release. On a Wednesday. Tagged like `rc-7_34_0-3`. Release candidate tarballs are ephemeral and each such tarball is only kept From 78267398e5a2ce347a6729926c68272b2b6fbed5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 23 Jun 2026 11:28:15 +0200 Subject: [PATCH 0532/1062] GHA/linux: bump pizlonator/fil-c to v0.680, fixup quoting Also put the version number in quotes to avoid GHA altering the number into `0.68` (without rightmost zero) and ending up storing that in the `FIL_C_VERSION` env. Hopefully Renovate will honor this on future bumps. Do the same for the rest of `x.y` format version numbers. Fixing: ``` env: [...] FIL_C_VERSION: 0.68 ``` Ref: https://github.com/curl/curl/actions/runs/28006009260/job/82920767558?pr=22142#step:5:16 Fixes #22142 Closes #22143 --- .github/workflows/linux.yml | 2 +- .github/workflows/non-native.yml | 4 ++-- .github/workflows/windows.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1d0043c29fef..fff7f1c8765c 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.679 + FIL_C_VERSION: '0.680' # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 420c976d9c38..18c04c76e409 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -196,7 +196,7 @@ jobs: env: MAKEFLAGS: -j 5 MATRIX_BUILD: '${{ matrix.build }}' - AMISSL_VERSION: 5.27 + AMISSL_VERSION: '5.27' AMISSL_SHA256: 5003bef8c5930354d16b0ce7196d71b2811891c42fad38a9238c5ce4098ad42a TOOLCHAIN_VERSION: 6.5.0 TOOLCHAIN_SHA256: 381e227c9ef552f073771d6f851cfdf873b574f3cf5db7c1c0107ea5d7146edc @@ -412,7 +412,7 @@ jobs: MAKEFLAGS: -j 5 MATRIX_BUILD: '${{ matrix.build }}' # renovate: datasource=github-releases depName=andrewwutw/build-djgpp versioning=semver-coerced registryUrl=https://github.com - TOOLCHAIN_VERSION: 3.4 + TOOLCHAIN_VERSION: '3.4' TOOLCHAIN_SHA256: 8464f17017d6ab1b2bb2df4ed82357b5bf692e6e2b7fee37e315638f3d505f00 strategy: matrix: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 604bcbbdc3fb..53429d1f2bcf 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,7 +39,7 @@ env: OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 - STUNNEL_VERSION: 5.78 + STUNNEL_VERSION: '5.78' STUNNEL_SHA256: 32a88dcc5654f955266109be8bf10fd7d56fa4e125cab821ee508230570e46c5 jobs: From 9adc32a6e2601c820a6f8bdf18fedf081623ec5a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 23 Jun 2026 17:07:34 +0200 Subject: [PATCH 0533/1062] test 679: add a quoted string name in a netrc test By using quotes a user name can have a space in netrc Closes #22147 --- tests/data/test679 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/data/test679 b/tests/data/test679 index 478e2d25796a..6b995e3d1039 100644 --- a/tests/data/test679 +++ b/tests/data/test679 @@ -30,13 +30,13 @@ Funny-head: yesyes http -netrc with quoted password +netrc with quoted username and password --netrc-optional --netrc-file %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/ -machine %HOSTIP login user1 password "with spaces and \"\n\r\t\a" +machine %HOSTIP login "user one" password "with spaces and \"\n\r\t\a" @@ -45,7 +45,7 @@ machine %HOSTIP login user1 password "with spaces and \"\n\r\t\a" GET / HTTP/1.1 Host: %HOSTIP:%HTTPPORT -Authorization: Basic %b64[user1:with%20spaces%20and%20"%0a%0d%09a]b64% +Authorization: Basic %b64[user%20one:with%20spaces%20and%20"%0a%0d%09a]b64% User-Agent: curl/%VERSION Accept: */* From bf29c3e17544ff13c67e94157e6440662317355b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 23 Jun 2026 12:08:25 +0200 Subject: [PATCH 0534/1062] GHA/non-native: BSD overhaul, test more autotools, bump versions - add autotools jobs for MidnightBSD, NetBSD, OpenBSD. Takes under 3 minutes per new job, under +6m in total. - comment out MidnightBSD to save CI time. - to make them as fast as possible, skip building tests and examples, and omit libidn2, openldap dependencies. - add DragonFly BSD cmake job, which finally works. (keep it commented out since the package server fails frequently.) - do `mport index/upgrade` to make MidnightBSD autotools builds work. - rework filtering MidnightBSD package manager's excessive log output. - fixup OpenBSD autotools job to uninstall system curl to avoid linking against it (and breaking debug builds). - make OpenBSD package manager commands non-interactive. - specify install packages for each matrix entry. - make autotools build step verbose (to ease debugging). - add link to DragonFly BSD package repo. - bump cross-platform-actions from 1.1.0 to 1.3.0. - bump FreeBSD 15.0 to 15.1. - bump OpenBSD to 7.7 to 7.9. This did not go well last time with 7.8, let's see with 7.9. Ref: 8d00e28136baf661455f1fe5980a0d18c4d872e3 #19372 Ref: c3b890b2c005401e18b54dacf9e63d33412e2b4f #19368 - sync test-skipper keywords with rest of workflows. - drop installing impacket. It was unused. (also a slow install with many dependencies) The original motivation was to prepare fixing OpenBSD's (and possibly other platforms) `getaddrinfo()` thread-safety check. Ref: https://github.com/curl/curl/pull/22138#issuecomment-4773617195 Closes #22145 --- .github/workflows/non-native.yml | 106 ++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 31 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 18c04c76e409..621605a45ebd 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -49,26 +49,67 @@ jobs: MAKEFLAGS: -j 3 MATRIX_ARCH: '${{ matrix.arch }}' MATRIX_BUILD: '${{ matrix.build }}' + MATRIX_INSTALL: '${{ matrix.install }}' MATRIX_OPTIONS: '${{ matrix.options }}' MATRIX_OS: '${{ matrix.os }}' + MATRIX_VERSION: '${{ matrix.version }}' strategy: matrix: include: - # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl !runtests', - # options: '--with-openssl' } - - { os: 'freebsd' , version: '15.0', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', - options: '--with-openssl --with-gssapi' } - - { os: 'freebsd' , version: '15.0', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity !runtests !examples', + # https://github.com/DragonFlyBSD/DPorts + # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skiprun', + # install: 'autoconf automake libtool openldap26-client libidn2', + # options: '--with-openssl --enable-ldap --enable-ldaps --with-libidn2' } + + # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall', + # install: 'cmake ninja' } + + # https://ports.freebsd.org/ + - { os: 'freebsd' , version: '15.1', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', + install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', + options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } + + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity skiprun !examples', + install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', - options: '--with-openssl --with-gssapi' } + install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', + options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', options: '-DCURL_USE_GSSAPI=ON' } - - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls !runtests', + + # https://app.midnightbsd.org/ + # https://man.midnightbsd.org/cgi-bin/man.cgi/mport + # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skipall !examples', + # install: 'autoconf autoconf-archive automake libtool gnutls', + # options: '--with-gnutls' } + + - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skiprun', + install: 'cmake-core ninja perl5 gnutls openldap26-client libidn2', options: '-DCURL_USE_GNUTLS=ON' } + + # https://pkgsrc.se/ + - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall !examples', + install: 'autoconf automake libtool mit-krb5', + options: '--with-openssl --with-gssapi' } + - { os: 'netbsd' , version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', + install: 'cmake ninja-build mit-krb5 openldap-client libidn2', options: '-DCURL_USE_GSSAPI=ON' } - - { os: 'openbsd' , version: '7.7' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl' } + + # https://openbsd.app/ + # https://www.openbsd.org/faq/faq15.html + # https://github.com/OpenMPT/openmpt/blob/master/.github/workflows/OpenBSD-Autotools.yml + - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'libressl skipall !examples', + install: 'autoconf-2.72p0 automake-1.18.1 libtool', # NOTE: also sync these versions with the autoreconf step! + options: '--with-openssl' } + + - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl', + install: 'cmake ninja openldap-client-- libidn2' } + fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -76,9 +117,9 @@ jobs: persist-credentials: false - name: 'setup VM' - uses: cross-platform-actions/action@0c165ad7eb2d6a7e8552d6af5aad2bbedfc646b0 # v1.1.0 + uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 with: - environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_OPTIONS MATRIX_OS' + environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION' operating_system: '${{ matrix.os }}' version: '${{ matrix.version }}' architecture: '${{ matrix.arch }}' @@ -86,31 +127,34 @@ jobs: - name: 'install prereqs' run: | if [ "${MATRIX_OS}" = 'dragonflybsd' ]; then - sudo pkg install -y autoconf automake libtool perl5 pkgconf brotli openldap26-client libidn2 libnghttp2 + sudo pkg install -y pkgconf brotli libnghttp2 ${MATRIX_INSTALL} elif [ "${MATRIX_OS}" = 'freebsd' ]; then - # https://ports.freebsd.org/ - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - tools='cmake-core ninja perl5' - else - tools='autoconf automake libtool' - fi - sudo pkg install -y ${tools} pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket + sudo pkg install -y pkgconf brotli libnghttp2 ${MATRIX_INSTALL} elif [ "${MATRIX_OS}" = 'midnightbsd' ]; then - # https://app.midnightbsd.org/ - # https://man.midnightbsd.org/cgi-bin/man.cgi/mport - sudo mport -q install cmake-core ninja perl5 pkgconf brotli gnutls openldap26-client libidn2 libnghttp2 | grep -E '(Downloading.+100|Installing)' || true + if [ "${MATRIX_BUILD}" = 'autotools' ]; then + sudo mport index | grep -v -E 'Downloading.+%' + sudo mport upgrade | grep -v -E '(Downloading.+%|^/usr/local)' + fi + sudo mport install pkgconf brotli libnghttp2 ${MATRIX_INSTALL} | grep -v -E '(Downloading.+%|^/usr/local)' || true elif [ "${MATRIX_OS}" = 'netbsd' ]; then - # https://pkgsrc.se/ - sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket + sudo pkgin -y install pkg-config perl brotli libssh2 libpsl nghttp2 ${MATRIX_INSTALL} elif [ "${MATRIX_OS}" = 'openbsd' ]; then - # https://openbsd.app/ - # https://www.openbsd.org/faq/faq15.html - sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket + sudo pkg_add -I brotli libssh2 libpsl nghttp2 ${MATRIX_INSTALL} + if [ "${MATRIX_BUILD}" = 'autotools' ]; then + sudo pkg_delete -I curl # to avoid autotools build linking against system libcurl + fi fi - name: 'autoreconf' if: ${{ matrix.build == 'autotools' }} - run: autoreconf -fi + run: | + if [ "${MATRIX_OS}" = 'openbsd' ]; then + if [ "${MATRIX_VERSION}" = '7.9' ]; then + export AUTOCONF_VERSION=2.72 + export AUTOMAKE_VERSION=1.18 + fi + fi + autoreconf -fi - name: 'configure' run: | @@ -126,7 +170,7 @@ jobs: mkdir bld && cd bld ../configure --prefix="$HOME"/curl-install --enable-unity --enable-debug --enable-warnings --enable-werror --disable-static \ --disable-dependency-tracking --enable-option-checking=fatal \ - --with-brotli --enable-ldap --enable-ldaps --with-libidn2 --with-libssh2 --with-nghttp2 \ + --with-brotli --with-libssh2 --with-nghttp2 \ ${options} ${MATRIX_OPTIONS} fi @@ -144,7 +188,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld else - make -C bld + make -C bld V=1 fi - name: 'curl -V' @@ -159,7 +203,7 @@ jobs: fi - name: 'build tests' - if: ${{ matrix.arch == 'x86_64' }} # Slow on emulated CPU + if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --target testdeps @@ -168,7 +212,7 @@ jobs: fi - name: 'run tests' - if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, '!runtests') }} # Slow on emulated CPU + if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') && !contains(matrix.desc, 'skiprun') }} # Slow on emulated CPU run: | export TFLAGS='-j8' if [ "${MATRIX_OS}" = 'openbsd' ]; then From ec37b024d8a2514484dcc06e4827361ee1670d1c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 24 Jun 2026 07:52:50 +0200 Subject: [PATCH 0535/1062] THANKS: added names from 8.21.0 release --- docs/THANKS | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/docs/THANKS b/docs/THANKS index 16a3b907b25c..a70bf7527dd1 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -6,6 +6,8 @@ 0xee on github 0xflotus on github +0xN3R3K3 +11soda11 12932 on github 1337vt on github 1ocalhost on github @@ -50,6 +52,7 @@ Adrian Burcea Adriano Meirelles Adrian Peniak Adrian Schuur +Ady Elouej afengsoft on github afrind on github Aftab Alam @@ -58,6 +61,7 @@ ahodesuka on github aisle-research-bot ajak in #curl Ajit Dhumale +A Johnston Akhilesh Nema Akhil Kedia Aki Koskinen @@ -67,6 +71,7 @@ Akshay Vernekar Alain Danteny Alain Miniussi Alan Coopersmith +Alan De Smet Alan Jenkins Alan Pinstein Albert Chin-A-Young @@ -151,7 +156,9 @@ Alex Vinnik Alex Xu Alfonso Martone Alfred Gebert +alhudz Alice Lee Poetics +alienowo on hackerone Ali Khodkar ALittleDruid on github Ali Utku Selen @@ -159,8 +166,10 @@ Allen Pulsifer Alois Klink Alona Rossen Amaury Denoyelle +ambikeesshh Ameda Amahru amishmm on github +amitbidlan Amit Katyal Ammar Faizi Amol Pattekar @@ -229,6 +238,7 @@ Andrew Kurushin Andrew Kvalheim Andrew Lambert Andrew Moise +Andrew Nesbitt Andrew Olsen Andrew Potter Andrew Robbins @@ -287,6 +297,7 @@ Aquila Macedo arainchik on github Archangel_SDY on github Arian van Putten +Aritra Basu Arjan van de Ven Arkadiusz Miskiewicz Arkadi Vainbrand @@ -330,6 +341,7 @@ Axel Tillequin Ayesh Karunaratne Ayoub Boudhar Ayushman Singh Chauhan +azraelxuemo on hackerone b9a1 on github Bachue Zhou Baitinq on github @@ -344,15 +356,18 @@ BANADDA baranyaib90 on github Barry Abrahamson Barry Pollard +Bartel Sielski Bartosz Ruszczak Bart Whiteley Baruch Siach Bas Mevissen +Bastian Jesuiter Bastian Krause Bastien Bouclet Basuke Suzuki Bas van Schaik baumanj on github +BazaarAcc32 on github bdry on github beckenc on github behindtheblackwall on hackerone @@ -402,6 +417,7 @@ Bill Egert Bill Hoffman billionai on github Bill Middlecamp +Bill Mill Bill Nagel Bill Pyne Billy O'Neal @@ -501,6 +517,7 @@ buzo-ffm on github bxac on github Bylon2 on github Byrial Jensen +ByteRay on hackerone Cajus Pollmeier Caleb Raitto calm329 @@ -609,6 +626,7 @@ Christoph M. Becker Christoph Reiter Chris Webb Chris Young +chrizilla on github chrysos349 on github Chungtsun Li Ciprian Badescu @@ -626,6 +644,7 @@ Clint Clayton Cloudogu Siebels CMD cmfrolick on github +co-authors in libssh2 codesniffer13 on github Cody Jones Cody Mack @@ -752,6 +771,7 @@ Dan Zitter Daphne Luong Darío Hereñú Dario Nieuwenhuis +Dario Vinella Dario Weißer Darren Banfi Darryl House @@ -767,6 +787,7 @@ Dave Nicolson Dave Reisner Dave Thompson Dave Vasilevsky +Dave Walker Davey Shafik David Bau David Benjamin @@ -819,6 +840,7 @@ David Woodhouse David Wright David Yan David Zhuang +daviey on hackerone Da-Yoon Chung dbalsom dbrowndan on github @@ -944,6 +966,7 @@ Duy Phan Thanh Dwarakanath Yadavalli dwickr Dwij Mehta +dyingc on github Dylam De La Torre Dylan Anthony Dylan Ellicott @@ -981,6 +1004,7 @@ elelel on github elephoenix on github Elia Tufarolo Eli Schwartz +Elise Vance Elliot Killick Elliot Saba Elliott Balsley @@ -994,6 +1018,7 @@ emanruse on github Emanuele Bovisio Emanuele Torre Emanuel Komínek +Emanuel Krollmann Emil Engler Emiliano Ida Emilio Cobos Álvarez @@ -1060,9 +1085,11 @@ Ethan Glasser Camp Ethan Wilkes Etienne Simard Eugene Kotlyarov +Eunsoo Kim Evangelos Foutras Evan Jordan Even Rouault +evergarden1123 on hackerone Evert Pot Evgeny Grin (Karlson2k) Evgeny Turnaev @@ -1106,6 +1133,7 @@ Felix Yan Feng Tu Fernando Muñoz ffath-vo on github +Filipe Casal Filip Lundgren Filip Salomonsson finkjsc on github @@ -1171,6 +1199,7 @@ galen11 on github Gambit Communications Ganesh Kamath Ganesh Viswanathan +Gao Liyou gaoxingwang on github Garrett Holmstrom Garrett Squire @@ -1256,6 +1285,8 @@ Griffin Downs Grigory Entin Grisha Levit Gruber Glass +Guancheng Li +Guannan Wang gudyuu on hackerone Guenole Bescon Guido Berhoerster @@ -1314,6 +1345,7 @@ Helge Klein Helmut Grohne Helmut K. C. Tessarek Helwing Lutz +Hem Parekh Hendrik Visage Henning Schild Henri Gomez @@ -1348,6 +1380,7 @@ Howard Blaise Howard Chu hsiao yi HsiehYuho on github +htasta htasta on github huanghuihui0904 Hubert Kario @@ -1529,6 +1562,7 @@ Jean-Philippe Barrette-LaPierre Jean-Philippe Menil Jeff Connelly Jeff Hodges +jeffhuang Jeff Johnson Jeff King Jeff Lawson @@ -1554,6 +1588,7 @@ Jeremy Huddleston Jeremy Lainé Jeremy Lin Jeremy Maitin-Shepard +Jeremy Nicoll Jeremy Pearson Jérémy Rabasco Jérémy Rocher @@ -1581,6 +1616,7 @@ jhauga jhoyla on github Jiacai Liu Jiang Wenjian +Jiashuo Liang Jiawen Geng Jicea Jie He @@ -1604,6 +1640,7 @@ Jishan Shaikh Jiwoo Park Jixinqi Jiyong Yang +jjchuck on hackerone jkamp-aws on github jmaggard10 on github jmdavitt on github @@ -1632,6 +1669,7 @@ Johannes Ernst Johannes G. Kristinsson Johannes Lesr Johannes Schindelin +Johannes Schlatow Johan Nilsson Johann Sebastian Schicho Johan van Selst @@ -1715,6 +1753,7 @@ Jordan Brown Jörg Mueller-Tolk Jörn Hartroth Jose Alf +Josef Cejka Josef Wolf José Joaquín Atria Jose Kahan @@ -2103,6 +2142,7 @@ Mark Brand Mark Butler Mark Davies Mark Dodgson +Mark Esler Mark Gaiser Mark Hamilton Mark Huang @@ -2311,6 +2351,7 @@ Miguel Angel Miguel Diaz migueljcrum on github Mihai Ionescu +mik Mikael Johansson Mikael Sennerholm Mikalai Ananenka @@ -2325,6 +2366,7 @@ Mike Giancola Mike Hasselberg Mike Henshaw Mike Hommey +Mike-menny on github Mike Mio Mike Norton Mike Power @@ -2373,6 +2415,7 @@ Muhamad Arga Reksapati Muhammad Herdiansyah Muhammad Hussein Ammari Muhammed Yavuz Nuzumlalı +mulan_dh on hackerone Murugan Balraj musvaage on github Muz Dima @@ -2413,6 +2456,7 @@ Neil Spring NeimadTL nekopsykose on github Nemos2024 on github +netspacer.research neutric on github nevakrien on github nevv on HackerOne/curl @@ -2524,6 +2568,7 @@ opensignature on github opensslonzos-github on github Ophir Lojkine Orange Tsai +oreadvanthink on github Oren Souroujon Oren Tirosh Orgad Shaneh @@ -2548,6 +2593,7 @@ Palo Markovic pandada8 on github Paolo Mossino Paolo Piacentini +parasol-aser Paras Sethia parazyd on github Pascal Gaudette @@ -2622,6 +2668,7 @@ pendrek at hackerone Peng Li Peng-Yu Chen pennae on github +penpal Per Jensen Per Lundberg Per Malmberg @@ -2671,6 +2718,7 @@ Phil E. Taylor Philip Chan Philip Craig Philip Gladstone +Philip H. Philip Heiduck Philip Langdale Philippe Antoine @@ -2779,6 +2827,7 @@ Rasmus Thomsen Raul Onitza-Klugman Ravi Pratap Ray Dassen +Raymond Steen Ray Pekowski Ray Satiro Razvan Cojocaru @@ -2802,6 +2851,7 @@ Renaud Guillard Renaud Lehoux Rene Bernhardt Rene Rebe +renjian on hackerone renovate[bot] renovate[bot] RepoRascal on hackerone @@ -3006,6 +3056,7 @@ Sascha Swiercy Sascha Zengler Satadru Pramanik Satana de Sant'Ana +Saud Alshareef Saul good saurabhsingh-dev on github Saurav Babu @@ -3062,6 +3113,7 @@ Sergii Pylypenko Sergio Ballestrero Sergio Barresi Sergio Borghese +Sergio Correia Sergio Durigan Junior Sergio-IME on github Sergio Mijatovic @@ -3090,6 +3142,7 @@ Sh Diao Sheshadri.V Shikha Sharma Shine Fan +Shintomon Mathew Shiraz Kanga shithappens2016 on github Shlomi Fish @@ -3097,6 +3150,7 @@ Shmulik Regev Shohei Maeda Siddhartha Prakash Jain siddharthchhabrap on github +sideshowbarker on github Sidney San Martín Siegfried Gyuricsko silveja1 on github @@ -3121,10 +3175,13 @@ smuellerDD on github sn on hackerone sofaboss on github Sohom Datta +Sollace on github Somnath Kundu Song Ma +Song X. Gao Sonia Subramanian Sören Tempel +sourceturner southernedge on github Spacen Jasset spectreglobalsec on hackerone @@ -3309,6 +3366,7 @@ Tim Friedrich Brüggemann Tim Harder Tim Heckman Tim Hill +Tim Martin Tim Mcdonough Timmy Schierling Tim Newsome @@ -3339,6 +3397,7 @@ tlahn on github tmkk on github Tobias Blomberg Tobias Bora +Tobias Frauenschläger Tobias Gabriel Tobias Hieta Tobias Hintze @@ -3453,9 +3512,12 @@ Valerii Zapodovnikov vanillajonathan on github Varnavas Papaioannou Vasiliy Faronov +Vasiliy-Kkk Vasiliy Ulyanov Vasily Lobaskin Vasy Okhin +vectorqueue on hackerone +vegagent on hackerone Venkat Akella Venkataramana Mokkapati Venkat Krishna R @@ -3480,6 +3542,7 @@ Vincent Le Normand Vincent Penquerc'h Vincent Sanders Vincent Torri +violet12331 on hackerone violetlige on github vitaha85 on github Vitaly Varyvdin @@ -3533,6 +3596,7 @@ Wez Furlong Wham Bang Wilfredo Sanchez Wilhelm von Thiele +Will Cosgrove Will Dietz Willem Hoek Willem Sparreboom @@ -3549,6 +3613,7 @@ Wojciech Zwiefka wolfsage on hackerone Wolf Vollprecht Wouter Van Rooy +wulin-nudt on github Wu Yongzheng Wu Zheng wxiaoguang on github @@ -3625,6 +3690,7 @@ Yves Lejeune YX Hao z2_ Zachary Seguin +Zartaj Majeed Zdenek Pavlas Zekun Ni zelinchen on github @@ -3636,6 +3702,8 @@ Zespre Schmidt zhanghu on xiaomi Zhang Wen Zhang Xiuhua +zhanhb on github +Zhanpeng Liu Zhaoming Luo Zhaoyang Wu Zhao Yisha From 9187ef7ec8a8d5650adb8ca6b89a5800e94fba26 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 24 Jun 2026 07:52:50 +0200 Subject: [PATCH 0536/1062] VERSIONS: 8.21.0 release --- docs/VERSIONS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 007f448e6e6d..5db6a51a7673 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -68,7 +68,8 @@ dates. The tool was called `httpget` before 2.0, `urlget` before 4.0 then `curl` since 4.0. `libcurl` and `curl` are always released in sync, using the same version numbers. -- 8.21.0: pending +- 8.22.0: pending +- 8.21.0: June 24 2026 - 8.20.0: April 29 2026 - 8.19.0: March 11 2026 - 8.18.0: January 7 2026 From 68720b4837284335b2d63cb358f8f6ce65f5bc55 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 24 Jun 2026 07:52:50 +0200 Subject: [PATCH 0537/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 101 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8c3a69c531d4..f6f67d988c01 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -5,7 +5,7 @@ curl and libcurl 8.21.0 curl_easy_setopt() options: 308 Public functions in libcurl: 100 Authors: 1489 - Contributors: 3728 + Contributors: 3731 This release includes the following changes: @@ -40,6 +40,8 @@ This release includes the following bugfixes: o cfilters: fix busy loop on blocked transfers [72] o chunked: reject invalid bytes in trailer [210] o CIPHERS.md: fix the example that uses only TLS 1.3 [137] + o cmake/FindGSS: drop "MIT Unknown" version value, related tidy ups [292] + o cmake/FindGSS: drop CMake <3.16 compatibility logic [291] o cmake/FindGSS: fix comment, adjust custom flavor property name [261] o cmake/FindGSS: prioritize MIT over GNU in pkg-config detection [196] o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] @@ -48,25 +50,31 @@ This release includes the following bugfixes: o cmake: fix zstd CMake config name [5] o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] + o cmake: simplify `LINK_ONLY` imported target extraction [294] o config2setopts: use default protocol properly [286] o connect: remove deref of freed pointer in trace call [128] o content_encoding: fix limit failure message [171] o content_encoding: fix non-last chunked rejection [209] o content_encoding: timeout during slow decoding [170] + o cookie: check __Secure- and __Host- case sensitively when read from file [256] o cookie: compare path case sensitively [52] + o cookie: reject control octets in file-loaded cookies [289] o cookie: simplify strstore(), remove outdated comment [12] o cookie: tailmatch the domains for secure override [200] o cookie: trim trailing dots when checking PSL [39] o creds: add sasl service name [75] + o creds: create with empty user+pass [304] o creds: mask OAuth bearer token in trace logs [117] o creds: remove two unused functions [158] o curl_easy_pause.md: rephrase the stream cache when pause clause [120] o curl_easy_setopt.md: change options when no transfer runs [122] o curl_formdata: fix to pass long where missing, document `CURLFORM_NAMELENGTH` [243] + o curl_multi_assign.md: clarify lifetime [264] o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] o curl_sha512_256: fix result code on error [166] o CURLINFO_CONTENT_LENGTH_UPLOAD_T.md: expand [215] + o CURLMOPT_SOCKETFUNCTION.md: this sends *all* file descriptors [266] o CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only [156] o CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only [61] o CURLOPT_DOH_URL.md: does not inherit proxy options [213] @@ -83,6 +91,7 @@ This release includes the following bugfixes: o digest: escape control codes too [206] o digest: flush proxy state on proxy or credential change [225] o digest: flush state on origin or credential change [235] + o dns-httpsrr-lookup: use origin, not peer [302] o dnscache: remove Curl_dns_entry_link [160] o docs/libcurl: fix the version for curl_multi_socket_action o docs: end "...can be used several times..." sentences with period [34] @@ -92,6 +101,9 @@ This release includes the following bugfixes: o docs: fix odd wording in CONTRIBUTE.md [107] o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] o docs: returned header size reflects HTTP/1-style format [203] + o doh: cap the maximum TTL to 24 hours [234] + o doh: drop redundant `curlx_dyn_free()` call in `doh_probe_done()` [232] + o doh: stricter HTTPS RNAME parsing [233] o ECH: cleanups [20] o event: fix wakeup consumption [93] o ftp: avoid accessing EPSV response one byte past the NULL [9] @@ -112,11 +124,14 @@ This release includes the following bugfixes: o hsts.md: mention multiple curl invokes effect [189] o hsts: duplicate live HSTS data in curl_easy_duphandle [183] o http-proxy: verify CONNECT response headers [192] + o HTTP3.md: update quiche build [229] o http: don't pass on set cookies to new origins [140] o http: prefer chunked encoding over Content-Length: 0 [146] o http: reject spurious CR bytes in headers [157] o http_digest: return better error [204] o idn: replace header guards with forward declaration [100] + o INSTALL-CMAKE.md: document CMake environment variables [246] + o INTERNALS.md: document minimum nghttp3 and ngtcp2 versions [299] o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] o KNOWN_BUGS: remove stale Threads::Threads entry [135] o krb5_sspi: fix error message on `DecryptMessage()` fail [269] @@ -130,6 +145,8 @@ This release includes the following bugfixes: o lib: two minor typos [16] o libcurl-easy.md: minor clarifications [19] o libssh2: do not use deprecated macros when unavailable [177] + o libssh2: drop stray double-negative from `strncmp()` result [194] + o libssh2: fix to return error code on missing parameter [198] o libssh2: replace macro names with non-misspelled alternatives [169] o libssh2: save non-standard port to `known_hosts` [217] o libssh2: sync version check with INTERNALS.md [176] @@ -145,11 +162,14 @@ This release includes the following bugfixes: o multi: handle pause in multi socket callback [109] o multi: remove a stale comment [216] o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] + o multi: xfers_really_alive [288] o netrc: remember and check filename loaded [212] o netrc: scanner refactor [121] o ngtcp2: fail handshake directly [138] + o openssl: do not mix OpenSSL int result with `CURLcode` variable [265] o os400sys: fix theoretical length overflows [141] o peer.h: fix typo in comment [202] + o pingpong: reject nul byte in server response line [268] o progress: fix CURLINFO time reporting [145] o psl: require libpsl 0.16.0 (2016-12-10) or greater [188] o pytest: pass `--disable` to curl [175] @@ -157,6 +177,7 @@ This release includes the following bugfixes: o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] o quic: count zero length packets against max [179] o ratelimits: use minimal burst rate [245] + o RELEASE-PROCEDURE.md: update coming relese dates o resolve: mention in error that IP address is expected [205] o rtsp: bump buf after rtsp_filter_rtp() [88] o runner.pm: apply minor correctness fix [105] @@ -181,8 +202,10 @@ This release includes the following bugfixes: o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] o setopt: gate a few proxy TLS options by checking backend support [35] o setopt: more careful cleanup of the HSTS cache [45] + o setopt: return error if received `curl_blob->data` is NULL [185] o show-headers.md: mention bold headers and --no-styled-output [17] o sigv4: URL encode the user name in the header [193] + o smb: constify `strchr()` result variable [257] o smb: integer overflow proof a size check [263] o smbserver: update internal id generation for Python 3 [238] o socket: introduce `SOCK_EAGAIN()` and use it [278] @@ -192,6 +215,7 @@ This release includes the following bugfixes: o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] o src: fix comment typos [83] + o src: sync nghttp2 versions checks with current requirements [300] o ssl native_ca_store: always reinit [211] o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] @@ -233,6 +257,7 @@ This release includes the following bugfixes: o url: connection credentials origin [228] o url: connection reuse fixes for starttls [68] o url: detect proxy changes read from environment [110] + o url: don't log bits.close state [290] o url: fix connection reuse for starttls protocols [27] o url: keep the question mark for empty queries [73] o url: remove superfluous check [131] @@ -253,6 +278,7 @@ This release includes the following bugfixes: o var: use a dedicated pointer for the alloc [219] o verify-release: verify more thoroughly with git [249] o vquic: drop stray casts for `iovec.iov_len` [162] + o vquic: fix `-Wunused-parameter` with proxies disabled [260] o vtls: more large buffer support and error checks for SHA-256 [164] o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] o vtls_scache: include signature_algorithms in the SSL peer cache key [123] @@ -262,6 +288,7 @@ This release includes the following bugfixes: o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] o VULN-DISCLOSURE-POLICY: non-released code [253] o websockets: auto-tunnel through http proxy [102] + o websockets: buffer ugprade data at connection level [237] o windows: update MS SDK versions in comments [60] o winldap: avoid NULL pointer deref on `ldap_get_dn()` fail [242] o ws: make pong sending lazy [201] @@ -290,28 +317,30 @@ advice from friends like these: 0xN3R3K3, 11soda11, Ady Elouej, A Johnston, Alan De Smet, alhudz, alienowo on hackerone, ambikeesshh, amitbidlan, Andreas Falkenhahn, - Andrei Rybak, Andrew Nesbitt, Aritra Basu, azraelxuemo on hackerone, - Bartel Sielski, Bastian Jesuiter, BazaarAcc32 on github, Bill Mill, - ByteRay on hackerone, chrizilla on github, co-authors in libssh2, - correctmost on github, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, - Dario Vinella, Darren Banfi, Dave Walker, daviey on hackerone, - dependabot[bot], dyingc on github, Earnestly on github, Elise Vance, - Emanuel Krollmann, Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, - Filipe Casal, Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, - Hem Parekh, htasta, jeffhuang, Jeremy Nicoll, Jiashuo Liang, - jjchuck on hackerone, Johannes Schlatow, Josef Cejka, Joshua Rogers, - Kai Pastor, Marcel Raad, Mark Esler, Max Dymond, mik, Mike-menny on github, - Muhamad Arga Reksapati, mulan_dh on hackerone, oreadvanthink on github, - parasol-aser, penpal, Peter Krefting, Philip H., Rainer Jung, - Randall S. Becker, Raymond Steen, Ray Satiro, renjian on hackerone, - renovate[bot], Ross Burton, Saud Alshareef, Sergio Correia, sfan5 on github, - Shintomon Mathew, Sollace on github, Song X. Gao, sourceturner, - Stefan Eissing, Tatsuhiro Tsujikawa, Tim Martin, tiymat, - Tobias Frauenschläger, Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, - vegagent on hackerone, Viktor Szakats, violet12331 on hackerone, - Will Cosgrove, wulin-nudt on github, Xi Ruoyao, x-xiang on github, - Yedaya Katsman, zhanhb on github, Zhanpeng Liu - (96 contributors) + Andrei Rybak, Andrew Nesbitt, Aritra Basu, av223119 on github, + azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, + BazaarAcc32 on github, Bill Mill, Bryan Henderson, ByteRay on hackerone, + chrizilla on github, co-authors in libssh2, correctmost on github, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, + Darren Banfi, Dave Walker, daviey on hackerone, dependabot[bot], + dyingc on github, Earnestly on github, Elise Vance, Emanuel Krollmann, + Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, Filipe Casal, + Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, Hem Parekh, htasta, + jeffhuang, Jeremy Nicoll, Jiashuo Liang, jjchuck on hackerone, + Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, + Mark Esler, Max Dymond, Michael Kaufmann, mik, Mike-menny on github, + Muhamad Arga Reksapati, mulan_dh on hackerone, netspacer.research, + oreadvanthink on github, parasol-aser, penpal, Peter Krefting, Philip H., + Rainer Jung, Randall S. Becker, Raymond Steen, Ray Satiro, + renjian on hackerone, renovate[bot], Ross Burton, Saud Alshareef, + Sergio Correia, sfan5 on github, Shintomon Mathew, sideshowbarker on github, + Sollace on github, Song X. Gao, sourceturner, Stefan Eissing, + Tatsuhiro Tsujikawa, Tim Martin, tiymat, Tobias Frauenschläger, + Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, vegagent on hackerone, + Viktor Szakats, violet12331 on hackerone, Will Cosgrove, + wulin-nudt on github, Xi Ruoyao, x-xiang on github, Yedaya Katsman, + Zartaj Majeed, zhanhb on github, Zhanpeng Liu + (102 contributors) References to bug reports and discussions on issues: @@ -498,6 +527,7 @@ References to bug reports and discussions on issues: [182] = https://curl.se/bug/?i=21858 [183] = https://curl.se/bug/?i=21809 [184] = https://curl.se/bug/?i=21930 + [185] = https://curl.se/bug/?i=22129 [186] = https://curl.se/bug/?i=21976 [187] = https://curl.se/bug/?i=21970 [188] = https://curl.se/bug/?i=21933 @@ -506,9 +536,11 @@ References to bug reports and discussions on issues: [191] = https://curl.se/bug/?i=21773 [192] = https://curl.se/bug/?i=21927 [193] = https://curl.se/bug/?i=21923 + [194] = https://curl.se/bug/?i=22126 [195] = https://curl.se/bug/?i=21881 [196] = https://curl.se/bug/?i=22052 [197] = https://curl.se/bug/?i=21922 + [198] = https://curl.se/bug/?i=22125 [199] = https://curl.se/bug/?i=21914 [200] = https://curl.se/bug/?i=21910 [201] = https://curl.se/bug/?i=21911 @@ -538,20 +570,33 @@ References to bug reports and discussions on issues: [226] = https://curl.se/bug/?i=21945 [227] = https://curl.se/bug/?i=22048 [228] = https://curl.se/bug/?i=22040 + [229] = https://curl.se/bug/?i=22105 [230] = https://curl.se/bug/?i=21949 [231] = https://curl.se/bug/?i=22038 + [232] = https://curl.se/bug/?i=22133 + [233] = https://curl.se/bug/?i=22124 + [234] = https://curl.se/bug/?i=22122 [235] = https://curl.se/bug/?i=21944 [236] = https://curl.se/bug/?i=22033 + [237] = https://curl.se/bug/?i=22107 [238] = https://curl.se/bug/?i=21937 [239] = https://curl.se/bug/?i=22030 [242] = https://curl.se/bug/?i=22000 [243] = https://curl.se/bug/?i=22017 [245] = https://curl.se/bug/?i=22016 + [246] = https://curl.se/bug/?i=22114 [249] = https://curl.se/bug/?i=22018 [253] = https://curl.se/bug/?i=22025 + [256] = https://curl.se/bug/?i=22085 + [257] = https://curl.se/bug/?i=22094 + [260] = https://curl.se/bug/?i=22104 [261] = https://curl.se/bug/?i=22013 [262] = https://curl.se/bug/?i=22004 [263] = https://curl.se/bug/?i=22001 + [264] = https://curl.se/bug/?i=22088 + [265] = https://curl.se/bug/?i=22087 + [266] = https://curl.se/bug/?i=22081 + [268] = https://curl.se/bug/?i=21996 [269] = https://curl.se/bug/?i=22003 [270] = https://curl.se/bug/?i=22002 [271] = https://curl.se/bug/?i=21998 @@ -567,3 +612,13 @@ References to bug reports and discussions on issues: [281] = https://curl.se/bug/?i=21979 [283] = https://curl.se/bug/?i=21989 [286] = https://curl.se/bug/?i=21983 + [288] = https://curl.se/bug/?i=22050 + [289] = https://curl.se/bug/?i=22070 + [290] = https://curl.se/bug/?i=22073 + [291] = https://curl.se/bug/?i=22072 + [292] = https://curl.se/bug/?i=22052 + [294] = https://curl.se/bug/?i=22063 + [299] = https://curl.se/bug/?i=22062 + [300] = https://curl.se/bug/?i=22061 + [302] = https://curl.se/bug/?i=22059 + [304] = https://curl.se/bug/?i=21943 From 93235c1b52b5e0ee4c865ff50fb0d4bf44e6882f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 24 Jun 2026 11:03:18 +0200 Subject: [PATCH 0538/1062] GHA/linux: drop unused pip packages from Alpine job py3-impacket, py3-asn1, py3-six and py3-pycryptodomex. Number of tests run remain 1848 / 1847 for the two jobs respectively. Number of installed packages go from 158 to 117, and from 177 to 142 (for the two jobs respectively; using different Alpine versions). Closes #22153 --- .github/workflows/linux.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fff7f1c8765c..fd8a430c275c 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -503,7 +503,6 @@ jobs: apk add --no-cache build-base autoconf automake libtool perl openssl-dev \ libssh2-dev zlib-dev brotli-dev zstd-dev libidn2-dev openldap-dev \ krb5-dev libpsl-dev c-ares-dev \ - py3-impacket py3-asn1 py3-six py3-pycryptodomex \ perl-time-hires openssh stunnel sudo git openssl - name: 'install Fil-C' From a1af38be0902a87d52f57dba5ebf3154adcb615b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 04:24:50 +0000 Subject: [PATCH 0539/1062] GHA: update debian:bookworm-slim Docker digest to 60eac75 Closes #22151 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 369583f62fdc..85d2f35139c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:96e378d7e6531ac9a15ad505478fcc2e69f371b10f5cdf87857c4b8188404716 +FROM debian:bookworm-slim@sha256:60eac759739651111db372c07be67863818726f754804b8707c90979bda511df RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From 4ce309d968cfe02c402d07b2911241c38a8adf6b Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Wed, 24 Jun 2026 01:59:05 -0400 Subject: [PATCH 0540/1062] ldap: support insecure mode for Windows native LDAP - Bypass cert verification if verifypeer is disabled. Prior to this change libcurl lacked the ability to bypass certificate verification for Windows native LDAP (USE_WIN32_LDAP). A comment said "Win32 LDAP SDK does not support insecure mode without CA!" but I found that we can bypass the check by setting a verify callback to override Windows' internal verify check. Closes https://github.com/curl/curl/pull/22152 --- lib/ldap.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/ldap.c b/lib/ldap.c index a6a69eb7f1e7..29bc5efab2d5 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -251,6 +251,17 @@ static bool ldap_value_needs_base64(const char *attr, size_t attr_len, return FALSE; } +#ifdef USE_WIN32_LDAP +static BOOLEAN bypass_cert_verify(PLDAP Connection, + PCCERT_CONTEXT *ppServerCert) +{ + (void)Connection; + CertFreeCertificateContext(*ppServerCert); + /* approve any certificate since the verification is set to bypass */ + return TRUE; +} +#endif + static CURLcode ldap_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -340,8 +351,18 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP - /* Win32 LDAP SDK does not support insecure mode without CA! */ + /* Win32 LDAP uses the Windows CA store to verify certificates */ ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); + if(!conn->ssl_config.verifypeer) { + if(conn->ssl_config.verifyhost) { + failf(data, "LDAP local: host verification cannot be enabled when " + "peer verification is disabled for Windows native LDAP"); + result = CURLE_NOT_BUILT_IN; + goto quit; + } + ldap_set_option(server, LDAP_OPT_SERVER_CERTIFICATE, + (void *)(uintptr_t)bypass_cert_verify); + } #else /* !USE_WIN32_LDAP */ int ldap_option; const char *ldap_ca = conn->ssl_config.CAfile; From 5232c1620897213db1781937c0bace0b7320474f Mon Sep 17 00:00:00 2001 From: Samuel Dainard Date: Wed, 24 Jun 2026 19:53:52 +0000 Subject: [PATCH 0541/1062] sws: allow connection-monitor to log all disconnects Remove the suppression of connmon after the first disconnect event. The connmon flag is set per-test via the 'connection-monitor' server command and should remain active for the lifetime of that test's connections to properly detect connection reuse failures. The suppression was introduced in 510fdad to work around a connection reuse regression that has since been resolved by the credentials refactoring in 8f71d0f. With the underlying issue fixed, restoring full disconnect logging strengthens tests like 338 to catch future regressions in connection reuse logic. Fixes #22158 Closes #22159 --- tests/server/sws.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/server/sws.c b/tests/server/sws.c index 04e6025fa5fe..a24b3277ca7d 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -2327,7 +2327,6 @@ static int test_sws(int argc, const char *argv[]) if(req->connmon) { const char *keepopen = "[DISCONNECT]\n"; storerequest(keepopen, strlen(keepopen), REQUEST_DUMP_FILENAME); - req->connmon = FALSE; } if(!req->open) From c10a7aa2596136a744856d7d614f3d69384b7b05 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 25 Jun 2026 08:41:27 +0200 Subject: [PATCH 0542/1062] RELEASE-NOTES: synced and bump curlver.h --- RELEASE-NOTES | 600 +---------------------------------------- include/curl/curlver.h | 6 +- 2 files changed, 13 insertions(+), 593 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index f6f67d988c01..5587e8e89667 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,299 +1,19 @@ -curl and libcurl 8.21.0 +curl and libcurl 8.21.1 - Public curl releases: 275 + Public curl releases: 276 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1489 - Contributors: 3731 + Authors: 1490 + Contributors: 3732 This release includes the following changes: - o curl: named globs in output file name for upload glob references [77] - o HTTP/3: add proxy CONNECT and MASQUE CONNECT-UDP support (ngtcp2 QUIC) [53] - o http2: remove stream dependency tracking [40] - o lib: drop support for CURLAUTH_DIGEST_IE [4] - o libssh: add support for SHA256 host public keys [57] - o tool_urlglob: add named globs [92] This release includes the following bugfixes: - o _ENVIRONMENT.md. Windows does case insensitive env variables [214] - o _URL.md: remove the zone-id mention [227] - o AmigaOS: curl_setup.h avoid explicit_bzero with clib2 [283] - o AmigaOS: fix build fallouts, re-add to CI [279] - o asyn-thrdd: add IPv6 guards [195] - o asyn-thrdd: fix result processing without wakeup socketpair [2] - o autotools: mbedtls detection fixes [163] - o BINDINGS: Update Hollywood link [181] - o BUFQ.md: re-sync with source code [111] - o build: enable `-Wlogical-op` picky warning for GCC 4.4+ [277] - o build: omit zlib pkg-config reference for Android [130] - o cf-h2-prox: fix peer leak [132] - o cf-h2-proxy: drop interim responses [47] - o cf-https-connect: do not engage on proxy origin [236] - o cf-ip-happy.c: minor comment typo - o cf-ip-happy: update documentation [223] - o cf-socket: make Curl_addr2string static [224] - o cf-socket: set scope_id for IPv6 link-local addresses [150] - o cf-socket: store errno from do_connect in ctx->error [199] - o cfilters: fix busy loop on blocked transfers [72] - o chunked: reject invalid bytes in trailer [210] - o CIPHERS.md: fix the example that uses only TLS 1.3 [137] - o cmake/FindGSS: drop "MIT Unknown" version value, related tidy ups [292] - o cmake/FindGSS: drop CMake <3.16 compatibility logic [291] - o cmake/FindGSS: fix comment, adjust custom flavor property name [261] - o cmake/FindGSS: prioritize MIT over GNU in pkg-config detection [196] - o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] - o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] - o cmake: fix three issues generating lib options in config files [126] - o cmake: fix zstd CMake config name [5] - o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] - o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] - o cmake: simplify `LINK_ONLY` imported target extraction [294] - o config2setopts: use default protocol properly [286] - o connect: remove deref of freed pointer in trace call [128] - o content_encoding: fix limit failure message [171] - o content_encoding: fix non-last chunked rejection [209] - o content_encoding: timeout during slow decoding [170] - o cookie: check __Secure- and __Host- case sensitively when read from file [256] - o cookie: compare path case sensitively [52] - o cookie: reject control octets in file-loaded cookies [289] - o cookie: simplify strstore(), remove outdated comment [12] - o cookie: tailmatch the domains for secure override [200] - o cookie: trim trailing dots when checking PSL [39] - o creds: add sasl service name [75] - o creds: create with empty user+pass [304] - o creds: mask OAuth bearer token in trace logs [117] - o creds: remove two unused functions [158] - o curl_easy_pause.md: rephrase the stream cache when pause clause [120] - o curl_easy_setopt.md: change options when no transfer runs [122] - o curl_formdata: fix to pass long where missing, document `CURLFORM_NAMELENGTH` [243] - o curl_multi_assign.md: clarify lifetime [264] - o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] - o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] - o curl_sha512_256: fix result code on error [166] - o CURLINFO_CONTENT_LENGTH_UPLOAD_T.md: expand [215] - o CURLMOPT_SOCKETFUNCTION.md: this sends *all* file descriptors [266] - o CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only [156] - o CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only [61] - o CURLOPT_DOH_URL.md: does not inherit proxy options [213] - o CURLOPT_ECH.md: simplify the description language [18] - o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] - o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] - o CURLOPT_PINNEDPUBLICKEY.md: does not apply for other origins [152] - o CURLOPT_PORT.md: use stronger language [133] - o CURLOPT_SHARE: warn about early remove [51] - o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] - o CURLOPT_WRITEFUNCTION.md: mention redirects [226] - o CURLOPT_WRITEFUNCTION.md: remove stray reference to HSTS [208] - o delta: harden external command invocations [98] - o digest: escape control codes too [206] - o digest: flush proxy state on proxy or credential change [225] - o digest: flush state on origin or credential change [235] - o dns-httpsrr-lookup: use origin, not peer [302] - o dnscache: remove Curl_dns_entry_link [160] - o docs/libcurl: fix the version for curl_multi_socket_action - o docs: end "...can be used several times..." sentences with period [34] - o docs: fix --follow doc typo [97] - o docs: fix a couple of typos [62] - o docs: fix grammar and wording in FAQ [66] - o docs: fix odd wording in CONTRIBUTE.md [107] - o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] - o docs: returned header size reflects HTTP/1-style format [203] - o doh: cap the maximum TTL to 24 hours [234] - o doh: drop redundant `curlx_dyn_free()` call in `doh_probe_done()` [232] - o doh: stricter HTTPS RNAME parsing [233] - o ECH: cleanups [20] - o event: fix wakeup consumption [93] - o ftp: avoid accessing EPSV response one byte past the NULL [9] - o ftp: remove 2 Curl_resolv_blocking() calls [30] - o ftp: remove bits.ftp_use_control_ssl [28] - o ftplistparser: clear strings.target if not symlink [148] - o gnutls: allow building with nettle 4.0 [96] - o gnutls: fix more nettle 4+ compatibility issues [94] - o gnutls: require 3.7.2 for earlydata [103] - o gsasl: fix potential double free [56] - o gtls: fix ignored return and uninitialized status in OCSP check [49] - o gtls: fix some typos [15] - o gtls: minor fixes and improvements [190] - o gtls: use the correct return code in trace output [173] - o gtls: verify OCSP response signature in gtls_verify_ocsp_status [86] - o h3-proxy: fix callback return values, and a typo in tests [139] - o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] - o hsts.md: mention multiple curl invokes effect [189] - o hsts: duplicate live HSTS data in curl_easy_duphandle [183] - o http-proxy: verify CONNECT response headers [192] - o HTTP3.md: update quiche build [229] - o http: don't pass on set cookies to new origins [140] - o http: prefer chunked encoding over Content-Length: 0 [146] - o http: reject spurious CR bytes in headers [157] - o http_digest: return better error [204] - o idn: replace header guards with forward declaration [100] - o INSTALL-CMAKE.md: document CMake environment variables [246] - o INTERNALS.md: document minimum nghttp3 and ngtcp2 versions [299] - o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] - o KNOWN_BUGS: remove stale Threads::Threads entry [135] - o krb5_sspi: fix error message on `DecryptMessage()` fail [269] - o ldap: base64 encode binary LDIF values with WinLDAP [273] - o ldap: fix minor leak on write callback error [24] - o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] - o ldap: switch off chasing referrals [114] - o lib678: fix to not be perma-skipped [10] - o lib: make `__STDC_VERSION__` literals `L` (where missing) - o lib: transfer origin and proxy handling [276] - o lib: two minor typos [16] - o libcurl-easy.md: minor clarifications [19] - o libssh2: do not use deprecated macros when unavailable [177] - o libssh2: drop stray double-negative from `strncmp()` result [194] - o libssh2: fix to return error code on missing parameter [198] - o libssh2: replace macro names with non-misspelled alternatives [169] - o libssh2: save non-standard port to `known_hosts` [217] - o libssh2: sync version check with INTERNALS.md [176] - o libssh2: use non-deprecated `libssh2_knownhost_addc()` [178] - o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] - o m4: drop redundant conditions in TLS library detections [155] - o Makefile.am: drop test1190 listed twice [144] - o managen: apply minor fixes and improvements [115] - o mbedtls: null-terminate the private key blob [36] - o mk-unity.pl: `#include`, and not concatenate input headers [124] - o mqtt: return error on truncated Remaining Length [230] - o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] - o multi: handle pause in multi socket callback [109] - o multi: remove a stale comment [216] - o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] - o multi: xfers_really_alive [288] - o netrc: remember and check filename loaded [212] - o netrc: scanner refactor [121] - o ngtcp2: fail handshake directly [138] - o openssl: do not mix OpenSSL int result with `CURLcode` variable [265] - o os400sys: fix theoretical length overflows [141] - o peer.h: fix typo in comment [202] - o pingpong: reject nul byte in server response line [268] - o progress: fix CURLINFO time reporting [145] - o psl: require libpsl 0.16.0 (2016-12-10) or greater [188] - o pytest: pass `--disable` to curl [175] - o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] - o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] - o quic: count zero length packets against max [179] - o ratelimits: use minimal burst rate [245] - o RELEASE-PROCEDURE.md: update coming relese dates - o resolve: mention in error that IP address is expected [205] - o rtsp: bump buf after rtsp_filter_rtp() [88] - o runner.pm: apply minor correctness fix [105] - o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] - o runtests: fix tests for curl builds with embedded CA bundle [187] - o rustls: error on CURLOPT_CRLFILE with native CA store [59] - o schannel: check `schannel_sha256sum()` success, and more [165] - o schannel: enforce Extended Key Usage for custom CA roots [29] - o schannel: error on TLS 1.3-only with cipher list [136] - o schannel: fix https proxy for client cert and certinfo [280] - o schannel: fix revoke_best_effort setting for proxy [70] - o schannel: use fopen instead CreateFile [191] - o schannel_verify: avoid out of blob access [11] - o schannel_verify: simplify CryptQueryObject use [159] - o scripts: catch Credits-to contributors [127] - o SECURITY-ADVISORY.md: expand [220] - o setopt: changing the proxy port is also a proxy change [23] - o setopt: clear proxy auth properly on NULL [81] - o setopt: clear the "custom" CA booleans when set to NULL [218] - o setopt: CURLOPT_MAXCONNECTS set to 0 restores default value [161] - o setopt: defref the old referer when setting a new [168] - o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] - o setopt: gate a few proxy TLS options by checking backend support [35] - o setopt: more careful cleanup of the HSTS cache [45] - o setopt: return error if received `curl_blob->data` is NULL [185] - o show-headers.md: mention bold headers and --no-styled-output [17] - o sigv4: URL encode the user name in the header [193] - o smb: constify `strchr()` result variable [257] - o smb: integer overflow proof a size check [263] - o smbserver: update internal id generation for Python 3 [238] - o socket: introduce `SOCK_EAGAIN()` and use it [278] - o socket: use name `sockerr` for socket error variables [271] - o socks_sspi: invalid response length is a fatal error [272] - o socks_sspi: store socks5_gssapi_enctype [262] - o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] - o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] - o src: fix comment typos [83] - o src: sync nghttp2 versions checks with current requirements [300] - o ssl native_ca_store: always reinit [211] - o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] - o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] - o sspi: free libcurl allocated memory with curlx_free [274] - o telnet: drop an `int` cast no longer necessary [270] - o telnet: drop redundant interim variables [275] - o telnet: fix error message typos [186] - o telnet: fix old copy-paste typo in variable name [281] - o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] - o test1588: use %TESTNUMBER, not hard-coded number [118] - o test1981: explicitly set the locale [85] - o tests: add `cookies` feature to some tests [182] - o tests: add an assert to avoid IPC blocking [69] - o tests: add the "--resolve" keyword to tests that lack it [184] - o tests: fix unit1636 with --disable-progress-meter [37] - o tftp: avoid the timeout calc if the timeout is crazy [151] - o tftp: stricter option name checks [90] - o tidy-up: add space around operators, where missing [147] - o tidy-up: apply clang-format fixes [153] - o tidy-up: drop stray casts for allocated pointers [174] - o tidy-up: miscellaneous [106] - o tls: fix incomplete mTLS config in conn reuse and session cache [108] - o tls: wolfssl: fixes for PQC key shares [239] - o tool: warn when --ssl and --ftp-ssl-control override each other [129] - o tool_formparse.c: fix two minor comment typos [25] - o tool_formparse: polish error message + make two functions static [1] - o tool_formparse: tool2curlparts is no longer recursive [33] - o tool_help: rectify a bad assert [143] - o tool_operhlp: avoid NULL to %s [142] - o tool_urlglob: avoid overflow at end of range [22] - o tool_urlglob: better 'Duplicate glob name' position [82] - o tool_urlglob: make globbing error reported for correct position [91] - o tool_writeout: fix %time{} output for %s [231] - o transfer: clear referer when set to NULL [112] - o unit1675: fix potential memory leak on dynbuf fail path [197] - o unix-sockets: ignore proxy settings [6] - o URL-SYNTAX: document more URL parsing details [134] - o url: compare full origin when setting credentials [42] - o url: connection credentials origin [228] - o url: connection reuse fixes for starttls [68] - o url: detect proxy changes read from environment [110] - o url: don't log bits.close state [290] - o url: fix connection reuse for starttls protocols [27] - o url: keep the question mark for empty queries [73] - o url: remove superfluous check [131] - o url: url_match_destination fix [43] - o urlapi: accept 0X prefix in IPv4 address as well [63] - o urlapi: change more lowercase percent-encoded to uppercase [71] - o urlapi: compare zone-id in Curl_url_same_origin() [95] - o urlapi: consume trailing dots after IPv4 numerical addresses [50] - o urlapi: deny hostnames with more than one trailing dot [58] - o urlapi: drop base fragment on empty redirect [64] - o urlapi: fix an issue parsing file URLs [149] - o urlapi: fix memleaks on error in `parse_hostname_login()` [221] - o urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set [46] - o urlapi: forbid '|' in host [172] - o urlapi: handle redirect without set scheme with default-scheme [38] - o urlapi: URL decode hostname before IP address normalization [207] - o user-agent.md: mention double quotes too [3] - o var: use a dedicated pointer for the alloc [219] - o verify-release: verify more thoroughly with git [249] - o vquic: drop stray casts for `iovec.iov_len` [162] - o vquic: fix `-Wunused-parameter` with proxies disabled [260] - o vtls: more large buffer support and error checks for SHA-256 [164] - o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] - o vtls_scache: include signature_algorithms in the SSL peer cache key [123] - o vtls_spack: drop redundant macro fallbacks [167] - o VULN-DISCLOSURE-POLICY.md: emphasize comm as a human [180] - o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] - o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] - o VULN-DISCLOSURE-POLICY: non-released code [253] - o websockets: auto-tunnel through http proxy [102] - o websockets: buffer ugprade data at connection level [237] - o windows: update MS SDK versions in comments [60] - o winldap: avoid NULL pointer deref on `ldap_get_dn()` fail [242] - o ws: make pong sending lazy [201] - o x509asn1: fix DH public key parameter extraction [44] - o x509asn1: fix operator order in do_pubkey [21] + o ldap: support insecure mode for Windows native LDAP [3] + o sws: allow connection-monitor to log all disconnects [2] This release includes the following known bugs: @@ -315,310 +35,10 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 0xN3R3K3, 11soda11, Ady Elouej, A Johnston, Alan De Smet, alhudz, - alienowo on hackerone, ambikeesshh, amitbidlan, Andreas Falkenhahn, - Andrei Rybak, Andrew Nesbitt, Aritra Basu, av223119 on github, - azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, - BazaarAcc32 on github, Bill Mill, Bryan Henderson, ByteRay on hackerone, - chrizilla on github, co-authors in libssh2, correctmost on github, - Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, - Darren Banfi, Dave Walker, daviey on hackerone, dependabot[bot], - dyingc on github, Earnestly on github, Elise Vance, Emanuel Krollmann, - Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, Filipe Casal, - Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, Hem Parekh, htasta, - jeffhuang, Jeremy Nicoll, Jiashuo Liang, jjchuck on hackerone, - Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, - Mark Esler, Max Dymond, Michael Kaufmann, mik, Mike-menny on github, - Muhamad Arga Reksapati, mulan_dh on hackerone, netspacer.research, - oreadvanthink on github, parasol-aser, penpal, Peter Krefting, Philip H., - Rainer Jung, Randall S. Becker, Raymond Steen, Ray Satiro, - renjian on hackerone, renovate[bot], Ross Burton, Saud Alshareef, - Sergio Correia, sfan5 on github, Shintomon Mathew, sideshowbarker on github, - Sollace on github, Song X. Gao, sourceturner, Stefan Eissing, - Tatsuhiro Tsujikawa, Tim Martin, tiymat, Tobias Frauenschläger, - Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, vegagent on hackerone, - Viktor Szakats, violet12331 on hackerone, Will Cosgrove, - wulin-nudt on github, Xi Ruoyao, x-xiang on github, Yedaya Katsman, - Zartaj Majeed, zhanhb on github, Zhanpeng Liu - (102 contributors) + Daniel Stenberg, Ray Satiro, renovate[bot], Samuel Dainard, Viktor Szakats + (5 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=21510 - [2] = https://curl.se/bug/?i=21476 - [3] = https://curl.se/mail/archive-2026-04/0029.html - [4] = https://curl.se/bug/?i=21486 - [5] = https://curl.se/bug/?i=21538 - [6] = https://curl.se/bug/?i=21630 - [7] = https://hackerone.com/reports/3702718 - [8] = https://curl.se/bug/?i=21470 - [9] = https://curl.se/bug/?i=21545 - [10] = https://curl.se/bug/?i=21641 - [11] = https://curl.se/bug/?i=21543 - [12] = https://curl.se/bug/?i=21541 - [13] = https://curl.se/bug/?i=21640 - [14] = https://curl.se/bug/?i=21634 - [15] = https://curl.se/bug/?i=21498 - [16] = https://curl.se/bug/?i=21496 - [17] = https://curl.se/bug/?i=21495 - [18] = https://curl.se/bug/?i=21536 - [19] = https://curl.se/bug/?i=21491 - [20] = https://curl.se/bug/?i=21532 - [21] = https://curl.se/bug/?i=21533 - [22] = https://curl.se/bug/?i=21529 - [23] = https://curl.se/bug/?i=21485 - [24] = https://curl.se/bug/?i=21530 - [25] = https://curl.se/bug/?i=21480 - [26] = https://curl.se/bug/?i=21631 - [27] = https://curl.se/bug/?i=21522 - [28] = https://curl.se/bug/?i=21521 - [29] = https://curl.se/bug/?i=21629 - [30] = https://curl.se/bug/?i=21512 - [32] = https://curl.se/bug/?i=21517 - [33] = https://curl.se/bug/?i=21518 - [34] = https://curl.se/bug/?i=21644 - [35] = https://curl.se/bug/?i=21514 - [36] = https://curl.se/bug/?i=21515 - [37] = https://curl.se/bug/?i=21500 - [38] = https://curl.se/bug/?i=21632 - [39] = https://curl.se/bug/?i=21636 - [40] = https://curl.se/bug/?i=21723 - [41] = https://curl.se/bug/?i=21720 - [42] = https://curl.se/bug/?i=21575 - [43] = https://curl.se/bug/?i=21573 - [44] = https://curl.se/bug/?i=21595 - [45] = https://curl.se/bug/?i=21615 - [46] = https://curl.se/bug/?i=21721 - [47] = https://curl.se/bug/?i=21626 - [48] = https://curl.se/bug/?i=21606 - [49] = https://curl.se/bug/?i=21679 - [50] = https://curl.se/bug/?i=21635 - [51] = https://curl.se/bug/?i=21633 - [52] = https://curl.se/bug/?i=21616 - [53] = https://curl.se/bug/?i=21153 - [54] = https://curl.se/bug/?i=21707 - [55] = https://curl.se/bug/?i=21714 - [56] = https://curl.se/bug/?i=21609 - [57] = https://curl.se/bug/?i=21605 - [58] = https://curl.se/bug/?i=21622 - [59] = https://curl.se/bug/?i=21614 - [60] = https://curl.se/bug/?i=21621 - [61] = https://curl.se/bug/?i=21890 - [62] = https://curl.se/bug/?i=21617 - [63] = https://curl.se/bug/?i=21820 - [64] = https://curl.se/bug/?i=21745 - [65] = https://curl.se/bug/?i=21682 - [66] = https://curl.se/bug/?i=21593 - [67] = https://curl.se/bug/?i=21597 - [68] = https://curl.se/bug/?i=21665 - [69] = https://curl.se/bug/?i=21688 - [70] = https://curl.se/bug/?i=21683 - [71] = https://curl.se/bug/?i=21592 - [72] = https://curl.se/bug/?i=21671 - [73] = https://curl.se/bug/?i=21544 - [74] = https://curl.se/bug/?i=21583 - [75] = https://curl.se/bug/?i=21585 - [76] = https://curl.se/bug/?i=21642 - [77] = https://curl.se/bug/?i=21407 - [78] = https://curl.se/bug/?i=21582 - [79] = https://curl.se/bug/?i=21576 - [80] = https://curl.se/bug/?i=21699 - [81] = https://curl.se/bug/?i=21696 - [82] = https://curl.se/bug/?i=21567 - [83] = https://curl.se/bug/?i=21570 - [84] = https://curl.se/bug/?i=21569 - [85] = https://curl.se/bug/?i=21749 - [86] = https://curl.se/bug/?i=21677 - [87] = https://curl.se/bug/?i=21562 - [88] = https://curl.se/bug/?i=21563 - [89] = https://curl.se/bug/?i=21528 - [90] = https://curl.se/bug/?i=21560 - [91] = https://curl.se/bug/?i=21561 - [92] = https://curl.se/bug/?i=21409 - [93] = https://curl.se/bug/?i=21547 - [94] = https://curl.se/bug/?i=21557 - [95] = https://curl.se/bug/?i=21686 - [96] = https://curl.se/bug/?i=21169 - [97] = https://curl.se/bug/?i=21553 - [98] = https://curl.se/bug/?i=21104 - [99] = https://curl.se/bug/?i=21523 - [100] = https://curl.se/bug/?i=21551 - [101] = https://curl.se/bug/?i=21550 - [102] = https://curl.se/bug/?i=21663 - [103] = https://curl.se/bug/?i=21750 - [104] = https://curl.se/bug/?i=21685 - [105] = https://curl.se/bug/?i=21672 - [106] = https://curl.se/bug/?i=21646 - [107] = https://curl.se/bug/?i=21705 - [108] = https://curl.se/bug/?i=21667 - [109] = https://curl.se/bug/?i=21748 - [110] = https://curl.se/bug/?i=21666 - [111] = https://curl.se/bug/?i=21678 - [112] = https://curl.se/bug/?i=21741 - [113] = https://curl.se/bug/?i=21747 - [114] = https://curl.se/bug/?i=21732 - [115] = https://curl.se/bug/?i=21670 - [116] = https://curl.se/bug/?i=21668 - [117] = https://curl.se/bug/?i=21659 - [118] = https://curl.se/bug/?i=21662 - [119] = https://curl.se/bug/?i=21660 - [120] = https://curl.se/bug/?i=21658 - [121] = https://curl.se/bug/?i=21624 - [122] = https://curl.se/bug/?i=21604 - [123] = https://curl.se/bug/?i=21651 - [124] = https://curl.se/bug/?i=21656 - [125] = https://curl.se/bug/?i=21724 - [126] = https://curl.se/bug/?i=21654 - [127] = https://curl.se/bug/?i=21653 - [128] = https://curl.se/bug/?i=21649 - [129] = https://curl.se/bug/?i=21887 - [130] = https://curl.se/bug/?i=21647 - [131] = https://curl.se/bug/?i=21650 - [132] = https://curl.se/bug/?i=21602 - [133] = https://curl.se/bug/?i=21886 - [134] = https://curl.se/bug/?i=21841 - [135] = https://curl.se/bug/?i=21734 - [136] = https://curl.se/bug/?i=21702 - [137] = https://curl.se/bug/?i=21719 - [138] = https://curl.se/bug/?i=21712 - [139] = https://curl.se/bug/?i=21802 - [140] = https://curl.se/bug/?i=21794 - [141] = https://curl.se/bug/?i=21840 - [142] = https://curl.se/bug/?i=21836 - [143] = https://curl.se/bug/?i=21837 - [144] = https://curl.se/bug/?i=21839 - [145] = https://curl.se/bug/?i=21828 - [146] = https://curl.se/bug/?i=21706 - [147] = https://curl.se/bug/?i=21793 - [148] = https://curl.se/bug/?i=21884 - [149] = https://curl.se/bug/?i=21743 - [150] = https://curl.se/bug/?i=21669 - [151] = https://curl.se/bug/?i=21782 - [152] = https://curl.se/bug/?i=21885 - [153] = https://curl.se/bug/?i=21786 - [154] = https://curl.se/bug/?i=21784 - [155] = https://curl.se/bug/?i=21781 - [156] = https://curl.se/bug/?i=21883 - [157] = https://curl.se/bug/?i=21882 - [158] = https://curl.se/bug/?i=21776 - [159] = https://curl.se/bug/?i=21760 - [160] = https://curl.se/bug/?i=21774 - [161] = https://curl.se/bug/?i=21829 - [162] = https://curl.se/bug/?i=21877 - [163] = https://curl.se/bug/?i=21727 - [164] = https://curl.se/bug/?i=21771 - [165] = https://curl.se/bug/?i=21739 - [166] = https://curl.se/bug/?i=21767 - [167] = https://curl.se/bug/?i=21768 - [168] = https://curl.se/bug/?i=21826 - [169] = https://curl.se/bug/?i=21876 - [170] = https://curl.se/bug/?i=21603 - [171] = https://curl.se/bug/?i=21756 - [172] = https://curl.se/bug/?i=21762 - [173] = https://curl.se/bug/?i=21766 - [174] = https://curl.se/bug/?i=21865 - [175] = https://curl.se/bug/?i=21816 - [176] = https://curl.se/bug/?i=21868 - [177] = https://curl.se/bug/?i=21867 - [178] = https://curl.se/bug/?i=21866 - [179] = https://curl.se/bug/?i=21869 - [180] = https://curl.se/bug/?i=21870 - [181] = https://curl.se/bug/?i=21862 - [182] = https://curl.se/bug/?i=21858 - [183] = https://curl.se/bug/?i=21809 - [184] = https://curl.se/bug/?i=21930 - [185] = https://curl.se/bug/?i=22129 - [186] = https://curl.se/bug/?i=21976 - [187] = https://curl.se/bug/?i=21970 - [188] = https://curl.se/bug/?i=21933 - [189] = https://curl.se/bug/?i=21851 - [190] = https://curl.se/bug/?i=21850 - [191] = https://curl.se/bug/?i=21773 - [192] = https://curl.se/bug/?i=21927 - [193] = https://curl.se/bug/?i=21923 - [194] = https://curl.se/bug/?i=22126 - [195] = https://curl.se/bug/?i=21881 - [196] = https://curl.se/bug/?i=22052 - [197] = https://curl.se/bug/?i=21922 - [198] = https://curl.se/bug/?i=22125 - [199] = https://curl.se/bug/?i=21914 - [200] = https://curl.se/bug/?i=21910 - [201] = https://curl.se/bug/?i=21911 - [202] = https://curl.se/bug/?i=21920 - [203] = https://curl.se/bug/?i=21912 - [204] = https://curl.se/bug/?i=21969 - [205] = https://curl.se/bug/?i=21913 - [206] = https://curl.se/bug/?i=21915 - [207] = https://curl.se/bug/?i=21918 - [208] = https://curl.se/bug/?i=21968 - [209] = https://curl.se/bug/?i=21966 - [210] = https://curl.se/bug/?i=21896 - [211] = https://curl.se/bug/?i=21902 - [212] = https://curl.se/bug/?i=21903 - [213] = https://curl.se/bug/?i=21904 - [214] = https://curl.se/bug/?i=21907 - [215] = https://curl.se/bug/?i=21953 - [216] = https://curl.se/bug/?i=21961 - [217] = https://curl.se/bug/?i=21863 - [218] = https://curl.se/bug/?i=21901 - [219] = https://curl.se/bug/?i=21898 - [220] = https://curl.se/bug/?i=21964 - [221] = https://curl.se/bug/?i=21879 - [223] = https://curl.se/bug/?i=21957 - [224] = https://curl.se/bug/?i=21946 - [225] = https://curl.se/bug/?i=21951 - [226] = https://curl.se/bug/?i=21945 - [227] = https://curl.se/bug/?i=22048 - [228] = https://curl.se/bug/?i=22040 - [229] = https://curl.se/bug/?i=22105 - [230] = https://curl.se/bug/?i=21949 - [231] = https://curl.se/bug/?i=22038 - [232] = https://curl.se/bug/?i=22133 - [233] = https://curl.se/bug/?i=22124 - [234] = https://curl.se/bug/?i=22122 - [235] = https://curl.se/bug/?i=21944 - [236] = https://curl.se/bug/?i=22033 - [237] = https://curl.se/bug/?i=22107 - [238] = https://curl.se/bug/?i=21937 - [239] = https://curl.se/bug/?i=22030 - [242] = https://curl.se/bug/?i=22000 - [243] = https://curl.se/bug/?i=22017 - [245] = https://curl.se/bug/?i=22016 - [246] = https://curl.se/bug/?i=22114 - [249] = https://curl.se/bug/?i=22018 - [253] = https://curl.se/bug/?i=22025 - [256] = https://curl.se/bug/?i=22085 - [257] = https://curl.se/bug/?i=22094 - [260] = https://curl.se/bug/?i=22104 - [261] = https://curl.se/bug/?i=22013 - [262] = https://curl.se/bug/?i=22004 - [263] = https://curl.se/bug/?i=22001 - [264] = https://curl.se/bug/?i=22088 - [265] = https://curl.se/bug/?i=22087 - [266] = https://curl.se/bug/?i=22081 - [268] = https://curl.se/bug/?i=21996 - [269] = https://curl.se/bug/?i=22003 - [270] = https://curl.se/bug/?i=22002 - [271] = https://curl.se/bug/?i=21998 - [272] = https://curl.se/bug/?i=21999 - [273] = https://curl.se/bug/?i=21926 - [274] = https://curl.se/bug/?i=21990 - [275] = https://curl.se/bug/?i=21995 - [276] = https://curl.se/bug/?i=21967 - [277] = https://curl.se/bug/?i=21893 - [278] = https://curl.se/bug/?i=21992 - [279] = https://curl.se/bug/?i=21993 - [280] = https://curl.se/bug/?i=21986 - [281] = https://curl.se/bug/?i=21979 - [283] = https://curl.se/bug/?i=21989 - [286] = https://curl.se/bug/?i=21983 - [288] = https://curl.se/bug/?i=22050 - [289] = https://curl.se/bug/?i=22070 - [290] = https://curl.se/bug/?i=22073 - [291] = https://curl.se/bug/?i=22072 - [292] = https://curl.se/bug/?i=22052 - [294] = https://curl.se/bug/?i=22063 - [299] = https://curl.se/bug/?i=22062 - [300] = https://curl.se/bug/?i=22061 - [302] = https://curl.se/bug/?i=22059 - [304] = https://curl.se/bug/?i=21943 + [2] = https://curl.se/bug/?i=22158 + [3] = https://curl.se/bug/?i=22152 diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 5cc0e27c61dd..d1c295c7f86c 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.21.0-DEV" +#define LIBCURL_VERSION "8.21.1-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 #define LIBCURL_VERSION_MINOR 21 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always follows this syntax: @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081500 +#define LIBCURL_VERSION_NUM 0x081501 /* * This is the date and time when the full source package was created. The From f746780a1e548b4b93790b58d70c3b22fde5360d Mon Sep 17 00:00:00 2001 From: alhudz Date: Fri, 19 Jun 2026 22:15:20 +0530 Subject: [PATCH 0543/1062] mqtt: reject control bytes in the topic Closes #22112 --- lib/mqtt.c | 2 +- tests/data/Makefile.am | 2 +- tests/data/test2109 | 50 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2109 diff --git a/lib/mqtt.c b/lib/mqtt.c index 8482477b9725..ad397929875f 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -438,7 +438,7 @@ static CURLcode mqtt_get_topic(struct Curl_easy *data, const char *path = data->state.up.path; CURLcode result = CURLE_URL_MALFORMAT; if(strlen(path) > 1) { - result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); + result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_CTRL); if(!result && (*topiclen > 0xffff)) { failf(data, "Too long MQTT topic"); result = CURLE_URL_MALFORMAT; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 9fb1720f7c9d..85e523a36392 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 \ +test2108 test2109 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2109 b/tests/data/test2109 new file mode 100644 index 000000000000..323ea144c4d2 --- /dev/null +++ b/tests/data/test2109 @@ -0,0 +1,50 @@ + + + + +MQTT +MQTT SUBSCRIBE + + + +# Server-side + + +hello + + + +# Client-side + + +mqtt + + +mqtt + + +MQTT rejects a control byte in the topic + + +mqtt://%HOSTIP:%MQTTPORT/aa%00bb + + + +# Verify data after the test has been "shot" + +# These are hexadecimal protocol dumps from the client +# Strip out the random part of the client id from the CONNECT message +# before comparison + +s/^(.* 00044d5154540402003c000c6375726c).*/$1/ + +# the topic is decoded and the embedded nul rejected before SUBSCRIBE is sent + +client CONNECT 18 00044d5154540402003c000c6375726c +server CONNACK 2 20020000 + + +3 + + + From e9a2b0dc72dd3a9aa3a5210e89079a3b719475dc Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 10:19:17 +0200 Subject: [PATCH 0544/1062] multi_ev: refresh sock entry after remove callback This was missed in the fix for CVE-2026-9080. Reported-by: stze on hackerone URL: https://hackerone.com/reports/3823985 Closes #22163 --- lib/multi_ev.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 0da5b0e9044b..71e22b9b1958 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -213,7 +213,11 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->user_data); mev_in_callback(multi, FALSE); - entry->announced = FALSE; + /* curl_easy_pause() is documented as callable from any callback; it + * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + entry = mev_sh_entry_get(&multi->ev.sh_entries, s); + if(entry) + entry->announced = FALSE; } mev_sh_entry_kill(multi, s); From bf5cc6ab8b30514ca70bf663f8d3cd24ae4608d9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 22 Jun 2026 16:31:49 +0200 Subject: [PATCH 0545/1062] curl-functions.m4: verify working RFC 4291 zero prefixed IPv4 - mapped format Detect incompatible inet_ntop() call for IPv6 output. Closes #22136 --- m4/curl-functions.m4 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 3a273770168d..debd76d1d534 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -2324,7 +2324,6 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ ipv4a[2] = 0x64; ipv4a[3] = 0x01; ipv4a[4] = 0x01; - /* - */ ipv4ptr = inet_ntop(AF_INET, ipv4a, ipv4res, sizeof(ipv4res)); if(!ipv4ptr) return 1; /* fail */ @@ -2348,7 +2347,6 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ ipv6a[14] = 0x76; ipv6a[15] = 0xc8; ipv6a[25] = 0x01; - /* - */ ipv6ptr = inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); if(!ipv6ptr) return 1; /* fail */ @@ -2358,7 +2356,22 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ return 1; /* fail */ if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) return 1; /* fail */ - /* - */ + + /* verify working RFC 4291 zero prefixed IPv4 - mapped format */ + memset(ipv6a, 0, sizeof(ipv6a)); + ipv6a[12] = 0x7f; + ipv6a[13] = 0x0; + ipv6a[14] = 0x0; + ipv6a[15] = 0x01; + ipv6ptr = inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); + if(!ipv6ptr) + return 1; /* fail */ + if(ipv6ptr != ipv6res) + return 1; /* fail */ + if(!ipv6ptr[0]) + return 1; /* fail */ + if(memcmp(ipv6res, "::127.0.0.1", 11)) + return 1; /* fail */ return 0; ]]) ],[ From c6c4c6a16d69f2338beab9013d11813ccecfac31 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 22 Jun 2026 16:14:23 +0200 Subject: [PATCH 0546/1062] test 1560: test RFC4291 style IPv6 IPv4-mapped addresses Closes #22136 --- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/data/test1560 b/tests/data/test1560 index bd72d35d29f3..7dcf7fc50475 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -37,7 +37,7 @@ lib%TESTNUMBER success -Allocations: 3350 +Allocations: 3450 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index d4274bfb3e40..82f06a6ab4ec 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -153,6 +153,28 @@ struct clearurlcase { }; static const struct testcase get_parts_list[] = { + /* RFC 4291 IPv4-Mapped IPv6 Addresses */ + {"https://[0:0:0:0:0:FFFF:129.144.52.38]:1234", + "https | [11] | [12] | [13] | [::ffff:129.144.52.38] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[::FFFF:127.0.0.1]:1234", + "https | [11] | [12] | [13] | [::ffff:127.0.0.1] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[::13.1.68.3]:1234", + "https | [11] | [12] | [13] | [::13.1.68.3] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[0:0:0:0:0:FFFF:7f00:0001]:1234", + "https | [11] | [12] | [13] | [::ffff:127.0.0.1] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[::FFFF:7f00:0001]:1234", + "https | [11] | [12] | [13] | [::ffff:127.0.0.1] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[%3A%3A13%2e1%2e68%2e3%25eth0]:1234", + "https | [11] | [12] | [13] | [::13.1.68.3] eth0 | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"https://[0:0:0:0:0:0:0d01:4403]:1234", + "https | [11] | [12] | [13] | [::13.1.68.3] | 1234 " + "| / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, { /* query and fragments with control characters */ "http://host/path/?\001#\002", "", CURLU_URLENCODE, 0, CURLUE_MALFORMED_INPUT }, From 46e19c87445f23af43bba2c8d3b409126704b459 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 10:42:17 +0200 Subject: [PATCH 0547/1062] tool: init progress bar on demand Determine the terminal size on first invocation, not for all transfers. Closes #22164 --- src/tool_cb_prg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tool_cb_prg.c b/src/tool_cb_prg.c index 1af621dbaaa0..7d7f3a8d519e 100644 --- a/src/tool_cb_prg.c +++ b/src/tool_cb_prg.c @@ -129,6 +129,9 @@ int tool_progress_cb(void *clientp, curl_off_t total; curl_off_t point; + if(!bar->calls) + update_width(bar); + /* Calculate expected transfer size. initial_size can be less than zero when indicating that we are expecting to get the filesize from the remote */ if(bar->initial_size < 0) { @@ -230,8 +233,6 @@ void progressbarinit(struct ProgressData *bar, struct OperationConfig *config) if(config->use_resume) bar->initial_size = config->resume_from; - update_width(bar); - bar->out = tool_stderr; bar->tick = 150; bar->barmove = 1; From 7b3316984f3e4e223f732fb0b1323c0d6da15347 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 10:45:02 +0200 Subject: [PATCH 0548/1062] tool: do not flush on out-null Do not flush output when --out-null is set. Closes #22165 --- src/tool_operate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index c9bb63682701..97a1d9304702 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -671,7 +671,7 @@ static CURLcode post_output_handling(struct per_transfer *per, return CURLE_WRITE_ERROR; } - if(!outs->regular_file && outs->stream) { + if(!outs->regular_file && outs->stream && !outs->out_null) { /* Dump standard stream buffered data */ rc = fflush(outs->stream); if(!result && rc) { From 44ed43efe70f919f28f8b1bfe0df40216260a808 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 10:47:22 +0200 Subject: [PATCH 0549/1062] tool: optimize setopt use - tool_setopt_long: lookup default value on for --libcurl where it is used - tool_setoot_str: use `const char *` arg instead of var args. Closes #22166 --- src/tool_setopt.c | 41 ++++++++++++++++++----------------------- src/tool_setopt.h | 2 +- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 2174571ad4d8..71e2ffba04a4 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -609,23 +609,24 @@ CURLcode tool_setopt_slist(CURL *curl, const char *name, CURLoption tag, CURLcode tool_setopt_long(CURL *curl, const char *name, CURLoption tag, long lval) { - long defval = 0L; - const struct NameValue *nv = NULL; CURLcode result = CURLE_OK; DEBUGASSERT(tag < CURLOPTTYPE_OBJECTPOINT); - for(nv = setopt_nv_CURLNONZERODEFAULTS; nv->name; nv++) { - if(!strcmp(name, nv->name)) { - defval = nv->value; - break; /* found it */ - } - } - result = curl_easy_setopt(curl, tag, lval); - if((lval != defval) && global->libcurl && !result) { + if(!result && global->libcurl) { + const struct NameValue *nv = NULL; + long defval = 0L; /* we only use this for real if --libcurl was used */ - result = easysrc_addf(&easysrc_code, "curl_easy_setopt(curl, %s, %ldL);", - name, lval); + for(nv = setopt_nv_CURLNONZERODEFAULTS; nv->name; nv++) { + if(!strcmp(name, nv->name)) { + defval = nv->value; + break; /* found it */ + } + } + if(lval != defval) { + result = easysrc_addf(&easysrc_code, "curl_easy_setopt(curl, %s, %ldL);", + name, lval); + } } return result; } @@ -680,28 +681,23 @@ CURLcode tool_setopt_ptr(CURL *curl, const char *name, CURLoption tag, ...) /* setopt wrapper for setting strings */ CURLcode tool_setopt_str(CURL *curl, struct OperationConfig *config, - const char *name, CURLoption tag, ...) + const char *name, CURLoption tag, + const char *value) { - char *str; - va_list arg; CURLcode result; DEBUGASSERT(tag >= CURLOPTTYPE_OBJECTPOINT); DEBUGASSERT((tag < CURLOPTTYPE_OFF_T) || (tag >= CURLOPTTYPE_BLOB)); DEBUGASSERT(tag < CURLOPTTYPE_BLOB); DEBUGASSERT(tag < CURLOPTTYPE_FUNCTIONPOINT); - va_start(arg, tag); - /* argument is a string */ - str = va_arg(arg, char *); - - result = curl_easy_setopt(curl, tag, str); - if(global->libcurl && str && !result) { + result = curl_easy_setopt(curl, tag, value); + if(global->libcurl && value && !result) { /* we only use this if --libcurl was used */ curl_off_t len = ZERO_TERMINATED; char *escaped; if(tag == CURLOPT_POSTFIELDS) len = curlx_dyn_len(&config->postdata); - escaped = c_escape(str, len); + escaped = c_escape(value, len); if(escaped) { result = easysrc_addf(&easysrc_code, "curl_easy_setopt(curl, %s, \"%s\");", @@ -712,7 +708,6 @@ CURLcode tool_setopt_str(CURL *curl, struct OperationConfig *config, result = CURLE_OUT_OF_MEMORY; } - va_end(arg); return result; } diff --git a/src/tool_setopt.h b/src/tool_setopt.h index ee4d2a6cd155..20419733ba29 100644 --- a/src/tool_setopt.h +++ b/src/tool_setopt.h @@ -99,7 +99,7 @@ CURLcode tool_setopt_offt(CURL *curl, const char *name, CURLoption tag, curl_off_t lval); CURLcode tool_setopt_str(CURL *curl, struct OperationConfig *config, const char *name, CURLoption tag, - ...) WARN_UNUSED_RESULT; + const char *value) WARN_UNUSED_RESULT; CURLcode tool_setopt_ptr(CURL *curl, const char *name, CURLoption tag, ...); #define my_setopt_long(x, y, z) tool_setopt_long(x, #y, y, z) From 62e095cee0d6692969b58f27f0e552923bda97cd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 20 Jun 2026 11:08:07 +0200 Subject: [PATCH 0550/1062] ssh: dedupe `ssh_pollset()` functions Closes #22115 --- lib/vssh/libssh.c | 46 ++++++++-------------------------------------- lib/vssh/libssh2.c | 46 ++++++++-------------------------------------- lib/vssh/vssh.c | 31 +++++++++++++++++++++++++++++++ lib/vssh/vssh.h | 2 ++ 4 files changed, 49 insertions(+), 76 deletions(-) diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index e4071caa46d4..d0bc13cadb70 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2448,36 +2448,6 @@ static CURLcode myssh_statemachine(struct Curl_easy *data, return result; } -/* called by the multi interface to figure out what socket(s) to wait for and - for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static CURLcode myssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int waitfor; - - if(!sshc || (sock == CURL_SOCKET_BAD)) - return CURLE_FAILED_INIT; - - waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; - if(waitfor) { - int flags = 0; - if(waitfor & REQ_IO_RECV) - flags |= CURL_POLL_IN; - if(waitfor & REQ_IO_SEND) - flags |= CURL_POLL_OUT; - DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); - return Curl_pollset_change(data, ps, sock, flags, 0); - } - /* While we still have a session, we listen incoming data. */ - if(sshc->ssh_session) - return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0); - return CURLE_OK; -} - /* called repeatedly until done from multi.c */ static CURLcode myssh_multi_statemach(struct Curl_easy *data, bool *done) @@ -2485,8 +2455,8 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); - bool block; /* we store the status and use that to provide a ssh_pollset() - implementation */ + bool block; /* we store the status and use that to provide + a Curl_ssh_pollset() implementation */ CURLcode result; if(!sshc || !sshp) @@ -3168,10 +3138,10 @@ const struct Curl_protocol Curl_protocol_scp = { myssh_connect, /* connect_it */ myssh_multi_statemach, /* connecting */ scp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ + Curl_ssh_pollset, /* proto_pollset */ + Curl_ssh_pollset, /* doing_pollset */ ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ + Curl_ssh_pollset, /* perform_pollset */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -3191,10 +3161,10 @@ const struct Curl_protocol Curl_protocol_sftp = { myssh_connect, /* connect_it */ myssh_multi_statemach, /* connecting */ sftp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ + Curl_ssh_pollset, /* proto_pollset */ + Curl_ssh_pollset, /* doing_pollset */ ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ + Curl_ssh_pollset, /* perform_pollset */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index b12d69f3177a..a3d0adf3b284 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3196,36 +3196,6 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, return result; } -/* called by the multi interface to figure out what socket(s) to wait for and - for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static CURLcode ssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int waitfor; - - if(!sshc || (sock == CURL_SOCKET_BAD)) - return CURLE_FAILED_INIT; - - waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; - if(waitfor) { - int flags = 0; - if(waitfor & REQ_IO_RECV) - flags |= CURL_POLL_IN; - if(waitfor & REQ_IO_SEND) - flags |= CURL_POLL_OUT; - DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); - return Curl_pollset_change(data, ps, sock, flags, 0); - } - /* While we still have a session, we listen incoming data. */ - if(sshc->ssh_session) - return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0); - return CURLE_OK; -} - /* * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this * function is used to figure out in what direction and stores this info so @@ -3259,8 +3229,8 @@ static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_pollset() - implementation */ + bool block; /* we store the status and use that to provide + a Curl_ssh_pollset() implementation */ if(!sshc || !sshp) return CURLE_FAILED_INIT; @@ -4014,10 +3984,10 @@ const struct Curl_protocol Curl_protocol_scp = { ssh_connect, /* connect_it */ ssh_multi_statemach, /* connecting */ scp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ + Curl_ssh_pollset, /* proto_pollset */ + Curl_ssh_pollset, /* doing_pollset */ ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ + Curl_ssh_pollset, /* perform_pollset */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ @@ -4037,10 +4007,10 @@ const struct Curl_protocol Curl_protocol_sftp = { ssh_connect, /* connect_it */ ssh_multi_statemach, /* connecting */ sftp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ + Curl_ssh_pollset, /* proto_pollset */ + Curl_ssh_pollset, /* doing_pollset */ ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ + Curl_ssh_pollset, /* perform_pollset */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c index 0ba4a9e69078..9c82d0a88465 100644 --- a/lib/vssh/vssh.c +++ b/lib/vssh/vssh.c @@ -30,6 +30,8 @@ #include "curlx/strparse.h" #include "curl_trc.h" #include "escape.h" +#include "select.h" /* for Curl_pollset_change() */ +#include "url.h" /* for Curl_conn_meta_get() */ #ifdef CURLVERBOSE const char *Curl_ssh_statename(sshstate state) @@ -330,4 +332,33 @@ CURLcode Curl_ssh_range(struct Curl_easy *data, return CURLE_OK; } +/* called by the multi interface to figure out what socket(s) to wait for and + for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ +CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps) +{ + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int waitfor; + + if(!sshc || (sock == CURL_SOCKET_BAD)) + return CURLE_FAILED_INIT; + + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; + if(waitfor) { + int flags = 0; + if(waitfor & REQ_IO_RECV) + flags |= CURL_POLL_IN; + if(waitfor & REQ_IO_SEND) + flags |= CURL_POLL_OUT; + DEBUGASSERT(flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); + return Curl_pollset_change(data, ps, sock, flags, 0); + } + /* While we still have a session, we listen incoming data. */ + if(sshc->ssh_session) + return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0); + return CURLE_OK; +} + #endif /* USE_SSH */ diff --git a/lib/vssh/vssh.h b/lib/vssh/vssh.h index 492108fbd766..89fe41e0ae6e 100644 --- a/lib/vssh/vssh.h +++ b/lib/vssh/vssh.h @@ -38,5 +38,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); CURLcode Curl_ssh_range(struct Curl_easy *data, const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep); + +CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps); #endif /* USE_SSH */ #endif /* HEADER_CURL_VSSH_VSSH_H */ From fbcce4da85b85fe876268ffd4f7f044410cce47f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 22:41:07 +0200 Subject: [PATCH 0551/1062] build: enable thread-safe `getaddrinfo()` for OpenBSD, and other tidy-ups Aka `HAVE_GETADDRINFO_THREADSAFE`. - enable it for OpenBSD 5.4+ (2013-11-01). Also adjust cmake pre-fill. - explicitly opt-in DragonFly BSD, for 2.2.0+ (2009-02-17). Assuming this commit implemented this feature: https://github.com/DragonFlyBSD/DragonFlyBSD/commit/21fcee4d6e84a10a35d21e920106590570511048#diff-498ff649e8770eb9a94e99a399a4b473fcea41ef58b033a2a2dae3809e17bb04 Prior to this patch it was enabled in autotools by accident, because its host ID misses `bsd` so the global BSD disable was not applied. - autotools: fix to enable for Darwin 9+ / macOS 10.5+ (2007-10-26). Prior to this patch it was Darwin 6 / macOS 10.2. Source: https://github.com/python/cpython/issues/70112 Ref: https://en.wikipedia.org/wiki/MacOS_version_history 10.5 was the first one UNIX '03 certified, which requires this feature. iOS and others were forked from this codebase, so they also have support. - cmake: add version numbers as comments. Sadly the OS versions are not verified. This and the autotools logic could possibly be implemented in `curl-setup.h` more accurately, for all build systems, without duplicating logic. Also: - cmake: `BSD` variable requires CMake 3.25+, add fallback code where missing. Ref: https://cmake.org/cmake/help/v3.25/variable/BSD.html - unix-cache.cmake: stop listing all BSDs for `HAVE_PIPE2`, and fix generic BSD filter for CMake <3.25. - unix-cache.cmake: stop listing all BSDs, since it's prefilled anyway for those via `OtherTests.cmake`. Ref: https://github.com/curl/curl/pull/22138#issuecomment-4773617195 Closes #22148 --- CMake/OtherTests.cmake | 14 ++++++++------ CMake/unix-cache.cmake | 16 ++-------------- CMakeLists.txt | 2 +- m4/curl-functions.m4 | 22 +++++++++++++++++++--- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 6619f3ab3e88..b0727d32e434 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -77,12 +77,14 @@ if(WIN32) set(HAVE_GETADDRINFO_THREADSAFE ${HAVE_GETADDRINFO}) elseif(NOT HAVE_GETADDRINFO) set(HAVE_GETADDRINFO_THREADSAFE FALSE) -elseif(APPLE OR - AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR - CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR +elseif(APPLE OR # Darwin 9+ / macOS 10.5+ + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR # 5.2+ + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # 2.2.0+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # 5.5+ + CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR # 11.11+ + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR # all versions + CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR # 4+ + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR # 5.4+ CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_GETADDRINFO_THREADSAFE TRUE) elseif(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD") diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 78816499ca01..44dc6472b22e 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -106,14 +106,7 @@ elseif(CYGWIN OR set(HAVE_FSETXATTR_6 0) endif() set(HAVE_GETADDRINFO 1) -if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") - set(HAVE_GETADDRINFO_THREADSAFE 0) -elseif(CYGWIN OR - CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "NetBSD") +if(CYGWIN) set(HAVE_GETADDRINFO_THREADSAFE 1) endif() set(HAVE_GETEUID 1) @@ -219,12 +212,7 @@ if(APPLE OR CYGWIN) set(HAVE_PIPE2 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - BSD OR - CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_PIPE2 1) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index e459b3d0761a..c956dc411578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ endif() if(UNIX) string(APPEND _target_flags " UNIX") endif() -if(BSD) +if(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD") string(APPEND _target_flags " BSD") endif() if(ANDROID) diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index debd76d1d534..8b81d736cbda 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -1330,7 +1330,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ if test "$curl_cv_func_getaddrinfo" = "yes"; then AC_MSG_CHECKING([if getaddrinfo is thread-safe]) if test "$curl_cv_apple" = "yes"; then - dnl Darwin 6.0 and macOS 10.2.X and newer + dnl Darwin 9+ and macOS 10.5+ tst_tsafe_getaddrinfo="yes" fi case $host_os in @@ -1342,10 +1342,18 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ dnl AIX 5.2 and newer tst_tsafe_getaddrinfo="yes" ;; - darwin[[12345]].*) - dnl Darwin 5.0 and macOS 10.1.X and older + darwin[[12345678]].*) + dnl Darwin <=8.0 and macOS <=10.4 tst_tsafe_getaddrinfo="no" ;; + dragonfly1.* | dragonfly2.[[01]]) + dnl DragonFly BSD <=2.1.0 + tst_tsafe_getaddrinfo="no" + ;; + dragonfly*) + dnl DragonFly BSD 2.2.0+ + tst_tsafe_getaddrinfo="yes" + ;; freebsd[[1234]].* | freebsd5.[[1234]]*) dnl FreeBSD 5.4 and older tst_tsafe_getaddrinfo="no" @@ -1374,6 +1382,14 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ dnl NetBSD 4.X and newer tst_tsafe_getaddrinfo="yes" ;; + openbsd[[1234]].* | openbsd5.[[0123]]) + dnl OpenBSD <=5.3 + tst_tsafe_getaddrinfo="no" + ;; + openbsd*) + dnl OpenBSD 5.4+ + tst_tsafe_getaddrinfo="yes" + ;; *bsd*) dnl All other BSD's tst_tsafe_getaddrinfo="no" From ccf19d59e7da30ddc7bdbd0c83a439361ec9113b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 23 Jun 2026 23:14:24 +0200 Subject: [PATCH 0552/1062] scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands Where missing. To avoid passing an option by accident. End-of-option marker (`--`) is not POSIX, but most of these scripts are internal and/or CI-focused, where this is fine. `maketgz` and `verify-release` are meant for public use, and I asses this is fine too, but let us know if this causes issues in real world envs. Also: - maketgz: pass args with `:?` to `rm -rf`, where missing. Cannot cause an actual issue with current code. - verified `cp`, `rm` instances too, but none were affected. - tests/cmake/test.sh: replace `$PWD` with `$(pwd)` for sturdiness. - appveyor.sh: replace `$PWD` with `$(pwd)` for sturdiness. Assisted-by: Dan Fandrich Follow-up to 6aab1dc2639d4fc8aaaa1190914b0491e029dace #19450 Closes #22150 --- .github/scripts/codespell.sh | 2 +- .github/scripts/shellcheck.sh | 2 +- .github/scripts/typos.sh | 2 +- .github/scripts/yamlcheck.sh | 2 +- appveyor.sh | 4 ++-- scripts/cmakelint.sh | 2 +- scripts/maketgz | 8 ++++---- scripts/perlcheck.sh | 2 +- scripts/verify-release | 6 +++--- tests/cmake/test.sh | 10 +++++----- tests/ech_tests.sh | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/scripts/codespell.sh b/.github/scripts/codespell.sh index c5ddf90a4763..f93a788a1e49 100755 --- a/.github/scripts/codespell.sh +++ b/.github/scripts/codespell.sh @@ -5,7 +5,7 @@ set -eu -cd "$(dirname "${0}")"/../.. +cd -- "$(dirname "${0}")"/../.. git ls-files -z | xargs -0 -r \ codespell \ diff --git a/.github/scripts/shellcheck.sh b/.github/scripts/shellcheck.sh index 59b49131ac4b..3153a2c7a8e2 100755 --- a/.github/scripts/shellcheck.sh +++ b/.github/scripts/shellcheck.sh @@ -5,7 +5,7 @@ set -eu -cd "$(dirname "${0}")"/../.. +cd -- "$(dirname "${0}")"/../.. git grep -z -l -E '^#!(/usr/bin/env bash|/bin/sh|/bin/bash)' | xargs -0 -r \ shellcheck --exclude=1091,2248 \ diff --git a/.github/scripts/typos.sh b/.github/scripts/typos.sh index 76735c1d9fc8..39a7faa81eaf 100755 --- a/.github/scripts/typos.sh +++ b/.github/scripts/typos.sh @@ -5,7 +5,7 @@ set -eu -cd "$(dirname "${0}")"/../.. +cd -- "$(dirname "${0}")"/../.. git ls-files | typos \ --isolated \ diff --git a/.github/scripts/yamlcheck.sh b/.github/scripts/yamlcheck.sh index 4bdeff45cb96..402aa088fccf 100755 --- a/.github/scripts/yamlcheck.sh +++ b/.github/scripts/yamlcheck.sh @@ -5,7 +5,7 @@ set -eu -cd "$(dirname "${0}")"/../.. +cd -- "$(dirname "${0}")"/../.. git ls-files '*.yaml' '*.yml' -z | xargs -0 -r \ yamllint \ diff --git a/appveyor.sh b/appveyor.sh index 8852caec9b46..e9900191c5d3 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -61,7 +61,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ --location --proto-redir =https "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${fn}.zip" --output pkg.bin sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${CMAKE_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin - PATH="$PWD/${fn}/bin:$PATH" + PATH="$(pwd)/${fn}/bin:$PATH" fi # Set env CHKPREFILL to the value '_chkprefill' to compare feature detection @@ -89,7 +89,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then fi echo 'curl_config.h'; grep -F '#define' _bld/lib/curl_config.h | sort || true time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 - [[ "${CMAKE_GENERATE:-}" != *'-DBUILD_SHARED_LIBS=OFF'* ]] && PATH="$PWD/_bld/lib/${PRJ_CFG}:$PATH" + [[ "${CMAKE_GENERATE:-}" != *'-DBUILD_SHARED_LIBS=OFF'* ]] && PATH="$(pwd)/_bld/lib/${PRJ_CFG}:$PATH" [[ "${CMAKE_GENERATE:-}" = *'-DCURL_USE_OPENSSL=ON'* ]] && { PATH="${openssl_root}:$PATH"; cp "${openssl_root}"/*.dll "_bld/src/${PRJ_CFG}"; } curl="_bld/src/${PRJ_CFG}/curl.exe" else diff --git a/scripts/cmakelint.sh b/scripts/cmakelint.sh index 3fe258346df2..150c21f2914d 100755 --- a/scripts/cmakelint.sh +++ b/scripts/cmakelint.sh @@ -41,7 +41,7 @@ set -eu -cd "$(dirname "$0")"/.. +cd -- "$(dirname "$0")"/.. { if [ -n "${1:-}" ]; then diff --git a/scripts/maketgz b/scripts/maketgz index e6bc53dcdc1b..5ee69983fdae 100755 --- a/scripts/maketgz +++ b/scripts/maketgz @@ -180,7 +180,7 @@ fi retar() { tempdir=$1 - rm -rf "$tempdir" + rm -rf "${tempdir:?}" mkdir "$tempdir" cd "$tempdir" gzip -dc "../$targz" | tar -xf - @@ -188,7 +188,7 @@ retar() { tar --create --format=ustar --owner=0 --group=0 --numeric-owner --sort=name curl-* | gzip --best --no-name > out.tar.gz mv out.tar.gz ../ cd .. - rm -rf "$tempdir" + rm -rf "${tempdir:?}" } retar ".tarbuild" @@ -218,14 +218,14 @@ gzip -dc "$targz" | xz -6e - > "$xz" # Now make a zip archive from the tar.gz original # makezip() { - rm -rf "$tempdir" + rm -rf "${tempdir:?}" mkdir "$tempdir" cd "$tempdir" gzip -dc "../$targz" | tar -xf - find . | sort | zip -9 -X "$zip" -@ >/dev/null mv "$zip" ../ cd .. - rm -rf "$tempdir" + rm -rf "${tempdir:?}" } zip="curl-$version.zip" diff --git a/scripts/perlcheck.sh b/scripts/perlcheck.sh index 28c5be2c1678..92970e401553 100755 --- a/scripts/perlcheck.sh +++ b/scripts/perlcheck.sh @@ -30,7 +30,7 @@ set -eu -cd "$(dirname "$0")"/.. +cd -- "$(dirname "$0")"/.. procs=6 command -v nproc >/dev/null && procs="$(nproc)" diff --git a/scripts/verify-release b/scripts/verify-release index fd7aa3ded6be..5a231c485e07 100755 --- a/scripts/verify-release +++ b/scripts/verify-release @@ -63,7 +63,7 @@ mkdir -p _tarballs rm -rf _tarballs/* # checksum the original tarball to compare with later -sha256sum "$tarball" >_tarballs/checksum +sha256sum -- "$tarball" >_tarballs/checksum # extract version number from file name tarver=$(echo "$tarball" | sed 's/curl-\([0-9.]*\)\..*/\1/') @@ -85,7 +85,7 @@ if test "$withgit" = 0; then tar xf "$tarball" # move away the original tarball - mv "$tarball" "_tarballs/orig-$tarball" + mv -- "$tarball" "_tarballs/orig-$tarball" pwd=$(pwd) cd "curl-$curlver" @@ -101,7 +101,7 @@ else echo "*** Use git tag $tag" # move away the original tarball - mv "$tarball" "_tarballs/orig-$tarball" + mv -- "$tarball" "_tarballs/orig-$tarball" prevtag=$(git symbolic-ref -q --short HEAD || git rev-parse HEAD) git checkout -f "$tag" diff --git a/tests/cmake/test.sh b/tests/cmake/test.sh index bcc1bd37dc5d..b9bb1fc771e7 100755 --- a/tests/cmake/test.sh +++ b/tests/cmake/test.sh @@ -7,7 +7,7 @@ set -eu -cd "$(dirname "$0")" +cd -- "$(dirname "$0")" mode="${1:-all}"; shift @@ -31,7 +31,7 @@ runresults() { if [ "${mode}" = 'all' ] || [ "${mode}" = 'ExternalProject' ]; then (cd "${src}"; git archive --format=tar HEAD) | gzip > source.tar.gz - src="${PWD}/source.tar.gz" + src="$(pwd)/source.tar.gz" sha="$(sha256sum "${src}" | grep -a -i -o -w -E '[0-9a-f]{64}')" bldc='bld-externalproject' rm -rf "${bldc}" @@ -43,7 +43,7 @@ if [ "${mode}" = 'all' ] || [ "${mode}" = 'ExternalProject' ]; then fi if [ "${mode}" = 'all' ] || [ "${mode}" = 'FetchContent' ]; then - src="${PWD}/${src}" + src="$(pwd)/${src}" bldc='bld-fetchcontent' rm -rf "${bldc}" "${cmake_consumer}" -B "${bldc}" -G "${gen}" ${cmake_opts} -DCMAKE_UNITY_BUILD=ON ${TEST_CMAKE_FLAGS:-} "$@" \ @@ -70,9 +70,9 @@ if [ "${mode}" = 'all' ] || [ "${mode}" = 'add_subdirectory' ]; then fi if [ "${mode}" = 'all' ] || [ "${mode}" = 'find_package' ]; then - src="${PWD}/${src}" + src="$(pwd)/${src}" bldp='bld-curl' - prefix="${PWD}/${bldp}/_pkg" + prefix="$(pwd)/${bldp}/_pkg" rm -rf "${bldp}" "${cmake_provider}" -B "${bldp}" -S "${src}" -G "${gen}" ${cmake_opts} -DCMAKE_UNITY_BUILD=ON ${TEST_CMAKE_FLAGS:-} ${TEST_CMAKE_FLAGS_PROVIDER:-} "$@" \ -DBUILD_SHARED_LIBS=ON \ diff --git a/tests/ech_tests.sh b/tests/ech_tests.sh index 5b6f60d55558..72a2d454e49c 100755 --- a/tests/ech_tests.sh +++ b/tests/ech_tests.sh @@ -211,7 +211,7 @@ have_portsblocked="no" NOW=$(whenisitagain) BINNAME=$(basename "$0" .sh) if [ ! -d "$LTOP" ]; then - mkdir -p "$LTOP" + mkdir -p -- "$LTOP" fi if [ ! -d "$LTOP" ]; then echo "Cannot see $LTOP for logs - exiting" From 2f4d3221aa0777b6174638c7fefc3e5e7b96f38d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 24 Jun 2026 13:43:28 +0200 Subject: [PATCH 0553/1062] autotools: minor fixes and improvements - Makefile.am: add root `test-quiet` target that maps to `tests/quiet-test`, for completeness. Ref: #22098 - tests/Makefile.am: drop unused remains of `MANFILES`. Follow-up to fa3f889752e6b5034966de61a372a60773a69ca8 #17463 Also replace the `distclean` target to delete `CLEANFILES`, otherwise the CI dist job fails. (I'm not grokking why) - tests/Makefile.am: sync up `tests` target with CMake, to pass `TFLAGS` `-a`. Follow-up to 904e7ecb66519951681377758fe6b07dde28ce36 #19347 - tests/certs/Makefile.am: fix to clean generated certs via `CLEANFILES` to comply with autotools documentation, which says the previously used variable `DISTCLEANFILES` is meant for files created by `./configure`. Ref: https://www.gnu.org/software/automake/manual/html_node/Clean.html Follow-up to 44341e736a3e2f7a2b25a774be3a9796e81abab9 #16824 - tests/http/Makefile.am: add `./` prefix to glob passed to `rm -rf` on clean, to avoid deleting unintented files, or pass unintended options. Follow-up to 671158242db32032a41e8da89da33d773b481ef8 #10349 Cherry-picked from #22102 Cherry-picked from #22150 Closes #22154 --- Makefile.am | 5 ++++- tests/Makefile.am | 8 +++++--- tests/certs/Makefile.am | 2 +- tests/http/Makefile.am | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index 83fdadf035fe..08828fd4093e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,6 +88,7 @@ dist-hook: check: test examples check-docs if CROSSCOMPILING +test-quiet: test test-full: test test-nonflaky: test test-torture: test @@ -102,7 +103,9 @@ test: else -test: +test: test-quiet + +test-quiet: @(cd tests; $(MAKE) all quiet-test) test-full: diff --git a/tests/Makefile.am b/tests/Makefile.am index a77c6259eba8..c76c0c802356 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -118,6 +118,7 @@ TEST_COMMON += !documentation endif TEST = srcdir=$(srcdir) @PERL@ $(PERLFLAGS) $(srcdir)/runtests.pl $(TEST_COMMON) +TEST_A = -a TEST_Q = -a -s TEST_AM = -a -am TEST_F = -a -p -r @@ -142,7 +143,8 @@ build-certs: perlcheck (cd certs && $(MAKE)) # alias for 'test' to match CMake, where 'test' is a reserved target -tests: test +tests: perlcheck all + $(TEST) $(TEST_A) $(TFLAGS) test: perlcheck all $(TEST) $(TFLAGS) @@ -179,7 +181,7 @@ checksrc: (cd tunit && $(MAKE) checksrc) (cd server && $(MAKE) checksrc) -all-local: $(MANFILES) build-certs +all-local: build-certs distclean: - rm -f $(MANFILES) + rm -f $(CLEANFILES) diff --git a/tests/certs/Makefile.am b/tests/certs/Makefile.am index 078b4490b6e2..a88f47417515 100644 --- a/tests/certs/Makefile.am +++ b/tests/certs/Makefile.am @@ -29,7 +29,7 @@ include Makefile.inc EXTRA_DIST = $(CERTCONFIG_CA) $(CERTCONFIGS) $(SRPFILES) CMakeLists.txt \ genserv.pl -DISTCLEANFILES = $(GENERATEDCERTS) +CLEANFILES = $(GENERATEDCERTS) all-am: test-ca.cacert diff --git a/tests/http/Makefile.am b/tests/http/Makefile.am index 7232c1e8aa15..6b4b6054ebf7 100644 --- a/tests/http/Makefile.am +++ b/tests/http/Makefile.am @@ -77,7 +77,7 @@ EXTRA_DIST = \ $(TESTENV) clean-local: - rm -rf *.pyc __pycache__ + rm -rf ./*.pyc __pycache__ rm -rf gen check: libtests From 481e10160f4810dc7731922b3a98461deb1dcbed Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 24 Jun 2026 17:04:03 +0200 Subject: [PATCH 0554/1062] runtests: restore `-k` option and actively process as no-op Restore processing this option to avoid falling it through and misinterpreted as something else, which in turn disables tests. Exit with an error instead. We delete completely in December 2026. Reported-by: Sam James Bug: https://github.com/curl/curl/pull/22100#issuecomment-4789828929 Follow-up to 04305a3e40989d3731e97bd0ef41bbd55c680a3f #22100 Follow-up to 6617db6a7ed322d28322896aa20bcabf3a479e7c #4035 Closes #22157 --- tests/runtests.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/runtests.pl b/tests/runtests.pl index 61949614b970..a326fc37b69e 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2539,6 +2539,10 @@ sub pickrunner { $jobs = $1; } } + elsif($ARGV[0] eq "-k") { # delete this check after December 2026 + print "Option -k became always-on in 7.65.2 (2019) and now a no-op. Delete it to continue.\n"; + exit; + } elsif($ARGV[0] eq "-r") { # run time statistics needs Time::HiRes if($Time::HiRes::VERSION) { From d2fe26cfe25b78a51bddc7320b178172093fdf6d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 25 Jun 2026 11:34:41 +0200 Subject: [PATCH 0555/1062] runtests: exit with error on `-k` option Ref: https://github.com/curl/curl/pull/22157#discussion_r3473254887 Follow-up to 481e10160f4810dc7731922b3a98461deb1dcbed #22157 --- tests/runtests.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtests.pl b/tests/runtests.pl index a326fc37b69e..d938e186940d 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2541,7 +2541,7 @@ sub pickrunner { } elsif($ARGV[0] eq "-k") { # delete this check after December 2026 print "Option -k became always-on in 7.65.2 (2019) and now a no-op. Delete it to continue.\n"; - exit; + exit 1; } elsif($ARGV[0] eq "-r") { # run time statistics needs Time::HiRes From 26c6a4d89875e6652b47e32a36d0fb00d85fae4e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 11:18:27 +0200 Subject: [PATCH 0556/1062] urlapi: do not keep an internal port string Do not allocated an internal port string, just keep and number and a bit if it is present. Format the port string in a local buffer when someone asks for it and copy that. This avoid the format/alloc penalty when parsing URLs and no one ever asking for the port string. (And there will be a method to get the uint16_t value directly in another PR) Closes #22167 --- lib/urlapi-int.h | 4 +-- lib/urlapi.c | 90 +++++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h index 4d8f2c1cb8df..4d23dd87b44c 100644 --- a/lib/urlapi-int.h +++ b/lib/urlapi-int.h @@ -34,11 +34,11 @@ struct Curl_URL { char *options; /* IMAP only? */ char *host; char *zoneid; /* for numerical IPv6 addresses */ - char *port; char *path; char *query; char *fragment; - unsigned short portnum; /* the numerical version (if 'port' is set) */ + uint16_t portnum; /* the numerical port if present */ + BIT(port_present); /* to support missing port */ BIT(query_present); /* to support blank */ BIT(fragment_present); /* to support blank */ BIT(guessed_scheme); /* when a URL without scheme is parsed */ diff --git a/lib/urlapi.c b/lib/urlapi.c index 05c79dfb0df4..77414bd2f800 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -73,7 +73,6 @@ static void free_urlhandle(struct Curl_URL *u) curlx_free(u->options); curlx_free(u->host); curlx_free(u->zoneid); - curlx_free(u->port); curlx_free(u->path); curlx_free(u->query); curlx_free(u->fragment); @@ -358,6 +357,8 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, /* * Find the end of an IPv6 address on the ']' ending bracket. */ + u->portnum = 0; + u->port_present = FALSE; if(hostname[0] == '[') { portptr = strchr(hostname, ']'); if(!portptr) @@ -393,12 +394,8 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, if(curlx_str_number(&portptr, &port, 0xffff) || *portptr) return CURLUE_BAD_PORT_NUMBER; - u->portnum = (unsigned short)port; - /* generate a new port number string to get rid of leading zeroes etc */ - curlx_free(u->port); - u->port = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); - if(!u->port) - return CURLUE_OUT_OF_MEMORY; + u->portnum = (uint16_t)port; + u->port_present = TRUE; } return CURLUE_OK; @@ -1344,12 +1341,12 @@ CURLU *curl_url_dup(const CURLU *in) DUP(u, in, password); DUP(u, in, options); DUP(u, in, host); - DUP(u, in, port); DUP(u, in, path); DUP(u, in, query); DUP(u, in, fragment); DUP(u, in, zoneid); u->portnum = in->portnum; + u->port_present = in->port_present; u->fragment_present = in->fragment_present; u->query_present = in->query_present; } @@ -1484,7 +1481,7 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) else { const char *scheme; char *options = u->options; - char *port = u->port; + char *port = NULL; const struct Curl_scheme *h = NULL; char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) @@ -1494,19 +1491,26 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) else return CURLUE_NO_SCHEME; + if(u->port_present) { + curl_msnprintf(portbuf, sizeof(portbuf), "%u", u->portnum); + port = portbuf; + } + h = Curl_get_scheme(scheme); if(h) { - if(!port && (flags & CURLU_DEFAULT_PORT)) { + if(!u->port_present && (flags & CURLU_DEFAULT_PORT)) { /* there is no stored port number, but asked to deliver a default one for the scheme */ curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } - else if(port && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) + else if(u->port_present && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) { /* there is a stored port number, but asked to inhibit if it matches the default port for the scheme */ port = NULL; + } + if(!(h->flags & PROTOPT_URLOPTIONS)) options = NULL; } @@ -1614,10 +1618,24 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ifmissing = CURLUE_NO_ZONEID; break; case CURLUPART_PORT: - ptr = u->port; + ptr = NULL; ifmissing = CURLUE_NO_PORT; flags &= ~U_CURLU_URLDECODE; /* never for port */ - if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { + if(u->port_present) { + const struct Curl_scheme *h = u->scheme ? + Curl_get_scheme(u->scheme) : NULL; + /* there is a stored port number, but ask to inhibit if + it matches the default one for the scheme */ + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) { + ptr = NULL; + } + else { + curl_msnprintf(portbuf, sizeof(portbuf), "%u", u->portnum); + ptr = portbuf; + } + } + else if((flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there is no stored port number, but asked to deliver a default one for the scheme */ const struct Curl_scheme *h = Curl_get_scheme(u->scheme); @@ -1626,14 +1644,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ptr = portbuf; } } - else if(ptr && u->scheme) { - /* there is a stored port number, but ask to inhibit if - it matches the default one for the scheme */ - const struct Curl_scheme *h = Curl_get_scheme(u->scheme); - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - ptr = NULL; - } break; case CURLUPART_PATH: ptr = u->path; @@ -1700,7 +1710,6 @@ static CURLUcode set_url_scheme(CURLU *u, const char *scheme, static CURLUcode set_url_port(CURLU *u, const char *provided_port) { - char *tmp; curl_off_t port; if(!ISDIGIT(provided_port[0])) /* not a number */ @@ -1708,12 +1717,8 @@ static CURLUcode set_url_port(CURLU *u, const char *provided_port) if(curlx_str_number(&provided_port, &port, 0xffff) || *provided_port) /* weirdly provided number, not good! */ return CURLUE_BAD_PORT_NUMBER; - tmp = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); - if(!tmp) - return CURLUE_OUT_OF_MEMORY; - curlx_free(u->port); - u->port = tmp; - u->portnum = (unsigned short)port; + u->portnum = (uint16_t)port; + u->port_present = TRUE; return CURLUE_OK; } @@ -1796,7 +1801,7 @@ static CURLUcode urlset_clear(CURLU *u, CURLUPart what) break; case CURLUPART_PORT: u->portnum = 0; - curlx_safefree(u->port); + u->port_present = FALSE; break; case CURLUPART_PATH: curlx_safefree(u->path); @@ -2077,26 +2082,25 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) if(href->host) { if(!curl_strequal(base->host, href->host)) return FALSE; - if(!curl_strequal(base->zoneid ? base->zoneid : "", - href->zoneid ? href->zoneid : "")) - return FALSE; - if(!curl_strequal(base->port, href->port)) { - /* This may still match if only one has an explicit port - * and it is the default for the scheme. */ - if(base->port && href->port) - return FALSE; + if(base->port_present != href->port_present) { + /* one is present, one is not */ s = Curl_get_scheme(base->scheme); if(!s) /* Cannot match default port for unknown scheme */ return FALSE; - - /* The port which is set must be the default one */ - if((base->port && (base->portnum != s->defport)) || - (href->port && (href->portnum != s->defport))) + /* to match, the present one must be the default port */ + if((base->port_present && (base->portnum != s->defport)) || + (href->port_present && (href->portnum != s->defport))) return FALSE; } + else if(base->portnum != href->portnum) /* both present or missing */ + return FALSE; + + if(!curl_strequal(base->zoneid ? base->zoneid : "", + href->zoneid ? href->zoneid : "")) + return FALSE; } - else if(href->port) /* no host in href, then there must be no port */ + else if(href->port_present) /* no host in href, then there must be no port */ return FALSE; return TRUE; } From 27370e9fc77c8d99f047ab53e17e84a6e7255c63 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 18 Jun 2026 19:39:33 +0200 Subject: [PATCH 0557/1062] GHA: bump some jobs to ubuntu-26.04 In jobs that may benefit from newer tool versions (pyspelling, possibly CodeQL, CM integration, configure-vs-cmake). Also curl-for-win to match the curl/curl-for-win repo that also bumped. Bump some Linux jobs to add variation. Also the rest of `check*` jobs for no particular reason other than using the latest. Follow-up to 8f5e4f020e79ec1fd1e8540bdea3c1c20030405b #22097 Follow-up to 139ce4d37cfdc3126179bdb166ec61a095360c62 #22096 Follow-up to e44f1a1446f8e72573e5a1026807e71a0495f88d #22094 Closes #22092 --- .github/workflows/checkdocs.yml | 6 +++--- .github/workflows/checksrc.yml | 4 ++-- .github/workflows/codeql.yml | 4 ++-- .github/workflows/configure-vs-cmake.yml | 2 +- .github/workflows/curl-for-win.yml | 10 +++++----- .github/workflows/distcheck.yml | 2 +- .github/workflows/linux.yml | 10 +++++----- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 52f002fa711c..446aa4668f03 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -41,7 +41,7 @@ jobs: # config file help: https://github.com/amperser/proselint/ proselint: name: 'proselint' - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-26.04-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -92,7 +92,7 @@ jobs: pyspelling: name: 'pyspelling' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -122,7 +122,7 @@ jobs: synopsis-man-examples: name: 'synopsis, man-examples' - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-26.04-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 83f5afe7a1aa..405e66af457b 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -46,7 +46,7 @@ jobs: linters: name: 'spellcheck, linters, REUSE' - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-26.04-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -160,7 +160,7 @@ jobs: miscchecks: name: 'misc checks' - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-26.04-arm timeout-minutes: 5 steps: - name: 'install prereqs' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 15b91c18c41a..ea701495384c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,7 +40,7 @@ jobs: gha_python: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} name: 'GHA and Python' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 permissions: security-events: write # To create/update security events steps: @@ -60,7 +60,7 @@ jobs: c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} name: 'C' - runs-on: ${{ matrix.platform == 'Linux' && 'ubuntu-latest' || 'windows-2022' }} + runs-on: ${{ matrix.platform == 'Linux' && 'ubuntu-26.04' || 'windows-2022' }} permissions: security-events: write # To create/update security events strategy: diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index bcab34395f59..2c2042381001 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -142,7 +142,7 @@ jobs: check-windows: name: 'Windows' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 env: TRIPLET: 'x86_64-w64-mingw32' steps: diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 2f47321a1996..23e10decc1fa 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -41,7 +41,7 @@ env: jobs: linux-glibc-gcc: name: 'Linux gcc glibc (amd64, arm64)' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -70,7 +70,7 @@ jobs: linux-glibc-gcc-minimal: # use gcc to minimize installed packages name: 'Linux gcc glibc minimal (amd64)' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 5 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -97,7 +97,7 @@ jobs: linux-musl-llvm: name: 'Linux llvm MUSL (amd64, riscv64)' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -146,7 +146,7 @@ jobs: win-llvm: name: 'Windows llvm (x64)' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -173,7 +173,7 @@ jobs: win-gcc-zlibold-x64: name: 'Windows gcc zlib-classic (x64)' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index 6aa969cd0f4f..e3d37902d702 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -266,7 +266,7 @@ jobs: strategy: fail-fast: false matrix: - image: [ubuntu-24.04-arm, macos-latest, windows-2022] + image: [ubuntu-26.04-arm, macos-latest, windows-2022] steps: - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ contains(matrix.image, 'windows') }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fd8a430c275c..8d9c1cd55d70 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -189,13 +189,13 @@ jobs: configure: --with-rustls --enable-ech --enable-debug - name: 'wolfssl-all' - image: ubuntu-24.04-arm + image: ubuntu-26.04-arm install_steps: wolfssl-all-arm LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-all/lib configure: --with-wolfssl=/home/runner/wolfssl-all --enable-ech --enable-debug - name: 'wolfssl-opensslextra valgrind 1' - image: ubuntu-24.04-arm + image: ubuntu-26.04-arm install_packages: valgrind install_steps: wolfssl-opensslextra-arm tflags: '--min=815 1 to 1000' @@ -203,7 +203,7 @@ jobs: configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - name: 'wolfssl-opensslextra valgrind 2' - image: ubuntu-24.04-arm + image: ubuntu-26.04-arm install_packages: valgrind install_steps: wolfssl-opensslextra-arm tflags: '--min=835 1001 to 9999' @@ -314,7 +314,7 @@ jobs: --with-openssl --with-libssh2 --with-libidn2 --enable-ares --enable-debug - name: '!ssl !http !smtp !imap' - image: ubuntu-24.04-arm + image: ubuntu-26.04-arm tflags: '--min=500' configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity @@ -434,7 +434,7 @@ jobs: tflags: '-n --test-event --min=1420' - name: 'duphandle' - image: ubuntu-24.04-arm + image: ubuntu-26.04-arm install_packages: libssh-dev configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl tflags: '-n --test-duphandle' From 2c1e625a6f6b8eafbdfcef6408407e08c63949bd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 19 Jun 2026 22:32:06 +0200 Subject: [PATCH 0558/1062] INSTALL.md: add building-from-source overview section Ref: #22098 Closes #22113 --- docs/INSTALL.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 467aa64c058f..1d3f1205aa79 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -13,6 +13,24 @@ document does not describe how to install curl or libcurl using such a binary package. This document describes how to compile, build and install curl and libcurl from [source code](https://curl.se/download.html). +## Building from source + +You can use autotools or CMake to build curl from source. They work equally +well and have close to feature parity. + +autotools' advantages are wide portability and the Unix philosophy, while +CMake typically has faster configuration and build times, and supports MSVC. + +Option defaults and dependency detection details may differ. + +With both build tools, some features require Perl, and the pytest test suite +requires Python. + +You can find live examples for both tools and many build cases in curl's CI +scripts: `.github/workflows/*.yml`, `.circleci/*.yml` and `appveyor.*` (in the +Git repository only). For CMake-specific instructions, see +[INSTALL-CMAKE.md](https://github.com/curl/curl/blob/master/docs/INSTALL-CMAKE.md). + ## Building using vcpkg You can download and install curl and libcurl using From cff59ddf4a6486ab8b06f051d630693d67a2bcde Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 09:39:41 +0200 Subject: [PATCH 0559/1062] docs/INSTALL: minor polish Closes #22183 --- docs/INSTALL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 1d3f1205aa79..25dbe9a633a3 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -179,7 +179,7 @@ runtime when curl starts up. ### Selecting TLS Trust Anchors Defaults -Verifying a server certificate established a chain of trust that needs to +Verifying a server certificate establishes a chain of trust that needs to start somewhere. Those "root" certificates make the set of Trust Anchors. While the build system tries to find good defaults on the platform you @@ -312,7 +312,7 @@ curl from the source code: > If an error occurs during the installation, then try: - Use `cmake` to configure and/or build -- Use `ninja` to build (***much** faster*) +- Use `ninja` to build (much faster) - Reinstalling the required Cygwin packages from the list above without passing `-I` to `setup-x86_64` - Temporarily move Cygwin to the top of your path @@ -566,10 +566,10 @@ export CC=ppc_405-gcc export NM=ppc_405-nm ./configure \ - --target=powerpc-hardhat-linux - --host=powerpc-hardhat-linux - --build=i586-pc-linux-gnu - --prefix=/opt/hardhat/devkit/ppc/405/target/usr/local + --target=powerpc-hardhat-linux \ + --host=powerpc-hardhat-linux \ + --build=i586-pc-linux-gnu \ + --prefix=/opt/hardhat/devkit/ppc/405/target/usr/local \ --exec-prefix=/usr/local ``` From fdbc5beed39609067d46958fd4d44a7a977caf0b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 09:34:01 +0200 Subject: [PATCH 0560/1062] urlapi: make two #defines instead of magic values/strings in code Closes #22181 --- lib/urlapi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 77414bd2f800..07817c780cd1 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -53,6 +53,10 @@ /* scheme is not URL encoded, the longest libcurl supported ones are... */ #define MAX_SCHEME_LEN 40 +#define MAX_ZONEID_LEN 16 + +/* characters not allowed in hostnames */ +#define HOSTNAME_INVALID_CHARS " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%|" /* * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make @@ -425,13 +429,13 @@ UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, hlen = len; if(hostname[len] == '%') { /* this could now be '%[zone id]' */ - char zoneid[16]; + char zoneid[MAX_ZONEID_LEN]; int i = 0; char *h = &hostname[len + 1]; /* pass '25' if present and is a URL encoded percent sign */ if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) h += 2; - while(*h && (*h != ']') && (i < 15)) + while(*h && (*h != ']') && (i < (MAX_ZONEID_LEN - 1))) zoneid[i++] = *h++; if(!i || (']' != *h)) return CURLUE_BAD_IPV6; @@ -474,7 +478,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return ipv6_parse(u, hostname, hlen); else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%|"); + len = strcspn(hostname, HOSTNAME_INVALID_CHARS); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; From 231445df2aca782ff265c508760e1dbf04392a65 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 25 Jun 2026 20:42:16 +0200 Subject: [PATCH 0561/1062] cmake: drop logic for `GNU/kFreeBSD` systems Adopting change made in CMake, with description: "kFreeBSD is no longer maintained or supported and was never an officially-supported release architecture for Debian." This system name was also dropped by CMake 4.1+: https://cmake.org/cmake/help/v4.2/variable/CMAKE_SYSTEM_NAME.html Credits-to: Roger Leigh Ref: https://gitlab.kitware.com/cmake/cmake/-/commit/99c8abed55c354f5674e52b25bb65391d4afca52 Ref: https://gitlab.kitware.com/cmake/cmake/-/work_items/26742 Ref: https://gitlab.kitware.com/cmake/cmake/-/work_items/26722 Follow-up to 5de6848f104d7cb0017080e31216265ac19d0dde #10023 #5935 Closes #22176 --- lib/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3ef623536e97..c202a6c6d752 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -251,7 +251,6 @@ if(BUILD_SHARED_LIBS) CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR # OpenHarmony - CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR # FreeBSD comes with the a.out and ELF flavors but a.out was supported # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake # on those ancient systems. From 15bd6c5d4c4cd91c3ffb32920e338c1759aeec7c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 25 Jun 2026 21:19:25 +0200 Subject: [PATCH 0562/1062] cmake: document upstream variable `ZLIB_ROOT` Closes #22177 --- docs/INSTALL-CMAKE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 64975ab63073..8ed9e405ec77 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -372,7 +372,7 @@ Details via CMake ## Dependency options (via CMake) -- `OPENSSL_ROOT_DIR`: Absolute path to the root installation of OpenSSL (and forks). +- `OPENSSL_ROOT_DIR`: Absolute path to the installation root of OpenSSL (and forks). - `OPENSSL_INCLUDE_DIR`: Absolute path to OpenSSL include directory. - `OPENSSL_SSL_LIBRARY`: Absolute path to `ssl` library. With MSVC, CMake uses variables `SSL_EAY_DEBUG`/`SSL_EAY_RELEASE` instead. @@ -381,6 +381,7 @@ Details via CMake - `OPENSSL_USE_STATIC_LIBS`: Look for static OpenSSL libraries. - `ZLIB_INCLUDE_DIR`: Absolute path to zlib include directory. - `ZLIB_LIBRARY`: Absolute path to `zlib` library. +- `ZLIB_ROOT`: Absolute path to the installation root of zlib. - `ZLIB_USE_STATIC_LIBS`: Look for static `zlib` library (requires CMake v3.24). - `_DIR`: Absolute path to `` CMake Config directory where `*.cmake` files reside. Used when `CURL_USE_CMAKECONFIG` is enabled. @@ -410,7 +411,7 @@ Details via CMake - `DL_LIBRARY`: Absolute path to `dl` library. (for Rustls) - `GNUTLS_INCLUDE_DIR`: Absolute path to GnuTLS include directory. - `GNUTLS_LIBRARY`: Absolute path to `gnutls` library. -- `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) +- `GSS_ROOT_DIR`: Absolute path to the installation root of GSS. (also supported as environment) - `LDAP_INCLUDE_DIR`: Absolute path to LDAP include directory. - `LDAP_LIBRARY`: Absolute path to `ldap` library. - `LDAP_LBER_LIBRARY`: Absolute path to `lber` library. @@ -458,7 +459,7 @@ Details via CMake - `QUICHE_LIBRARY`: Absolute path to `quiche` library. - `RUSTLS_INCLUDE_DIR`: Absolute path to Rustls include directory. - `RUSTLS_LIBRARY`: Absolute path to `rustls` library. -- `WATT_ROOT`: Absolute path to the root installation of Watt-32. +- `WATT_ROOT`: Absolute path to the installation root of Watt-32. - `WOLFSSL_INCLUDE_DIR`: Absolute path to wolfSSL include directory. - `WOLFSSL_LIBRARY`: Absolute path to `wolfssl` library. - `ZSTD_INCLUDE_DIR`: Absolute path to zstd include directory. From 397b8f0ec6758dcc743f8b224fa9202f4bac931a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 15 Jun 2026 14:33:20 +0200 Subject: [PATCH 0563/1062] tidy-up: docs, comments, typos, whitespace - GHA/windows: mention `IgnoreStandardErrorWarningFormat=true` in comment. - cmake: fix MIT/GNU GSS order in messages. - drop some exclamation marks from messages. - drop redundant ending newlines from messages. - fold/unfold where possible. - fix indent, whitespace, typos and other nits. Closes #22185 --- .github/workflows/windows.yml | 2 ++ CMake/FindGSS.cmake | 4 +-- CMakeLists.txt | 15 ++++---- acinclude.m4 | 2 +- configure.ac | 3 +- docs/HTTP3.md | 3 +- docs/INSTALL-CMAKE.md | 33 ++++++++++++------ docs/INSTALL.md | 12 +++---- docs/MANUAL.md | 4 +-- docs/RELEASE-PROCEDURE.md | 2 +- docs/cmdline-opts/Makefile.am | 6 ++-- docs/cmdline-opts/_URL.md | 8 ++--- docs/examples/http2-serverpush.c | 2 +- docs/libcurl/curl_formadd.md | 4 +-- docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md | 2 +- lib/creds.h | 4 +-- lib/curl_addrinfo.c | 14 ++++---- lib/curl_setup.h | 6 ++-- lib/formdata.c | 2 +- lib/http_aws_sigv4.c | 2 +- lib/if2ip.c | 18 +++++----- lib/socks_sspi.c | 2 +- lib/transfer.c | 2 +- lib/url.c | 2 +- lib/urlapi.c | 2 +- lib/urldata.h | 2 +- lib/vtls/openssl.h | 2 +- m4/curl-apple-sectrust.m4 | 1 - m4/curl-compilers.m4 | 16 ++++----- m4/curl-confopts.m4 | 2 +- m4/curl-functions.m4 | 38 ++++++++++----------- m4/curl-reentrant.m4 | 4 +-- scripts/cd2nroff | 4 +-- src/tool_getparam.c | 2 +- tests/data/Makefile.am | 3 +- tests/ftpserver.pl | 2 +- tests/http/test_32_ftps_vsftpd.py | 4 +-- tests/libtest/lib668.c | 4 +-- tests/runtests.pl | 20 +++++------ tests/server/dnsd.c | 2 +- tests/server/rtspd.c | 2 +- tests/server/sws.c | 10 +++--- tests/server/tftpd.c | 2 +- tests/server/util.c | 9 ++--- tests/test1139.pl | 6 ++-- tests/test1140.pl | 2 +- tests/unit/unit2413.c | 14 ++++---- 47 files changed, 157 insertions(+), 150 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 53429d1f2bcf..96cfecde2628 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1031,6 +1031,8 @@ jobs: # Officially this requires the vcvarsall.bat MS-DOS batch file (as of # VS2022). Since it integrates badly with CI steps and shell scripts, # reproduce the necessary build configuration manually, without envs. + # The option is likely called: IgnoreStandardErrorWarningFormat=true, + # but there seems to be no option to pass it via CMake or CLI. MSVC_EDITION='2022/Enterprise/vc/tools/msvc' [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]] && MSVC_EDITION='18/Enterprise/vc/tools/msvc' [[ "$(uname -s)" = *'ARM64'* ]] && MSVC_HOST='arm64' || MSVC_HOST='x64' # x86 diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 6f352b2e4f67..a49d529d8154 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -189,7 +189,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr endif() endif() if(NOT _gss_flavor) - message(FATAL_ERROR "GNU or MIT GSS is required") + message(FATAL_ERROR "MIT or GNU GSS is required") endif() else() if(_gss_MODULE_NAME STREQUAL _gnu_modname) @@ -199,7 +199,7 @@ else() set(_gss_flavor "MIT") set(_gss_pc_requires ${_mit_modname}) else() - message(FATAL_ERROR "GNU or MIT GSS is required") + message(FATAL_ERROR "MIT or GNU GSS is required") endif() message(STATUS "Found GSS/${_gss_flavor} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index c956dc411578..9b0d974daa10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1813,7 +1813,8 @@ if(NOT CMAKE_CROSSCOMPILING) include(CheckCSourceRuns) check_c_source_runs(" #include - int main(void) { + int main(void) + { time_t t = -1; return t < 0; }" HAVE_TIME_T_UNSIGNED) @@ -2087,12 +2088,12 @@ message(STATUS "Features: ${SUPPORT_FEATURES}") # Clear list and collect SSL backends set(_items "") -curl_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) -curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL) -curl_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) -curl_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) -curl_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) -curl_add_if("Rustls" _ssl_enabled AND USE_RUSTLS) +curl_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) +curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL) +curl_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) +curl_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) +curl_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) +curl_add_if("Rustls" _ssl_enabled AND USE_RUSTLS) if(_items) list(SORT _items CASE INSENSITIVE) diff --git a/acinclude.m4 b/acinclude.m4 index ed8b0499dd6a..3528b8507e1d 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -885,7 +885,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$curl_func_clock_gettime" = "yes"; then + test "$curl_func_clock_gettime" = "yes"; then AC_MSG_CHECKING([if monotonic clock_gettime works]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ diff --git a/configure.ac b/configure.ac index a21847ea6663..0054bdb250f4 100644 --- a/configure.ac +++ b/configure.ac @@ -4097,7 +4097,8 @@ case $host_os in CURL_RUN_IFELSE( [ #include - int main(void) { + int main(void) + { time_t t = -1; return t < 0; } diff --git a/docs/HTTP3.md b/docs/HTTP3.md index 74254233b21b..07535f1559fa 100644 --- a/docs/HTTP3.md +++ b/docs/HTTP3.md @@ -232,8 +232,7 @@ Build curl: % git clone --depth 1 https://github.com/curl/curl % cd curl % autoreconf -fi - % ./configure --with-openssl=$PWD/../quiche/boringssl \ - --with-quiche=$PWD/../quiche/target/release + % ./configure --with-openssl=$PWD/../quiche/boringssl --with-quiche=$PWD/../quiche/target/release % make % make install diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 8ed9e405ec77..815cdddcd4a1 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -468,7 +468,7 @@ Details via CMake Examples: -- `-DLIBPSL_INCLUDE_DIR=/path/to/libpl/include`, +- `-DLIBPSL_INCLUDE_DIR=/path/to/libpsl/include`, which directory contains `libpsl.h`. No ending slash or backslash is necessary. @@ -521,7 +521,8 @@ Available variables: - `HAVE_MBEDTLS_DES_CRYPT_ECB`: `mbedtls_des_crypt_ecb` present in mbedTLS <4. - `HAVE_OPENSSL_SRP`: `SSL_CTX_set_srp_username` present in OpenSSL (or fork). - `HAVE_QUICHE_CONN_SET_QLOG_FD`: `quiche_conn_set_qlog_fd` present in quiche. -- `HAVE_RUSTLS_SUPPORTED_HPKE`: `rustls_supported_hpke` present in Rustls (unused if Rustls is detected via `pkg-config`). +- `HAVE_RUSTLS_SUPPORTED_HPKE`: `rustls_supported_hpke` present in Rustls + (unused if Rustls is detected via `pkg-config`). - `HAVE_SSL_SET0_WBIO`: `SSL_set0_wbio` present in OpenSSL (or fork). - `HAVE_SSL_SET1_ECH_CONFIG_LIST`: `SSL_set1_ech_config_list` present in OpenSSL (or fork). - `HAVE_SSL_SET_QUIC_TLS_CBS`: `SSL_set_quic_tls_cbs` in OpenSSL. @@ -543,27 +544,37 @@ Note: These variables are internal and subject to change. ## Useful build targets -- `testdeps`: Build test dependencies (test binaries, test certificates). - Test certificates: `build-certs` (clean with `clean-certs`) -- `tests`: Run tests (`runtests.pl`). Customize via the `TFLAGS` environment variable, e.g. `TFLAGS=1621`. - Other flavors: `test-am`, `test-ci`, `test-event`, `test-full`, `test-nonflaky`, `test-quiet`, `test-torture` +- `testdeps`: Build test dependencies (test binaries, + test certificates). + Test certificates: `build-certs` + (clean with `clean-certs`) +- `tests`: Run tests (`runtests.pl`). Customize via the `TFLAGS` + environment variable, e.g. `TFLAGS=1621`. + Other flavors: `test-am`, `test-ci`, `test-event`, + `test-full`, `test-nonflaky`, `test-quiet`, + `test-torture` - `tt`: Build test binaries (servers, tools). - Individual targets: `curlinfo`, `libtests`, `servers`, `tunits`, `units` + Individual targets: `curlinfo`, `libtests`, + `servers`, `tunits`, `units` - `curl-pytest`: Run tests (pytest). Other flavor: `curl-test-ci` - `curl-examples`: Build examples Individual targets: `curl-example-`, where is the .c filename without extension. -- `curl-examples-build`: Build examples quickly but without the ability to run them. (for build tests) +- `curl-examples-build`: Build examples quickly but without the ability + to run them. (for build tests) - `curl-man`: Build man pages. (built by default unless disabled) - `curl`: Build curl tool. - `curl_uninstall`: Uninstall curl. -- `curl-completion-fish`: Build shell completions for fish. (built by default if enabled) -- `curl-completion-zsh`: Build shell completions for zsh. (built by default if enabled) +- `curl-completion-fish`: Build shell completions for fish. + (built by default if enabled) +- `curl-completion-zsh`: Build shell completions for zsh. + (built by default if enabled) - `curl-ca-bundle`: Build the CA bundle via `scripts/mk-ca-bundle.pl`. - `curl-ca-firefox`: Build the CA bundle via `scripts/firefox-db2pem.sh`. - `curl-lint`: Run lint checks. -- `curl-listcats`: Generate help category constants for `src/tool_help.h` from documentation. +- `curl-listcats`: Generate help category constants for + `src/tool_help.h` from documentation. - `curl-listhelp`: Generate `src/tool_listhelp.c` from documentation. - `curl-optiontable`: Generate `lib/easyoptions.c` from documentation. diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 25dbe9a633a3..f98cdb11b01d 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -311,17 +311,17 @@ curl from the source code: > [!Note] > If an error occurs during the installation, then try: -- Use `cmake` to configure and/or build -- Use `ninja` to build (much faster) +- Using `cmake` to configure and/or build +- Using `ninja` to build (much faster) - Reinstalling the required Cygwin packages from the list above without passing `-I` to `setup-x86_64` -- Temporarily move Cygwin to the top of your path -- Install all of the Cygwin build packages using +- Temporarily moving Cygwin to the top of your path +- Installing all of the Cygwin build packages using `setup-x86_64 --build-depends curl` ## MS-DOS -You can use either autotools or cmake: +You can use either autotools or CMake: ```sh ./configure \ @@ -360,7 +360,7 @@ Notes: ## AmigaOS -You can use either autotools or cmake: +You can use either autotools or CMake: ```sh ./configure \ diff --git a/docs/MANUAL.md b/docs/MANUAL.md index fb62f64ba24c..d979455d8496 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -24,7 +24,7 @@ Get a directory listing of an FTP site: curl ftp://ftp.example.com/ -Get the all terms matching curl from a dictionary: +Get all terms matching curl from a dictionary: curl dict://dict.example.com/m:curl @@ -57,7 +57,7 @@ Get a file from an SSH server using SCP using a private key (password-protected) to authenticate: curl -u username: --key ~/.ssh/id_rsa --pass private_key_password - scp://example.com/~/file.txt + scp://example.com/~/file.txt Get the main page from an IPv6 web server: diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index e62a90cbd743..81dcbc628455 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -104,7 +104,7 @@ Release candidate tarballs are ephemeral and each such tarball is only kept around for a few weeks. They are provided on their dedicated webpage at: https://curl.se/rc/ -The git tags for release candidate are temporary and remain set only for a +The git tags for release candidates are temporary and remain set only for a limited period of time. **Do not use release candidates in production**. They are work in progress. diff --git a/docs/cmdline-opts/Makefile.am b/docs/cmdline-opts/Makefile.am index 019dd57b8a56..7c2604b64796 100644 --- a/docs/cmdline-opts/Makefile.am +++ b/docs/cmdline-opts/Makefile.am @@ -37,11 +37,11 @@ GN_0 = @echo " GENERATE" $@; GN_1 = GN_ = $(GN_0) -MANAGEN=$(top_srcdir)/scripts/managen -MAXLINE=$(top_srcdir)/scripts/maxline +MANAGEN = $(top_srcdir)/scripts/managen +MAXLINE = $(top_srcdir)/scripts/maxline # Maximum number of columns accepted in the ASCII version of the man page -INCDIR=$(top_srcdir)/include +INCDIR = $(top_srcdir)/include if BUILD_DOCS CLEANFILES = $(MANPAGE) $(ASCIIPAGE) diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index 700e711359d9..c20cd762bdd4 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -15,10 +15,10 @@ specify command line options and URLs mixed and in any order on the command line. curl attempts to reuse connections when doing multiple transfers, so that -getting many files from the same server do not use multiple connects and setup -handshakes. This improves speed. Connection reuse can only be done for URLs -specified for a single command line invocation and cannot be performed between -separate curl runs. +getting many files from the same server does not use multiple connects and +setup handshakes. This improves speed. Connection reuse can only be done for +URLs specified for a single command line invocation and cannot be performed +between separate curl runs. Everything provided on the command line that is not a command line option or its argument, curl assumes is a URL and treats it as such. diff --git a/docs/examples/http2-serverpush.c b/docs/examples/http2-serverpush.c index e54675ceac16..0d97c1e64a85 100644 --- a/docs/examples/http2-serverpush.c +++ b/docs/examples/http2-serverpush.c @@ -38,7 +38,7 @@ #include #ifndef CURLPIPE_MULTIPLEX -#error "too old libcurl, cannot do HTTP/2 server push!" +#error "too old libcurl, cannot do HTTP/2 server push" #endif #if defined(_MSC_VER) && (_MSC_VER < 1900) diff --git a/docs/libcurl/curl_formadd.md b/docs/libcurl/curl_formadd.md index f988e038767f..fadb1b95a5dc 100644 --- a/docs/libcurl/curl_formadd.md +++ b/docs/libcurl/curl_formadd.md @@ -213,7 +213,7 @@ See example below. ~~~c #include /* for strlen */ -static const char record[]="data in a buffer"; +static const char record[] = "data in a buffer"; int main(void) { @@ -277,7 +277,7 @@ int main(void) forms[0].value = file1; forms[1].option = CURLFORM_FILE; forms[1].value = file2; - forms[2].option = CURLFORM_END; + forms[2].option = CURLFORM_END; /* Add a buffer to upload */ curl_formadd(&post, &last, diff --git a/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md b/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md index d7613f124760..96269eb4c850 100644 --- a/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md +++ b/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md @@ -63,7 +63,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { CURLcode result; - char local_buffer[1024]="data to send"; + char local_buffer[1024] = "data to send"; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* size of the data to copy from the buffer and send in the request */ diff --git a/lib/creds.h b/lib/creds.h index 0664d08a4f4f..4a469d69f6aa 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -73,8 +73,8 @@ bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2); /* Provides properties for creds or, if creds is NULL, the empty string */ #define Curl_creds_has_user(c) ((c) && (c)->user[0]) #define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) -#define Curl_creds_has_user_or_pass(c) \ - ((c) && ((c)->user[0] || (c)->passwd[0])) +#define Curl_creds_has_user_or_pass(c) \ + ((c) && ((c)->user[0] || (c)->passwd[0])) #define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) #define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) #define Curl_creds_user(c) ((c) ? (c)->user : "") diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index a927d44c9923..ea818a38e52b 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -24,24 +24,24 @@ #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H -# include +#include #endif #ifdef HAVE_NETINET_IN6_H -# include +#include #endif #ifdef HAVE_NETDB_H -# include +#include #endif #ifdef HAVE_ARPA_INET_H -# include +#include #endif #ifdef HAVE_SYS_UN_H -# include +#include #endif #ifdef __VMS -# include -# include +#include +#include #endif #include /* for offsetof() */ diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 861e07649dcd..a26bb23cb250 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -957,7 +957,7 @@ struct timeval { (RECV_TYPE_ARG4)(0)) #else /* HAVE_RECV */ #ifndef sread -#error "Missing definition of macro sread!" +#error "Missing definition of macro sread" #endif #endif /* HAVE_RECV */ @@ -980,7 +980,7 @@ struct timeval { #endif /* SEND_NONCONST_ARG2 */ #else /* HAVE_SEND */ #ifndef swrite -#error "Missing definition of macro swrite!" +#error "Missing definition of macro swrite" #endif #endif /* HAVE_SEND */ @@ -1194,7 +1194,7 @@ typedef unsigned int curl_bit; */ #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) # if defined(SOCKET) || defined(USE_WINSOCK) -# error "Winsock and lwIP TCP/IP stack definitions shall not coexist!" +# error "Winsock and lwIP TCP/IP stack definitions shall not coexist" # endif #endif diff --git a/lib/formdata.c b/lib/formdata.c index 681902db71e1..823f4bf590ad 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -186,7 +186,7 @@ static void free_formlist(struct FormInfo *ptr) * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10L, * CURLFORM_END); - * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) + * (if CURLFORM_CONTENTSLENGTH is missing strlen() is used) * * storing a filename (CONTENTTYPE is optional!): * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 780eed67b931..9c80c1ea9b5f 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -52,7 +52,7 @@ #define TIMESTAMP_SIZE 17 -/* hex-encoded with trailing null */ +/* hex-encoded with null-terminator */ #define SHA256_HEX_LENGTH ((2 * CURL_SHA256_DIGEST_LENGTH) + 1) #define MAX_QUERY_COMPONENTS 128 diff --git a/lib/if2ip.c b/lib/if2ip.c index fd34f204b5be..5ae256e13c39 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -24,31 +24,31 @@ #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H -# include +#include #endif #ifdef HAVE_ARPA_INET_H -# include +#include #endif #ifdef HAVE_NET_IF_H -# include +#include #endif #ifdef HAVE_SYS_IOCTL_H -# include +#include #endif #ifdef HAVE_NETDB_H -# include +#include #endif #ifdef HAVE_SYS_SOCKIO_H -# include +#include #endif #ifdef HAVE_IFADDRS_H -# include +#include #endif #ifdef HAVE_STROPTS_H -# include +#include #endif #ifdef __VMS -# include +#include #endif #include "curlx/inet_ntop.h" diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 10a8be1773ae..4474180e7b84 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -37,7 +37,7 @@ #include "curlx/multibyte.h" /* - * Helper sspi error functions. + * Helper SSPI error functions. */ static int check_sspi_err(struct Curl_easy *data, SECURITY_STATUS status, diff --git a/lib/transfer.c b/lib/transfer.c index 1a4138f6c156..11d36ac42721 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -51,7 +51,7 @@ #endif #ifndef HAVE_SOCKET -#error "We cannot compile without socket() support!" +#error "We cannot compile without socket() support" #endif #include "urldata.h" diff --git a/lib/url.c b/lib/url.c index 505e08a7e10a..d0e0286d2fff 100644 --- a/lib/url.c +++ b/lib/url.c @@ -55,7 +55,7 @@ #endif #ifndef HAVE_SOCKET -#error "We cannot compile without socket() support!" +#error "We cannot compile without socket() support" #endif #if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) diff --git a/lib/urlapi.c b/lib/urlapi.c index 07817c780cd1..a664c7304b55 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1764,7 +1764,7 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, /* if the old URL is incomplete (we cannot get an absolute URL in 'oldurl'), replace the existing with the new. Always include "scheme://" to make the URL "complete" */ - uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags& ~CURLU_NO_GUESS_SCHEME); + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags & ~CURLU_NO_GUESS_SCHEME); if(uc == CURLUE_OUT_OF_MEMORY) return uc; else if(uc) diff --git a/lib/urldata.h b/lib/urldata.h index d4d336d8dbdb..35d2c3f52823 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -37,7 +37,7 @@ #define DEFAULT_CONNCACHE_SIZE 5 -/* length of longest IPv6 address string including the trailing null */ +/* length of longest IPv6 address string including the null-terminator */ #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") /* Max string input length is a precaution against abuse and to detect junk diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 4fa466b36789..e707910be009 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -33,7 +33,7 @@ * , , or something else, does this: * #define X509_NAME ((LPCSTR)7) * - * In AWC-LC/BoringSSL's there is: + * In AWS-LC/BoringSSL's there is: * typedef struct X509_name_st X509_NAME; * etc. * diff --git a/m4/curl-apple-sectrust.m4 b/m4/curl-apple-sectrust.m4 index c70b7ac8cd61..decef05c58c8 100644 --- a/m4/curl-apple-sectrust.m4 +++ b/m4/curl-apple-sectrust.m4 @@ -57,5 +57,4 @@ if test "x$OPT_APPLE_SECTRUST" = "xyes"; then else AC_MSG_RESULT(no) fi - ]) diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index 8b7da20caaba..5d838a318c41 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -188,7 +188,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_GNU_C], [ AC_MSG_CHECKING([if compiler is GNU C]) CURL_CHECK_DEF([__GNUC__], [], [silent]) if test "$curl_cv_have_def___GNUC__" = "yes" && - test "$compiler_id" = "unknown"; then + test "$compiler_id" = "unknown"; then AC_MSG_RESULT([yes]) compiler_id="GNU_C" AC_MSG_CHECKING([compiler version]) @@ -305,8 +305,8 @@ AC_DEFUN([CURL_CHECK_COMPILER_SGI_MIPS_C], [ CURL_CHECK_DEF([__GNUC__], [], [silent]) CURL_CHECK_DEF([__sgi], [], [silent]) if test "$curl_cv_have_def___GNUC__" = "no" && - test "$curl_cv_have_def___sgi" = "yes" && - test "$compiler_id" = "unknown"; then + test "$curl_cv_have_def___sgi" = "yes" && + test "$compiler_id" = "unknown"; then AC_MSG_RESULT([yes]) compiler_id="SGI_MIPS_C" flags_dbg_yes="-g" @@ -330,8 +330,8 @@ AC_DEFUN([CURL_CHECK_COMPILER_SGI_MIPSPRO_C], [ CURL_CHECK_DEF([_COMPILER_VERSION], [], [silent]) CURL_CHECK_DEF([_SGI_COMPILER_VERSION], [], [silent]) if test "$curl_cv_have_def___GNUC__" = "no" && - (test "$curl_cv_have_def__SGI_COMPILER_VERSION" = "yes" || - test "$curl_cv_have_def__COMPILER_VERSION" = "yes"); then + (test "$curl_cv_have_def__SGI_COMPILER_VERSION" = "yes" || + test "$curl_cv_have_def__COMPILER_VERSION" = "yes"); then AC_MSG_RESULT([yes]) compiler_id="SGI_MIPSPRO_C" flags_dbg_yes="-g" @@ -1016,7 +1016,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pointer-arith write-strings]) dnl If not cross-compiling with a gcc older than 3.0 if test "$cross_compiling" != "yes" || - test "$compiler_num" -ge "300"; then + test "$compiler_num" -ge "300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused shadow]) fi fi @@ -1026,7 +1026,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [nested-externs]) dnl If not cross-compiling with a gcc older than 3.0 if test "$cross_compiling" != "yes" || - test "$compiler_num" -ge "300"; then + test "$compiler_num" -ge "300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-declarations]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-prototypes]) fi @@ -1476,7 +1476,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_STRUCT_MEMBER_SIZE], [ tst_compiler_check_two_works="yes" ]) if test "$tst_compiler_check_one_works" = "yes" && - test "$tst_compiler_check_two_works" = "yes"; then + test "$tst_compiler_check_two_works" = "yes"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index d3e1a33b6bb5..146e2e308ea4 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -371,7 +371,7 @@ AC_DEFUN([CURL_CONFIGURE_SYMBOL_HIDING], [ CFLAG_CURL_SYMBOL_HIDING="" doing_symbol_hiding="no" if test "$want_symbol_hiding" = "yes" && - test "$supports_symbol_hiding" = "yes"; then + test "$supports_symbol_hiding" = "yes"; then doing_symbol_hiding="yes" CFLAG_CURL_SYMBOL_HIDING="$symbol_hiding_CFLAGS" AC_DEFINE_UNQUOTED(CURL_EXTERN_SYMBOL, $symbol_hiding_EXTERN, diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 8b81d736cbda..490c1e5d1fb4 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -1254,7 +1254,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_getaddrinfo" = "yes"; then + test "$tst_compi_getaddrinfo" = "yes"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -1301,7 +1301,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ fi if test "$tst_compi_getaddrinfo" = "yes" && - test "$tst_works_getaddrinfo" != "no"; then + test "$tst_works_getaddrinfo" != "no"; then AC_MSG_CHECKING([if getaddrinfo usage allowed]) if test "x$curl_disallow_getaddrinfo" != "xyes"; then AC_MSG_RESULT([yes]) @@ -1958,7 +1958,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_getifaddrs" = "yes"; then + test "$tst_compi_getifaddrs" = "yes"; then AC_MSG_CHECKING([if getifaddrs seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -1986,7 +1986,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ fi if test "$tst_compi_getifaddrs" = "yes" && - test "$tst_works_getifaddrs" != "no"; then + test "$tst_works_getifaddrs" != "no"; then AC_MSG_CHECKING([if getifaddrs usage allowed]) if test "x$curl_disallow_getifaddrs" != "xyes"; then AC_MSG_RESULT([yes]) @@ -2079,7 +2079,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_gmtime_r" = "yes"; then + test "$tst_compi_gmtime_r" = "yes"; then AC_MSG_CHECKING([if gmtime_r seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -2106,7 +2106,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ fi if test "$tst_compi_gmtime_r" = "yes" && - test "$tst_works_gmtime_r" != "no"; then + test "$tst_works_gmtime_r" != "no"; then AC_MSG_CHECKING([if gmtime_r usage allowed]) if test "x$curl_disallow_gmtime_r" != "xyes"; then AC_MSG_RESULT([yes]) @@ -2199,7 +2199,7 @@ AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_localtime_r" = "yes"; then + test "$tst_compi_localtime_r" = "yes"; then AC_MSG_CHECKING([if localtime_r seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -2226,7 +2226,7 @@ AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ fi if test "$tst_compi_localtime_r" = "yes" && - test "$tst_works_localtime_r" != "no"; then + test "$tst_works_localtime_r" != "no"; then AC_MSG_CHECKING([if localtime_r usage allowed]) if test "x$curl_disallow_localtime_r" != "xyes"; then AC_MSG_RESULT([yes]) @@ -2319,7 +2319,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_inet_ntop" = "yes"; then + test "$tst_compi_inet_ntop" = "yes"; then AC_MSG_CHECKING([if inet_ntop seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -2400,7 +2400,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ fi if test "$tst_compi_inet_ntop" = "yes" && - test "$tst_works_inet_ntop" != "no"; then + test "$tst_works_inet_ntop" != "no"; then AC_MSG_CHECKING([if inet_ntop usage allowed]) if test "x$curl_disallow_inet_ntop" != "xyes"; then AC_MSG_RESULT([yes]) @@ -2493,7 +2493,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_compi_inet_pton" = "yes"; then + test "$tst_compi_inet_pton" = "yes"; then AC_MSG_CHECKING([if inet_pton seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -2554,7 +2554,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ fi if test "$tst_compi_inet_pton" = "yes" && - test "$tst_works_inet_pton" != "no"; then + test "$tst_works_inet_pton" != "no"; then AC_MSG_CHECKING([if inet_pton usage allowed]) if test "x$curl_disallow_inet_pton" != "xyes"; then AC_MSG_RESULT([yes]) @@ -3994,7 +3994,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_glibc_strerror_r" = "yes"; then + test "$tst_glibc_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -4023,7 +4023,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ fi if test "$tst_compi_strerror_r" = "yes" && - test "$tst_works_glibc_strerror_r" != "yes"; then + test "$tst_works_glibc_strerror_r" != "yes"; then AC_MSG_CHECKING([if strerror_r is POSIX like]) tst_posix_strerror_r_type_arg3="unknown" for arg3 in 'size_t' 'int' 'unsigned int'; do @@ -4057,7 +4057,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ dnl only do runtime verification when not cross-compiling if test "$cross_compiling" != "yes" && - test "$tst_posix_strerror_r" = "yes"; then + test "$tst_posix_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r seems to work]) CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -4092,13 +4092,13 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_glibc_strerror_r="no" fi if test "$tst_glibc_strerror_r" = "yes" && - test "$tst_works_glibc_strerror_r" != "no" && - test "$tst_posix_strerror_r" != "yes"; then + test "$tst_works_glibc_strerror_r" != "no" && + test "$tst_posix_strerror_r" != "yes"; then tst_allow_strerror_r="check" fi if test "$tst_posix_strerror_r" = "yes" && - test "$tst_works_posix_strerror_r" != "no" && - test "$tst_glibc_strerror_r" != "yes"; then + test "$tst_works_posix_strerror_r" != "no" && + test "$tst_glibc_strerror_r" != "yes"; then tst_allow_strerror_r="check" fi if test "$tst_allow_strerror_r" = "check"; then diff --git a/m4/curl-reentrant.m4 b/m4/curl-reentrant.m4 index eb585ad64d27..bf073e2ff6e3 100644 --- a/m4/curl-reentrant.m4 +++ b/m4/curl-reentrant.m4 @@ -409,7 +409,7 @@ AC_DEFUN([CURL_CONFIGURE_REENTRANT], [ AC_MSG_CHECKING([if _REENTRANT is onwards defined]) if test "$tmp_reentrant_initially_defined" = "yes" || - test "$tmp_need_reentrant" = "yes"; then + test "$tmp_need_reentrant" = "yes"; then CURL_CONFIGURE_FROM_NOW_ON_WITH_REENTRANT AC_MSG_RESULT([yes]) else @@ -463,7 +463,7 @@ AC_DEFUN([CURL_CONFIGURE_THREAD_SAFE], [ AC_MSG_CHECKING([if _THREAD_SAFE is onwards defined]) if test "$tmp_thread_safe_initially_defined" = "yes" || - test "$tmp_need_thread_safe" = "yes"; then + test "$tmp_need_thread_safe" = "yes"; then CURL_CONFIGURE_FROM_NOW_ON_WITH_THREAD_SAFE AC_MSG_RESULT([yes]) else diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 27717cfbe31b..23fc4285cbe1 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -59,7 +59,7 @@ Usage: cd2nroff [options] [file.md] specified directory, instead of writing to stdout -e If -d is used, this option can provide an added "extension", arbitrary text really, to append to the filename. --h This help text, +-h This help text -v Show version then exit HELP ; @@ -542,7 +542,7 @@ sub single { } if($d =~ /^[ \t]*\n/) { # replaced away all contents - $blankline= 1; + $blankline = 1; } else { push @desc, $d; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 35c01b1d92d2..a039ad1cb897 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -73,7 +73,7 @@ static ParameterError getstrn(char **str, const char *val, } /* this array MUST be alphasorted based on the 'lname' */ -static const struct LongShort aliases[]= { +static const struct LongShort aliases[] = { {"abstract-unix-socket", ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET}, {"alpn", ARG_BOOL|ARG_NO|ARG_TLS, ' ', C_ALPN}, {"alt-svc", ARG_STRG, ' ', C_ALT_SVC}, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 85e523a36392..0ef33e0adc80 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -287,8 +287,7 @@ test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ \ test3300 test3301 test3302 test3303 test3304 test3305 \ \ -test3400 \ -test3401 \ +test3400 test3401 \ \ test4000 test4001 diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index bbfa05ce7419..d0a3e22fa28e 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -2884,7 +2884,7 @@ sub customize { $retrnosize = 1; } elsif($_ =~ /RETRSIZE (\d+)/) { - $retrsize= $1; + $retrsize = $1; logmsg "FTPD: instructed to use RETRSIZE = $1\n"; } elsif($_ =~ /PASVBADIP/) { diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index 1181bb3211ce..a466d130b6dc 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -121,8 +121,8 @@ def test_32_03b_ftp_compat_ftps(self, env: Env, vsftpds: VsFTPD): curl = CurlClient(env=env) docname = 'data-1k' count = 2 - url1= f'ftps://{env.ftp_domain}:{vsftpds.port}/{docname}' - url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}' + url1 = f'ftps://{env.ftp_domain}:{vsftpds.port}/{docname}' + url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}' r = curl.ftp_get(urls=[url1, url2], with_stats=True) r.check_stats(count=count, http_status=226) assert r.total_connects == count + 1, 'should reuse the control conn' diff --git a/tests/libtest/lib668.c b/tests/libtest/lib668.c index 33cf25e54982..cd9897f7e90c 100644 --- a/tests/libtest/lib668.c +++ b/tests/libtest/lib668.c @@ -94,8 +94,8 @@ static CURLcode test_lib668(const char *URL) curl_mime_data_cb(part, (curl_off_t)-1, t668_read_cb, NULL, NULL, &pooh2); part = curl_mime_addpart(mime); curl_mime_name(part, "field3"); - /* Regular file part sources early end of data can be detected because - the file size is known. In addition, and EOF test is performed. */ + /* For regular file parts, early end of data can be detected because + the file size is known. In addition, an EOF test is performed. */ curl_mime_filedata(part, libtest_arg2); /* Bind mime data to its easy handle. */ diff --git a/tests/runtests.pl b/tests/runtests.pl index d938e186940d..999b8ebb01b4 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -558,7 +558,7 @@ sub checksystemfeatures { $curl = $_; $CURLVERSION = $1; $CURLVERNUM = $CURLVERSION; - $CURLVERNUM =~ s/^([0-9.]+)(.*)/$1/; # leading dots and numbers + $CURLVERNUM =~ s/^([0-9.]+)(.*)/$1/; # leading digits and dots $curl =~ s/^(.*)(libcurl.*)/$1/g or die "Failure determining curl binary version"; $libcurl = $2; @@ -983,7 +983,7 @@ sub citest_starttest { my $testnum = $_[0]; # get the name of the test early - my $testname= (getpart("client", "name"))[0]; + my $testname = (getpart("client", "name"))[0]; chomp $testname; # create test result in CI services @@ -1234,7 +1234,7 @@ sub singletest_count { logmsg sprintf("test %04d...", $testnum) if(!$automakestyle); # name of the test - my $testname= (getpart("client", "name"))[0]; + my $testname = (getpart("client", "name"))[0]; chomp $testname; logmsg "[$testname]\n" if(!$short); @@ -1280,7 +1280,7 @@ sub singletest_check { my $ok = ""; my $res; chomp $errorcode; - my $testname= (getpart("client", "name"))[0]; + my $testname = (getpart("client", "name"))[0]; chomp $testname; # what parts to cut off from stdout/stderr @@ -1429,7 +1429,7 @@ sub singletest_check { my @strippart = getpart("verify", "strippart"); # this is the valid protocol blurb curl should generate - my @protocol= getpart("verify", "protocol"); + my @protocol = getpart("verify", "protocol"); if(@protocol) { # Verify the sent request my @out = loadarray("$logdir/$SERVERIN"); @@ -1447,7 +1447,7 @@ sub singletest_check { # strip off all lines that match the patterns from both arrays chomp $_; @out = striparray( $_, \@out); - @protocol= striparray( $_, \@protocol); + @protocol = striparray( $_, \@protocol); } for my $strip (@strippart) { @@ -1617,7 +1617,7 @@ sub singletest_check { # strip off all lines that match the patterns from both arrays chomp $_; @out = striparray( $_, \@out); - @proxyprot= striparray( $_, \@proxyprot); + @proxyprot = striparray( $_, \@proxyprot); } for my $strip (@strippart) { @@ -1951,8 +1951,8 @@ sub singletest_check { sub singletest_success { my ($testnum, $count, $total, $errorreturncode) = @_; - my $sofar= time()-$start; - my $esttotal = $sofar/$count * $total; + my $sofar = time() - $start; + my $esttotal = $sofar / $count * $total; my $estleft = $esttotal - $sofar; my $timeleft = sprintf("remaining: %02d:%02d", $estleft / 60, @@ -1965,7 +1965,7 @@ sub singletest_success { $count, $total, $timeleft, $took, $duration); } else { - my $testname= (getpart("client", "name"))[0]; + my $testname = (getpart("client", "name"))[0]; chomp $testname; logmsg "PASS: $testnum - $testname\n"; } diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 8fb899bf2686..b9a50c120b27 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -722,7 +722,7 @@ static void read_instructions(void) rc = buf[0] ? 0 : 1; } if(rc != 1) { - logmsg("Bad line in %s: '%s'\n", file, buf); + logmsg("Bad line in %s: '%s'", file, buf); } else if(rtype) { logmsg("added %s record via '%s'", rtype, buf); diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 36973185ed7b..fe2dded9fa8e 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -738,7 +738,7 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) break; case DOCNUMBER_404: default: - logmsg("Replying to with a 404"); + logmsg("Replying with a 404"); if(req->protocol == RPROT_HTTP) { buffer = doc404_HTTP; } diff --git a/tests/server/sws.c b/tests/server/sws.c index a24b3277ca7d..8190002af2fd 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -721,7 +721,7 @@ static int sws_ProcessRequest(struct sws_httprequest *req) test case send a rejection before any such data has been sent. Test case 154 uses this.*/ if(req->auth_req && !req->auth) { - logmsg("Return early due to auth requested by none provided"); + logmsg("Return early due to auth requested but none provided"); return 1; /* done */ } @@ -813,7 +813,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) break; case DOCNUMBER_404: default: - logmsg("Replying to with a 404"); + logmsg("Replying with a 404"); buffer = doc404; break; } @@ -1893,11 +1893,11 @@ static int service_connection(curl_socket_t *msgsock, /* if we got a CONNECT, loop and get another request as well! */ if(req->open) { - logmsg("=> persistent connection request ended, awaits new request\n"); + logmsg("=> persistent connection request ended, awaits new request"); return 1; } else { - logmsg("=> NOT a persistent connection, close close CLOSE\n"); + logmsg("=> NOT a persistent connection, close close CLOSE"); } return -1; @@ -2036,7 +2036,7 @@ static int test_sws(int argc, const char *argv[]) opt = argv[arg]; if(curlx_str_number(&opt, &num, 0xffff)) { fprintf(stderr, "sws: invalid --keepalive argument (%s), must " - "be number of seconds\n", argv[arg]); + "be a number of seconds\n", argv[arg]); return 0; } keepalive_secs = (unsigned short)num; diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index f730506649bc..8f2ab642cdff 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -287,7 +287,7 @@ static void nak(int error) memcpy(tp->th_msg, pe->e_msg, length + 1); length += 5; if(swrite(peer, &trsbuf.storage[0], length) != length) - logmsg("nak: fail\n"); + logmsg("nak: fail"); } /* diff --git a/tests/server/util.c b/tests/server/util.c index 77d635a9e907..05a2e898cc5f 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -336,23 +336,18 @@ typedef void (*SIGHANDLER_T)(int); #ifdef SIGHUP static SIGHANDLER_T old_sighup_handler = SIG_ERR; #endif - #ifdef SIGPIPE static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; #endif - #ifdef SIGALRM static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; #endif - #ifdef SIGINT static SIGHANDLER_T old_sigint_handler = SIG_ERR; #endif - #ifdef SIGTERM static SIGHANDLER_T old_sigterm_handler = SIG_ERR; #endif - #if defined(SIGBREAK) && defined(_WIN32) static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; #endif @@ -502,8 +497,8 @@ static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, } return DefWindowProc(hwnd, uMsg, wParam, lParam); } -/* Window message queue loop for hidden main window, details see above. - */ + +/* Window message queue loop for hidden main window, details see above. */ static DWORD WINAPI main_window_loop(void *lpParameter) { WNDCLASS wc; diff --git a/tests/test1139.pl b/tests/test1139.pl index 81a33bb5375e..ac09d4f59021 100755 --- a/tests/test1139.pl +++ b/tests/test1139.pl @@ -114,7 +114,7 @@ sub scanmdpage { open($r, "<", $syms) or die "no input file"; while(<$r>) { chomp; - my $l= $_; + my $l = $_; if($l =~ /(CURL(OPT|INFO|MOPT)_\w+) *([0-9.]*) *([0-9.-]*) *([0-9.]*)/) { my ($opt, $type, $add, $dep, $rem) = ($1, $2, $3, $4, $5); @@ -232,7 +232,7 @@ sub scanmdpage { my @manpage; # store all parsed parameters while(<$r>) { chomp; - my $l= $_; + my $l = $_; $l =~ s/\\-/-/g; if($l =~ /^\.IP \"(-[^\"]*)\"/) { my $str = $1; @@ -259,7 +259,7 @@ sub scanmdpage { my @toolhelp; # store all parsed parameters while(<$r>) { chomp; - my $l= $_; + my $l = $_; if(/^ \{ \" *(.*)/) { my $str = $1; my $combo; diff --git a/tests/test1140.pl b/tests/test1140.pl index d7f56043f451..320f9f94392b 100755 --- a/tests/test1140.pl +++ b/tests/test1140.pl @@ -91,7 +91,7 @@ sub file { $errors++; } if($l =~ /^\.BR (.*)/) { - my $i= $1; + my $i = $1; while($i =~ s/((lib|)curl([^ ]*)) *\"\(3\)(,|) *\" *//i ) { my $man = "$1.3"; $man =~ s/\\//g; # cut off backslashes diff --git a/tests/unit/unit2413.c b/tests/unit/unit2413.c index a800971ae1a0..ef11c48b7cfe 100644 --- a/tests/unit/unit2413.c +++ b/tests/unit/unit2413.c @@ -90,19 +90,19 @@ static CURLcode test_unit2413(const char *arg) } test_create2413("peer1", curl, &Curl_scheme_https, "test.curl.se", 1234, - "test.curl.se", FALSE, NULL); + "test.curl.se", FALSE, NULL); test_create2413("peer2", curl, &Curl_scheme_https, "127.0.0.1", 1234, - "127.0.0.1", FALSE, NULL); + "127.0.0.1", FALSE, NULL); test_create2413("peer3", curl, &Curl_scheme_https, "::1", 1234, - "::1", TRUE, NULL); + "::1", TRUE, NULL); test_create2413("peer3", curl, &Curl_scheme_https, "[::1]", 1234, - "::1", TRUE, NULL); + "::1", TRUE, NULL); test_create2413("peer4", curl, &Curl_scheme_https, "test.curl.se.", 1234, - "test.curl.se.", FALSE, NULL); + "test.curl.se.", FALSE, NULL); test_create2413("peer5", curl, &Curl_scheme_https, "[::1%tada]", 1234, - "::1", TRUE, "tada"); + "::1", TRUE, "tada"); test_create2413("peer6", curl, &Curl_scheme_https, "::1%tada", 1234, - "::1", TRUE, "tada"); + "::1", TRUE, "tada"); curl_easy_cleanup(curl); Curl_peer_unlink(&peer); From d8f9f0a91284a8b83b67be55f6744a90fd3e5e1e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 09:37:09 +0200 Subject: [PATCH 0564/1062] tool_operate: remove call to abort() Make an assert and return a plain error instead. No abort in release code. - also remove from two examples - add as a banned function in checksrc - document the banned state Closes #22182 --- docs/examples/multi-event.c | 2 +- docs/examples/multi-uv.c | 2 +- docs/internals/CODE_STYLE.md | 1 + scripts/checksrc.pl | 1 + src/tool_operate.c | 3 ++- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/examples/multi-event.c b/docs/examples/multi-event.c index 58744b6e2e6f..b52adefa0cae 100644 --- a/docs/examples/multi-event.c +++ b/docs/examples/multi-event.c @@ -205,7 +205,7 @@ static int handle_socket(CURL *curl, curl_socket_t s, int action, void *userp, } break; default: - abort(); + return -1; /* unknown */ } return 0; diff --git a/docs/examples/multi-uv.c b/docs/examples/multi-uv.c index 094df35105ca..2671556ba0c3 100644 --- a/docs/examples/multi-uv.c +++ b/docs/examples/multi-uv.c @@ -219,7 +219,7 @@ static int cb_socket(CURL *curl, curl_socket_t s, int action, } break; default: - abort(); + return -1; /* unknown */ } return 0; diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index 31c333af8229..ce3c19913555 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -348,6 +348,7 @@ This is the full list of functions generally banned. _wfopen _wfreopen _wopen + abort accept accept4 access diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 46d02ae3c83a..7ef3074b0cb7 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -65,6 +65,7 @@ "_wfopen" => 1, "_wfreopen" => 1, "_wopen" => 1, + "abort" => 1, "accept" => 1, "accept4" => 1, "access" => 1, diff --git a/src/tool_operate.c b/src/tool_operate.c index 97a1d9304702..27f4de6a9eea 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1768,7 +1768,8 @@ static int cb_socket(CURL *easy, curl_socket_t s, int action, } break; default: - abort(); + DEBUGASSERT(0); + return -1; } return 0; From a6af50c484f6bbd1c01573685d6be6234ae944ea Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 25 Jun 2026 22:52:07 +0200 Subject: [PATCH 0565/1062] multi: forbid curl_easy_pause from within multi socket callback - there is a risk for a nasty recursive situation - we avoid certain risks that the pause call changes things so that when returning from the callback, the state of some internals is undefined and we need to reload which is easy to miss - we can't think of legitiate use cases for doing this. This is basically just the new favorite point for AI and security researchers to find hypothetical problems Closes #22179 --- docs/libcurl/curl_easy_pause.md | 4 ++-- lib/easy.c | 11 ++++++----- lib/multi.c | 7 ++++--- lib/multi_ev.c | 13 +++++-------- lib/multihandle.h | 6 +++++- lib/multiif.h | 4 ++-- tests/libtest/lib758.c | 9 +++++++++ 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index 4a37ed1c14a7..f0c9230da5fd 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -28,8 +28,8 @@ CURLcode curl_easy_pause(CURL *handle, int action); Using this function, you can explicitly mark a running connection to get paused, and you can unpause a connection that was previously paused. Unlike -most other libcurl functions, curl_easy_pause(3) can be used from within -callbacks. +most other libcurl functions, curl_easy_pause(3) can be used from within all +callbacks except the socket callback set with CURLMOPT_SOCKETFUNCTION(3). A connection can be paused by using this function or by letting the read or the write callbacks return the proper return code (*CURL_READFUNC_PAUSE* and diff --git a/lib/easy.c b/lib/easy.c index d60bdaed7ba3..2e2c4d0e252c 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1138,18 +1138,19 @@ void curl_easy_reset(CURL *curl) CURLcode curl_easy_pause(CURL *curl, int action) { CURLcode result = CURLE_OK; - bool recursive = FALSE; bool changed = FALSE; struct Curl_easy *data = curl; bool recv_paused, recv_paused_new; bool send_paused, send_paused_new; + uint8_t in_c; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, do not continue */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - recursive = TRUE; + in_c = Curl_is_in_callback(data); + if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE) + return CURLE_RECURSIVE_API_CALL; recv_paused = Curl_xfer_recv_is_paused(data); recv_paused_new = (action & CURLPAUSE_RECV); @@ -1188,10 +1189,10 @@ CURLcode curl_easy_pause(CURL *curl, int action) if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) result = CURLE_ABORTED_BY_CALLBACK; - if(recursive) + if(in_c) /* this might have called a callback recursively which might have set this to false again on exit */ - Curl_set_in_callback(data, TRUE); + Curl_set_in_callback(data, in_c); return result; } diff --git a/lib/multi.c b/lib/multi.c index d6ae111d8ee3..51b0917ce781 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3906,15 +3906,16 @@ static void process_pending_handles(struct Curl_multi *multi) } } -void Curl_set_in_callback(struct Curl_easy *data, bool value) +/* 'value' used to be a boolean but can now also contain more info */ +void Curl_set_in_callback(struct Curl_easy *data, uint8_t value) { if(data && data->multi) data->multi->in_callback = value; } -bool Curl_is_in_callback(struct Curl_easy *data) +uint8_t Curl_is_in_callback(struct Curl_easy *data) { - return data && data->multi && data->multi->in_callback; + return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO; } unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 71e22b9b1958..12d1ca2abba0 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -34,8 +34,7 @@ #include "uint-spbset.h" #include "multihandle.h" - -static void mev_in_callback(struct Curl_multi *multi, bool value) +static void mev_in_callback(struct Curl_multi *multi, uint8_t value) { multi->in_callback = value; } @@ -209,12 +208,10 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, if(entry->announced && multi->socket_cb) { NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); - mev_in_callback(multi, TRUE); + mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->user_data); - mev_in_callback(multi, FALSE); - /* curl_easy_pause() is documented as callable from any callback; it - * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + mev_in_callback(multi, IN_CALLBACK_NO); entry = mev_sh_entry_get(&multi->ev.sh_entries, s); if(entry) entry->announced = FALSE; @@ -285,10 +282,10 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)", s, (comboaction & CURL_POLL_IN) ? "IN" : "", (comboaction & CURL_POLL_OUT) ? "OUT" : ""); - mev_in_callback(multi, TRUE); + mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->user_data); - mev_in_callback(multi, FALSE); + mev_in_callback(multi, IN_CALLBACK_NO); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; diff --git a/lib/multihandle.h b/lib/multihandle.h index 19dd2ffcdf16..5cf2c640e547 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -45,6 +45,10 @@ struct Curl_message { struct CURLMsg extmsg; }; +#define IN_CALLBACK_NO 0 +#define IN_CALLBACK_YES 1 +#define IN_CALLBACK_FORBID_EASY_PAUSE 2 + /* NOTE: if you add a state here, add the name to the statenames[] array * in curl_trc.c as well! */ @@ -179,10 +183,10 @@ struct Curl_multi { #endif uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ uint32_t last_resolv_id; /* id of the last DNS resolve operation */ + uint8_t in_callback; /* conditions for executing in callbacks */ BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ - BIT(in_callback); /* true while executing a callback */ BIT(in_ntfy_callback); /* true while dispatching notifications */ #ifdef USE_OPENSSL BIT(ssl_seeded); diff --git a/lib/multiif.h b/lib/multiif.h index 039db269e001..7fad9ca33e4f 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -37,8 +37,8 @@ void Curl_attach_connection(struct Curl_easy *data, struct connectdata *conn); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); -void Curl_set_in_callback(struct Curl_easy *data, bool value); -bool Curl_is_in_callback(struct Curl_easy *data); +void Curl_set_in_callback(struct Curl_easy *data, uint8_t value); +uint8_t Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); bool Curl_is_connecting(struct Curl_easy *data); diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index 2fa8ef3ab110..794e4cce066d 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -147,6 +147,7 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action, void *userp, void *socketp) { struct t758_ReadWriteSockets *sockets = userp; + CURLcode result; (void)curl; (void)socketp; @@ -158,6 +159,14 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action, return -1; } + /* Pause is forbidden in this callback. This also returns + CURLE_BAD_FUNCTION_ARGUMENT before the connection has been setup. */ + result = curl_easy_pause(curl, CURLPAUSE_ALL); + if(!result) { + t758_msg("<- curl_easy_pause should return error!"); + return -1; + } + if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) if(t758_addFd(&sockets->read, s, "read")) return -1; /* bail out */ From 00d5562650e660fe9302afccccb7365abe8968fe Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 13:14:17 +0200 Subject: [PATCH 0566/1062] proxy: optimized env lookups Optimize proxy env var lookups for the commen case of 'https' and 'http' urls. We do this for every transfer. Closes #22168 --- lib/proxy.c | 95 +++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/lib/proxy.c b/lib/proxy.c index 920dc455986d..28738a15d6a8 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -282,8 +282,6 @@ UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy) static char *proxy_detect_proxy(struct Curl_easy *data, const struct Curl_scheme *scheme) { - char *proxy = NULL; - /* If proxy was not specified, we check for default proxy environment * variables, to enable i.e Lynx compliance: * @@ -301,58 +299,63 @@ static char *proxy_detect_proxy(struct Curl_easy *data, * For compatibility, the all-uppercase versions of these variables are * checked if the lowercase versions do not exist. */ - char proxy_env[20]; - const char *envp; - VERBOSE(envp = proxy_env); - - curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name); - - /* read the protocol proxy: */ - proxy = curl_getenv(proxy_env); + const char *env_name = NULL; + char *proxy = NULL; + char name_buf[20]; + + /* Try scheme specific env var first, unless http(s). + * lowercase first, then uppercase. */ + if((scheme != &Curl_scheme_https) && (scheme != &Curl_scheme_http)) { + curl_msnprintf(name_buf, sizeof(name_buf), "%s_proxy", scheme->name); + env_name = name_buf; + proxy = curl_getenv(env_name); + if(!proxy) { + Curl_strntoupper(name_buf, name_buf, sizeof(name_buf)); + proxy = curl_getenv(env_name); + } + } - /* - * We do not try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!proxy && !curl_strequal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - proxy = curl_getenv(proxy_env); + if(!proxy && + ((scheme == &Curl_scheme_https) || (scheme == &Curl_scheme_wss))) { + /* Not found, check 'https' env vars, also for 'wss'. + * Again, first lowercase then uppercase. */ + env_name = "https_proxy"; + proxy = curl_getenv(env_name); + if(!proxy) { + env_name = "HTTPS_PROXY"; + proxy = curl_getenv(env_name); + } + } + else if(!proxy && + ((scheme == &Curl_scheme_http) || (scheme == &Curl_scheme_ws))) { + /* Not found, check 'http' env vars, also for 'ws'. + * We do NOT try the uppercase version 'HTTP_PROXY' because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + env_name = "http_proxy"; + proxy = curl_getenv(env_name); } if(!proxy) { -#ifndef CURL_DISABLE_WEBSOCKETS - /* websocket proxy fallbacks */ - if(curl_strequal("ws_proxy", proxy_env)) { - proxy = curl_getenv("http_proxy"); - } - else if(curl_strequal("wss_proxy", proxy_env)) { - proxy = curl_getenv("https_proxy"); - if(!proxy) - proxy = curl_getenv("HTTPS_PROXY"); - } + /* still not found, last resort checks. */ + env_name = "all_proxy"; + proxy = curl_getenv(env_name); if(!proxy) { -#endif - envp = "all_proxy"; - proxy = curl_getenv(envp); /* default proxy to use */ - if(!proxy) { - envp = "ALL_PROXY"; - proxy = curl_getenv(envp); - } -#ifndef CURL_DISABLE_WEBSOCKETS + env_name = "ALL_PROXY"; + proxy = curl_getenv(env_name); } -#endif } + if(proxy) - infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); + infof(data, "Uses proxy env variable %s == '%s'", env_name, proxy); return proxy; } From 2f1e94eaede6bd0534203f00abc90cced1d5dc4d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 13:25:35 +0200 Subject: [PATCH 0567/1062] conncache: connection alive checks intervals Do not check if a particular connection is alive or not more than once every second. We did this on every connection reuse which is overkill when sending many requests to the same host. Closes #22169 --- lib/conncache.c | 9 ++++++--- lib/url.c | 13 ++++++++++--- lib/url.h | 3 ++- lib/urldata.h | 1 + 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/conncache.c b/lib/conncache.c index 5ba236862200..6fc29be4b4e2 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -687,6 +687,7 @@ void Curl_conn_terminate(struct Curl_easy *data, struct cpool_reaper_ctx { size_t checked; size_t reaped; + struct curltime now; }; static int cpool_reap_dead_cb(struct Curl_easy *data, @@ -697,7 +698,7 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, if(!terminate) { reaper->checked++; - terminate = Curl_conn_seems_dead(conn, data); + terminate = Curl_conn_seems_dead(conn, data, &reaper->now); } if(terminate) { /* stop the iteration here, pass back the connection that was pruned */ @@ -718,17 +719,19 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, void Curl_cpool_prune_dead(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); - struct cpool_reaper_ctx reaper; timediff_t elapsed; if(!cpool) return; - memset(&reaper, 0, sizeof(reaper)); CPOOL_LOCK(cpool, data); elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &cpool->last_cleanup); if(elapsed >= 1000L) { + struct cpool_reaper_ctx reaper; + + memset(&reaper, 0, sizeof(reaper)); + reaper.now = *Curl_pgrs_now(data); while(cpool_foreach(data, cpool, &reaper, cpool_reap_dead_cb)) ; cpool->last_cleanup = *Curl_pgrs_now(data); diff --git a/lib/url.c b/lib/url.c index d0e0286d2fff..1567d5bf3c50 100644 --- a/lib/url.c +++ b/lib/url.c @@ -606,7 +606,8 @@ static bool conn_maxage(struct Curl_easy *data, * Return TRUE iff the given connection is considered dead. */ bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data) + struct Curl_easy *data, + const struct curltime *pnow) { DEBUGASSERT(!data->conn); if(!CONN_INUSE(conn)) { @@ -614,10 +615,12 @@ bool Curl_conn_seems_dead(struct connectdata *conn, use */ bool dead; - if(conn_maxage(data, conn, *Curl_pgrs_now(data))) { + if(conn_maxage(data, conn, *pnow)) { /* avoid check if already too old */ dead = TRUE; } + else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) + dead = FALSE; else if(conn->scheme->run->connection_is_dead) { /* The protocol has a special method for checking the state of the connection. Use it to check if the connection is dead. */ @@ -625,6 +628,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, Curl_attach_connection(data, conn); dead = conn->scheme->run->connection_is_dead(data, conn); Curl_detach_connection(data); + conn->lastchecked = *pnow; } else { bool input_pending = FALSE; @@ -644,6 +648,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, dead = TRUE; } Curl_detach_connection(data); + conn->lastchecked = *pnow; } if(dead) { @@ -690,6 +695,7 @@ struct url_conn_match { struct connectdata *found; struct Curl_easy *data; struct connectdata *needle; + struct curltime now; BIT(may_multiplex); BIT(want_ntlm_http); BIT(want_proxy_ntlm_http); @@ -1172,7 +1178,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_limits(conn, m)) return FALSE; - if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data)) { + if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, &m->now)) { /* remove and disconnect. */ Curl_conn_terminate(m->data, conn, FALSE); return FALSE; @@ -1227,6 +1233,7 @@ static bool url_attach_existing(struct Curl_easy *data, memset(&match, 0, sizeof(match)); match.data = data; match.needle = needle; + match.now = *Curl_pgrs_now(data); match.may_multiplex = xfer_may_multiplex(data, needle); #ifdef USE_NTLM diff --git a/lib/url.h b/lib/url.h index ebbe9d53c47f..02f1bd3943ab 100644 --- a/lib/url.h +++ b/lib/url.h @@ -75,7 +75,8 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key); * Return TRUE iff the given connection is considered dead. */ bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data); + struct Curl_easy *data, + const struct curltime *pnow); /** * Perform upkeep operations on the connection. diff --git a/lib/urldata.h b/lib/urldata.h index 35d2c3f52823..4666fa3658ec 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -305,6 +305,7 @@ struct connectdata { char *options; /* options string, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ + struct curltime lastchecked; /* when last checked alive status */ /* A connection can have one or two sockets and connection filters. * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */ From cfc7922377d20ea1a4a989fb043c3c5d189edfee Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 26 Jun 2026 10:50:14 +0200 Subject: [PATCH 0568/1062] altsvc: use Curl_peer in processing Use `data->state.origin` when processing alt-avc information. Decomplexify parsing function. Closes #22184 --- lib/altsvc.c | 356 +++++++++++++++++++++++------------------- lib/altsvc.h | 10 +- lib/connect.c | 6 - lib/connect.h | 1 - lib/http.c | 4 +- lib/url.c | 11 +- tests/unit/unit1654.c | 59 +++++-- 7 files changed, 248 insertions(+), 199 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index 80913d9a1d8b..6b8b2617f9f3 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -78,6 +78,12 @@ const char *Curl_alpnid2str(enum alpnid id) } } +static enum alpnid Curl_str2alpnid(const struct Curl_str *cstr) +{ + return Curl_alpn2alpnid((const unsigned char *)curlx_str(cstr), + curlx_strlen(cstr)); +} + #define altsvc_free(x) curlx_free(x) static struct altsvc *altsvc_createid(const char *srchost, @@ -447,23 +453,115 @@ static bool hostcompare(const char *host, const char *check) /* altsvc_flush() removes all alternatives for this source origin from the list */ -static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, - const char *srchost, unsigned short srcport) +static void altsvc_flush(struct altsvcinfo *asi, + struct Curl_peer *origin, + enum alpnid origin_alpnid) { struct Curl_llist_node *e; struct Curl_llist_node *n; for(e = Curl_llist_head(&asi->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); - if((srcalpnid == as->src.alpnid) && - (srcport == as->src.port) && - hostcompare(srchost, as->src.host)) { + if((origin_alpnid == as->src.alpnid) && + (origin->port == as->src.port) && + hostcompare(origin->hostname, as->src.host)) { Curl_node_remove(e); altsvc_free(as); } } } +static void altsvc_parse_params(const char **pp, + time_t *pmaxage, + bool *ppersist) +{ + curlx_str_passblanks(pp); + if(curlx_str_single(pp, ';')) + return; + + for(;;) { + struct Curl_str name; + struct Curl_str val; + const char *vp; + curl_off_t num; + bool quoted; + + /* allow some extra whitespaces around name and value */ + if(curlx_str_until(pp, &name, 20, '=') || + curlx_str_single(pp, '=') || + curlx_str_cspn(pp, &val, ",;")) + break; /* skip further parameter parsing */ + + curlx_str_trimblanks(&name); + curlx_str_trimblanks(&val); + /* the value might be quoted */ + vp = curlx_str(&val); + quoted = (*vp == '\"'); + if(quoted) + vp++; + /* we process 2 number value parameters: 'ma' and 'persist' */ + if(curlx_str_number(&vp, &num, TIME_T_MAX)) + break; /* not a number, skip further parameter parsing */ + + if(curlx_str_casecompare(&name, "ma")) + *pmaxage = (time_t)num; + else if(curlx_str_casecompare(&name, "persist") && (num == 1)) + *ppersist = TRUE; + + *pp = vp; /* point to the byte ending the value */ + curlx_str_passblanks(pp); + if(quoted && curlx_str_single(pp, '\"')) + break; /* was quoted but not ended in quote, skip */ + curlx_str_passblanks(pp); + if(curlx_str_single(pp, ';')) + break; /* no further parameters */ + } +} + +static bool altsvc_parse_dest(const char **pp, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_str *dsthost, + uint16_t *pdstport) +{ + curl_off_t port = 0; + + if(curlx_str_single(pp, '\"')) + return FALSE; + + /* quoted string, with hostname or just :port ? */ + if(curlx_str_single(pp, ':')) { /* is hostname:port ? */ + if(curlx_str_single(pp, '[')) { /* DNS hostname/ipv4 */ + if(curlx_str_until(pp, dsthost, MAX_ALTSVC_HOSTLEN, ':')) { + infof(data, "Bad alt-svc hostname, ignoring."); + return FALSE; + } + } + else { /* IPv6 hostname */ + if(curlx_str_until(pp, dsthost, MAX_IPADR_LEN, ']') || + curlx_str_single(pp, ']')) { + infof(data, "Bad alt-svc IPv6 hostname, ignoring."); + return FALSE; + } + } + if(curlx_str_single(pp, ':')) + return FALSE; /* not followed by ':' */ + } + else /* is only :port, hostname is effectively origin */ + curlx_str_assign(dsthost, origin->hostname, + strlen(origin->hostname)); + + if(curlx_str_number(pp, &port, 0xffff)) { + infof(data, "Unknown alt-svc port number, ignoring."); + return FALSE; + } + + *pdstport = (uint16_t)port; + if(curlx_str_single(pp, '\"')) + return FALSE; /* quoted string not ending here as expected */ + return TRUE; +} + /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. @@ -477,161 +575,91 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, */ CURLcode Curl_altsvc_parse(struct Curl_easy *data, struct altsvcinfo *asi, const char *value, - enum alpnid srcalpnid, const char *srchost, - unsigned short srcport) + struct Curl_peer *origin, + enum alpnid origin_alpnid) { - const char *p = value; struct altsvc *as; - unsigned short dstport = srcport; /* the same by default */ size_t entries = 0; struct Curl_str alpn; + const char *p; DEBUGASSERT(asi); + DEBUGASSERT(origin); + /* RFC 7838, The "Alt-Svc" header field value is basically + * Alt-Svc: (clear|alpn="(host)?:port"\s*(;\s*parameter=value)*) + * This can be repeated, comma-separated. + * + * We parse "best effort", ignoring values we do not recognize. + */ - /* initial check for "clear" */ + /* Try to catch a standalone "clear" */ + p = value; if(!curlx_str_cspn(&p, &alpn, ";\n\r")) { curlx_str_trimblanks(&alpn); /* "clear" is a magic keyword */ if(curlx_str_casecompare(&alpn, "clear")) { /* Flush cached alternatives for this source origin */ - altsvc_flush(asi, srcalpnid, srchost, srcport); + altsvc_flush(asi, origin, origin_alpnid); return CURLE_OK; } } - p = value; + /* Not a standalone "clear", parse from start for alpn entries */ + for(p = value; *p;) { + time_t maxage = 24 * 3600; /* default is 24 hours */ + bool persist = FALSE; + enum alpnid dstalpnid; + struct Curl_str dsthost; + uint16_t dstport; - if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '=')) - return CURLE_OK; /* strange line */ - - curlx_str_trimblanks(&alpn); - - do { - if(!curlx_str_single(&p, '=')) { - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; - /* [protocol]="[host][:port], [protocol]="[host][:port]" */ - enum alpnid dstalpnid = Curl_str2alpnid(&alpn); - if(!curlx_str_single(&p, '\"')) { - struct Curl_str dsthost; - curl_off_t port = 0; - if(curlx_str_single(&p, ':')) { - /* hostname starts here */ - if(curlx_str_single(&p, '[')) { - if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) { - infof(data, "Bad alt-svc hostname, ignoring."); - break; - } - } - else { - /* IPv6 hostname */ - if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') || - curlx_str_single(&p, ']')) { - infof(data, "Bad alt-svc IPv6 hostname, ignoring."); - break; - } - } - if(curlx_str_single(&p, ':')) - break; - } - else - /* no destination name, use source host */ - curlx_str_assign(&dsthost, srchost, strlen(srchost)); + if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '=')) + break; /* not another entry, leave */ + curlx_str_trimblanks(&alpn); + dstalpnid = Curl_str2alpnid(&alpn); - if(curlx_str_number(&p, &port, 0xffff)) { - infof(data, "Unknown alt-svc port number, ignoring."); - break; - } + if(curlx_str_single(&p, '=')) + break; - dstport = (unsigned short)port; - - if(curlx_str_single(&p, '\"')) - break; - - /* Handle the optional 'ma' and 'persist' flags. Unknown flags are - skipped. */ - curlx_str_passblanks(&p); - if(!curlx_str_single(&p, ';')) { - for(;;) { - struct Curl_str name; - struct Curl_str val; - const char *vp; - curl_off_t num; - bool quoted; - /* allow some extra whitespaces around name and value */ - if(curlx_str_until(&p, &name, 20, '=') || - curlx_str_single(&p, '=') || - curlx_str_cspn(&p, &val, ",;")) - break; - curlx_str_trimblanks(&name); - curlx_str_trimblanks(&val); - /* the value might be quoted */ - vp = curlx_str(&val); - quoted = (*vp == '\"'); - if(quoted) - vp++; - if(!curlx_str_number(&vp, &num, TIME_T_MAX)) { - if(curlx_str_casecompare(&name, "ma")) - maxage = (time_t)num; - else if(curlx_str_casecompare(&name, "persist") && (num == 1)) - persist = TRUE; - } - else - break; - p = vp; /* point to the byte ending the value */ - curlx_str_passblanks(&p); - if(quoted && curlx_str_single(&p, '\"')) - break; - curlx_str_passblanks(&p); - if(curlx_str_single(&p, ';')) - break; - } - } - if(dstalpnid) { - if(!entries++) - /* Flush cached alternatives for this source origin, if any - when - this is the first entry of the line. */ - altsvc_flush(asi, srcalpnid, srchost, srcport); - - as = altsvc_createid(srchost, strlen(srchost), - curlx_str(&dsthost), - curlx_strlen(&dsthost), - srcalpnid, dstalpnid, - srcport, dstport); - if(as) { - time_t secs = time(NULL); - /* The expires time also needs to take the Age: value (if any) - into account. [See RFC 7838 section 3.1] */ - if(maxage > (TIME_T_MAX - secs)) - as->expires = TIME_T_MAX; - else - as->expires = maxage + secs; - as->persist = persist; - altsvc_append(asi, as); - infof(data, "Added alt-svc: %.*s:%d over %s", - (int)curlx_strlen(&dsthost), curlx_str(&dsthost), - dstport, Curl_alpnid2str(dstalpnid)); - } - else - return CURLE_OUT_OF_MEMORY; - } - } - else - break; + /* Parse altsvc hostname:port */ + if(!altsvc_parse_dest(&p, data, origin, &dsthost, &dstport)) + break; - /* after the double quote there can be a comma if there is another - string or a semicolon if no more */ - if(curlx_str_single(&p, ',')) - break; + /* Parse optional parameters */ + altsvc_parse_params(&p, &maxage, &persist); - /* comma means another alternative is present */ - if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '=')) - break; - curlx_str_trimblanks(&alpn); + if(dstalpnid) { /* this is a known ALPN id, e.g. not ALPN_none */ + if(!entries++) + /* Flush cached alternatives for this source origin, if any - when + this is the first entry of the line. */ + altsvc_flush(asi, origin, origin_alpnid); + + as = altsvc_createid(origin->hostname, strlen(origin->hostname), + curlx_str(&dsthost), + curlx_strlen(&dsthost), + origin_alpnid, dstalpnid, + origin->port, dstport); + if(as) { + time_t secs = time(NULL); + /* The expires time also needs to take the Age: value (if any) + into account. [See RFC 7838 section 3.1] */ + if(maxage > (TIME_T_MAX - secs)) + as->expires = TIME_T_MAX; + else + as->expires = maxage + secs; + as->persist = persist; + altsvc_append(asi, as); + infof(data, "Added alt-svc: %.*s:%u over %s", + (int)curlx_strlen(&dsthost), curlx_str(&dsthost), + dstport, Curl_alpnid2str(dstalpnid)); + } + else + return CURLE_OUT_OF_MEMORY; } - else + + /* When this is followed by a comma, we expect another entry */ + if(curlx_str_single(&p, ',')) break; - } while(1); + } return CURLE_OK; } @@ -640,38 +668,42 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, * Return TRUE on a match */ bool Curl_altsvc_lookup(struct altsvcinfo *asi, - enum alpnid srcalpnid, const char *srchost, - int srcport, + struct Curl_peer *origin, + enum alpnid origin_alpnid, struct altsvc **dstentry, const int versions, /* one or more bits */ bool *psame_destination) { - struct Curl_llist_node *e; - struct Curl_llist_node *n; - time_t now = time(NULL); DEBUGASSERT(asi); - DEBUGASSERT(srchost); + DEBUGASSERT(origin); DEBUGASSERT(dstentry); - *psame_destination = FALSE; - for(e = Curl_llist_head(&asi->list); e; e = n) { - struct altsvc *as = Curl_node_elem(e); - n = Curl_node_next(e); - if(as->expires < now) { - /* an expired entry, remove */ - Curl_node_remove(e); - altsvc_free(as); - continue; - } - if((as->src.alpnid == srcalpnid) && - hostcompare(srchost, as->src.host) && - (as->src.port == srcport) && - (versions & (int)as->dst.alpnid)) { - /* match */ - *dstentry = as; - *psame_destination = (srcport == as->dst.port) && - hostcompare(srchost, as->dst.host); - return TRUE; + + if(Curl_llist_count(&asi->list)) { + struct Curl_llist_node *e; + struct Curl_llist_node *n; + time_t now = time(NULL); + + for(e = Curl_llist_head(&asi->list); e; e = n) { + struct altsvc *as = Curl_node_elem(e); + n = Curl_node_next(e); + if(as->expires < now) { + /* an expired entry, remove */ + Curl_node_remove(e); + altsvc_free(as); + continue; + } + if((origin_alpnid == as->src.alpnid) && + (versions & (int)as->dst.alpnid) && + (origin->port == as->src.port) && + hostcompare(origin->hostname, as->src.host)) { + /* match */ + *dstentry = as; + /* alt-svc on the same host+port or another one? */ + *psame_destination = (origin->port == as->dst.port) && + hostcompare(origin->hostname, as->dst.host); + return TRUE; + } } } return FALSE; diff --git a/lib/altsvc.h b/lib/altsvc.h index dc1740bce13e..cf9e21808ecf 100644 --- a/lib/altsvc.h +++ b/lib/altsvc.h @@ -28,6 +28,8 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) #include "llist.h" +struct Curl_peer; + /* the maximum number of alt-svc entries kept in a single cache */ #define MAX_ALTSVC_ENTRIES 5000 @@ -60,11 +62,11 @@ CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl); void Curl_altsvc_cleanup(struct altsvcinfo **asi); CURLcode Curl_altsvc_parse(struct Curl_easy *data, struct altsvcinfo *asi, const char *value, - enum alpnid srcalpnid, const char *srchost, - unsigned short srcport); + struct Curl_peer *origin, + enum alpnid origin_alpnid); bool Curl_altsvc_lookup(struct altsvcinfo *asi, - enum alpnid srcalpnid, const char *srchost, - int srcport, + struct Curl_peer *origin, + enum alpnid origin_alpnid, struct altsvc **dstentry, const int versions, /* CURLALTSVC_H* bits */ bool *psame_destination); diff --git a/lib/connect.c b/lib/connect.c index a2b86c74587d..6d6ddd2e91a1 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -57,12 +57,6 @@ enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len) return ALPN_none; /* unknown, probably rubbish input */ } -enum alpnid Curl_str2alpnid(const struct Curl_str *cstr) -{ - return Curl_alpn2alpnid((const unsigned char *)curlx_str(cstr), - curlx_strlen(cstr)); -} - #endif /* diff --git a/lib/connect.h b/lib/connect.h index 95bde1eb4cf6..a5b7042d6e59 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -32,7 +32,6 @@ struct Curl_peer; struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); -enum alpnid Curl_str2alpnid(const struct Curl_str *cstr); /* generic function that returns how much time there is left to run, according to the timeouts set */ diff --git a/lib/http.c b/lib/http.c index 83d0e011528e..a0aee51c7880 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3208,9 +3208,7 @@ static CURLcode http_header_a(struct Curl_easy *data, struct SingleRequest *k = &data->req; enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; - return Curl_altsvc_parse( - data, data->asi, v, id, data->state.origin->hostname, - curlx_uitous((unsigned int)data->state.origin->port)); + return Curl_altsvc_parse(data, data->asi, v, data->state.origin, id); } #else (void)data; diff --git a/lib/url.c b/lib/url.c index 1567d5bf3c50..ea635fd1e43b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1901,7 +1901,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = CURLE_OK; - const struct Curl_peer *origin = conn->origin; + struct Curl_peer *origin = conn->origin; struct Curl_peer *via_peer = NULL; struct curl_slist *conn_to_entry = data->set.connect_to; @@ -1953,8 +1953,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data, if(!hit && (neg->wanted & CURL_HTTP_V3x)) { srcalpnid = ALPN_h3; hit = Curl_altsvc_lookup(data->asi, - ALPN_h3, origin->hostname, - origin->port, /* from */ + origin, ALPN_h3, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -1964,8 +1963,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data, !neg->h2_prior_knowledge) { srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h2, origin->hostname, - origin->port, /* from */ + origin, ALPN_h2, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -1974,8 +1972,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data, !neg->only_10) { srcalpnid = ALPN_h1; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, origin->hostname, - origin->port, /* from */ + origin, ALPN_h1, /* from */ &as /* to */, allowed_alpns, &same_dest); } diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index ef42153ba1d8..3f851d1bc752 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -31,9 +31,12 @@ static CURLcode test_unit1654(const char *arg) #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) char outname[256]; - CURL *curl; + CURL *curl = NULL; CURLcode result; struct altsvcinfo *asi = Curl_altsvc_init(); + struct Curl_peer *origin = NULL; + const struct Curl_scheme *scheme = &Curl_scheme_https; + abort_if(!asi, "Curl_altsvc_i"); result = Curl_altsvc_load(asi, arg); fail_if(result, "Curl_altsvc_load"); @@ -44,101 +47,125 @@ static CURLcode test_unit1654(const char *arg) fail_if(!curl, "curl_easy_init"); if(!curl) goto fail; + fail_unless(Curl_llist_count(&asi->list) == 4, "wrong number of entries"); curl_msnprintf(outname, sizeof(outname), "%s-out", arg); + if(Curl_peer_create(curl, scheme, "example.org", 8080, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:8080\"\r\n", - ALPN_h1, "example.org", 8080); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse() failed!"); fail_unless(Curl_llist_count(&asi->list) == 5, "wrong number of entries"); + if(Curl_peer_create(curl, scheme, "2.example.org", 8080, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h3=\":8080\"\r\n", - ALPN_h1, "2.example.org", 8080); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(2) failed!"); fail_unless(Curl_llist_count(&asi->list) == 6, "wrong number of entries"); + if(Curl_peer_create(curl, scheme, "3.example.org", 8080, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:8080\", " "h3=\"yesyes.com:8080\"\r\n", - ALPN_h1, "3.example.org", 8080); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(3) failed!"); /* that one should make two entries */ fail_unless(Curl_llist_count(&asi->list) == 8, "wrong number of entries"); + if(Curl_peer_create(curl, scheme, "example.org", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:443\"; ma = 120;\r\n", - ALPN_h2, "example.org", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(4) failed!"); fail_unless(Curl_llist_count(&asi->list) == 9, "wrong number of entries"); /* quoted 'ma' value */ + if(Curl_peer_create(curl, scheme, "example.net", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.net:443\"; ma=\"180\";\r\n", - ALPN_h2, "example.net", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(5) failed!"); fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries"); + if(Curl_peer_create(curl, scheme, "curl.se", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\":443\"; ma=180, h3=\":443\"; " "persist = \"1\"; ma = 120;\r\n", - ALPN_h1, "curl.se", 80); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(6) failed!"); fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries"); /* clear that one again and decrease the counter */ result = Curl_altsvc_parse(curl, asi, "clear;\r\n", - ALPN_h1, "curl.se", 80); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(7) failed!"); fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries"); result = Curl_altsvc_parse(curl, asi, "h2=\":443\", h3=\":443\"; " "persist = \"1\"; ma = 120;\r\n", - ALPN_h1, "curl.se", 80); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(6) failed!"); fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries"); /* clear - without semicolon */ result = Curl_altsvc_parse(curl, asi, "clear\r\n", - ALPN_h1, "curl.se", 80); + origin, ALPN_h1); fail_if(result, "Curl_altsvc_parse(7) failed!"); fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries"); /* only a non-existing alpn */ + if(Curl_peer_create(curl, scheme, "5.example.net", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h6=\"example.net:443\"; ma=\"180\";\r\n", - ALPN_h2, "5.example.net", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(8) failed!"); /* missing quote in alpn host */ + if(Curl_peer_create(curl, scheme, "6.example.net", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.net:443,; ma=\"180\";\r\n", - ALPN_h2, "6.example.net", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(9) failed!"); /* missing port in hostname */ + if(Curl_peer_create(curl, scheme, "7.example.net", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.net\"; ma=\"180\";\r\n", - ALPN_h2, "7.example.net", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(10) failed!"); /* illegal port in hostname */ + if(Curl_peer_create(curl, scheme, "8.example.net", 80, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"example.net:70000\"; ma=\"180\";\r\n", - ALPN_h2, "8.example.net", 80); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(11) failed!"); + if(Curl_peer_create(curl, scheme, "test.se", 443, &origin)) + goto fail; result = Curl_altsvc_parse(curl, asi, "h2=\"test2.se:443\"; ma=\"180 \" ; unknown=2, " "h2=\"test3.se:443\"; ma = 120;\r\n", - ALPN_h2, "test.se", 443); + origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(12) failed!"); Curl_altsvc_save(curl, asi, outname); - curl_easy_cleanup(curl); fail: + curl_easy_cleanup(curl); Curl_altsvc_cleanup(&asi); + Curl_peer_unlink(&origin); #endif UNITTEST_END(curl_global_cleanup()) From 18475e662cb3655dc2c5ac962ac81a233ca86faf Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 11:36:23 +0200 Subject: [PATCH 0569/1062] urlpieces: remove members that live in peers/creds Remove members of `struct urlpieces` that now live in peers/creds. Change all remaining uses of those urlpieces. When comparing schemes, use the protocol constants. Closes #22171 --- lib/http.c | 4 ++-- lib/http_proxy.c | 4 ++-- lib/ldap.c | 3 ++- lib/openldap.c | 22 +++++++++++++++------- lib/peer.c | 30 +++++++++++------------------- lib/peer.h | 2 -- lib/url.c | 26 ++++++++++++-------------- lib/urlapi-int.h | 2 ++ lib/urlapi.c | 17 +++++++++++++++++ lib/urldata.h | 5 ----- lib/vssh/libssh.c | 4 ++-- 11 files changed, 65 insertions(+), 54 deletions(-) diff --git a/lib/http.c b/lib/http.c index a0aee51c7880..072b1ed3b4f7 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2115,7 +2115,7 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(curl_strequal("http", data->state.up.scheme)) { + if(data->state.origin->scheme == &Curl_scheme_http) { /* when getting HTTP, we do not want the userinfo the URL */ uc = curl_url_set(h, CURLUPART_USER, NULL, 0); if(uc) { @@ -2157,7 +2157,7 @@ static CURLcode http_target(struct Curl_easy *data, if(result) return result; - if(curl_strequal("ftp", data->state.up.scheme) && + if((data->state.origin->scheme == &Curl_scheme_ftp) && data->set.proxy_transfer_mode) { /* when doing ftp, append ;type= if not present */ size_t len = strlen(path); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index d019796b5324..b2cb018c10da 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -449,10 +449,10 @@ CURLcode Curl_http_proxy_create_tunnel_request( return result; if(udp_tunnel) - infof(data, "Establishing %s proxy UDP tunnel to %s:%s", + infof(data, "Establishing %s proxy UDP tunnel to %s:%u", (ver == PROXY_HTTP_V2) ? "HTTP/2" : (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", - data->state.up.hostname, data->state.up.port); + dest->user_hostname, dest->port); else infof(data, "Establishing %s proxy tunnel to %s", (ver == PROXY_HTTP_V2) ? "HTTP/2" : diff --git a/lib/ldap.c b/lib/ldap.c index 29bc5efab2d5..b4b107a57266 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -739,7 +739,8 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, if(!data || !data->state.up.path || data->state.up.path[0] != '/' || - !curl_strnequal("LDAP", data->state.up.scheme, 4)) + ((data->state.origin->scheme != &Curl_scheme_ldap) && + (data->state.origin->scheme != &Curl_scheme_ldaps))) return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; diff --git a/lib/openldap.c b/lib/openldap.c index 8ec6bb27cd84..ee765d8d05b2 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -164,7 +164,7 @@ static CURLcode oldap_map_error(int rc, CURLcode result) static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) { CURLcode result = CURLE_OK; - int rc = LDAP_URL_ERR_BADURL; + int rc; static const char * const url_errs[] = { "success", "out of memory", @@ -180,9 +180,16 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) }; *ludp = NULL; - if(!data->state.up.user && !data->state.up.password && - !data->state.up.options) + /* `ldap_url_parse() seems to be terrible with urls + * that have user/pass/options in it. So when we have options or + * creds from the url, fail without calling the function. + * Yes, this is super-weird code and I do not like it. */ + if((data->state.creds && (data->state.creds->source == CREDS_URL)) || + data->state.up.options) + rc = LDAP_URL_ERR_BADURL; + else rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), ludp); + if(rc != LDAP_URL_SUCCESS) { const char *msg = "url parsing problem"; @@ -605,7 +612,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) if(result) goto out; - li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); + li->proto = ldap_pvt_url_scheme2proto(data->state.origin->scheme->name); /* Initialize the SASL storage */ Curl_sasl_init(&li->sasl, data, &saslldap); @@ -614,10 +621,11 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) if(result) goto out; - hosturl = curl_maprintf("%s://%s:%d", + hosturl = curl_maprintf("%s://%s:%u", conn->scheme->name, - (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->origin->hostname, + conn->origin->ipv6 ? + conn->origin->user_hostname : + conn->origin->hostname, conn->origin->port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; diff --git a/lib/peer.c b/lib/peer.c index 44bcb52f2314..4a7a12d8cf06 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -74,6 +74,7 @@ #include "peer.h" #include "urldata.h" #include "url.h" +#include "urlapi-int.h" #include "vtls/vtls.h" struct peer_parse { @@ -350,31 +351,28 @@ bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, uint16_t port_override, uint32_t scopeid_override, - struct urlpieces *up, struct Curl_peer **ppeer) { struct peer_parse pp; - char *zoneid = NULL; + char *zoneid = NULL, *scheme = NULL, *hostname = NULL; CURLUcode uc; CURLcode result; Curl_peer_unlink(ppeer); memset(&pp, 0, sizeof(pp)); - curlx_safefree(up->scheme); - uc = curl_url_get(uh, CURLUPART_SCHEME, &up->scheme, 0); + uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, 0); if(uc) return Curl_uc_to_curlcode(uc); - pp.scheme = Curl_get_scheme(up->scheme); + pp.scheme = Curl_get_scheme(scheme); if(!pp.scheme) { - failf(data, "Protocol \"%s\" not supported%s", up->scheme, + failf(data, "Protocol \"%s\" not supported%s", scheme, data->state.this_is_a_follow ? " (in redirect)" : ""); result = CURLE_UNSUPPORTED_PROTOCOL; goto out; } - curlx_safefree(up->hostname); - uc = curl_url_get(uh, CURLUPART_HOST, &up->hostname, 0); + uc = curl_url_get(uh, CURLUPART_HOST, &hostname, 0); if(uc) { if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) ; /* acceptable */ @@ -383,13 +381,13 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, goto out; } } - else if(strlen(up->hostname) > MAX_URL_LEN) { + else if(strlen(hostname) > MAX_URL_LEN) { failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); result = CURLE_URL_MALFORMAT; goto out; } - pp.host_user.str = up->hostname ? up->hostname : ""; + pp.host_user.str = hostname ? hostname : ""; pp.host_user.len = strlen(pp.host_user.str); if(pp.host_user.len) { result = peer_parse_host(data, &pp, FALSE); @@ -399,7 +397,6 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, else pp.host = pp.host_user; - curlx_safefree(up->port); if(port_override) { /* if set, we use this instead of the port possibly given in the URL */ char portbuf[16]; @@ -413,7 +410,7 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, pp.port = port_override; } else { - uc = curl_url_get(uh, CURLUPART_PORT, &up->port, CURLU_DEFAULT_PORT); + uc = Curl_url_get_port(uh, &pp.port); if(uc) { if(uc == CURLUE_OUT_OF_MEMORY) { result = CURLE_OUT_OF_MEMORY; @@ -425,13 +422,6 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, } /* no port ok when not a network scheme */ } - else { - const char *p = up->port; - curl_off_t offt; - if(curlx_str_number(&p, &offt, 0xffff)) - return CURLE_URL_MALFORMAT; - pp.port = (uint16_t)offt; - } } if(scopeid_override) @@ -456,6 +446,8 @@ CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, out: peer_parse_clear(&pp); + curlx_free(scheme); + curlx_free(hostname); curlx_free(zoneid); return result; } diff --git a/lib/peer.h b/lib/peer.h index 1ceb230739b2..f6ed00cd485e 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -25,7 +25,6 @@ ***************************************************************************/ struct Curl_scheme; -struct urlpieces; /* if peer hostname starts with this, the peer is a unix domain socket * path, e.g. the remainder after 'localhost'. */ @@ -84,7 +83,6 @@ bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, uint16_t port_override, uint32_t scopeid_override, - struct urlpieces *up, struct Curl_peer **ppeer); CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, diff --git a/lib/url.c b/lib/url.c index ea635fd1e43b..de5111920a3b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -178,11 +178,6 @@ void Curl_freeset(struct Curl_easy *data) static void up_free(struct Curl_easy *data) { struct urlpieces *up = &data->state.up; - curlx_safefree(up->scheme); - curlx_safefree(up->hostname); - curlx_safefree(up->port); - curlx_safefree(up->user); - curlx_safefree(up->password); curlx_safefree(up->options); curlx_safefree(up->path); curlx_safefree(up->query); @@ -1381,7 +1376,6 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, CURLUcode uc; CURLcode result; - curlx_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); if(uc) return Curl_uc_to_curlcode(uc); @@ -1393,7 +1387,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, Curl_bufref_set(&data->state.url, url, 0, curl_free); result = Curl_peer_from_url(uh, data, port_override, scope_id, - &data->state.up, &data->state.origin); + &data->state.origin); if(result) return result; infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); @@ -1549,26 +1543,28 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) /* Extract credentials from the URL only if there are none OR * if no CURLOPT_USER was set. */ if(!newcreds || !Curl_creds_has_user(newcreds)) { + char *user = NULL; + char *passwd = NULL; char *udecoded = NULL; char *pdecoded = NULL; CURLUcode uc; - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + uc = curl_url_get(uh, CURLUPART_USER, &user, 0); if(uc && (uc != CURLUE_NO_USER)) result = Curl_uc_to_curlcode(uc); if(!result) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + uc = curl_url_get(uh, CURLUPART_PASSWORD, &passwd, 0); if(uc && (uc != CURLUE_NO_PASSWORD)) result = Curl_uc_to_curlcode(uc); } - if(!result && data->state.up.user) { - result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, + if(!result && user) { + result = Curl_urldecode(user, 0, &udecoded, NULL, (data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) ? REJECT_ZERO : REJECT_CTRL); } - if(!result && data->state.up.password) { - result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, + if(!result && passwd) { + result = Curl_urldecode(passwd, 0, &pdecoded, NULL, (data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) ? REJECT_ZERO : REJECT_CTRL); @@ -1579,6 +1575,8 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) curlx_free(udecoded); curlx_free(pdecoded); + curlx_free(passwd); + curlx_free(user); if(result) { failf(data, "error extracting credentials from URL"); goto out; @@ -2276,7 +2274,7 @@ static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data) /* `uh` is now as the connection should use it, probably. */ result = Curl_peer_from_url(uh, data, port_override, scope_id, - &data->state.up, &data->state.origin); + &data->state.origin); if(result) goto out; /* The origin might get changed when HSTS applies */ diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h index 4d23dd87b44c..cf1fe6e7cc6c 100644 --- a/lib/urlapi-int.h +++ b/lib/urlapi-int.h @@ -65,4 +65,6 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace); bool Curl_url_same_origin(CURLU *base, CURLU *href); +CURLUcode Curl_url_get_port(CURLU *u, uint16_t *pport); + #endif /* HEADER_CURL_URLAPI_INT_H */ diff --git a/lib/urlapi.c b/lib/urlapi.c index a664c7304b55..66f00c6b42de 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -2108,3 +2108,20 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) return FALSE; return TRUE; } + +CURLUcode Curl_url_get_port(CURLU *u, uint16_t *pport) +{ + if(u->port_present) { + *pport = u->portnum; + return CURLUE_OK; + } + else if(u->scheme) { + const struct Curl_scheme *s = Curl_get_scheme(u->scheme); + if(s && s->defport) { + *pport = s->defport; + return CURLUE_OK; + } + } + *pport = 0; + return CURLUE_NO_PORT; +} diff --git a/lib/urldata.h b/lib/urldata.h index 4666fa3658ec..8a716d559025 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -560,11 +560,6 @@ struct time_node { /* individual pieces of the URL */ struct urlpieces { - char *scheme; - char *hostname; - char *port; - char *user; - char *password; char *options; char *path; char *query; diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index d0bc13cadb70..1ff7714af03a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2586,8 +2586,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) } rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, - (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->origin->hostname); + conn->origin->ipv6 ? + conn->origin->user_hostname : conn->origin->hostname); if(rc != SSH_OK) { failf(data, "Could not set remote host"); From a36384ab94b868b70b39209ced3e89927a14922d Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Fri, 26 Jun 2026 02:18:07 +0530 Subject: [PATCH 0570/1062] http: trim custom header name before the Authorization drop A custom header name padded with blanks (`Authorization :`) missed the exact-length compare and slipped past the cross-host Authorization and Cookie drop, forwarding the header to the redirect target. Trim the parsed name in both the request and proxy CONNECT header builders. Closes #22178 --- lib/http.c | 6 +++ lib/http_proxy.c | 3 ++ tests/data/Makefile.am | 2 +- tests/data/test2113 | 94 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2113 diff --git a/lib/http.c b/lib/http.c index 072b1ed3b4f7..d952b90ceb56 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1805,6 +1805,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, continue; } + /* a field name is a token and carries no surrounding whitespace, so + trim the parsed name before matching. Otherwise `Authorization :` + (space before the colon) slips past the Authorization/Cookie check + below and gets forwarded to another host on a redirect. */ + curlx_str_trimblanks(&name); + /* only send this if the contents was non-blank or done special */ if(data->state.aptr.host && diff --git a/lib/http_proxy.c b/lib/http_proxy.c index b2cb018c10da..aa20a52ebef2 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -129,6 +129,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, continue; DEBUGASSERT(curlx_strlen(&name) && value); + /* trim surrounding whitespace so a padded field name (e.g. + `Authorization :`) cannot slip past the Authorization/Cookie check */ + curlx_str_trimblanks(&name); if(data->state.aptr.host && /* a Host: header was sent already, do not pass on any custom Host: header as that will produce *two* in the same request! */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 0ef33e0adc80..2edc771070d1 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 \ +test2108 test2109 test2113 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2113 b/tests/data/test2113 new file mode 100644 index 000000000000..884c0af0a732 --- /dev/null +++ b/tests/data/test2113 @@ -0,0 +1,94 @@ + + + + +HTTP +HTTP proxy +HTTP Basic auth +HTTP proxy Basic auth +followlocation + + +# Server-side + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +contents + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +# Client-side + + +http + + +HTTP custom Authorization: with whitespace before colon and redirect to new host + + +http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -H "Authorization : s3cr3t" --proxy-user testing:this --location + + +proxy + + + +# Verify data after the test has been "shot" + + +GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1 +Host: first.host.it.is +Proxy-Authorization: Basic %b64[testing:this]b64% +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Authorization : s3cr3t + +GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1 +Host: goto.second.host.now +Proxy-Authorization: Basic %b64[testing:this]b64% +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + From 84c5dcdb05202d3260b4631c2fa842123f87b362 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 15 Jun 2026 14:36:05 +0200 Subject: [PATCH 0571/1062] tidy-up: syntax and code nits - cmp-pkg-config.sh: replace `-r -f` with `-rf` to match rest of repo. - configure.ac: add double quotes for robustness (not a bug). - curl-openssl.m4: merge nested `if`s. - CurlTests.c: drop `!= 0`, also to sync with m4. - CurlTests.c: replace `example.com` with `localhost` in `gethostbyname()` feature test code. (compile-only, not a bug) - GHA/http3-linux: drop literal `true` from bool expression. - lib650: drop redundant `&`. - move variable/call to left-hand side of equality checks, where missing. - perl: detach `<`/`>` from filename in `open()`, where missing. - schannel: apply two nit fixes lost in rebase. - scripts/verify-release: drop redundant double quotes. - scripts/verify-release: exit with error code on error. - synctime: replace magic numbers with `sizeof()`. - telnet: add missing parentheses to macro value. - tests/Makefile.am: use single quotes. - tool_operate: drop redundant `break` after `return` in VMS code. - unit2413: drop unused NULL pointer + free call. - unit2413: fix duplicate test case name. - urlapi: drop redundant parentheses. - urlapi: drop `CURL_UNCONST()` that became redundant. Closes #22186 --- .github/scripts/cmp-config.pl | 4 ++-- .github/scripts/cmp-pkg-config.sh | 2 +- .github/scripts/verify-synopsis.pl | 4 ++-- .github/workflows/http3-linux.yml | 2 +- CMake/CurlTests.c | 16 ++++++++-------- configure.ac | 2 +- docs/examples/adddocsref.pl | 4 ++-- docs/examples/log_failed_transfers.c | 2 +- docs/examples/synctime.c | 9 ++++++--- lib/telnet.c | 2 +- lib/urlapi.c | 16 ++++++++-------- lib/vtls/mbedtls.c | 8 ++++---- lib/vtls/openssl.c | 24 ++++++++++++------------ lib/vtls/schannel.c | 13 ++++++------- lib/vtls/vtls.c | 2 +- lib/vtls/wolfssl.c | 22 +++++++++++----------- m4/curl-openssl.m4 | 7 +++---- scripts/badwords | 6 +++--- scripts/cd2cd | 2 +- scripts/managen | 2 +- scripts/mk-ca-bundle.pl | 2 +- scripts/verify-release | 6 +++--- src/tool_operate.c | 1 - src/tool_ssls.c | 8 ++++---- tests/Makefile.am | 2 +- tests/allversions.pm | 2 +- tests/libtest/lib650.c | 6 +++--- tests/server/util.c | 12 ++++++------ tests/test1707.pl | 2 +- tests/testutil.pm | 2 +- tests/unit/unit2413.c | 10 ++++------ 31 files changed, 100 insertions(+), 102 deletions(-) diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index 9c353432bbfb..b697ae1203a4 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -105,7 +105,7 @@ sub grepit { my ($input, $output) = @_; my @defines; # first get all the #define lines - open(F, "<$input"); + open(F, "<", $input); while() { if($_ =~ /^#def/) { chomp; @@ -114,7 +114,7 @@ sub grepit { } close(F); - open(O, ">$output"); + open(O, ">", $output); # output the sorted list through the filter foreach my $d(sort @defines) { diff --git a/.github/scripts/cmp-pkg-config.sh b/.github/scripts/cmp-pkg-config.sh index 37a316218c83..940b02d78a7b 100755 --- a/.github/scripts/cmp-pkg-config.sh +++ b/.github/scripts/cmp-pkg-config.sh @@ -44,6 +44,6 @@ am=$(mktemp -t autotools.XXX); sort_lists "$1" > "${am}" cm=$(mktemp -t cmake.XXX) ; sort_lists "$2" > "${cm}" diff -u "${am}" "${cm}" res="$?" -rm -r -f "${am}" "${cm}" +rm -rf "${am}" "${cm}" exit "${res}" diff --git a/.github/scripts/verify-synopsis.pl b/.github/scripts/verify-synopsis.pl index 02918dd65d1e..7ff90c049196 100755 --- a/.github/scripts/verify-synopsis.pl +++ b/.github/scripts/verify-synopsis.pl @@ -45,8 +45,8 @@ sub extract { my $syn = 0; my $l = 0; my $iline = 0; - open(F, "<$f"); - open(O, ">$cfile"); + open(F, "<", $f); + open(O, ">", $cfile); while() { $iline++; if(/^# SYNOPSIS/) { diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 2be23997447c..83e83c886afa 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -219,7 +219,7 @@ jobs: run: echo 'needs-build=true' >> "$GITHUB_OUTPUT" - name: 'install build prereqs' - if: ${{ steps.settings.outputs.needs-build == 'true' }} + if: ${{ steps.settings.outputs.needs-build }} timeout-minutes: 2 run: | sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index be3b6f73eee9..9f5a2678aac5 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -48,7 +48,7 @@ int main(void) { /* O_NONBLOCK source test */ int flags = 0; - if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) + if(fcntl(0, F_SETFL, flags | O_NONBLOCK)) return 1; return 0; } @@ -65,7 +65,7 @@ int main(void) #include int main(void) { - const char *address = "example.com"; + const char *address = "localhost"; struct hostent h; int rc = 0; #if defined(HAVE_GETHOSTBYNAME_R_3) || \ @@ -162,7 +162,7 @@ int main(void) int main(void) { /* IoctlSocket source code */ - if(0 != IoctlSocket(0, 0, 0)) + if(IoctlSocket(0, 0, 0)) return 1; return 0; } @@ -177,7 +177,7 @@ int main(void) { /* IoctlSocket source code */ long flags = 0; - if(0 != IoctlSocket(0, FIONBIO, &flags)) + if(IoctlSocket(0, FIONBIO, &flags)) return 1; (void)flags; return 0; @@ -191,7 +191,7 @@ int main(void) int main(void) { unsigned long flags = 0; - if(0 != ioctlsocket(0, FIONBIO, &flags)) + if(ioctlsocket(0, FIONBIO, &flags)) return 1; (void)flags; return 0; @@ -218,7 +218,7 @@ int main(void) int main(void) { int flags = 0; - if(0 != ioctl(0, FIONBIO, &flags)) + if(ioctl(0, FIONBIO, &flags)) return 1; (void)flags; return 0; @@ -246,7 +246,7 @@ int main(void) int main(void) { struct ifreq ifr; - if(0 != ioctl(0, SIOCGIFADDR, &ifr)) + if(ioctl(0, SIOCGIFADDR, &ifr)) return 1; (void)ifr; return 0; @@ -265,7 +265,7 @@ int main(void) #endif int main(void) { - if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) + if(setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) return 1; return 0; } diff --git a/configure.ac b/configure.ac index 0054bdb250f4..575cf4046af9 100644 --- a/configure.ac +++ b/configure.ac @@ -5156,7 +5156,7 @@ fi AC_SUBST(LIBCURL_PC_REQUIRES) AC_SUBST(LIBCURL_PC_LIBS) -rm $compilersh +rm "$compilersh" dnl dnl For keeping supported features and protocols also in pkg-config file diff --git a/docs/examples/adddocsref.pl b/docs/examples/adddocsref.pl index 8c9d7f8c20bf..2f99c8544908 100755 --- a/docs/examples/adddocsref.pl +++ b/docs/examples/adddocsref.pl @@ -33,8 +33,8 @@ my $docroot = "https://curl.se/libcurl/c"; for my $f (@ARGV) { - open(NEW, ">$f.new"); - open(F, "<$f"); + open(NEW, ">", "$f.new"); + open(F, "<", $f); while() { my $l = $_; if($l =~ /\/* $docroot/) { diff --git a/docs/examples/log_failed_transfers.c b/docs/examples/log_failed_transfers.c index 564735e30dd8..19e2a232e84a 100644 --- a/docs/examples/log_failed_transfers.c +++ b/docs/examples/log_failed_transfers.c @@ -305,7 +305,7 @@ int main(void) if(failed) { FILE *fp = fopen(t->logfile, "wb"); - if(fp && t->log.len == fwrite(t->log.buf, 1, t->log.len, fp)) + if(fp && fwrite(t->log.buf, 1, t->log.len, fp) == t->log.len) fprintf(stderr, "Transfer log written to %s\n", t->logfile); else { fprintf(stderr, "Failed to write transfer log to %s: %s\n", diff --git a/docs/examples/synctime.c b/docs/examples/synctime.c index 1157edbe3fa0..2c7501d4db56 100644 --- a/docs/examples/synctime.c +++ b/docs/examples/synctime.c @@ -300,7 +300,8 @@ int main(int argc, const char *argv[]) /* Get current system time and local time */ GetSystemTime(&SYSTime); GetLocalTime(&LOCALTime); - snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", + snprintf(timeBuf, sizeof(timeBuf) - 1, + "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, LOCALTime.wMilliseconds); @@ -314,7 +315,8 @@ int main(int argc, const char *argv[]) #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) GetLocalTime(&LOCALTime); - snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", + snprintf(timeBuf, sizeof(timeBuf) - 1, + "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, LOCALTime.wMilliseconds); @@ -329,7 +331,8 @@ int main(int argc, const char *argv[]) else { /* Successfully re-adjusted computer clock */ GetLocalTime(&LOCALTime); - snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", + snprintf(timeBuf, sizeof(timeBuf) - 1, + "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, diff --git a/lib/telnet.c b/lib/telnet.c index 2870d7e2c9c0..d135477c2064 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -59,7 +59,7 @@ #define SUBBUFSIZE 512 -#define CURL_SB_CLEAR(x) x->subpointer = (x)->subbuffer +#define CURL_SB_CLEAR(x) (x)->subpointer = (x)->subbuffer #define CURL_SB_TERM(x) \ do { \ (x)->subend = (x)->subpointer; \ diff --git a/lib/urlapi.c b/lib/urlapi.c index 66f00c6b42de..1ec87859ddaf 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -584,7 +584,7 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) return HOST_NAME; curlx_dyn_reset(host); result = curlx_dyn_addf(host, "%u.%u.%u.%u", - (parts[0]), + parts[0], ((parts[1] >> 16) & 0xff), ((parts[1] >> 8) & 0xff), (parts[1] & 0xff)); @@ -594,8 +594,8 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) return HOST_NAME; curlx_dyn_reset(host); result = curlx_dyn_addf(host, "%u.%u.%u.%u", - (parts[0]), - (parts[1]), + parts[0], + parts[1], ((parts[2] >> 8) & 0xff), (parts[2] & 0xff)); break; @@ -605,10 +605,10 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) return HOST_NAME; curlx_dyn_reset(host); result = curlx_dyn_addf(host, "%u.%u.%u.%u", - (parts[0]), - (parts[1]), - (parts[2]), - (parts[3])); + parts[0], + parts[1], + parts[2], + parts[3]); break; } if(result) @@ -1961,7 +1961,7 @@ static CURLUcode url_sethost(CURLU *u, struct dynbuf *encp, bad = TRUE; curlx_free(decoded); } - else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) + else if(hostname_check(u, newp, n)) bad = TRUE; if(bad) { curlx_dyn_free(encp); diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 57727613ae9a..785a71f66e4e 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -1468,19 +1468,19 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, *done = FALSE; connssl->io_need = CURL_SSL_IO_NEED_NONE; - if(ssl_connect_1 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_1) { result = mbed_connect_step1(cf, data); if(result) return result; } - if(ssl_connect_2 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_2) { result = mbed_connect_step2(cf, data); if(result) return result; } - if(ssl_connect_3 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_3) { /* For tls1.3 we get notified about new sessions */ struct ssl_connect_data *ctx = cf->ctx; struct mbed_ssl_backend_data *backend = @@ -1495,7 +1495,7 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_done; } - if(ssl_connect_done == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_done) { connssl->state = ssl_connection_complete; *done = TRUE; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 010bbb9825e4..ceff6266dbda 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1898,7 +1898,7 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, *done = TRUE; goto out; } - if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, rc)) { + if(SSL_get_error(octx->ssl, rc) == SSL_ERROR_WANT_WRITE) { CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; @@ -4014,7 +4014,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, BIO *bio; CURLcode result; - DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(connssl->connecting_state == ssl_connect_1); DEBUGASSERT(octx); DEBUGASSERT(connssl->peer.origin); @@ -4129,7 +4129,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); + DEBUGASSERT(connssl->connecting_state == ssl_connect_2); DEBUGASSERT(octx); connssl->io_need = CURL_SSL_IO_NEED_NONE; @@ -4161,25 +4161,25 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, int detail = SSL_get_error(octx->ssl, err); CURL_TRC_CF(data, cf, "SSL_connect() -> err=%d, detail=%d", err, detail); - if(SSL_ERROR_WANT_READ == detail) { + if(detail == SSL_ERROR_WANT_READ) { CURL_TRC_CF(data, cf, "SSL_connect() -> want recv"); connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_AGAIN; } - if(SSL_ERROR_WANT_WRITE == detail) { + if(detail == SSL_ERROR_WANT_WRITE) { CURL_TRC_CF(data, cf, "SSL_connect() -> want send"); connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_AGAIN; } #ifdef SSL_ERROR_WANT_ASYNC - if(SSL_ERROR_WANT_ASYNC == detail) { + if(detail == SSL_ERROR_WANT_ASYNC) { CURL_TRC_CF(data, cf, "SSL_connect() -> want async"); connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_AGAIN; } #endif #ifdef SSL_ERROR_WANT_RETRY_VERIFY - if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { + if(detail == SSL_ERROR_WANT_RETRY_VERIFY) { CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify"); Curl_xfer_pause_recv(data, TRUE); return CURLE_AGAIN; @@ -4856,7 +4856,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(connssl->connecting_state == ssl_connect_3); /* * We check certificates to authenticate the server; otherwise we risk @@ -4968,7 +4968,7 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, *done = FALSE; connssl->io_need = CURL_SSL_IO_NEED_NONE; - if(ssl_connect_1 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_1) { if(Curl_ossl_need_httpsrr(data) && !Curl_conn_dns_resolved_https(data, cf->sockindex, connssl->peer.peer)) { @@ -4981,7 +4981,7 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, goto out; } - if(ssl_connect_2 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_2) { CURL_TRC_CF(data, cf, "ossl_connect, step2"); #ifdef HAVE_OPENSSL_EARLYDATA if(connssl->earlydata_state == ssl_earlydata_await) { @@ -5002,7 +5002,7 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, goto out; } - if(ssl_connect_3 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_3) { CURL_TRC_CF(data, cf, "ossl_connect, step3"); result = ossl_connect_step3(cf, data); if(result) @@ -5020,7 +5020,7 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, #endif } - if(ssl_connect_done == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_done) { CURL_TRC_CF(data, cf, "ossl_connect, done"); connssl->state = ssl_connection_complete; } diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 3e871227aa00..72aedabc7c62 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -452,8 +452,7 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; if(continue_reading && (certsize < CURL_MAX_INPUT_LENGTH)) certdata = curlx_malloc(certsize + 1); - if((!certdata) || - ((int) fread(certdata, certsize, 1, fInCert) != 1)) + if(!certdata || ((int)fread(certdata, certsize, 1, fInCert) != 1)) continue_reading = FALSE; curlx_fclose(fInCert); if(!continue_reading) { @@ -1596,7 +1595,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, SecPkgContext_ApplicationProtocol alpn_result; #endif - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(connssl->connecting_state == ssl_connect_3); DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", @@ -1721,25 +1720,25 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, *done = FALSE; - if(ssl_connect_1 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_1) { result = schannel_connect_step1(cf, data); if(result) return result; } - if(ssl_connect_2 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_2) { result = schannel_connect_step2(cf, data); if(result) return result; } - if(ssl_connect_3 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_3) { result = schannel_connect_step3(cf, data); if(result) return result; } - if(ssl_connect_done == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_done) { connssl->state = ssl_connection_complete; #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 82ce007b3d7d..ed2008a604f2 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -544,7 +544,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, do { char buffer[1024]; size_t want = left > sizeof(buffer) ? sizeof(buffer) : left; - if(want != fread(buffer, 1, want, fp)) + if(fread(buffer, 1, want, fp) != want) goto end; if(curlx_dyn_addn(&buf, buffer, want)) goto end; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 92eaa7a75100..09a8494edd9b 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -630,7 +630,7 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, ssl_cafile, ssl_capath, WOLFSSL_LOAD_FLAG_IGNORE_ERR); - if(WOLFSSL_SUCCESS != rc) { + if(rc != WOLFSSL_SUCCESS) { if(conn_config->verifypeer && !imported_native_ca && !imported_ca_info_blob) { /* Fail if we insist on successfully verifying the server. */ @@ -1723,15 +1723,15 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } else { - if(WOLFSSL_ERROR_WANT_READ == detail) { + if(detail == WOLFSSL_ERROR_WANT_READ) { connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_AGAIN; } - else if(WOLFSSL_ERROR_WANT_WRITE == detail) { + else if(detail == WOLFSSL_ERROR_WANT_WRITE) { connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_AGAIN; } - else if(DOMAIN_NAME_MISMATCH == detail) { + else if(detail == DOMAIN_NAME_MISMATCH) { /* There is no easy way to override only the CN matching. * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ @@ -1739,7 +1739,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) connssl->peer.origin->hostname); return CURLE_PEER_FAILED_VERIFICATION; } - else if(ASN_NO_SIGNER_E == detail) { + else if(detail == ASN_NO_SIGNER_E) { if(conn_config->verifypeer) { failf(data, " CA signer not available for verification"); return CURLE_SSL_CACERT_BADFILE; @@ -1750,11 +1750,11 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) "continuing anyway"); return CURLE_OK; } - else if(ASN_AFTER_DATE_E == detail) { + else if(detail == ASN_AFTER_DATE_E) { failf(data, "server verification failed: certificate has expired."); return CURLE_PEER_FAILED_VERIFICATION; } - else if(ASN_BEFORE_DATE_E == detail) { + else if(detail == ASN_BEFORE_DATE_E) { failf(data, "server verification failed: certificate not valid yet."); return CURLE_PEER_FAILED_VERIFICATION; } @@ -1923,7 +1923,7 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, *done = TRUE; goto out; } - if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->ssl, nread)) { + if(wolfSSL_get_error(wctx->ssl, nread) == WOLFSSL_ERROR_WANT_WRITE) { CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; @@ -2116,7 +2116,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, *done = FALSE; connssl->io_need = CURL_SSL_IO_NEED_NONE; - if(ssl_connect_1 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_1) { #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG /* if we do ECH and need the HTTPS-RR information for it, * we delay the connect until it arrives or DNS resolve fails. */ @@ -2133,7 +2133,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_2; } - if(ssl_connect_2 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_2) { if(connssl->earlydata_state == ssl_earlydata_await) { /* We defer the handshake until request data arrives. */ DEBUGASSERT(connssl->state == ssl_connection_deferred); @@ -2146,7 +2146,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_3; } - if(ssl_connect_3 == connssl->connecting_state) { + if(connssl->connecting_state == ssl_connect_3) { /* Once the handshake has errored, it stays in that state and * errors again on every call. */ if(wssl->hs_result) { diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 816e7631fae5..6ac2c9d2bfa3 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -93,10 +93,9 @@ if test "x$OPT_OPENSSL" != "xno"; then fi fi - if test "$PKGTEST" != "yes"; then - if test ! -f "$PREFIX_OPENSSL/include/openssl/ssl.h"; then - AC_MSG_ERROR([$PREFIX_OPENSSL is a bad --with-openssl prefix!]) - fi + if test "$PKGTEST" != "yes" && + test ! -f "$PREFIX_OPENSSL/include/openssl/ssl.h"; then + AC_MSG_ERROR([$PREFIX_OPENSSL is a bad --with-openssl prefix!]) fi dnl in case pkg-config comes up empty, use what we got diff --git a/scripts/badwords b/scripts/badwords index 7d371b3beec8..a6cc6cd0a29c 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -149,7 +149,7 @@ sub sourcecode { my $flags = 0; my @lines; my $line; - open(F, "<$f"); + open(F, "<", $f); while() { my $l = $_; ($state, $flags, $line) = srcline($state, $flags, $l); @@ -175,7 +175,7 @@ my %wl; my @w; my @exact; my $file = shift @ARGV; -open(CONFIG, "<$file") or die "Cannot open '$file': $!"; +open(CONFIG, "<", $file) or die "Cannot open '$file': $!"; while() { chomp; if($_ =~ /^#/) { @@ -261,7 +261,7 @@ sub highlight { sub document { my ($f) = @_; my @lines; - open(F, "<$f"); + open(F, "<", $f); while() { push @lines, $_; } diff --git a/scripts/cd2cd b/scripts/cd2cd index ead664dfd66f..6c9cd0c278c8 100755 --- a/scripts/cd2cd +++ b/scripts/cd2cd @@ -212,7 +212,7 @@ HEAD close(F); if($inplace) { - open(O, ">$f") or return 1; + open(O, ">", $f) or return 1; print O @desc; close(O); } diff --git a/scripts/managen b/scripts/managen index 2bf1c3d75ded..eded1205464f 100755 --- a/scripts/managen +++ b/scripts/managen @@ -1047,7 +1047,7 @@ sub header { sub sourcecategories { my ($dir) = @_; my %cats; - open(H, "<$dir/../../src/tool_help.h") or + open(H, "<", "$dir/../../src/tool_help.h") or die "cannot find the header file"; while() { if(/^\#define CURLHELP_([A-Z0-9]*)/) { diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index c83f2ba5e3c9..494f1d21d192 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -257,7 +257,7 @@ sub sha256 { sub oldhash { my $hash = ""; - open(C, "<$_[0]") or return 0; + open(C, "<", $_[0]) or return 0; while() { chomp; if($_ =~ /^\#\# SHA256: (.*)/) { diff --git a/scripts/verify-release b/scripts/verify-release index 5a231c485e07..920303aec009 100755 --- a/scripts/verify-release +++ b/scripts/verify-release @@ -39,14 +39,14 @@ tarball="${1:-}" if [ -z "$tarball" ]; then echo "Provide a curl release tarball name as argument" - exit + exit 1 fi i="$(find . -maxdepth 1 -type d -name 'curl-*' | wc -l)" if test "$i" -gt 1; then echo "multiple curl-* entries found, disambiguate please" - exit + exit 1 fi # check if this is in a git clone directory @@ -115,7 +115,7 @@ else mv "$f" _tarballs/ done fi -cd "_tarballs" +cd _tarballs # compare the new tarball against the original sha256sum -c checksum diff --git a/src/tool_operate.c b/src/tool_operate.c index 27f4de6a9eea..8ccd791bf53f 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -178,7 +178,6 @@ static curl_off_t VmsSpecialSize(const char *name, case FAB$C_VAR: case FAB$C_VFC: return vms_realfilesize(name, stat_buf); - break; default: return stat_buf->st_size; } diff --git a/src/tool_ssls.c b/src/tool_ssls.c index e9c486435d90..1f59fbcfe7b8 100644 --- a/src/tool_ssls.c +++ b/src/tool_ssls.c @@ -161,18 +161,18 @@ static CURLcode tool_ssls_exp(CURL *easy, void *userptr, if(result) goto out; result = CURLE_WRITE_ERROR; - if(enc_len != fwrite(enc, 1, enc_len, ctx->fp)) + if(fwrite(enc, 1, enc_len, ctx->fp) != enc_len) goto out; - if(EOF == fputc(':', ctx->fp)) + if(fputc(':', ctx->fp) == EOF) goto out; curlx_safefree(enc); result = curlx_base64_encode(sdata, sdata_len, &enc, &enc_len); if(result) goto out; result = CURLE_WRITE_ERROR; - if(enc_len != fwrite(enc, 1, enc_len, ctx->fp)) + if(fwrite(enc, 1, enc_len, ctx->fp) != enc_len) goto out; - if(EOF == fputc('\n', ctx->fp)) + if(fputc('\n', ctx->fp) == EOF) goto out; result = CURLE_OK; ctx->exported++; diff --git a/tests/Makefile.am b/tests/Makefile.am index c76c0c802356..29cacacdfcc1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -137,7 +137,7 @@ endif # make sure that PERL is pointing to an executable perlcheck: - @if ! test -x "@PERL@"; then echo "No perl!"; exit 2; fi + @if ! test -x "@PERL@"; then echo 'No perl!'; exit 2; fi build-certs: perlcheck (cd certs && $(MAKE)) diff --git a/tests/allversions.pm b/tests/allversions.pm index 0dba43e0fed4..edd4ec1139fd 100644 --- a/tests/allversions.pm +++ b/tests/allversions.pm @@ -32,7 +32,7 @@ our %pastversion; sub allversions { my ($file) = @_; - open(A, "<$file") or + open(A, "<", $file) or die "cannot open the versions file $file\n"; my $before = 1; my $relcount; diff --git a/tests/libtest/lib650.c b/tests/libtest/lib650.c index b0766eee1d0a..ac56c4f35bc4 100644 --- a/tests/libtest/lib650.c +++ b/tests/libtest/lib650.c @@ -73,8 +73,8 @@ static CURLcode test_lib650(const char *URL) } headers = headers2; formrc = curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, &testname, - CURLFORM_COPYCONTENTS, &testdata, + CURLFORM_COPYNAME, testname, + CURLFORM_COPYCONTENTS, testdata, CURLFORM_CONTENTHEADER, headers, CURLFORM_END); if(formrc) { @@ -145,7 +145,7 @@ static CURLcode test_lib650(const char *URL) formrc = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "formlength", - CURLFORM_COPYCONTENTS, &flbuf, + CURLFORM_COPYCONTENTS, flbuf, CURLFORM_END); if(formrc) { diff --git a/tests/server/util.c b/tests/server/util.c index 05a2e898cc5f..6041bc0c6d26 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -657,31 +657,31 @@ void install_signal_handlers(bool keep_sigalrm) void restore_signal_handlers(bool keep_sigalrm) { #ifdef SIGHUP - if(SIG_ERR != old_sighup_handler) + if(old_sighup_handler != SIG_ERR) (void)set_signal(SIGHUP, old_sighup_handler, FALSE); #endif #ifdef SIGPIPE - if(SIG_ERR != old_sigpipe_handler) + if(old_sigpipe_handler != SIG_ERR) (void)set_signal(SIGPIPE, old_sigpipe_handler, FALSE); #endif #ifdef SIGALRM if(!keep_sigalrm) { - if(SIG_ERR != old_sigalrm_handler) + if(old_sigalrm_handler != SIG_ERR) (void)set_signal(SIGALRM, old_sigalrm_handler, FALSE); } #else (void)keep_sigalrm; #endif #ifdef SIGINT - if(SIG_ERR != old_sigint_handler) + if(old_sigint_handler != SIG_ERR) (void)set_signal(SIGINT, old_sigint_handler, FALSE); #endif #ifdef SIGTERM - if(SIG_ERR != old_sigterm_handler) + if(old_sigterm_handler != SIG_ERR) (void)set_signal(SIGTERM, old_sigterm_handler, FALSE); #endif #if defined(SIGBREAK) && defined(_WIN32) - if(SIG_ERR != old_sigbreak_handler) + if(old_sigbreak_handler != SIG_ERR) (void)set_signal(SIGBREAK, old_sigbreak_handler, FALSE); #endif #ifdef _WIN32 diff --git a/tests/test1707.pl b/tests/test1707.pl index a19940d141f1..d13de40be1fd 100755 --- a/tests/test1707.pl +++ b/tests/test1707.pl @@ -77,7 +77,7 @@ $fullopt = $longopt; } -open(R, "<$txt"); +open(R, "<", $txt); my $show = 0; my @txtout; while() { diff --git a/tests/testutil.pm b/tests/testutil.pm index a5e79df58d45..2deed4ce9528 100644 --- a/tests/testutil.pm +++ b/tests/testutil.pm @@ -101,7 +101,7 @@ sub clearlogs { sub includefile { my ($f, $text) = @_; - open(F, "<$f"); + open(F, "<", $f); if($text) { binmode F, ':crlf'; } diff --git a/tests/unit/unit2413.c b/tests/unit/unit2413.c index ef11c48b7cfe..6a74943476ba 100644 --- a/tests/unit/unit2413.c +++ b/tests/unit/unit2413.c @@ -80,7 +80,6 @@ static CURLcode test_unit2413(const char *arg) { UNITTEST_BEGIN_SIMPLE CURL *curl; - struct Curl_peer *peer = NULL; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); @@ -95,17 +94,16 @@ static CURLcode test_unit2413(const char *arg) "127.0.0.1", FALSE, NULL); test_create2413("peer3", curl, &Curl_scheme_https, "::1", 1234, "::1", TRUE, NULL); - test_create2413("peer3", curl, &Curl_scheme_https, "[::1]", 1234, + test_create2413("peer4", curl, &Curl_scheme_https, "[::1]", 1234, "::1", TRUE, NULL); - test_create2413("peer4", curl, &Curl_scheme_https, "test.curl.se.", 1234, + test_create2413("peer5", curl, &Curl_scheme_https, "test.curl.se.", 1234, "test.curl.se.", FALSE, NULL); - test_create2413("peer5", curl, &Curl_scheme_https, "[::1%tada]", 1234, + test_create2413("peer6", curl, &Curl_scheme_https, "[::1%tada]", 1234, "::1", TRUE, "tada"); - test_create2413("peer6", curl, &Curl_scheme_https, "::1%tada", 1234, + test_create2413("peer7", curl, &Curl_scheme_https, "::1%tada", 1234, "::1", TRUE, "tada"); curl_easy_cleanup(curl); - Curl_peer_unlink(&peer); curl_global_cleanup(); UNITTEST_END_SIMPLE From 4839029645bae1ff7f17e0f1c7543bc3eb30f471 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 23 Jun 2026 20:18:26 +0200 Subject: [PATCH 0572/1062] cmake: robustify base path in local file reference To make the macro find this file also when used elsewhere than the root `CMakeLists.txt`. Current code only uses it from the root. Follow-up to 88c17d5587447b367c7ec836ff9b847860f52f75 Closes #22187 --- CMake/Macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 953c1c6ee6dd..40968156d241 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -43,7 +43,7 @@ macro(curl_internal_test _curl_test) message(STATUS "Performing Test ${_curl_test}") try_compile(${_curl_test} ${PROJECT_BINARY_DIR} - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" + "${PROJECT_SOURCE_DIR}/CMake/CurlTests.c" COMPILE_DEFINITIONS "-D${_curl_test}" ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${CMAKE_REQUIRED_DEFINITIONS} LINK_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}" OUTPUT_VARIABLE CURL_TEST_OUTPUT) From 8198e388d31919ec610908d496d34ced2daf4fea Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 12:22:05 +0200 Subject: [PATCH 0573/1062] cmake: tidy-up base directory variable for `include/*`/`CMake/*` files Use `PROJECT_SOURCE_DIR` for these files, replacing `CMAKE_CURRENT_SOURCE_DIR`, to make it consistent with rest of CMake sources and to reflect that the locations of these files are fixed and do not depend on the CMake source location referencing them. Exception: keep as-is before calling `project()`, which is where `PROJECT_SOURCE_DIR` is initialized. Ref: https://cmake.org/cmake/help/v3.18/command/project.html Follow-up to #22187 Follow-up to 9126eb5a8ad96f156586942facb069383a09c046 #15331 Closes #22188 --- CMakeLists.txt | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b0d974daa10..5f03499d0a9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ unset(_curl_version_h_contents) message(STATUS "curl version=[${_curl_version}]") string(REGEX REPLACE "([0-9]+\.[0-9]+\.[0-9]+).+" "\\1" _curl_version_sem "${_curl_version}") +# Initializes PROJECT_BINARY_DIR, PROJECT_SOURCE_DIR project(CURL VERSION "${_curl_version_sem}" LANGUAGES C) @@ -622,9 +623,9 @@ option(_CURL_PREFILL "Fast-track known feature detection results (Windows, some mark_as_advanced(_CURL_PREFILL) if(_CURL_PREFILL) if(WIN32) - include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake") + include("${PROJECT_SOURCE_DIR}/CMake/win32-cache.cmake") elseif(UNIX) - include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/unix-cache.cmake") + include("${PROJECT_SOURCE_DIR}/CMake/unix-cache.cmake") message(STATUS "Pre-filling feature detection results for UNIX") endif() elseif(WIN32) @@ -2430,32 +2431,32 @@ if(NOT CURL_DISABLE_INSTALL) FILES ${_version_config} ${_project_config} - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindBrotli.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindCares.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindGSS.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindGnuTLS.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLDAP.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibbacktrace.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibgsasl.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibidn2.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibpsl.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh2.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibuv.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindMbedTLS.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGHTTP2.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGHTTP3.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGTCP2.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNettle.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindQuiche.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindRustls.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindWolfSSL.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindZstd.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindBrotli.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindCares.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindGSS.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindGnuTLS.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLDAP.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibbacktrace.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibgsasl.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibidn2.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibpsl.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibssh.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibssh2.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindLibuv.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindMbedTLS.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindNGHTTP2.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindNGHTTP3.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindNGTCP2.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindNettle.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindQuiche.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindRustls.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindWolfSSL.cmake" + "${PROJECT_SOURCE_DIR}/CMake/FindZstd.cmake" DESTINATION ${_install_cmake_dir}) if(NOT TARGET curl_uninstall) configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" + "${PROJECT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake" @ONLY) From 8be2daae0fc60a9ab9d16fd8a399ece39174f4bc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 04:03:45 +0200 Subject: [PATCH 0574/1062] GHA/windows: set `VCPKG_APPLOCAL_DEPS=OFF` for vcpkg jobs Add the necessary directory to `PATH` instead. To disable a vcpkg feature (auto-copying DLL dependencies next to the build targets) that was causing CI flakiness in the libssh2 repository. In curl it currently cannot cause flakiness because targets (with DLL dependencies) reside in separate directories. AFAIU this vcpkg feature has been present and enabled by default for a long time, but only a recent update made it visible in the log and flaky at the same time, due to switching implementation. This patch removes log noise and potential issues, improves efficiency, and saves disk space: 74MB -> 71 in UWP, and 69MB -> 65 in the arm64 job. Time saving is negligible with current jobs. Refs: https://github.com/libssh2/libssh2/pull/2114 https://github.com/libssh2/libssh2/commit/30a0484cb7b350d779dbca87dbf9d7ba18a03547 https://github.com/microsoft/vcpkg/pull/52315 https://learn.microsoft.com/vcpkg/reference/installation-tree-layout Closes #22189 --- .github/workflows/windows.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 96cfecde2628..4bf2ff5c4ac3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1072,6 +1072,7 @@ jobs: options+=" -DVCPKG_INSTALLED_DIR=$VCPKG_INSTALLATION_ROOT/installed" options+=" -DVCPKG_TARGET_TRIPLET=${MATRIX_ARCH}-${MATRIX_PLAT}" options+=" -DCMAKE_C_COMPILER_TARGET=${MATRIX_ARCH}-${MATRIX_PLAT}" + options+=' -DVCPKG_APPLOCAL_DEPS=OFF' fi cmake -B "bld${_chkprefill}" ${options} \ -DCMAKE_C_FLAGS="${cflags}" \ @@ -1110,7 +1111,7 @@ jobs: /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 file -- /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- if [ "${MATRIX_PLAT}" != 'uwp' ]; then # Missing: ucrtbased.dll, VCRUNTIME140D.dll, VCRUNTIME140D_APP.dll - PATH="$PWD/bld/lib/${MATRIX_TYPE}:$PATH" + PATH="$PWD/bld/lib/${MATRIX_TYPE}:$(cygpath --unix "${VCPKG_INSTALLATION_ROOT}")/installed/${MATRIX_ARCH}-${MATRIX_PLAT}/${MATRIX_TYPE}/bin:$PATH" "bld/src/${MATRIX_TYPE}/curl.exe" --disable --version fi @@ -1187,7 +1188,7 @@ jobs: PATH="/c/OpenSSH-Win64:$PATH" fi fi - PATH="$PWD/bld/lib/${MATRIX_TYPE}:$PATH:/c/my-stunnel/bin" + PATH="$PWD/bld/lib/${MATRIX_TYPE}:$(cygpath --unix "${VCPKG_INSTALLATION_ROOT}")/installed/${MATRIX_ARCH}-${MATRIX_PLAT}/${MATRIX_TYPE}/bin:$PATH:/c/my-stunnel/bin" cmake --build bld --config "${MATRIX_TYPE}" --target test-ci - name: 'build examples' From a7e8016fa20d9f47ff01f76e3fd283cc5a86feac Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 15:54:30 +0200 Subject: [PATCH 0575/1062] cmake: flatten build tree, tidy up base dir variables - drop `generated` subdir, move these files to build root. To move them next to CPack and other config files, and to avoid a subdirectory for only 2 files. Follow-up to 69328490fc91bfa00a6a90da84239524b01f5527 #2849 - add 'Consumed variables' comment for `CMake/cmake_uninstall.in.cmake`. - move generated `cmake_uninstall.cmake` to the build root directory (from `CMake/`). To: - avoid creating a `CMake` subdirectory within the build directory with this single file in it. - move it next to its `cmake_install.cmake` counterpart. - move it next to `install_manifest.txt` which it relies on. Follow-up to 27e2a4733c3321fb0b1a127360e8e96bc3d4ae53 - Use `PROJECT_SOURCE_DIR` for these files, replacing `CMAKE_CURRENT_SOURCE_DIR`, to make it consistent with rest of CMake sources, and to reduce ambiguity in `CMake/cmake_uninstall.in.cmake` template. Follow-up to 8198e388d31919ec610908d496d34ced2daf4fea #22188 Follow-up to 4839029645bae1ff7f17e0f1c7543bc3eb30f471 #22187 Follow-up to 9126eb5a8ad96f156586942facb069383a09c046 #15331 Closes #22192 --- CMake/cmake_uninstall.in.cmake | 6 +++--- CMakeLists.txt | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CMake/cmake_uninstall.in.cmake b/CMake/cmake_uninstall.in.cmake index bb95d85a0a72..801c4c406e94 100644 --- a/CMake/cmake_uninstall.in.cmake +++ b/CMake/cmake_uninstall.in.cmake @@ -21,8 +21,8 @@ # SPDX-License-Identifier: curl # ########################################################################### -if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +if(NOT EXISTS "@PROJECT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @PROJECT_BINARY_DIR@/install_manifest.txt") endif() if(NOT DEFINED CMAKE_INSTALL_PREFIX) @@ -30,7 +30,7 @@ if(NOT DEFINED CMAKE_INSTALL_PREFIX) endif() message(${CMAKE_INSTALL_PREFIX}) -file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" _files) +file(READ "@PROJECT_BINARY_DIR@/install_manifest.txt" _files) string(REGEX REPLACE "\n" ";" _files "${_files}") foreach(_file ${_files}) message(STATUS "Uninstalling $ENV{DESTDIR}${_file}") diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f03499d0a9b..8db6d79ad7bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1939,7 +1939,7 @@ include(GNUInstallDirs) set(_install_cmake_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") -set(_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(_generated_dir "${PROJECT_BINARY_DIR}") set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") @@ -2380,7 +2380,7 @@ if(NOT CURL_DISABLE_INSTALL) endif() ${_generated_version_config}") - # Consumed custom variables: + # Consumed variables: # CMAKE_MINIMUM_REQUIRED_VERSION # CURLVERSION # LIBCURL_PC_LIBS_PRIVATE_LIST @@ -2455,13 +2455,17 @@ if(NOT CURL_DISABLE_INSTALL) DESTINATION ${_install_cmake_dir}) if(NOT TARGET curl_uninstall) + # Consumed variables: + # CMAKE_COMMAND + # CMAKE_INSTALL_PREFIX + # PROJECT_BINARY_DIR configure_file( "${PROJECT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake" + "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY) add_custom_target(curl_uninstall - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake") + COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") endif() if(BUILD_CURL_EXE) From 6d008352c667799beecb1ab1dfbc408c415d018c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 17:25:16 +0200 Subject: [PATCH 0576/1062] cmake: replace deprecated `remove` command with `rm` and pass arg safely All curl-supported CMake versions support the modern method, along with the `--` marker. Also: - make sure to pass `-f` to not fail if the file is missing, as before this patch. - drop now redundant `OUTPUT_QUIET`, `ERROR_QUIET` and error handling in `cmake_uninstall`. - move filename to end of the error message for readability. - GHA/linux: test these two 'clean' targets in the aws-lc job. Refs: https://cmake.org/cmake/help/v4.4/manual/cmake.1.html#cmdoption-cmake-E-arg-remove https://cmake.org/cmake/help/v3.18/manual/cmake.1.html#run-a-command-line-tool Closes #22193 --- .github/workflows/linux.yml | 8 +++++++- CMake/cmake_uninstall.in.cmake | 12 ++---------- tests/certs/CMakeLists.txt | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8d9c1cd55d70..eab56c180c09 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -79,7 +79,7 @@ jobs: - name: 'awslc' install_packages: libidn2-dev - install_steps: awslc + install_steps: awslc clean generate: -DOPENSSL_ROOT_DIR=/home/runner/awslc -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF -DCURL_DROP_UNUSED=ON -DCURL_PATCHSTAMP=test-patch -DCURL_ENABLE_NTLM=ON - name: 'boringssl' @@ -916,6 +916,9 @@ jobs: run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --install bld --strip + if [[ "${MATRIX_INSTALL_STEPS}" = *'clean'* ]]; then + cmake --build bld --target curl_uninstall + fi else make -C bld V=1 install fi @@ -962,6 +965,9 @@ jobs: fi if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target "${TEST_TARGET}" + if [[ "${MATRIX_INSTALL_STEPS}" = *'clean'* ]]; then + cmake --build bld --target clean-certs + fi else make -C bld V=1 "${TEST_TARGET}" fi diff --git a/CMake/cmake_uninstall.in.cmake b/CMake/cmake_uninstall.in.cmake index 801c4c406e94..4cc34d928969 100644 --- a/CMake/cmake_uninstall.in.cmake +++ b/CMake/cmake_uninstall.in.cmake @@ -35,16 +35,8 @@ string(REGEX REPLACE "\n" ";" _files "${_files}") foreach(_file ${_files}) message(STATUS "Uninstalling $ENV{DESTDIR}${_file}") if(IS_SYMLINK "$ENV{DESTDIR}${_file}" OR EXISTS "$ENV{DESTDIR}${_file}") - execute_process( - COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${_file}" - RESULT_VARIABLE rm_retval - OUTPUT_QUIET - ERROR_QUIET - ) - if(NOT "${rm_retval}" STREQUAL 0) - message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${_file}") - endif() + execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -f -- "$ENV{DESTDIR}${_file}") else() - message(STATUS "File $ENV{DESTDIR}${_file} does not exist.") + message(STATUS "File does not exist: $ENV{DESTDIR}${_file}") endif() endforeach() diff --git a/tests/certs/CMakeLists.txt b/tests/certs/CMakeLists.txt index ebe6fbcfa7fb..a96593a24e0e 100644 --- a/tests/certs/CMakeLists.txt +++ b/tests/certs/CMakeLists.txt @@ -39,7 +39,7 @@ if(NOT _CURL_SKIP_BUILD_CERTS) endif() add_custom_target(clean-certs - COMMAND ${CMAKE_COMMAND} -E remove ${GENERATEDCERTS} + COMMAND ${CMAKE_COMMAND} -E rm -f ${GENERATEDCERTS} "test-*.csr" "test-*.der" "test-*.keyenc" From 65dae4ad80b02f25a25e17af62ea4f2940970330 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 23:00:05 +0200 Subject: [PATCH 0577/1062] cmake: stop probing unused `float.h` for `STDC_HEADERS` Also to sync up with current autotools, which stopped testing for it at one point. Follow-up to 4c5307b45655ba75ab066564afdc0c111a8b9291 Closes #22191 --- CMake/CurlTests.c | 1 - 1 file changed, 1 deletion(-) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 9f5a2678aac5..2da96eccc8d5 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -118,7 +118,6 @@ int main(void) #include #include #include -#include int main(void) { return 0; From b8fa725df33d5a4b6075cedbb00a678a1761d43a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 17:00:07 +0200 Subject: [PATCH 0578/1062] cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` Also: use `IN LISTS` syntax. Follow-up to 27e2a4733c3321fb0b1a127360e8e96bc3d4ae53 Closes #22194 --- CMake/cmake_uninstall.in.cmake | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CMake/cmake_uninstall.in.cmake b/CMake/cmake_uninstall.in.cmake index 4cc34d928969..c7fd8008e19c 100644 --- a/CMake/cmake_uninstall.in.cmake +++ b/CMake/cmake_uninstall.in.cmake @@ -21,8 +21,9 @@ # SPDX-License-Identifier: curl # ########################################################################### -if(NOT EXISTS "@PROJECT_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: @PROJECT_BINARY_DIR@/install_manifest.txt") +set(_manifest "@PROJECT_BINARY_DIR@/install_manifest.txt") +if(NOT EXISTS "${_manifest}") + message(FATAL_ERROR "Cannot find install manifest: ${_manifest}") endif() if(NOT DEFINED CMAKE_INSTALL_PREFIX) @@ -30,13 +31,14 @@ if(NOT DEFINED CMAKE_INSTALL_PREFIX) endif() message(${CMAKE_INSTALL_PREFIX}) -file(READ "@PROJECT_BINARY_DIR@/install_manifest.txt" _files) +file(READ "${_manifest}" _files) string(REGEX REPLACE "\n" ";" _files "${_files}") -foreach(_file ${_files}) - message(STATUS "Uninstalling $ENV{DESTDIR}${_file}") - if(IS_SYMLINK "$ENV{DESTDIR}${_file}" OR EXISTS "$ENV{DESTDIR}${_file}") - execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -f -- "$ENV{DESTDIR}${_file}") +foreach(_file IN LISTS _files) + set(_target "$ENV{DESTDIR}${_file}") + message(STATUS "Uninstalling ${_target}") + if(IS_SYMLINK "${_target}" OR EXISTS "${_target}") + execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -f -- "${_target}") else() - message(STATUS "File does not exist: $ENV{DESTDIR}${_file}") + message(STATUS "File does not exist: ${_target}") endif() endforeach() From 616356646159379ab091502cdc90113dc8cc11eb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 20:20:01 +0200 Subject: [PATCH 0579/1062] openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef Follow-up to 69c89bf3d3137fcbb2b8bc57233182adcf1e2817 #18330 Closes #22197 --- lib/vtls/openssl.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index ceff6266dbda..caab715b6db3 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2630,14 +2630,12 @@ static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, return CURLE_OK; } -#ifdef HAVE_BORINGSSL_LIKE -typedef uint32_t ctx_option_t; -#elif defined(HAVE_OPENSSL3) -typedef uint64_t ctx_option_t; -#elif defined(LIBRESSL_VERSION_NUMBER) +#ifdef LIBRESSL_VERSION_NUMBER typedef long ctx_option_t; +#elif defined(HAVE_BORINGSSL_LIKE) +typedef uint32_t ctx_option_t; #else -typedef unsigned long ctx_option_t; +typedef uint64_t ctx_option_t; #endif CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, From da2f05e6f6cee66ea46b8b1ca3943e5a6dde0b91 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 20:45:07 +0200 Subject: [PATCH 0580/1062] cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` It fails as expected, with a list of errors: ``` In file included from _x64-win-ucrt-for-trurl-bld/tests/tunit/tunits.c:5: tests/tunit/tool1394.c:76:11: error: call to undeclared function 'parse_cert_parameter'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 76 | err = parse_cert_parameter(p->param, &certname, &passphrase); | ^ [...] tests/tunit/tool1622.c:68:5: error: call to undeclared function 'timebuf'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 68 | timebuf(buffer, sizeof(buffer), secs); | ^ [...] tests/tunit/tool1623.c:104:26: error: call to undeclared function 'GetSizeParameter'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 104 | ParameterError err = GetSizeParameter(check[i].input, &output); | ^ [...] ``` Ref: https://github.com/curl/curl/actions/runs/28256499057/job/83720797064?pr=22195 Ref: #22195 Ref: https://github.com/curl/curl-for-win/commit/640fed88b65052e944edb466387bd8a062bd9b37 Closes #22198 --- tests/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6de7f96d0a26..55928fac511c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,7 +51,9 @@ add_dependencies(testdeps "tt") add_subdirectory(http) add_subdirectory(server) add_subdirectory(libtest) -add_subdirectory(tunit) +if(BUILD_CURL_EXE) + add_subdirectory(tunit) +endif() add_subdirectory(unit) add_subdirectory(certs) From 9f9d2d10d9b87cc86016b57c256387e83fc291e0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 19:48:17 +0200 Subject: [PATCH 0581/1062] lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI By exposing and reusing existing custom type via `vtls/openss.h`. Also: - GHA/curl-for-win: test in CI by enabling building tests. Cost is 45s per job, so limit it to the gcc job. Seen with Windows x64 gcc (not tested in CI prior to this patch): ``` tests/libtest/lib1587.c:50:7: error: conversion from 'opt1587' {aka 'long long unsigned int'} to 'long int' may change value [-Werror=conversion] 50 | SSL_CTX_set_options(info->internals, opts); | ^~~~~~~~~~~~~~~~~~~ tests/libtest/lib1587.c:59:7: error: conversion from 'opt1587' {aka 'long long unsigned int'} to 'long int' may change value [-Werror=conversion] 59 | SSL_set_options(info->internals, opts); | ^~~~~~~~~~~~~~~ ``` Ref: https://github.com/curl/curl/actions/runs/28258372229/job/83727170184?pr=22195#step:3:4884 Ref: da2f05e6f6cee66ea46b8b1ca3943e5a6dde0b91 #22198 Ref: 616356646159379ab091502cdc90113dc8cc11eb #22197 Follow-up to 3e40ccb87581b0f6180fbd9c1eef9c50f270c806 #21290 Follow-up to 2db8ae480fdcae7f005bf847fbbf837821c8184c #17809 #17801 Closes #22195 --- .github/workflows/curl-for-win.yml | 2 +- lib/vtls/openssl.c | 8 -------- lib/vtls/openssl.h | 8 ++++++++ tests/libtest/lib1587.c | 11 ++--------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 23e10decc1fa..f2048052c7e7 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -185,7 +185,7 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-nocertdata-win-x64-gcc-zlibold-noWINE' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-curltests-win-x64-gcc-zlibold-noWINE' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh sudo podman image trust set --type reject default diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index caab715b6db3..405a5bb67928 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2630,14 +2630,6 @@ static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, return CURLE_OK; } -#ifdef LIBRESSL_VERSION_NUMBER -typedef long ctx_option_t; -#elif defined(HAVE_BORINGSSL_LIKE) -typedef uint32_t ctx_option_t; -#else -typedef uint64_t ctx_option_t; -#endif - CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index e707910be009..53ab60528f4b 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -100,6 +100,14 @@ #define HAVE_OPENSSL_EARLYDATA #endif +#ifdef LIBRESSL_VERSION_NUMBER +typedef long ctx_option_t; +#elif defined(HAVE_BORINGSSL_LIKE) +typedef uint32_t ctx_option_t; +#else +typedef uint64_t ctx_option_t; +#endif + struct alpn_spec; struct ssl_peer; struct Curl_ssl_session; diff --git a/tests/libtest/lib1587.c b/tests/libtest/lib1587.c index 5789804acb47..9368b53d0b7f 100644 --- a/tests/libtest/lib1587.c +++ b/tests/libtest/lib1587.c @@ -26,13 +26,6 @@ #ifdef USE_OPENSSL #include -#ifdef HAVE_BORINGSSL_LIKE -/* AWS-LC and BoringSSL */ -typedef uint32_t opt1587; -#else -typedef uint64_t opt1587; -#endif - static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { const struct curl_tlssessioninfo *info; @@ -46,7 +39,7 @@ static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) if(result == CURLE_OK) { /* set and read stuff using the SSL_CTX to verify it */ - opt1587 opts = SSL_CTX_get_options(info->internals); + ctx_option_t opts = SSL_CTX_get_options(info->internals); SSL_CTX_set_options(info->internals, opts); curl_mprintf("CURLINFO_TLS_SESSION: OK\n"); } @@ -55,7 +48,7 @@ static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) if(result == CURLE_OK) { /* set and read stuff using the SSL pointer to verify it */ - opt1587 opts = SSL_get_options(info->internals); + ctx_option_t opts = SSL_get_options(info->internals); SSL_set_options(info->internals, opts); curl_mprintf("CURLINFO_TLS_SSL_PTR: OK\n"); } From 58771cf4f3f45ccce41e9309427df557c86ce81f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 16:23:08 +0200 Subject: [PATCH 0582/1062] VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW Closes #22190 --- docs/VULN-DISCLOSURE-POLICY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index a115ee171622..847579edb521 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -183,6 +183,10 @@ trigger. Due to timing, platform requirements or the fact that options or protocols involved are rare etc. [Past example](https://curl.se/docs/CVE-2022-43552.html) +Issues that are likely to be detected by basic testing are likely to not be +considered more severe than **Low**. Users that do not test cannot be expected +to have secure setups to begin with. + ## Medium This is a security problem that is less hard than **Low** to exploit or From d786a85f19927c34a10b91b144389db8b16888c7 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 22 Jun 2026 10:45:42 +0200 Subject: [PATCH 0583/1062] lib: hostname related cleanups * format `data->state.aptr.host` without crlf, so signatures do not need to strip it again. Add the crlf when adding the header to the request dynbuf * check `connect-to` strings on normalized hostname and user supplied hostname (when those differ) * libssh: always use the peer for setting ssh option SSH_OPTIONS_HOST, preserve ipv6 [] enclosure, use IDN converted hostname otherwise. This is the libssh documented expectation. Do NOT use strings from URL parsing. Closes #22128 --- lib/http.c | 61 +++++++++++++++++++++++++++----------------- lib/http_aws_sigv4.c | 7 ++--- lib/url.c | 18 +++++++------ lib/vssh/libssh.c | 2 ++ 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/http.c b/lib/http.c index d952b90ceb56..ef2bd8f96316 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2000,17 +2000,17 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) { struct connectdata *conn = data->conn; struct dynamically_allocated_data *aptr = &data->state.aptr; - const char *ptr; + const char *ptr = NULL; curlx_safefree(aptr->host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); #endif - ptr = Curl_checkheaders(data, STRCONST("Host")); - if(ptr && - (!data->state.this_is_a_follow || - Curl_peer_equal(data->state.initial_origin, data->state.origin))) { + if(Curl_peer_equal(data->state.initial_origin, data->state.origin)) + ptr = Curl_checkheaders(data, STRCONST("Host")); + + if(ptr) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2047,30 +2047,40 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) #endif if(!curl_strequal("Host:", ptr)) { - aptr->host = curl_maprintf("Host:%s\r\n", &ptr[5]); + aptr->host = curl_maprintf("Host:%s", &ptr[5]); if(!aptr->host) return CURLE_OUT_OF_MEMORY; } } else { - /* Use the hostname as present in the URL if it was IPv6. */ - char *host = (data->state.origin->user_hostname[0] == '[') ? - data->state.origin->user_hostname : data->state.origin->hostname; - - if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && - (data->state.origin->port == PORT_HTTPS)) || - ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && - (data->state.origin->port == PORT_HTTP))) - /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include - the port number in the host string */ - aptr->host = curl_maprintf("Host: %s\r\n", host); - else - aptr->host = curl_maprintf("Host: %s:%d\r\n", - host, data->state.origin->port); + /* This is the HTTP Host: header, so we want + * - for ipv6 origins: "[ipv6-address]" where the ipv6 address is + * found in origin->hostname, stripped of zoneid/scopeid. + * - the (IDN converted) origin->hostname (DNS name or ipv4) otherwise. + * Note: zoneid/scopeid only applies to local routing and has no + * meaning on the remote HTTP server (eg. would confuse it). */ + bool ipv6 = (bool)data->state.origin->ipv6; + struct dynbuf tmp; + size_t hlen; + CURLcode result; - if(!aptr->host) - /* without Host: we cannot make a nice request */ - return CURLE_OUT_OF_MEMORY; + curlx_dyn_init(&tmp, DYN_HTTP_REQUEST); + result = curlx_dyn_addn(&tmp, STRCONST("Host: ")); + if(!result && ipv6) + result = curlx_dyn_addn(&tmp, STRCONST("[")); + if(!result) + result = curlx_dyn_add(&tmp, data->state.origin->hostname); + if(!result && ipv6) + result = curlx_dyn_addn(&tmp, STRCONST("]")); + if(!result && + ((data->state.origin->port != data->state.origin->scheme->defport) || + (data->state.origin->scheme->family != conn->scheme->family))) { + result = curlx_dyn_addf(&tmp, ":%u", data->state.origin->port); + } + + aptr->host = result ? NULL : curlx_dyn_take(&tmp, &hlen); + curlx_dyn_free(&tmp); + return result; } return CURLE_OK; } @@ -2896,8 +2906,11 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_HOST: - if(data->state.aptr.host) + if(data->state.aptr.host) { result = curlx_dyn_add(req, data->state.aptr.host); + if(!result) + result = curlx_dyn_addn(req, STRCONST("\r\n")); + } break; #ifndef CURL_DISABLE_PROXY diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 9c80c1ea9b5f..a1b1120964f6 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -401,11 +401,8 @@ static CURLcode make_headers(struct Curl_easy *data, if(!Curl_checkheaders(data, STRCONST("Host"))) { char *fullhost; - if(data->state.aptr.host) { - /* remove /r/n as the separator for canonical request must be '\n' */ - size_t pos = strcspn(data->state.aptr.host, "\n\r"); - fullhost = curlx_memdup0(data->state.aptr.host, pos); - } + if(data->state.aptr.host) + fullhost = curlx_strdup(data->state.aptr.host); else fullhost = curl_maprintf("host:%s", hostname); diff --git a/lib/url.c b/lib/url.c index de5111920a3b..a95e3581b456 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1856,14 +1856,16 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, /* check whether the URL's hostname matches. Use the URL hostname * when it was an IPv6 address. Otherwise use the connection's hostname * that has IDN conversion. */ - const char *hostname_to_match = (dest->user_hostname[0] == '[') ? - dest->user_hostname : dest->hostname; - size_t hlen = strlen(hostname_to_match); - host_match = curl_strnequal(ptr, hostname_to_match, hlen); - ptr += hlen; - - host_match = host_match && *ptr == ':'; - ptr++; + size_t hlen = strlen(dest->hostname); + host_match = curl_strnequal(ptr, dest->hostname, hlen); + if(!host_match && (dest->user_hostname != dest->hostname)) { + /* hostname was normalized, could be IPv6 or IDN */ + hlen = strlen(dest->user_hostname); + host_match = curl_strnequal(ptr, dest->user_hostname, hlen); + } + host_match = host_match && ptr[hlen] == ':'; + if(host_match) + ptr += hlen + 1; } if(host_match) { diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 1ff7714af03a..93be7ca21f4a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2585,6 +2585,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } + /* For ipv6 origins, use the `user_hostname` that has the "[]" enclosure. + * Otherwise, use `hostname` that is IDN converted. */ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->origin->ipv6 ? conn->origin->user_hostname : conn->origin->hostname); From 65d8eaeaa2cd81e252f35301617f0e761375fbe1 Mon Sep 17 00:00:00 2001 From: alhudz Date: Sat, 20 Jun 2026 15:02:19 +0530 Subject: [PATCH 0584/1062] gopher: reject CR and LF in the selector Verifed in test 1609 Closes #22116 --- lib/gopher.c | 10 ++++++++++ tests/data/Makefile.am | 2 +- tests/data/test1609 | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/data/test1609 diff --git a/lib/gopher.c b/lib/gopher.c index 039697466b73..39f4bc0efc62 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -103,6 +103,16 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(result) return result; buf = buf_alloc; + + /* A decoded CR or LF would terminate the single-line gopher request and + let a crafted URL smuggle additional bytes onto the wire. REJECT_ZERO + only blocks NUL; reject CR and LF here too. A TAB is left alone as it + is the legitimate gopher type-7 selector/search separator. */ + if(memchr(buf, '\r', buf_len) || memchr(buf, '\n', buf_len)) { + curlx_free(buf_alloc); + failf(data, "Bad gopher selector, CR or LF not allowed"); + return CURLE_URL_MALFORMAT; + } } for(; buf_len;) { diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 2edc771070d1..a8a3b96e610b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -211,7 +211,7 @@ test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 \ test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 \ test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 \ -test1605 test1606 test1607 test1608 test1610 test1611 test1612 \ +test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 \ test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 \ test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 \ test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ diff --git a/tests/data/test1609 b/tests/data/test1609 new file mode 100644 index 000000000000..14058b17cbb6 --- /dev/null +++ b/tests/data/test1609 @@ -0,0 +1,38 @@ + + + + +GOPHER +CRLF-in-URL + + + +# Server-side + + + +# Client-side + + +gopher + + +Gopher with URL-encoded CR LF in the selector + + +gopher://%HOSTIP:%GOPHERPORT/1/sel%0d%0aINJECT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + +# 3 - CURLE_URL_MALFORMAT + +3 + +# nothing must reach the server, the selector is rejected before any send + + + + + From 334164cd80f56652f24b9d84d957d980907a8ca8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 23:17:26 +0200 Subject: [PATCH 0585/1062] RELEASE-NOTES: synced Add entry to .mailmap --- .mailmap | 1 + RELEASE-NOTES | 58 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 20384c010100..b698ebec8c52 100644 --- a/.mailmap +++ b/.mailmap @@ -123,3 +123,4 @@ Calvin Ruocco Hamza Bensliman Kaixuan Li Darren Banfi +Alhuda Khan diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 5587e8e89667..66aac9052dff 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -5,15 +5,40 @@ curl and libcurl 8.21.1 curl_easy_setopt() options: 308 Public functions in libcurl: 100 Authors: 1490 - Contributors: 3732 + Contributors: 3734 This release includes the following changes: This release includes the following bugfixes: + o autotools: minor fixes and improvements [33] + o build: enable thread-safe `getaddrinfo()` for OpenBSD, and other tidy-ups [35] + o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] + o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] + o cmake: flatten build tree, tidy up base dir variables [12] + o cmake: replace deprecated `remove` command with `rm` and pass arg safely [11] + o cmake: robustify base path in local file reference [15] + o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] + o conncache: connection alive checks intervals [20] + o curl-functions.m4: verify working RFC 4291 zero prefixed IPv4 - mapped format [41] + o gopher: reject CR and LF in the selector [1] + o http: trim custom header name before the Authorization drop [17] + o INSTALL.md: add building-from-source overview section [29] o ldap: support insecure mode for Windows native LDAP [3] + o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] + o mqtt: reject control bytes in the topic [43] + o multi: forbid curl_easy_pause from within multi socket callback [22] + o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] + o runtests: restore `-k` option and actively process as no-op [32] + o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] o sws: allow connection-monitor to log all disconnects [2] + o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] + o tool: do not flush on out-null [38] + o tool: init progress bar on demand [39] + o tool_operate: remove call to abort() [23] + o urlapi: do not keep an internal port string [31] + o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] This release includes the following known bugs: @@ -35,10 +60,37 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Daniel Stenberg, Ray Satiro, renovate[bot], Samuel Dainard, Viktor Szakats - (5 contributors) + Alhuda Khan, alhudz, Dan Fandrich, Daniel Stenberg, Ray Satiro, + renovate[bot], Roger Leigh, Sam James, Samuel Dainard, Stefan Eissing, + stze on hackerone, Viktor Szakats + (12 contributors) References to bug reports and discussions on issues: + [1] = https://curl.se/bug/?i=22116 [2] = https://curl.se/bug/?i=22158 [3] = https://curl.se/bug/?i=22152 + [5] = https://curl.se/bug/?i=22190 + [6] = https://curl.se/bug/?i=22195 + [7] = https://curl.se/bug/?i=22198 + [8] = https://curl.se/bug/?i=22197 + [9] = https://curl.se/bug/?i=22194 + [10] = https://curl.se/bug/?i=22191 + [11] = https://curl.se/bug/?i=22193 + [12] = https://curl.se/bug/?i=22192 + [15] = https://curl.se/bug/?i=22187 + [17] = https://curl.se/bug/?i=22178 + [20] = https://curl.se/bug/?i=22169 + [22] = https://curl.se/bug/?i=22179 + [23] = https://curl.se/bug/?i=22182 + [29] = https://curl.se/bug/?i=22113 + [31] = https://curl.se/bug/?i=22167 + [32] = https://curl.se/bug/?i=22100 + [33] = https://curl.se/bug/?i=22154 + [34] = https://curl.se/bug/?i=22150 + [35] = https://curl.se/bug/?i=22148 + [38] = https://curl.se/bug/?i=22165 + [39] = https://curl.se/bug/?i=22164 + [40] = https://curl.se/bug/?i=22136 + [41] = https://curl.se/bug/?i=22136 + [43] = https://curl.se/bug/?i=22112 From 996696621fb1dd1f81c92fcfe290025742cb327b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 Jun 2026 23:01:26 +0200 Subject: [PATCH 0586/1062] curl_trc: store callback info in a uint8, not a bool The state is no longer just a bool so make sure to use a proper uint8_t so that it gets restored properly. Follow-up to a6af50c484f6bbd1c01573685d Closes #22199 --- lib/curl_trc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/curl_trc.c b/lib/curl_trc.c index f8287e420fd5..792f4ed603c2 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -51,7 +51,7 @@ static void trc_write(struct Curl_easy *data, curl_infotype type, { if(data->set.verbose) { if(data->set.fdebug) { - bool inCallback = Curl_is_in_callback(data); + uint8_t inCallback = Curl_is_in_callback(data); Curl_set_in_callback(data, TRUE); (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size, data->set.debugdata); @@ -133,7 +133,7 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, char buf[TRC_LINE_MAX]; size_t len; if(data->set.fdebug) { - bool inCallback = Curl_is_in_callback(data); + uint8_t inCallback = Curl_is_in_callback(data); if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) { len = trc_print_ids(data, buf, TRC_LINE_MAX); From a62e08c5eb244d6471c85907e94acc314b58a7f3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 23:40:24 +0200 Subject: [PATCH 0587/1062] lib: fix 'ns' -> 'us' in trace messages Closes #22200 --- lib/curl_trc.c | 2 +- lib/multi.c | 2 +- lib/progress.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 792f4ed603c2..87082f031bc8 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -345,7 +345,7 @@ void Curl_trc_easy_timers(struct Curl_easy *data) while(e) { struct time_node *n = Curl_node_elem(e); e = Curl_node_next(e); - CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns", + CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "us", curlx_ptimediff_us(&n->time, pnow)); } } diff --git a/lib/multi.c b/lib/multi.c index 51b0917ce781..d65c48e2657b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3704,7 +3704,7 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data, this is the first timeout on the list */ Curl_llist_insert_next(timeoutlist, prev, node, &node->list); - CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "ns", + CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "us", curlx_ptimediff_us(&node->time, Curl_pgrs_now(data))); return CURLM_OK; } diff --git a/lib/progress.c b/lib/progress.c index 969b29a275ca..3b3642b9a0b6 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -337,7 +337,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; - CURL_TRC_M(data, "[%s] added %" FMT_TIMEDIFF_T "ns", + CURL_TRC_M(data, "[%s] added %" FMT_TIMEDIFF_T "us", pgrs_timer_name(timer), us); } else From 39dec13ec08f8511f22ddc78bb36b40241dee915 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 25 Jun 2026 12:48:26 +0200 Subject: [PATCH 0588/1062] build: always use local `inet_pton()`/`inet_ntop()` implementations Also repurpose existing build-time feature checks into unit test 1961, to verify. Prior to this patch these functions were auto-detected with both autotools and cmake. In case of autotools there was an extra verification phase ensuring the functions work as expected. This step required running the function, thus was limited to non-cross-builds. For cross-builds and CMake it always used the system implementation if present. On Windows it always used the local implementation, because availability/use is complicated there. After this patch all platforms, always use the local implementation, which is known to be accurate. This makes curl behave more consistently, and simplifies the build process, a fixes cross-builds and CMake auto-detection differences. Also: - test1960: enable unconditionally. - checksrc: disallow globally, allowlist in `block_ip.c` example. - dnsd: verify ntop result for NULL before passing to printf. Ref: https://github.com/curl/curl/pull/22137#issuecomment-4797440983 Ref: #22137 Ref: 8537a5b0bcf4565551774c2b2375c49767e405a7 #16577 Closes #22170 --- CMake/unix-cache.cmake | 2 - CMake/win32-cache.cmake | 2 - CMakeLists.txt | 2 - configure.ac | 2 - docs/examples/block_ip.c | 3 + docs/internals/CODE_STYLE.md | 2 + lib/config-win32.h | 6 - lib/curl_config-cmake.h.in | 6 - lib/curlx/inet_ntop.c | 3 - lib/curlx/inet_ntop.h | 20 -- lib/curlx/inet_pton.c | 4 - lib/curlx/inet_pton.h | 19 -- m4/curl-functions.m4 | 357 ----------------------------------- scripts/checksrc.pl | 2 + tests/data/Makefile.am | 8 +- tests/data/test1961 | 18 ++ tests/libtest/Makefile.inc | 2 + tests/libtest/first.h | 2 + tests/libtest/lib1960.c | 20 +- tests/server/dnsd.c | 8 +- tests/unit/Makefile.inc | 2 +- tests/unit/unit1961.c | 172 +++++++++++++++++ 22 files changed, 214 insertions(+), 448 deletions(-) create mode 100644 tests/data/test1961 create mode 100644 tests/unit/unit1961.c diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 44dc6472b22e..97be36d5f3c2 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -166,8 +166,6 @@ endif() set(HAVE_GMTIME_R 1) set(HAVE_IFADDRS_H 1) set(HAVE_IF_NAMETOINDEX 1) -set(HAVE_INET_NTOP 1) -set(HAVE_INET_PTON 1) set(HAVE_IOCTLSOCKET 0) set(HAVE_IOCTLSOCKET_CAMEL 0) set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index bd9bb6e3fc89..c10808727386 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -104,8 +104,6 @@ set(HAVE_GETSOCKNAME 1) set(HAVE_GLIBC_STRERROR_R 0) set(HAVE_GMTIME_R 0) set(HAVE_IFADDRS_H 0) -set(HAVE_INET_NTOP 0) -set(HAVE_INET_PTON 0) set(HAVE_IOCTLSOCKET 1) set(HAVE_IOCTLSOCKET_CAMEL 0) set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8db6d79ad7bf..0ad15f766791 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1685,8 +1685,6 @@ if(NOT WIN32) check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) - check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h - check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) diff --git a/configure.ac b/configure.ac index 575cf4046af9..a8bf9703fa71 100644 --- a/configure.ac +++ b/configure.ac @@ -4193,8 +4193,6 @@ if test "$curl_cv_native_windows" != "yes"; then realpath \ sched_yield \ ]) - CURL_CHECK_FUNC_INET_NTOP - CURL_CHECK_FUNC_INET_PTON CURL_CHECK_FUNC_STRCASECMP CURL_CHECK_FUNC_STRCMPI CURL_CHECK_FUNC_STRICMP diff --git a/docs/examples/block_ip.c b/docs/examples/block_ip.c index 675490701bfd..69ffb251953d 100644 --- a/docs/examples/block_ip.c +++ b/docs/examples/block_ip.c @@ -150,6 +150,7 @@ static struct ip *ip_list_append(struct ip *list, const char *data) ip->maskbits = 128; #endif + /* !checksrc! disable BANNEDFUNC 1 */ if(inet_pton(ip->family, ip->str, &ip->netaddr) != 1) { free(ip->str); free(ip); @@ -264,6 +265,7 @@ static curl_socket_t opensocket(void *clientp, curlsocktype purpose, if(ip && filter->type == CONNECTION_FILTER_BLACKLIST) { if(filter->verbose) { char buf[128] = { 0 }; + /* !checksrc! disable BANNEDFUNC 1 */ inet_ntop(address->family, cinaddr, buf, sizeof(buf)); fprintf(stderr, "* Rejecting IP %s due to blacklist entry %s.\n", buf, ip->str); @@ -273,6 +275,7 @@ static curl_socket_t opensocket(void *clientp, curlsocktype purpose, else if(!ip && filter->type == CONNECTION_FILTER_WHITELIST) { if(filter->verbose) { char buf[128] = { 0 }; + /* !checksrc! disable BANNEDFUNC 1 */ inet_ntop(address->family, cinaddr, buf, sizeof(buf)); fprintf(stderr, "* Rejecting IP %s due to missing whitelist entry.\n", buf); diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index ce3c19913555..5feb52f3dca6 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -371,6 +371,8 @@ This is the full list of functions generally banned. getaddrinfo gets gmtime + inet_ntop + inet_pton llseek LoadLibrary LoadLibraryA diff --git a/lib/config-win32.h b/lib/config-win32.h index b1fc1022094e..fedfb8f30ed8 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -154,12 +154,6 @@ /* Define to the function return type for send. */ #define SEND_TYPE_RETV int -/* Must always use local implementations on Windows. */ -/* Define to 1 if you have an IPv6 capable working inet_ntop function. */ -/* #undef HAVE_INET_NTOP */ -/* Define to 1 if you have an IPv6 capable working inet_pton function. */ -/* #undef HAVE_INET_PTON */ - /* Define to 1 if you have the `basename' function. */ #ifdef __MINGW32__ #define HAVE_BASENAME 1 diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index c38caa2a03fd..92280010c08e 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -336,12 +336,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IFADDRS_H 1 -/* Define to 1 if you have an IPv6 capable working inet_ntop function. */ -#cmakedefine HAVE_INET_NTOP 1 - -/* Define to 1 if you have an IPv6 capable working inet_pton function. */ -#cmakedefine HAVE_INET_PTON 1 - /* Define to 1 if symbol `sa_family_t' exists */ #cmakedefine HAVE_SA_FAMILY_T 1 diff --git a/lib/curlx/inet_ntop.c b/lib/curlx/inet_ntop.c index 803b9887ac91..7f6028a79101 100644 --- a/lib/curlx/inet_ntop.c +++ b/lib/curlx/inet_ntop.c @@ -18,8 +18,6 @@ */ #include "curl_setup.h" -#ifndef HAVE_INET_NTOP - #ifdef HAVE_SYS_PARAM_H #include #endif @@ -219,4 +217,3 @@ char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) return NULL; } } -#endif /* HAVE_INET_NTOP */ diff --git a/lib/curlx/inet_ntop.h b/lib/curlx/inet_ntop.h index ba8299fe4e3d..8f4c636d6944 100644 --- a/lib/curlx/inet_ntop.h +++ b/lib/curlx/inet_ntop.h @@ -25,26 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef HAVE_INET_NTOP -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifndef _WIN32 -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef __AMIGA__ -#define curlx_inet_ntop(af, src, buf, size) \ - (char *)inet_ntop(af, CURL_UNCONST(src), (unsigned char *)(buf), \ - (curl_socklen_t)(size)) -#else -#define curlx_inet_ntop(af, src, buf, size) \ - inet_ntop(af, src, buf, (curl_socklen_t)(size)) -#endif -#else char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size); -#endif /* HAVE_INET_NTOP */ #endif /* HEADER_CURL_INET_NTOP_H */ diff --git a/lib/curlx/inet_pton.c b/lib/curlx/inet_pton.c index 7994f258febd..c8f63a1413c6 100644 --- a/lib/curlx/inet_pton.c +++ b/lib/curlx/inet_pton.c @@ -19,8 +19,6 @@ */ #include "curl_setup.h" -#ifndef HAVE_INET_PTON - #ifdef HAVE_SYS_PARAM_H #include #endif @@ -217,5 +215,3 @@ int curlx_inet_pton(int af, const char *src, void *dst) } /* NOTREACHED */ } - -#endif /* HAVE_INET_PTON */ diff --git a/lib/curlx/inet_pton.h b/lib/curlx/inet_pton.h index 02ae7f2269dd..4e45e83285c4 100644 --- a/lib/curlx/inet_pton.h +++ b/lib/curlx/inet_pton.h @@ -25,25 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef HAVE_INET_PTON -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifndef _WIN32 -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef __AMIGA__ -#define curlx_inet_pton(x, y, z) \ - inet_pton(x, (unsigned char *)CURL_UNCONST(y), z) -#else -#define curlx_inet_pton(x, y, z) \ - inet_pton(x, y, z) -#endif -#else int curlx_inet_pton(int af, const char *src, void *dst); -#endif /* HAVE_INET_PTON */ #endif /* HEADER_CURL_INET_PTON_H */ diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 490c1e5d1fb4..64046a834afb 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -25,35 +25,6 @@ dnl File version for 'aclocal' use. Keep it a single number. dnl serial 73 -dnl CURL_INCLUDES_ARPA_INET -dnl ------------------------------------------------- -dnl Set up variable with list of headers that must be -dnl included when arpa/inet.h is to be included. - -AC_DEFUN([CURL_INCLUDES_ARPA_INET], [ -curl_includes_arpa_inet="\ -/* includes start */ -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_NETINET_IN_H -# include -#endif -#ifdef HAVE_ARPA_INET_H -# include -#endif -#ifdef _WIN32 -#include -#include -#else -#include -#endif -/* includes end */" - AC_CHECK_HEADERS( - sys/types.h netinet/in.h arpa/inet.h, - [], [], [$curl_includes_arpa_inet]) -]) - dnl CURL_INCLUDES_FCNTL dnl ------------------------------------------------- @@ -2254,334 +2225,6 @@ AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ ]) -dnl CURL_CHECK_FUNC_INET_NTOP -dnl ------------------------------------------------- -dnl Verify if inet_ntop is available, prototyped, can -dnl be compiled and seems to work. If all of these are -dnl true, and usage has not been previously disallowed -dnl with shell variable curl_disallow_inet_ntop, then -dnl HAVE_INET_NTOP is defined. - -AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB]) - AC_REQUIRE([CURL_INCLUDES_ARPA_INET]) - AC_REQUIRE([CURL_INCLUDES_STRING]) - - tst_links_inet_ntop="unknown" - tst_proto_inet_ntop="unknown" - tst_compi_inet_ntop="unknown" - tst_works_inet_ntop="unknown" - tst_allow_inet_ntop="unknown" - - AC_MSG_CHECKING([if inet_ntop can be linked]) - AC_LINK_IFELSE([ - AC_LANG_FUNC_LINK_TRY([inet_ntop]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_inet_ntop="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_inet_ntop="no" - ]) - - if test "$tst_links_inet_ntop" = "yes"; then - AC_MSG_CHECKING([if inet_ntop is prototyped]) - AC_EGREP_CPP([inet_ntop],[ - $curl_includes_arpa_inet - ],[ - AC_MSG_RESULT([yes]) - tst_proto_inet_ntop="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_inet_ntop="no" - ]) - fi - - if test "$tst_proto_inet_ntop" = "yes"; then - AC_MSG_CHECKING([if inet_ntop is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_arpa_inet - ]],[[ - char ipv4res[sizeof("255.255.255.255")]; - unsigned char ipv4a[5] = ""; - if(inet_ntop(0, ipv4a, ipv4res, 0)) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_inet_ntop="yes" - ],[ - AC_MSG_RESULT([no]) - tst_compi_inet_ntop="no" - ]) - fi - - dnl only do runtime verification when not cross-compiling - if test "$cross_compiling" != "yes" && - test "$tst_compi_inet_ntop" = "yes"; then - AC_MSG_CHECKING([if inet_ntop seems to work]) - CURL_RUN_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_stdlib - $curl_includes_arpa_inet - $curl_includes_string - ]],[[ - char ipv6res[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; - char ipv4res[sizeof("255.255.255.255")]; - unsigned char ipv6a[26]; - unsigned char ipv4a[5]; - const char *ipv6ptr = 0; - const char *ipv4ptr = 0; - /* - */ - ipv4res[0] = '\0'; - ipv4a[0] = 0xc0; - ipv4a[1] = 0xa8; - ipv4a[2] = 0x64; - ipv4a[3] = 0x01; - ipv4a[4] = 0x01; - ipv4ptr = inet_ntop(AF_INET, ipv4a, ipv4res, sizeof(ipv4res)); - if(!ipv4ptr) - return 1; /* fail */ - if(ipv4ptr != ipv4res) - return 1; /* fail */ - if(!ipv4ptr[0]) - return 1; /* fail */ - if(memcmp(ipv4res, "192.168.100.1", 13)) - return 1; /* fail */ - /* - */ - ipv6res[0] = '\0'; - memset(ipv6a, 0, sizeof(ipv6a)); - ipv6a[0] = 0xfe; - ipv6a[1] = 0x80; - ipv6a[8] = 0x02; - ipv6a[9] = 0x14; - ipv6a[10] = 0x4f; - ipv6a[11] = 0xff; - ipv6a[12] = 0xfe; - ipv6a[13] = 0x0b; - ipv6a[14] = 0x76; - ipv6a[15] = 0xc8; - ipv6a[25] = 0x01; - ipv6ptr = inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); - if(!ipv6ptr) - return 1; /* fail */ - if(ipv6ptr != ipv6res) - return 1; /* fail */ - if(!ipv6ptr[0]) - return 1; /* fail */ - if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) - return 1; /* fail */ - - /* verify working RFC 4291 zero prefixed IPv4 - mapped format */ - memset(ipv6a, 0, sizeof(ipv6a)); - ipv6a[12] = 0x7f; - ipv6a[13] = 0x0; - ipv6a[14] = 0x0; - ipv6a[15] = 0x01; - ipv6ptr = inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); - if(!ipv6ptr) - return 1; /* fail */ - if(ipv6ptr != ipv6res) - return 1; /* fail */ - if(!ipv6ptr[0]) - return 1; /* fail */ - if(memcmp(ipv6res, "::127.0.0.1", 11)) - return 1; /* fail */ - return 0; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_works_inet_ntop="yes" - ],[ - AC_MSG_RESULT([no]) - tst_works_inet_ntop="no" - ]) - fi - - if test "$tst_compi_inet_ntop" = "yes" && - test "$tst_works_inet_ntop" != "no"; then - AC_MSG_CHECKING([if inet_ntop usage allowed]) - if test "x$curl_disallow_inet_ntop" != "xyes"; then - AC_MSG_RESULT([yes]) - tst_allow_inet_ntop="yes" - else - AC_MSG_RESULT([no]) - tst_allow_inet_ntop="no" - fi - fi - - AC_MSG_CHECKING([if inet_ntop might be used]) - if test "$tst_links_inet_ntop" = "yes" && - test "$tst_proto_inet_ntop" = "yes" && - test "$tst_compi_inet_ntop" = "yes" && - test "$tst_allow_inet_ntop" = "yes" && - test "$tst_works_inet_ntop" != "no"; then - AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED(HAVE_INET_NTOP, 1, - [Define to 1 if you have an IPv6 capable working inet_ntop function.]) - curl_cv_func_inet_ntop="yes" - else - AC_MSG_RESULT([no]) - curl_cv_func_inet_ntop="no" - fi -]) - - -dnl CURL_CHECK_FUNC_INET_PTON -dnl ------------------------------------------------- -dnl Verify if inet_pton is available, prototyped, can -dnl be compiled and seems to work. If all of these are -dnl true, and usage has not been previously disallowed -dnl with shell variable curl_disallow_inet_pton, then -dnl HAVE_INET_PTON is defined. - -AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB]) - AC_REQUIRE([CURL_INCLUDES_ARPA_INET]) - AC_REQUIRE([CURL_INCLUDES_STRING]) - - tst_links_inet_pton="unknown" - tst_proto_inet_pton="unknown" - tst_compi_inet_pton="unknown" - tst_works_inet_pton="unknown" - tst_allow_inet_pton="unknown" - - AC_MSG_CHECKING([if inet_pton can be linked]) - AC_LINK_IFELSE([ - AC_LANG_FUNC_LINK_TRY([inet_pton]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_inet_pton="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_inet_pton="no" - ]) - - if test "$tst_links_inet_pton" = "yes"; then - AC_MSG_CHECKING([if inet_pton is prototyped]) - AC_EGREP_CPP([inet_pton],[ - $curl_includes_arpa_inet - ],[ - AC_MSG_RESULT([yes]) - tst_proto_inet_pton="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_inet_pton="no" - ]) - fi - - if test "$tst_proto_inet_pton" = "yes"; then - AC_MSG_CHECKING([if inet_pton is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_arpa_inet - ]],[[ - unsigned char ipv4a[4 + 1] = ""; - const char *ipv4src = "192.168.100.1"; - if(inet_pton(0, ipv4src, ipv4a)) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_inet_pton="yes" - ],[ - AC_MSG_RESULT([no]) - tst_compi_inet_pton="no" - ]) - fi - - dnl only do runtime verification when not cross-compiling - if test "$cross_compiling" != "yes" && - test "$tst_compi_inet_pton" = "yes"; then - AC_MSG_CHECKING([if inet_pton seems to work]) - CURL_RUN_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_stdlib - $curl_includes_arpa_inet - $curl_includes_string - ]],[[ - unsigned char ipv6a[16 + 1]; - unsigned char ipv4a[4 + 1]; - const char *ipv6src = "fe80::214:4fff:fe0b:76c8"; - const char *ipv4src = "192.168.100.1"; - /* - */ - memset(ipv4a, 1, sizeof(ipv4a)); - if(inet_pton(AF_INET, ipv4src, ipv4a) != 1) - return 1; /* fail */ - /* - */ - if((ipv4a[0] != 0xc0) || - (ipv4a[1] != 0xa8) || - (ipv4a[2] != 0x64) || - (ipv4a[3] != 0x01) || - (ipv4a[4] != 0x01)) - return 1; /* fail */ - /* - */ - memset(ipv6a, 1, sizeof(ipv6a)); - if(inet_pton(AF_INET6, ipv6src, ipv6a) != 1) - return 1; /* fail */ - /* - */ - if((ipv6a[0] != 0xfe) || - (ipv6a[1] != 0x80) || - (ipv6a[8] != 0x02) || - (ipv6a[9] != 0x14) || - (ipv6a[10] != 0x4f) || - (ipv6a[11] != 0xff) || - (ipv6a[12] != 0xfe) || - (ipv6a[13] != 0x0b) || - (ipv6a[14] != 0x76) || - (ipv6a[15] != 0xc8) || - (ipv6a[16] != 0x01)) - return 1; /* fail */ - /* - */ - if((ipv6a[2] != 0x0) || - (ipv6a[3] != 0x0) || - (ipv6a[4] != 0x0) || - (ipv6a[5] != 0x0) || - (ipv6a[6] != 0x0) || - (ipv6a[7] != 0x0)) - return 1; /* fail */ - /* - */ - return 0; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_works_inet_pton="yes" - ],[ - AC_MSG_RESULT([no]) - tst_works_inet_pton="no" - ]) - fi - - if test "$tst_compi_inet_pton" = "yes" && - test "$tst_works_inet_pton" != "no"; then - AC_MSG_CHECKING([if inet_pton usage allowed]) - if test "x$curl_disallow_inet_pton" != "xyes"; then - AC_MSG_RESULT([yes]) - tst_allow_inet_pton="yes" - else - AC_MSG_RESULT([no]) - tst_allow_inet_pton="no" - fi - fi - - AC_MSG_CHECKING([if inet_pton might be used]) - if test "$tst_links_inet_pton" = "yes" && - test "$tst_proto_inet_pton" = "yes" && - test "$tst_compi_inet_pton" = "yes" && - test "$tst_allow_inet_pton" = "yes" && - test "$tst_works_inet_pton" != "no"; then - AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED(HAVE_INET_PTON, 1, - [Define to 1 if you have an IPv6 capable working inet_pton function.]) - curl_cv_func_inet_pton="yes" - else - AC_MSG_RESULT([no]) - curl_cv_func_inet_pton="no" - fi -]) - - dnl CURL_CHECK_FUNC_IOCTL dnl ------------------------------------------------- dnl Verify if ioctl is available, prototyped, and diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 7ef3074b0cb7..b1062308a473 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -88,6 +88,8 @@ "getaddrinfo" => 1, "gets" => 1, "gmtime" => 1, + "inet_ntop" => 1, + "inet_pton" => 1, "llseek" => 1, "LoadLibrary" => 1, "LoadLibraryA" => 1, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a8a3b96e610b..940ee4bfe81d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -235,10 +235,10 @@ test1916 test1917 test1918 test1919 test1920 test1921 test1922 \ \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ -test1955 test1956 test1957 test1958 test1959 test1960 test1964 test1965 \ -test1966 test1967 test1970 test1971 test1972 test1973 \ -test1974 test1975 test1976 test1977 test1978 test1979 test1980 test1981 \ -test1982 test1983 test1984 \ +test1955 test1956 test1957 test1958 test1959 test1960 test1961 \ + test1964 test1965 test1966 test1967 test1970 \ +test1971 test1972 test1973 test1974 test1975 test1976 test1977 test1978 \ +test1979 test1980 test1981 test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ diff --git a/tests/data/test1961 b/tests/data/test1961 new file mode 100644 index 000000000000..0b44ca3c0edf --- /dev/null +++ b/tests/data/test1961 @@ -0,0 +1,18 @@ + + + + +unittest + + + +# Client-side + + +unittest + + +curlx_inet_ntop() and curlx_inet_pton() + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index bec648542b4f..64508d1285b8 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -36,6 +36,8 @@ UTILS_H = testutil.h testtrace.h unitcheck.h CURLX_C = \ ../../lib/curl_threads.c \ ../../lib/curlx/fopen.c \ + ../../lib/curlx/inet_ntop.c \ + ../../lib/curlx/inet_pton.c \ ../../lib/curlx/multibyte.c \ ../../lib/curlx/strcopy.c \ ../../lib/curlx/strerr.c \ diff --git a/tests/libtest/first.h b/tests/libtest/first.h index fda2fd9f1ff4..75ae75c6fb43 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -47,6 +47,8 @@ extern int unitfail; /* for unittests */ #include "curlx/base64.h" /* for curlx_base64* */ #include "curlx/dynbuf.h" /* for curlx_dyn_*() */ #include "curlx/fopen.h" /* for curlx_f*() */ +#include "curlx/inet_ntop.h" /* for curlx_inet_ntop() */ +#include "curlx/inet_pton.h" /* for curlx_inet_pton() */ #include "curlx/strcopy.h" /* for curlx_strcopy() */ #include "curlx/strerr.h" /* for curlx_strerror() */ #include "curlx/strparse.h" /* for curlx_str_* parsing functions */ diff --git a/tests/libtest/lib1960.c b/tests/libtest/lib1960.c index fbb0f7e597a8..1cda44040a85 100644 --- a/tests/libtest/lib1960.c +++ b/tests/libtest/lib1960.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#ifdef HAVE_INET_PTON - #ifdef HAVE_NETINET_IN_H #include #endif @@ -62,12 +60,6 @@ static int sockopt_cb(void *clientp, return CURL_SOCKOPT_ALREADY_CONNECTED; } -#ifdef __AMIGA__ -#define my_inet_pton(x, y, z) inet_pton(x, (unsigned char *)CURL_UNCONST(y), z) -#else -#define my_inet_pton(x, y, z) inet_pton(x, y, z) -#endif - /* Expected args: URL IP PORT */ static CURLcode test_lib1960(const char *URL) { @@ -104,8 +96,8 @@ static CURLcode test_lib1960(const char *URL) serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons((unsigned short)port); - if(my_inet_pton(AF_INET, libtest_arg2, &serv_addr.sin_addr) <= 0) { - curl_mfprintf(stderr, "inet_pton failed\n"); + if(curlx_inet_pton(AF_INET, libtest_arg2, &serv_addr.sin_addr) <= 0) { + curl_mfprintf(stderr, "curlx_inet_pton() failed\n"); goto test_cleanup; } @@ -143,11 +135,3 @@ static CURLcode test_lib1960(const char *URL) return result; } -#else -static CURLcode test_lib1960(const char *URL) -{ - (void)URL; - curl_mprintf("lacks inet_pton\n"); - return CURLE_OK; -} -#endif diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index b9a50c120b27..ca89c9c0536e 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -493,10 +493,12 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, case QTYPE_A: for(a = 0; a < ancount_a; a++) { const unsigned char *store = ipv4_pref; + const char *ip; if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A)) goto error; + ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)); logmsg("[%d] response A (%x) '%s'", qid, (unsigned int)QTYPE_A, - curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf))); + ip ? ip : "(null)"); } if(!ancount_a) logmsg("[%d] response A empty", qid); @@ -504,10 +506,12 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, case QTYPE_AAAA: for(a = 0; a < ancount_aaaa; a++) { const unsigned char *store = ipv6_pref; + const char *ip; if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA)) goto error; + ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)); logmsg("[%d] response AAAA (%x) '%s'", qid, (unsigned int)QTYPE_AAAA, - curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf))); + ip ? ip : "(null)"); } if(!ancount_aaaa) logmsg("[%d] response AAAA empty", qid); diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index 18d97b051ccc..d1fec804ee6a 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -43,7 +43,7 @@ TESTS_C = \ unit1657.c unit1658.c unit1660.c unit1661.c unit1663.c unit1664.c \ unit1666.c unit1667.c unit1668.c unit1669.c \ unit1674.c unit1675.c unit1676.c \ - unit1979.c unit1980.c \ + unit1961.c unit1979.c unit1980.c \ unit2413.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ diff --git a/tests/unit/unit1961.c b/tests/unit/unit1961.c new file mode 100644 index 000000000000..135386256a04 --- /dev/null +++ b/tests/unit/unit1961.c @@ -0,0 +1,172 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifndef _WIN32 +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif + +/* + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(USE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +static int test_ntop(void) +{ + char ipv6res[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char ipv4res[sizeof("255.255.255.255")]; + unsigned char ipv6a[26]; + unsigned char ipv4a[5]; + const char *ipv6ptr = 0; + const char *ipv4ptr = 0; + + ipv4res[0] = '\0'; + ipv4a[0] = 0xc0; + ipv4a[1] = 0xa8; + ipv4a[2] = 0x64; + ipv4a[3] = 0x01; + ipv4a[4] = 0x01; + ipv4ptr = curlx_inet_ntop(AF_INET, ipv4a, ipv4res, sizeof(ipv4res)); + if(!ipv4ptr) + return 1; /* fail */ + if(ipv4ptr != ipv4res) + return 1; /* fail */ + if(!ipv4ptr[0]) + return 1; /* fail */ + if(memcmp(ipv4res, "192.168.100.1", 13)) + return 1; /* fail */ + + ipv6res[0] = '\0'; + memset(ipv6a, 0, sizeof(ipv6a)); + ipv6a[0] = 0xfe; + ipv6a[1] = 0x80; + ipv6a[8] = 0x02; + ipv6a[9] = 0x14; + ipv6a[10] = 0x4f; + ipv6a[11] = 0xff; + ipv6a[12] = 0xfe; + ipv6a[13] = 0x0b; + ipv6a[14] = 0x76; + ipv6a[15] = 0xc8; + ipv6a[25] = 0x01; + ipv6ptr = curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); + if(!ipv6ptr) + return 1; /* fail */ + if(ipv6ptr != ipv6res) + return 1; /* fail */ + if(!ipv6ptr[0]) + return 1; /* fail */ + if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) + return 1; /* fail */ + + /* verify working RFC 4291 zero prefixed IPv4 - mapped format */ + memset(ipv6a, 0, sizeof(ipv6a)); + ipv6a[12] = 0x7f; + ipv6a[13] = 0x0; + ipv6a[14] = 0x0; + ipv6a[15] = 0x01; + ipv6ptr = curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); + if(!ipv6ptr) + return 1; /* fail */ + if(ipv6ptr != ipv6res) + return 1; /* fail */ + if(!ipv6ptr[0]) + return 1; /* fail */ + if(memcmp(ipv6res, "::127.0.0.1", 11)) + return 1; /* fail */ + + return 0; +} + +static int test_pton(void) +{ + unsigned char ipv6a[16 + 1]; + unsigned char ipv4a[4 + 1]; + const char *ipv6src = "fe80::214:4fff:fe0b:76c8"; + const char *ipv4src = "192.168.100.1"; + + memset(ipv4a, 1, sizeof(ipv4a)); + if(curlx_inet_pton(AF_INET, ipv4src, ipv4a) != 1) + return 1; /* fail */ + + if((ipv4a[0] != 0xc0) || + (ipv4a[1] != 0xa8) || + (ipv4a[2] != 0x64) || + (ipv4a[3] != 0x01) || + (ipv4a[4] != 0x01)) + return 1; /* fail */ + + memset(ipv6a, 1, sizeof(ipv6a)); + if(curlx_inet_pton(AF_INET6, ipv6src, ipv6a) != 1) + return 1; /* fail */ + + if((ipv6a[0] != 0xfe) || + (ipv6a[1] != 0x80) || + (ipv6a[8] != 0x02) || + (ipv6a[9] != 0x14) || + (ipv6a[10] != 0x4f) || + (ipv6a[11] != 0xff) || + (ipv6a[12] != 0xfe) || + (ipv6a[13] != 0x0b) || + (ipv6a[14] != 0x76) || + (ipv6a[15] != 0xc8) || + (ipv6a[16] != 0x01)) + return 1; /* fail */ + + if((ipv6a[2] != 0x0) || + (ipv6a[3] != 0x0) || + (ipv6a[4] != 0x0) || + (ipv6a[5] != 0x0) || + (ipv6a[6] != 0x0) || + (ipv6a[7] != 0x0)) + return 1; /* fail */ + + return 0; +} + +static CURLcode test_unit1961(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + + fail_if(test_ntop(), "curlx_inet_ntop()"); + fail_if(test_pton(), "curlx_inet_pton()"); + + UNITTEST_END_SIMPLE +} From 0a6c74a8025360599cddbe1d0d3568bce603738e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 20:17:16 +0200 Subject: [PATCH 0589/1062] cmake: minor tidy-ups - INSTALL-CMAKE.md: document that `CMAKE_INSTALL_PREFIX` may be set as environment (with CMake 3.29+) Ref: https://cmake.org/cmake/help/v3.29/variable/CMAKE_INSTALL_PREFIX.html - CurlTests: fix copy-paste typo in comment. Spotted by GitHub Code Quality - optimized out `_generated_dir` local variable. - unfold a `@ONLY` to match rest of code. Closes #22203 --- CMake/CurlTests.c | 2 +- CMakeLists.txt | 8 +++----- docs/INSTALL-CMAKE.md | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 2da96eccc8d5..95cd89bb6321 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -225,7 +225,7 @@ int main(void) #endif #ifdef HAVE_IOCTL_SIOCGIFADDR -/* headers for FIONBIO test */ +/* headers for SIOCGIFADDR test */ #ifdef HAVE_SYS_TYPES_H # include #endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ad15f766791..c0071f1a0764 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1937,9 +1937,8 @@ include(GNUInstallDirs) set(_install_cmake_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") -set(_generated_dir "${PROJECT_BINARY_DIR}") -set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") -set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +set(_project_config "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") +set(_version_config "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake") option(BUILD_TESTING "Build tests" ON) if(BUILD_TESTING AND Perl_FOUND) @@ -2459,8 +2458,7 @@ if(NOT CURL_DISABLE_INSTALL) # PROJECT_BINARY_DIR configure_file( "${PROJECT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" - "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" - @ONLY) + "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY) add_custom_target(curl_uninstall COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 815cdddcd4a1..52fc596cdb2c 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -332,7 +332,7 @@ Details via CMake - `CMAKE_INSTALL_BINDIR` (see CMake) - `CMAKE_INSTALL_INCLUDEDIR` (see CMake) - `CMAKE_INSTALL_LIBDIR` (see CMake) -- `CMAKE_INSTALL_PREFIX` (see CMake) +- `CMAKE_INSTALL_PREFIX` (see CMake) (in CMake 3.29+ also supported as environment) - `CMAKE_STATIC_LIBRARY_SUFFIX` (see CMake) - `CMAKE_UNITY_BUILD_BATCH_SIZE`: Set the number of sources in a "unity" unit. Default: `0` (all) - `CMAKE_UNITY_BUILD`: Enable "unity" (aka "jumbo") builds. Default: `OFF` From 88d85916f24ededa1b9f90b52c4c5d18af2c8072 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 22:56:02 +0200 Subject: [PATCH 0590/1062] cmake: minor improvements to `cmake_uninstall.in.cmake` - reduce log noise by showing 'Uninstalling' message only if the file exists. (and a different message otherwise) - replace cmake `rm` command with `file(REMOVE)`. For efficiency. Show manual message if the file could not be deleted. Ref: https://cmake.org/cmake/help/v3.18/command/file.html#remove Follow-up to 6d008352c667799beecb1ab1dfbc408c415d018c #22193 - reduce log noise by showing 'Uninstalled' only if the deletion was successful. (and a different message otherwise) - display `DESTDIR` env value if set. - drop checking, setting and showing `CMAKE_INSTALL_PREFIX`. The value was never predefined, also not used, besides showing it, and showing it is misleading because `--prefix` may override the configure-time value, and also superfluous because the filenames are showing the actual prefix anyway. Follow-up to 27e2a4733c3321fb0b1a127360e8e96bc3d4ae53 Closes #22201 --- CMake/cmake_uninstall.in.cmake | 16 ++++++++++------ CMakeLists.txt | 4 +--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CMake/cmake_uninstall.in.cmake b/CMake/cmake_uninstall.in.cmake index c7fd8008e19c..30e812b1246b 100644 --- a/CMake/cmake_uninstall.in.cmake +++ b/CMake/cmake_uninstall.in.cmake @@ -26,18 +26,22 @@ if(NOT EXISTS "${_manifest}") message(FATAL_ERROR "Cannot find install manifest: ${_manifest}") endif() -if(NOT DEFINED CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@") +set(_destdir "$ENV{DESTDIR}") +if(NOT _destdir STREQUAL "") + message(STATUS "DESTDIR environment: ${_destdir}") endif() -message(${CMAKE_INSTALL_PREFIX}) file(READ "${_manifest}" _files) string(REGEX REPLACE "\n" ";" _files "${_files}") foreach(_file IN LISTS _files) - set(_target "$ENV{DESTDIR}${_file}") - message(STATUS "Uninstalling ${_target}") + set(_target "${_destdir}${_file}") if(IS_SYMLINK "${_target}" OR EXISTS "${_target}") - execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -f -- "${_target}") + file(REMOVE "${_target}") + if(IS_SYMLINK "${_target}" OR EXISTS "${_target}") + message(STATUS "Failed to delete: ${_target}") + else() + message(STATUS "Uninstalled: ${_target}") + endif() else() message(STATUS "File does not exist: ${_target}") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index c0071f1a0764..5929b741cec6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2452,9 +2452,7 @@ if(NOT CURL_DISABLE_INSTALL) DESTINATION ${_install_cmake_dir}) if(NOT TARGET curl_uninstall) - # Consumed variables: - # CMAKE_COMMAND - # CMAKE_INSTALL_PREFIX + # Consumed variable: # PROJECT_BINARY_DIR configure_file( "${PROJECT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" From c253a96296e66c01e2063d809b5996abb3296bc1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2026 06:41:26 +0000 Subject: [PATCH 0591/1062] GHA: update dependency ngtcp2/nghttp3 to v1.17.0 Closes #22202 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 83e83c886afa..d7cd1adbf726 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -56,7 +56,7 @@ env: # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - NGHTTP3_VERSION: 1.16.0 + NGHTTP3_VERSION: 1.17.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com NGTCP2_VERSION: 1.23.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com From 99859ecca85708576e6567d02a24d2ef7501707d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 28 Jun 2026 13:20:50 +0200 Subject: [PATCH 0592/1062] cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+, bump in CI Fixes #22205 Closes #22207 --- .github/workflows/http3-linux.yml | 2 +- lib/vquic/cf-ngtcp2-cmn.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index d7cd1adbf726..c6cafaafcc39 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -58,7 +58,7 @@ env: # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com NGHTTP3_VERSION: 1.17.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - NGTCP2_VERSION: 1.23.0 + NGTCP2_VERSION: 1.24.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 # no tagged releases diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index e1ca18cf69c8..419c52009acd 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -596,6 +596,9 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* dcid_status2 */ ngtcp2_crypto_get_path_challenge_data2_cb, /* get_path_challenge_data2 */ #endif +#ifdef NGTCP2_CALLBACKS_V4 /* ngtcp2 v1.24.0+ */ + NULL, /* recv_stop_sending */ +#endif }; #if defined(_MSC_VER) && defined(_DLL) From 1671ebd975c1f146b0dd9929fa4c2a3f8d39b305 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 28 Jun 2026 12:19:11 +0200 Subject: [PATCH 0593/1062] build: drop superfluous `STDC_HEADERS` macro It is traditionally defined by autotools to detect the presence of set of standard C89 headers. autoconf 2.70 (2020-12-08) reduced the headers covered to `stdlib.h`, `string.h`. After 2.59d (2006-06-05) obsoleting it earlier. CMake replicated this detection, and curl included `curl/stdcheaders.h` if standard headers were missing. However, such condition could never happen because curl sources already assume all checked standard headers (`stdarg.h`, `stdlib.h`, `string.h`) and include them unconditionally. Since this is an unused feature detection and an impossible fallback path, drop them from CMake and curl's source. autotools continues to do the detection by default, but its result is unused after this patch. This leaves public `curl/stdcheaders.h` unused from within the codebase. Refs: https://github.com/autotools-mirror/autoconf/commit/f0c7c425539964047b0cb986d2ea5f5687a07069 https://github.com/autotools-mirror/autoconf/commit/86c213d0e355296f026a36e3203c0813041aae89 Follow-up to 65dae4ad80b02f25a25e17af62ea4f2940970330 #22191 Follow-up to 4c5307b45655ba75ab066564afdc0c111a8b9291 Ref: ae1912cb0d494b48d514d937826c9fe83ec96c4d Closes #22206 --- .github/scripts/cmp-config.pl | 1 + CMake/CurlTests.c | 10 ---------- CMake/unix-cache.cmake | 1 - CMake/win32-cache.cmake | 1 - CMakeLists.txt | 1 - lib/config-os400.h | 5 ----- lib/config-win32.h | 3 --- lib/curl_config-cmake.h.in | 3 --- lib/curl_setup.h | 4 ---- projects/vms/config_h.com | 10 ---------- 10 files changed, 1 insertion(+), 38 deletions(-) diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index b697ae1203a4..a0d33498a75f 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -88,6 +88,7 @@ '#define PACKAGE_TARNAME "curl"' => 1, '#define PACKAGE_URL ""' => 1, '#define PACKAGE_VERSION "-"' => 1, + '#define STDC_HEADERS 1' => 1, '#define VERSION "-"' => 1, '#define _FILE_OFFSET_BITS 64' => 1, ); diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 95cd89bb6321..c9d0b93d21d9 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -114,16 +114,6 @@ int main(void) } #endif -#ifdef STDC_HEADERS -#include -#include -#include -int main(void) -{ - return 0; -} -#endif - #ifdef HAVE_FILE_OFFSET_BITS #include /* Check that off_t can represent 2**63 - 1 correctly. diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 97be36d5f3c2..287c3fdf9ef6 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -314,5 +314,4 @@ set(HAVE_UTIME 1) set(HAVE_UTIMES 1) set(HAVE_UTIME_H 1) set(HAVE_WRITABLE_ARGV 1) -set(STDC_HEADERS 1) set(USE_UNIX_SOCKETS 1) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index c10808727386..a684b459b085 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -161,7 +161,6 @@ set(HAVE_TERMIO_H 0) set(HAVE_TIME_T_UNSIGNED 0) set(HAVE_UTIME 1) set(HAVE_UTIMES 0) -set(STDC_HEADERS 1) # Types and sizes diff --git a/CMakeLists.txt b/CMakeLists.txt index 5929b741cec6..5aa412fc98a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1731,7 +1731,6 @@ foreach(_curl_test IN ITEMS HAVE_GETHOSTBYNAME_R_5 HAVE_GETHOSTBYNAME_R_6 HAVE_BOOL_T - STDC_HEADERS HAVE_ATOMIC ) curl_internal_test(${_curl_test}) diff --git a/lib/config-os400.h b/lib/config-os400.h index 76841af202ec..050fd9d7dac6 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -238,11 +238,6 @@ /* Size of time_t in number of bytes */ #define SIZEOF_TIME_T 4 -/* Define to 1 if all of the C89 standard headers exist (not only the ones - required in a freestanding environment). This macro is provided for - backward compatibility; new code need not use it. */ -#define STDC_HEADERS 1 - /* Define if you want to enable IPv6 support */ #define USE_IPV6 diff --git a/lib/config-win32.h b/lib/config-win32.h index fedfb8f30ed8..1f4f12f433a2 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -71,9 +71,6 @@ /* OTHER HEADER INFO */ /* ---------------------------------------------------------------- */ -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - /* Define to 1 if bool is an available type. */ #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) #define HAVE_BOOL_T 1 diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 92280010c08e..add3b26aadf2 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -634,9 +634,6 @@ ${SIZEOF_SIZE_T_CODE} /* The size of `time_t', as computed by sizeof. */ ${SIZEOF_TIME_T_CODE} -/* Define to 1 if you have the ANSI C header files. */ -#cmakedefine STDC_HEADERS 1 - /* Define if you have POSIX pthreads */ #cmakedefine HAVE_THREADS_POSIX 1 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index a26bb23cb250..065fd04dde3f 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -490,10 +490,6 @@ # endif #endif -#ifndef STDC_HEADERS /* no standard C headers! */ -#include -#endif - #include #define HAVE_UINTPTR_T /* assume uintptr_t is provided by stdint.h */ diff --git a/projects/vms/config_h.com b/projects/vms/config_h.com index 0b23cc6633e5..2de76ad7318a 100644 --- a/projects/vms/config_h.com +++ b/projects/vms/config_h.com @@ -1648,16 +1648,6 @@ $ write tf "#endif" $ goto cfgh_in_loop1 $ endif $! -$! Process STDC_HEADERS (SAMBA!) -$!--------------------------- -$ if key2 .eqs. "STDC_HEADERS" -$ then -$ write tf "#ifndef STDC_HEADERS" -$ write tf "#define STDC_HEADERS 1" -$ write tf "#endif" -$ goto cfgh_in_loop1 -$ endif -$! $! Process PROTOTYPES directive $!------------------------------------- $ if key2 .eqs. "PROTOTYPES" From d647b84d53b5ba14ce82b0759c70e962a423c1a4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 29 Jun 2026 08:45:18 +0200 Subject: [PATCH 0594/1062] CREDENTIALS.md: remove comment about emtpy user/pass Closes #22212 --- docs/internals/CREDENTIALS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/internals/CREDENTIALS.md b/docs/internals/CREDENTIALS.md index e5e409ed7bd1..a99b0fa31774 100644 --- a/docs/internals/CREDENTIALS.md +++ b/docs/internals/CREDENTIALS.md @@ -15,8 +15,7 @@ Authorization credentials are kept in `struct Curl_creds`. This contains: * `source`: where the credentials from * `refcount`: a reference counter to link/unlink `creds` -A `creds` with all values empty is equivalent to NULL, e.g. no `creds` -instance. With reference counting, `creds` can be linked in several places. +With reference counting, `creds` can be linked in several places. Two `creds` are the same if all values are equal apart from `source` and `refcount`. The comparison of strings is done via `Curl_timestrcmp()` From c9d8a6bc1084cae8b9f63a504bdfc7c00230f4ff Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 29 Jun 2026 11:17:04 +0200 Subject: [PATCH 0595/1062] mod_curltest: fix compiler warnings ``` mod_curltest.c:331:25: warning: result of comparison of unsigned expression >= 0 is always true [-Wtautological-unsigned-zero-compare] 331 | if(chunk_size >= 0) { | ~~~~~~~~~~ ^ ~ mod_curltest.c:421:9: warning: declaration shadows a local variable [-Wshadow] 421 | int i, hd_len = (16 * 1024); | ^ mod_curltest.c:288:7: note: previous declaration is here 288 | int i, chunks = 3, error_bucket = 1; | ^ mod_curltest.c:501:40: warning: format specifies type 'int' but the argument has type 'unsigned int' [-Wformat] 500 | "error_handler: request cleanup, r->status=%d, aborted=%d, " | ~~ | %u 501 | "close=%d", r->status, c->aborted, close_conn); | ^~~~~~~~~~ mod_curltest.c:837:1: warning: missing field 'lock' initializer [-Wmissing-field-initializers] 837 | }; | ^ mod_curltest.c:914:43: warning: format specifies type 'int' but the argument has type 'apr_time_t' (aka 'long') [-Wformat] 914 | char *v = apr_psprintf(r->pool, "%d", limitrec.duration_sec); | ~~ ^~~~~~~~~~~~~~~~~~~~~ | %ld mod_curltest.c:956:16: warning: unused variable 'rv' [-Wunused-variable] 956 | apr_status_t rv; | ^~ ``` Closes #22214 --- .../http/testenv/mod_curltest/mod_curltest.c | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index bd09c8b377d8..25f32c79046b 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -328,15 +328,13 @@ static int curltest_tweak_handler(request_rec *r) } else if(!strcmp("chunk_size", arg)) { chunk_size = (int)apr_atoi64(val); - if(chunk_size >= 0) { - if(chunk_size > sizeof(buffer)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "chunk_size %zu too large", chunk_size); - ap_die(HTTP_BAD_REQUEST, r); - return OK; - } - continue; + if(chunk_size > sizeof(buffer)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "chunk_size %zu too large", chunk_size); + ap_die(HTTP_BAD_REQUEST, r); + return OK; } + continue; } else if(!strcmp("id", arg)) { /* an id for repeated requests with curl's URL globbing */ @@ -418,7 +416,7 @@ static int curltest_tweak_handler(request_rec *r) /* Discourage content-encodings */ apr_table_unset(r->headers_out, "Content-Encoding"); if(x_hd_len > 0) { - int i, hd_len = (16 * 1024); + int hd_len = (16 * 1024); int n = (x_hd_len / hd_len); char *hd_val = apr_palloc(r->pool, hd_len); memset(hd_val, 'X', hd_len); @@ -497,7 +495,7 @@ static int curltest_tweak_handler(request_rec *r) r->connection->keepalive = AP_CONN_CLOSE; } ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, - "error_handler: request cleanup, r->status=%d, aborted=%d, " + "error_handler: request cleanup, r->status=%d, aborted=%u, " "close=%d", r->status, c->aborted, close_conn); if(rv == APR_SUCCESS) { return OK; @@ -833,7 +831,7 @@ struct curltest_limit_rec { }; static struct curltest_limit_rec limitrec = { - 0, 5, 0, 2 + 0, 5, 0, 2, NULL }; static int curltest_limit_handler(request_rec *r) @@ -911,7 +909,7 @@ static int curltest_limit_handler(request_rec *r) apr_table_setn(r->subprocess_env, "no-gzip", "1"); if(denied) { - char *v = apr_psprintf(r->pool, "%d", limitrec.duration_sec); + char *v = apr_psprintf(r->pool, "%ld", limitrec.duration_sec); apr_table_set(r->headers_out, "Retry-After", v); } @@ -953,7 +951,6 @@ static int curltest_post_config(apr_pool_t *p, apr_pool_t *plog, { void *data = NULL; const char *key = "mod_curltest_init_counter"; - apr_status_t rv; (void)p; (void)plog; From f3c11692a9881b2d74263e20749ff58da1fd8443 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 29 Jun 2026 08:05:16 +0200 Subject: [PATCH 0596/1062] CURLOPT_SSH_*_KEYFILE: used for setting up, then no more So changing them after the connection is made still allows libcurl to reuse the existing connections. Reported-by: Bigtang on hackerone Closes #22211 --- docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md | 8 ++++++-- docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md b/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md index 2a1f79845dbf..efc627b8dcff 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md +++ b/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md @@ -33,8 +33,7 @@ used, libcurl defaults to **$HOME/.ssh/id_rsa** or **$HOME/.ssh/id_dsa** if the HOME environment variable is set, and in the current directory if HOME is not set. -If the file is password-protected, set the password with -CURLOPT_KEYPASSWD(3). +If the file is password-protected, set the password with CURLOPT_KEYPASSWD(3). The SSH library derives the public key from this private key when possible. If the SSH library cannot derive the public key from the private one and no @@ -44,6 +43,11 @@ fails. The application does not have to keep the string around after setting this option. +This option is used to set up a new connection only. The private key is used +when libcurl establishes a new SSH connection; once that connection has been +successfully set up and verified, it is deemed vetted and may be reused by +libcurl even if this option is changed. + # DEFAULT As explained above diff --git a/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md b/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md index 161daf4ecf8a..5cd2fc5b8c11 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md +++ b/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md @@ -40,6 +40,11 @@ no public one is provided, the transfer fails. The application does not have to keep the string around after setting this option. +This option is used to set up a new connection only. The public key is used +when libcurl establishes a new SSH connection; once that connection has been +successfully set up and verified, it is deemed vetted and may be reused by +libcurl even if this option is changed. + # DEFAULT NULL From 2b5911880ba715d10e0a8bf6dc4896b662fedc89 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 29 Jun 2026 18:36:33 +0200 Subject: [PATCH 0597/1062] openssl: prefer modern API flavors for `EVP_MD_CTX` new/free Available in all supported OpenSSL flavors and versions. They are functionally identical to the legacy API calls. Closes #22219 --- lib/curl_sha512_256.c | 6 +++--- lib/sha256.c | 6 +++--- lib/vtls/openssl.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index c429bba8f268..0ce669603ea5 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -112,7 +112,7 @@ static CURLcode Curl_sha512_256_init(void *context) { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; - *ctx = EVP_MD_CTX_create(); + *ctx = EVP_MD_CTX_new(); if(!*ctx) return CURLE_OUT_OF_MEMORY; @@ -126,7 +126,7 @@ static CURLcode Curl_sha512_256_init(void *context) } /* Cleanup */ - EVP_MD_CTX_destroy(*ctx); + EVP_MD_CTX_free(*ctx); return CURLE_FAILED_INIT; } @@ -178,7 +178,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ - EVP_MD_CTX_destroy(*ctx); + EVP_MD_CTX_free(*ctx); *ctx = NULL; return result; diff --git a/lib/sha256.c b/lib/sha256.c index 047119044bb8..38c9b9bc1b3b 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -61,12 +61,12 @@ typedef struct ossl_sha256_ctx my_sha256_ctx; static CURLcode my_sha256_init(void *in) { my_sha256_ctx *ctx = (my_sha256_ctx *)in; - ctx->openssl_ctx = EVP_MD_CTX_create(); + ctx->openssl_ctx = EVP_MD_CTX_new(); if(!ctx->openssl_ctx) return CURLE_OUT_OF_MEMORY; if(!EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL)) { - EVP_MD_CTX_destroy(ctx->openssl_ctx); + EVP_MD_CTX_free(ctx->openssl_ctx); return CURLE_FAILED_INIT; } return CURLE_OK; @@ -84,7 +84,7 @@ static void my_sha256_final(unsigned char *digest, void *in) { my_sha256_ctx *ctx = (my_sha256_ctx *)in; EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL); - EVP_MD_CTX_destroy(ctx->openssl_ctx); + EVP_MD_CTX_free(ctx->openssl_ctx); } #elif defined(USE_WOLFSSL) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 405a5bb67928..3962df160306 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5448,7 +5448,7 @@ static CURLcode ossl_sha256sum(const unsigned char *input, EVP_MD_CTX *mdctx; (void)unused; - mdctx = EVP_MD_CTX_create(); + mdctx = EVP_MD_CTX_new(); if(!mdctx) return CURLE_OUT_OF_MEMORY; if(!EVP_DigestInit(mdctx, EVP_sha256())) { @@ -5459,7 +5459,7 @@ static CURLcode ossl_sha256sum(const unsigned char *input, !EVP_DigestFinal_ex(mdctx, sha256sum, NULL)) result = CURLE_BAD_FUNCTION_ARGUMENT; out: - EVP_MD_CTX_destroy(mdctx); + EVP_MD_CTX_free(mdctx); return result; } From df350dd0d82cfcca52eef5aa615ad52a6d556c1c Mon Sep 17 00:00:00 2001 From: HwangRock Date: Thu, 25 Jun 2026 22:00:04 +0900 Subject: [PATCH 0598/1062] content_encoding: give a clear error on multi-member gzip A gzip Content-Encoding response holding more than one gzip member made curl decode the first member and then fail the transfer with a bare CURLE_WRITE_ERROR and no hint about the cause. Detect the trailing member and fail with a message that explains what happened. Fixes #22156 Closes #22172 --- lib/content_encoding.c | 8 ++++++ tests/data/Makefile.am | 2 +- tests/data/test2305 | 61 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/data/test2305 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index fd0807725997..df62741932b0 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -206,6 +206,14 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* No more data to flush: exit loop. */ break; case Z_STREAM_END: + if((started == ZLIB_INIT_GZIP) && (z->avail_in >= 2) && + (z->next_in[0] == 0x1f) && (z->next_in[1] == 0x8b)) { + /* a second gzip member follows; curl does not support + multi-member gzip responses */ + failf(data, "Multi-member gzip response not supported"); + result = exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); + break; + } result = process_trailer(data, zp); break; case Z_DATA_ERROR: diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 940ee4bfe81d..3cc89e34f450 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -259,7 +259,7 @@ test2108 test2109 test2113 \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ \ -test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ +test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 test2308 \ test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ diff --git a/tests/data/test2305 b/tests/data/test2305 new file mode 100644 index 000000000000..61a2d4401a7d --- /dev/null +++ b/tests/data/test2305 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +compressed + + +# Server-side + + +HTTP/1.1 200 OK +Date: Mon, 29 Nov 2004 21:56:53 GMT +Server: test-server/fake +Content-Type: text/plain; charset=UTF-8 +Content-Encoding: gzip +Content-Length: 54 + +%hex[%1f%8b%08%00%00%00%00%00%02%13%f3%48%cd%c9%c9%d7%51%00%00%05%6f%57%de%07%00%00%00%1f%8b%08%00%00%00%00%00%02%13%0b%cf%2f%ca%49%51%e4%02%00%dd%d1%ca%53%07%00%00%00]hex% + + + + +# Client-side + + +libz + + +http + + +HTTP GET gzip compressed content with two concatenated gzip members + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --compressed --silent --show-error + + + +# Verify data after the test has been "shot" + + +s/^Accept-Encoding: [a-zA-Z, ]*/Accept-Encoding: xxx/ + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Accept-Encoding: xxx + + + +23 + + +curl: (23) Multi-member gzip response not supported + + + From ea71c3b6b60e563651ea8596a975aef0c8199519 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 29 Jun 2026 10:44:47 +0200 Subject: [PATCH 0599/1062] openldap: handle Curl_sasl_continue() returns better Similar to how it gets treated already in other protocol handlers. Follow-up to eeca818b1e8d1e61c2d4 Reported-by: Eunsoo Kim Closes #22213 --- lib/openldap.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/openldap.c b/lib/openldap.c index ee765d8d05b2..6c15734d29ac 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -791,8 +791,19 @@ static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, } else { result = Curl_sasl_continue(&li->sasl, data, code, &progress); - if(!result && progress != SASL_INPROGRESS) - oldap_state(data, li, OLDAP_STOP); + if(!result) { + switch(progress) { + case SASL_DONE: + oldap_state(data, li, OLDAP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + break; + default: + break; + } + } } if(li->servercred) From 7791b1629b16cac4a49d677cbf7c778f4fbaaf0e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 29 Jun 2026 17:41:34 +0200 Subject: [PATCH 0600/1062] CURLSHOPT_(UN)SHARE.md: do not modify shares while in use Reported-by: smaeljaish on hackerone Closes #22217 --- docs/libcurl/opts/CURLSHOPT_SHARE.md | 3 +++ docs/libcurl/opts/CURLSHOPT_UNSHARE.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/libcurl/opts/CURLSHOPT_SHARE.md b/docs/libcurl/opts/CURLSHOPT_SHARE.md index 60e1405bb229..69b59779ad6b 100644 --- a/docs/libcurl/opts/CURLSHOPT_SHARE.md +++ b/docs/libcurl/opts/CURLSHOPT_SHARE.md @@ -35,6 +35,9 @@ CURLSHOPT_SHARE(3) multiple times with different data arguments to have the share object share multiple types of data. Unset a type again by setting CURLSHOPT_UNSHARE(3). +Do not add types to a shared object that is being in use. Add them only +between transfers. + If any of the data is to be shared in multiple threads then mutex callbacks must be set as well. See CURLSHOPT_LOCKFUNC(3) and CURLSHOPT_UNLOCKFUNC(3). diff --git a/docs/libcurl/opts/CURLSHOPT_UNSHARE.md b/docs/libcurl/opts/CURLSHOPT_UNSHARE.md index 45ee27dec0f0..5ad0c60c7bd6 100644 --- a/docs/libcurl/opts/CURLSHOPT_UNSHARE.md +++ b/docs/libcurl/opts/CURLSHOPT_UNSHARE.md @@ -36,6 +36,9 @@ below. You can set CURLSHOPT_UNSHARE(3) multiple times with different data arguments to remove multiple types from the shared object. Add data to share again with CURLSHOPT_SHARE(3). +Do not remove types from a shared object that is being in use. Unshare them +only between transfers. + ## CURL_LOCK_DATA_COOKIE Cookie data is no longer shared across the easy handles using this shared From 8aeef462e391639b621e6adbbd60de6bd77ee43b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 29 Jun 2026 12:02:14 +0200 Subject: [PATCH 0601/1062] config2setopt.c: refactor config2setopts Introduce sub functions Closes #22215 --- src/config2setopts.c | 252 +++++++++++++++++++++++++++--------------- src/tool_setopt.c | 8 -- src/tool_setopt.h | 3 - tests/data/data1402.c | 2 +- tests/data/data1404.c | 2 +- tests/data/data1405.c | 2 +- tests/data/data1406.c | 2 +- tests/data/data1407.c | 2 +- tests/data/data1465.c | 2 +- 9 files changed, 169 insertions(+), 106 deletions(-) diff --git a/src/config2setopts.c b/src/config2setopts.c index 8933668a2c7e..f27783d9fa45 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -46,6 +46,14 @@ #define BUFFER_SIZE 102400L +/* return TRUE if the error code is "lethal" */ +static bool setopt_bad(CURLcode result) +{ + return result && + (result != CURLE_NOT_BUILT_IN) && + (result != CURLE_UNKNOWN_OPTION); +} + #ifdef IP_TOS static int get_address_family(curl_socket_t sockfd) { @@ -773,6 +781,8 @@ static CURLcode proxy_setopts(struct OperationConfig *config, CURL *curl) if(config->haproxy_clientip) MY_SETOPT_STR(curl, CURLOPT_HAPROXY_CLIENT_IP, config->haproxy_clientip); + MY_SETOPT_STR(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd); + return result; } @@ -852,54 +862,12 @@ static void buffersize(struct OperationConfig *config, CURL *curl) my_setopt_long(curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE); } -CURLcode config2setopts(struct OperationConfig *config, - struct per_transfer *per, - CURL *curl, - CURLSH *share) +static CURLcode credentials_and_headers_setopts(struct OperationConfig *config, + CURL *curl) { - const char *use_proto; - CURLcode result = url_proto_and_rewrite(&per->url, config, &use_proto); - - /* Avoid having this setopt added to the --libcurl source output. */ - if(!result) - result = curl_easy_setopt(curl, CURLOPT_SHARE, share); - if(result) - return result; - - if(TRUE -#ifdef DEBUGBUILD - && getenv("CURL_QUICK_EXIT") -#endif - ) { - /* QUICK_EXIT allows for running threads to be detached and not - * joined. Preferably in non-debug runs. */ - result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); - if(result) - return result; - } - - gen_trace_setopts(config, curl); - - buffersize(config, curl); - - MY_SETOPT_STR(curl, CURLOPT_URL, per->url); - my_setopt_long(curl, CURLOPT_NOPROGRESS, - global->noprogress || global->silent); - /* call after the line above. It may override CURLOPT_NOPROGRESS */ - gen_cb_setopts(config, per, curl); + CURLcode result = CURLE_OK; - my_setopt_long(curl, CURLOPT_NOBODY, config->no_body); MY_SETOPT_STR(curl, CURLOPT_XOAUTH2_BEARER, config->oauth_bearer); - result = proxy_setopts(config, curl); - if(setopt_bad(result) || config->synthetic_error) - return result; - - my_setopt_long(curl, CURLOPT_FAILONERROR, config->fail == FAIL_WO_BODY); - MY_SETOPT_STR(curl, CURLOPT_REQUEST_TARGET, config->request_target); - my_setopt_long(curl, CURLOPT_UPLOAD, !!per->uploadfile); - my_setopt_long(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); - my_setopt_long(curl, CURLOPT_APPEND, config->ftp_append); - if(config->netrc_opt) my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); else if(config->netrc || config->netrc_file) @@ -908,19 +876,8 @@ CURLcode config2setopts(struct OperationConfig *config, my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); MY_SETOPT_STR(curl, CURLOPT_NETRC_FILE, config->netrc_file); - my_setopt_long(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); MY_SETOPT_STR(curl, CURLOPT_LOGIN_OPTIONS, config->login_options); MY_SETOPT_STR(curl, CURLOPT_USERPWD, config->userpwd); - MY_SETOPT_STR(curl, CURLOPT_RANGE, config->range); - my_setopt_ptr(curl, CURLOPT_ERRORBUFFER, per->errorbuffer); - my_setopt_long(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms); - - result = setopt_post(config, curl); - if(result) - return result; - - if(config->mime_options) - my_setopt_long(curl, CURLOPT_MIME_OPTIONS, config->mime_options); if(config->authtype) my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, config->authtype); @@ -933,12 +890,35 @@ CURLcode config2setopts(struct OperationConfig *config, config->useragent : CURL_NAME "/" CURL_VERSION); } - result = http_setopts(config, curl, use_proto); - if(!result) - result = ftp_setopts(config, curl, use_proto); + MY_SETOPT_STR(curl, CURLOPT_KEYPASSWD, config->key_passwd); + return result; +} + +static CURLcode transfer_setopts(struct OperationConfig *config, + struct per_transfer *per, + CURL *curl) +{ + CURLcode result = CURLE_OK; + + my_setopt_long(curl, CURLOPT_NOBODY, config->no_body); + my_setopt_long(curl, CURLOPT_FAILONERROR, config->fail == FAIL_WO_BODY); + MY_SETOPT_STR(curl, CURLOPT_REQUEST_TARGET, config->request_target); + my_setopt_long(curl, CURLOPT_UPLOAD, !!per->uploadfile); + my_setopt_long(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); + my_setopt_long(curl, CURLOPT_APPEND, config->ftp_append); + + my_setopt_long(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); + MY_SETOPT_STR(curl, CURLOPT_RANGE, config->range); + my_setopt_ptr(curl, CURLOPT_ERRORBUFFER, per->errorbuffer); + my_setopt_long(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms); + + result = setopt_post(config, curl); if(result) return result; + if(config->mime_options) + my_setopt_long(curl, CURLOPT_MIME_OPTIONS, config->mime_options); + my_setopt_long(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit); my_setopt_long(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); my_setopt_offt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, config->sendpersecond); @@ -949,8 +929,39 @@ CURLcode config2setopts(struct OperationConfig *config, else my_setopt_offt(curl, CURLOPT_RESUME_FROM_LARGE, 0); - MY_SETOPT_STR(curl, CURLOPT_KEYPASSWD, config->key_passwd); - MY_SETOPT_STR(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd); + if(config->path_as_is) + my_setopt_long(curl, CURLOPT_PATH_AS_IS, 1); + + if(config->no_body || config->remote_time) + my_setopt_long(curl, CURLOPT_FILETIME, 1); + + my_setopt_long(curl, CURLOPT_CRLF, config->crlf); + my_setopt_slist(curl, CURLOPT_QUOTE, config->quote); + my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote); + my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote); + + my_setopt_enum(curl, CURLOPT_TIMECONDITION, config->timecond); + my_setopt_offt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime); + MY_SETOPT_STR(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); + customrequest_helper(config->httpreq, config->customrequest); + + return result; +} + +static CURLcode protocol_setopts(struct OperationConfig *config, + struct per_transfer *per, + CURL *curl, + const char *use_proto) +{ + CURLcode result = CURLE_OK; +#ifndef DEBUGBUILD + (void)per; +#endif + result = http_setopts(config, curl, use_proto); + if(!result) + result = ftp_setopts(config, curl, use_proto); + if(result) + return result; result = ssh_setopts(config, curl, use_proto); if(setopt_bad(result)) @@ -971,23 +982,21 @@ CURLcode config2setopts(struct OperationConfig *config, #endif } - if(config->path_as_is) - my_setopt_long(curl, CURLOPT_PATH_AS_IS, 1); + if(feature_tls_srp) { + result = tls_srp_setopts(config, curl); + if(setopt_bad(result)) + return result; + } - if(config->no_body || config->remote_time) - /* no body or use remote time */ - my_setopt_long(curl, CURLOPT_FILETIME, 1); + return result; +} - my_setopt_long(curl, CURLOPT_CRLF, config->crlf); - my_setopt_slist(curl, CURLOPT_QUOTE, config->quote); - my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote); - my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote); +static CURLcode dns_and_network_setopts(struct OperationConfig *config, + struct per_transfer *per, + CURL *curl) +{ + CURLcode result = CURLE_OK; - my_setopt_enum(curl, CURLOPT_TIMECONDITION, config->timecond); - my_setopt_offt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime); - MY_SETOPT_STR(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); - customrequest_helper(config->httpreq, config->customrequest); - my_setopt_ptr(curl, CURLOPT_STDERR, tool_stderr); MY_SETOPT_STR(curl, CURLOPT_INTERFACE, config->iface); progressbarinit(&per->progressbar, config); MY_SETOPT_STR(curl, CURLOPT_DNS_SERVERS, config->dns_servers); @@ -1024,33 +1033,43 @@ CURLcode config2setopts(struct OperationConfig *config, if(config->tftp_blksize && proto_tftp) my_setopt_long(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize); + return result; +} + +static CURLcode mail_and_sasl_setopts(struct OperationConfig *config, + CURL *curl) +{ + CURLcode result = CURLE_OK; + MY_SETOPT_STR(curl, CURLOPT_MAIL_FROM, config->mail_from); my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); my_setopt_long(curl, CURLOPT_MAIL_RCPT_ALLOWFAILS, config->mail_rcpt_allowfails); + MY_SETOPT_STR(curl, CURLOPT_MAIL_AUTH, config->mail_auth); + MY_SETOPT_STR(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid); + my_setopt_long(curl, CURLOPT_SASL_IR, config->sasl_ir); + if(config->create_file_mode) my_setopt_long(curl, CURLOPT_NEW_FILE_PERMS, config->create_file_mode); - if(config->proto_present) - MY_SETOPT_STR(curl, CURLOPT_PROTOCOLS_STR, config->proto_str); - if(config->proto_redir_present) - MY_SETOPT_STR(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); + return result; +} +static CURLcode misc_setopts(struct OperationConfig *config, CURL *curl) +{ + CURLcode result = CURLE_OK; + + my_setopt_ptr(curl, CURLOPT_STDERR, tool_stderr); my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); - if(feature_tls_srp) { - result = tls_srp_setopts(config, curl); - if(setopt_bad(result)) - return result; - } - if(config->gssapi_delegation) my_setopt_long(curl, CURLOPT_GSSAPI_DELEGATION, config->gssapi_delegation); - MY_SETOPT_STR(curl, CURLOPT_MAIL_AUTH, config->mail_auth); - MY_SETOPT_STR(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid); - my_setopt_long(curl, CURLOPT_SASL_IR, config->sasl_ir); + if(config->proto_present) + MY_SETOPT_STR(curl, CURLOPT_PROTOCOLS_STR, config->proto_str); + if(config->proto_redir_present) + MY_SETOPT_STR(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); if(config->unix_socket_path) { if(config->abstract_unix_socket) @@ -1087,6 +1106,61 @@ CURLcode config2setopts(struct OperationConfig *config, } #endif } - my_setopt_long(curl, CURLOPT_UPLOAD_FLAGS, config->upload_flags); + if(!result) + my_setopt_long(curl, CURLOPT_UPLOAD_FLAGS, config->upload_flags); + + return result; +} + +CURLcode config2setopts(struct OperationConfig *config, + struct per_transfer *per, + CURL *curl, + CURLSH *share) +{ + const char *use_proto; + CURLcode result = url_proto_and_rewrite(&per->url, config, &use_proto); + + /* Avoid having this setopt added to the --libcurl source output. */ + if(!result) + result = curl_easy_setopt(curl, CURLOPT_SHARE, share); + if(result) + return result; + +#ifdef DEBUGBUILD + if(getenv("CURL_QUICK_EXIT")) +#endif + { + /* QUICK_EXIT allows for running threads to be detached and not + * joined. Preferably in non-debug runs. */ + result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); + if(result) + return result; + } + + gen_trace_setopts(config, curl); + + buffersize(config, curl); + + MY_SETOPT_STR(curl, CURLOPT_URL, per->url); + my_setopt_long(curl, CURLOPT_NOPROGRESS, + global->noprogress || global->silent); + /* call after the line above. It may override CURLOPT_NOPROGRESS */ + gen_cb_setopts(config, per, curl); + + result = proxy_setopts(config, curl); + if(setopt_bad(result) || config->synthetic_error) + return result; + + result = credentials_and_headers_setopts(config, curl); + if(!setopt_bad(result)) + result = transfer_setopts(config, per, curl); + if(!setopt_bad(result)) + result = protocol_setopts(config, per, curl, use_proto); + if(!setopt_bad(result)) + result = dns_and_network_setopts(config, per, curl); + if(!setopt_bad(result)) + result = mail_and_sasl_setopts(config, curl); + if(!setopt_bad(result)) + result = misc_setopts(config, curl); return result; } diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 71e2ffba04a4..51e22756aa4a 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -712,11 +712,3 @@ CURLcode tool_setopt_str(CURL *curl, struct OperationConfig *config, } #endif /* CURL_DISABLE_LIBCURL_OPTION */ - -/* return TRUE if the error code is "lethal" */ -bool setopt_bad(CURLcode result) -{ - return result && - (result != CURLE_NOT_BUILT_IN) && - (result != CURLE_UNKNOWN_OPTION); -} diff --git a/src/tool_setopt.h b/src/tool_setopt.h index 20419733ba29..a737fcddf54f 100644 --- a/src/tool_setopt.h +++ b/src/tool_setopt.h @@ -29,9 +29,6 @@ * Macros used in operate() */ -/* return TRUE if the error code is "lethal" */ -bool setopt_bad(CURLcode result); - #ifndef CURL_DISABLE_LIBCURL_OPTION /* Associate symbolic names with option values */ diff --git a/tests/data/data1402.c b/tests/data/data1402.c index 682345485d38..0490c96ea2cf 100644 --- a/tests/data/data1402.c +++ b/tests/data/data1402.c @@ -13,9 +13,9 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "foo=bar&baz=quux"); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)16); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); diff --git a/tests/data/data1404.c b/tests/data/data1404.c index df9364d10404..a09467fa1022 100644 --- a/tests/data/data1404.c +++ b/tests/data/data1404.c @@ -24,6 +24,7 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); mime1 = curl_mime_init(curl); part1 = curl_mime_addpart(mime1); curl_mime_data(part1, "value", CURL_ZERO_TERMINATED); @@ -44,7 +45,6 @@ int main(int argc, char *argv[]) mime2 = NULL; curl_mime_name(part1, "file"); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime1); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); diff --git a/tests/data/data1405.c b/tests/data/data1405.c index ec7a4a39c9a3..e79550806bf2 100644 --- a/tests/data/data1405.c +++ b/tests/data/data1405.c @@ -25,10 +25,10 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "ftp://%HOSTIP:%FTPPORT/%TESTNUMBER"); - curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L); curl_easy_setopt(curl, CURLOPT_QUOTE, slist1); curl_easy_setopt(curl, CURLOPT_POSTQUOTE, slist2); curl_easy_setopt(curl, CURLOPT_PREQUOTE, slist3); + curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); /* Here is a list of options the curl code used that cannot get generated diff --git a/tests/data/data1406.c b/tests/data/data1406.c index 30aff8d58875..4f9a3fccafd9 100644 --- a/tests/data/data1406.c +++ b/tests/data/data1406.c @@ -18,8 +18,8 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER"); - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "sender@example.com"); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, slist1); diff --git a/tests/data/data1407.c b/tests/data/data1407.c index 4f34db365527..b6f312741e18 100644 --- a/tests/data/data1407.c +++ b/tests/data/data1407.c @@ -13,8 +13,8 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "pop3://%HOSTIP:%POP3PORT/%TESTNUMBER"); - curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L); curl_easy_setopt(curl, CURLOPT_USERPWD, "user:secret"); + curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); /* Here is a list of options the curl code used that cannot get generated diff --git a/tests/data/data1465.c b/tests/data/data1465.c index dea3b61988c0..ac3c6db10c9f 100644 --- a/tests/data/data1465.c +++ b/tests/data/data1465.c @@ -13,9 +13,9 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L); curl_easy_setopt(curl, CURLOPT_URL, "http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "ab\201cd\000e\\\"\?\r\n\t\001fghi\x1ajklm\xfd"); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)24); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/%VERSION"); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); From bedeeaa4a7145cb6dabddf0e4377799f33d1f24b Mon Sep 17 00:00:00 2001 From: alhudz Date: Sun, 21 Jun 2026 17:59:09 +0530 Subject: [PATCH 0602/1062] smtp: reject CR and LF in the envelope address Verified in test 2110 Closes #22119 --- lib/smtp.c | 24 +++++++++++++++------ tests/data/Makefile.am | 2 +- tests/data/test2110 | 49 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 tests/data/test2110 diff --git a/lib/smtp.c b/lib/smtp.c index 2283f5e87d2d..e73e97255531 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -250,16 +250,26 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data, * calling function deems it to be) then the input will be returned in * the address part with the hostname being NULL. */ -static CURLcode smtp_parse_address(const char *fqma, char **address, - struct hostname *host, const char **suffix) +static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, + char **address, struct hostname *host, + const char **suffix) { CURLcode result = CURLE_OK; size_t length; char *addressend; + char *dup; + + /* A CR or LF in the address ends up verbatim in the MAIL FROM/RCPT TO + command line, so a crafted address could smuggle further SMTP commands + onto the wire. Reject it before the command is built. */ + if(strpbrk(fqma, "\r\n")) { + failf(data, "Refusing to send email address with a CR or LF"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } /* Duplicate the fully qualified email address so we can manipulate it, ensuring it does not contain the delimiters if specified */ - char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); + dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); if(!dup) return CURLE_OUT_OF_MEMORY; @@ -842,7 +852,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data, /* Parse the mailbox to verify into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(smtp->rcpt->data, + result = smtp_parse_address(data, smtp->rcpt->data, &address, &host, &suffix); if(result) return result; @@ -918,7 +928,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, /* Parse the FROM mailbox into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], + result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM], &address, &host, &suffix); if(result) goto out; @@ -960,7 +970,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, /* Parse the AUTH mailbox into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], + result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH], &address, &host, &suffix); if(result) goto out; @@ -1097,7 +1107,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data, /* Parse the recipient mailbox into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(smtp->rcpt->data, + result = smtp_parse_address(data, smtp->rcpt->data, &address, &host, &suffix); if(result) return result; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 3cc89e34f450..9311558e1348 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 test2113 \ +test2108 test2109 test2110 test2113 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2110 b/tests/data/test2110 new file mode 100644 index 000000000000..6b48608c8be8 --- /dev/null +++ b/tests/data/test2110 @@ -0,0 +1,49 @@ + + + + +SMTP + + + +# Server-side + + + +# Client-side + + +smtp + + +SMTP --mail-from with embedded CRLF is rejected + + +From: different +To: another + +body + +# read the sender from a config file so the raw CR LF reaches curl intact +# regardless of how the platform passes command line arguments + +mail-from = "sender@example.com\r\nDATA" + + +smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com -K %LOGDIR/test%TESTNUMBER.config -T %LOGDIR/test%TESTNUMBER.eml + + + +# Verify data after the test has been "shot" + +# 43 - CURLE_BAD_FUNCTION_ARGUMENT + +43 + +# the injected DATA command must never reach the server + +EHLO %TESTNUMBER +QUIT + + + From f02c53361e0a00775e3b250b3b60e3da77063ba2 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 29 Jun 2026 18:17:14 +0200 Subject: [PATCH 0603/1062] sasl: fix zero-length response encoding A sasl zero-length normal response is an empty string, unlike an initial response. Since the encoding of a zero-length initial response is protocol dependent, move its handling to the protocol-specific sendauth procedure. Similarly, do not check for an '=' server message, as this should normally never occur. Update the erroneous tests accordingly. Closes #22218 --- lib/curl_sasl.c | 6 ++---- lib/imap.c | 3 ++- lib/pop3.c | 3 ++- lib/smtp.c | 3 ++- tests/data/test838 | 4 ++-- tests/data/test884 | 4 ++-- tests/data/test943 | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 7e867d753fe7..2af6b97e9f44 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -229,7 +229,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { const char *serverdata = Curl_bufref_ptr(out); - if(!*serverdata || *serverdata == '=') + if(!*serverdata) Curl_bufref_set(out, NULL, 0, NULL); else { unsigned char *msg; @@ -252,9 +252,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) if(sasl->params->flags & SASL_FLAG_BASE64) { if(!Curl_bufref_ptr(msg)) /* Empty message. */ Curl_bufref_set(msg, "", 0, NULL); - else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ - Curl_bufref_set(msg, "=", 1, NULL); - else { + else if(Curl_bufref_len(msg)) { char *base64; size_t base64len; diff --git a/lib/imap.c b/lib/imap.c index 976c9f573e7c..fb23cfef41bf 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -641,7 +641,8 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data, return CURLE_FAILED_INIT; if(ir) { /* Send the AUTHENTICATE command with the initial response */ - result = imap_sendf(data, imapc, "AUTHENTICATE %s %s", mech, ir); + result = imap_sendf(data, imapc, "AUTHENTICATE %s %s", + mech, *ir ? ir : "="); } else { /* Send the AUTHENTICATE command */ diff --git a/lib/pop3.c b/lib/pop3.c index d609b4c2b31b..7557c7435056 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -620,7 +620,8 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data, if(ir) { /* AUTH ... */ /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir); + result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", + mech, *ir ? ir : "="); } else { /* Send the AUTH command */ diff --git a/lib/smtp.c b/lib/smtp.c index e73e97255531..f5d922ddb48c 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -746,7 +746,8 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data, if(ir) { /* AUTH ... */ /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir); + result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", + mech, *ir ? ir : "="); } else { /* Send the AUTH command */ diff --git a/tests/data/test838 b/tests/data/test838 index b28f7da3226e..f365ad678121 100644 --- a/tests/data/test838 +++ b/tests/data/test838 @@ -14,7 +14,7 @@ RFC4422 AUTH EXTERNAL REPLY AUTHENTICATE + -REPLY = A002 OK AUTHENTICATE completed +REPLY A002 OK AUTHENTICATE completed From: me@somewhere @@ -45,7 +45,7 @@ IMAP external authentication without credentials A001 CAPABILITY A002 AUTHENTICATE EXTERNAL -= + A003 SELECT %TESTNUMBER A004 FETCH 1 BODY[] A005 LOGOUT diff --git a/tests/data/test884 b/tests/data/test884 index 1a49a6a5a551..577c7376e63a 100644 --- a/tests/data/test884 +++ b/tests/data/test884 @@ -16,7 +16,7 @@ RFC5034 AUTH EXTERNAL REPLY AUTH + -REPLY = +OK Login successful +REPLY +OK Login successful From: me@somewhere @@ -47,7 +47,7 @@ POP3 external authentication without credentials CAPA AUTH EXTERNAL -= + RETR %TESTNUMBER QUIT diff --git a/tests/data/test943 b/tests/data/test943 index a9a28d6bd99b..102df73610de 100644 --- a/tests/data/test943 +++ b/tests/data/test943 @@ -15,7 +15,7 @@ RFC4954 AUTH EXTERNAL REPLY AUTH 334 EXTERNAL supported -REPLY = 235 Authenticated +REPLY 235 Authenticated @@ -40,7 +40,7 @@ mail body EHLO %TESTNUMBER AUTH EXTERNAL -= + MAIL FROM:%LTsender@example.com%GT RCPT TO:%LTrecipient@example.com%GT DATA From ddc76c24c539ac4632e663035c98a66ee8195cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Memduh=20=C3=87elik?= Date: Tue, 30 Jun 2026 07:12:11 +0300 Subject: [PATCH 0604/1062] tests: pin connection reuse rules for partial, host, and credentials Add classic tests 3223-3226 and extend tests/http/test_12_reuse.py so that connection reuse decisions are explicit: - partial/aborted HTTP/1.1 responses must not reuse the connection (premature multi_done closes non-multiplexed conns) - different target hostnames must not share a connection even when they resolve to the same address (url_match_destination) - HTTP Basic credentials are per-request (PROTOPT_CREDSPERREQUEST), so different -u values still reuse; assert that with num_connects - same host positive control reuses (num_connects 1 then 0) Closes #22224 --- tests/data/Makefile.am | 1 + tests/data/test3223 | 67 +++++++++++++++++++++++++++++++++++ tests/data/test3224 | 69 +++++++++++++++++++++++++++++++++++++ tests/data/test3225 | 69 +++++++++++++++++++++++++++++++++++++ tests/data/test3226 | 63 +++++++++++++++++++++++++++++++++ tests/http/test_12_reuse.py | 68 ++++++++++++++++++++++++++++++++++++ 6 files changed, 337 insertions(+) create mode 100644 tests/data/test3223 create mode 100644 tests/data/test3224 create mode 100644 tests/data/test3225 create mode 100644 tests/data/test3226 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 9311558e1348..a47680d6dd17 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -284,6 +284,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ +test3223 test3224 test3225 test3226 \ \ test3300 test3301 test3302 test3303 test3304 test3305 \ \ diff --git a/tests/data/test3223 b/tests/data/test3223 new file mode 100644 index 000000000000..5748d6d07aa3 --- /dev/null +++ b/tests/data/test3223 @@ -0,0 +1,67 @@ + + +# A partial/aborted HTTP/1.1 response marks the transfer premature. +# multi_conn_should_close() then closes non-multiplexed connections so they +# cannot be reused by a later transfer in the same process. + + +HTTP +HTTP GET +CURLE_PARTIAL_FILE +connection reuse +--next + + + + +HTTP/1.1 200 OK swsclose +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 100 +Content-Type: text/plain + +0123456789 + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +OK + + +connection-monitor + + + + +http + + +HTTP connection not reused after partial/aborted response + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -o %LOGDIR/3223-1 -w "connects=%{num_connects}/" --next http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001 -o %LOGDIR/3223-2 -w "connects=%{num_connects}/" + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +[DISCONNECT] +GET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +[DISCONNECT] + +# Second transfer must open a new connection (cannot reuse after premature). + +connects=1/connects=1/ + + + diff --git a/tests/data/test3224 b/tests/data/test3224 new file mode 100644 index 000000000000..4e62303aeb95 --- /dev/null +++ b/tests/data/test3224 @@ -0,0 +1,69 @@ + + +# HTTP has PROTOPT_CREDSPERREQUEST: Basic credentials are attached to the +# request, not to the connection for reuse matching. Different -u values +# therefore still reuse the idle connection (see also test1134). Protocols +# without CREDSPERREQUEST (and NTLM/Negotiate mid-handshake) refuse reuse. +# This test pins the HTTP Basic behavior with an explicit num_connects check. + + +HTTP +HTTP GET +HTTP Basic auth +connection reuse +--next + + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +A1 + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +B2 + + +connection-monitor + + + + +http + + +HTTP connection reused across different Basic auth credentials + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -u user1:password1 -o %LOGDIR/3224-1 -w "connects=%{num_connects}/" --next http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001 -u user2:password2 -o %LOGDIR/3224-2 -w "connects=%{num_connects}/" + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Basic %b64[user1:password1]b64% +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Basic %b64[user2:password2]b64% +User-Agent: curl/%VERSION +Accept: */* + +[DISCONNECT] + + +connects=1/connects=0/ + + + diff --git a/tests/data/test3225 b/tests/data/test3225 new file mode 100644 index 000000000000..a853f40fc86e --- /dev/null +++ b/tests/data/test3225 @@ -0,0 +1,69 @@ + + +# url_match_destination() / Curl_peer_same_destination() require matching +# origin hostname and port. Different Host names that resolve to the same +# address must not share a connection. Both connections may stay in the +# pool until process exit, so the server may see two DISCONNECT events only +# at the end; num_connects proves a new connection was opened. + + +HTTP +HTTP GET +--resolve +connection reuse +--next + + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +H1 + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +H2 + + +connection-monitor + + + + +http + + +HTTP connection not reused when target host changes + + +http://host-a.example.test:%HTTPPORT/%TESTNUMBER --resolve host-a.example.test:%HTTPPORT:%HOSTIP -o %LOGDIR/3225-1 -w "connects=%{num_connects}/" --next http://host-b.example.test:%HTTPPORT/%TESTNUMBER0001 --resolve host-b.example.test:%HTTPPORT:%HOSTIP -o %LOGDIR/3225-2 -w "connects=%{num_connects}/" + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: host-a.example.test:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0001 HTTP/1.1 +Host: host-b.example.test:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +[DISCONNECT] +[DISCONNECT] + +# Same IP via --resolve, different Host names => new connection each time. + +connects=1/connects=1/ + + + diff --git a/tests/data/test3226 b/tests/data/test3226 new file mode 100644 index 000000000000..92fa2421ef89 --- /dev/null +++ b/tests/data/test3226 @@ -0,0 +1,63 @@ + + +# Positive control: same host reuses the idle connection +# (second transfer reports num_connects=0). + + +HTTP +HTTP GET +connection reuse +--next + + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +OK + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 3 +Content-Type: text/plain + +OK + + +connection-monitor + + + + +http + + +HTTP connection is reused for same host + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -o %LOGDIR/3226-1 -w "connects=%{num_connects}/" --next http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001 -o %LOGDIR/3226-2 -w "connects=%{num_connects}/" + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +[DISCONNECT] + + +connects=1/connects=0/ + + + diff --git a/tests/http/test_12_reuse.py b/tests/http/test_12_reuse.py index 8ff062d6dc26..d4e2f70861a2 100644 --- a/tests/http/test_12_reuse.py +++ b/tests/http/test_12_reuse.py @@ -71,3 +71,71 @@ def test_12_02_h1_conn_timeout(self, env: Env, httpd, configures_httpd, nghttpx, r.check_response(count=count, http_status=200) # Connections time out on server before we send another request, assert r.total_connects == count + + # After a partial/aborted HTTP/1.1 response the connection must not be + # reused (multi_conn_should_close with premature on non-multiplexed conn). + @pytest.mark.parametrize("proto", ['http/1.1']) + def test_12_03_no_reuse_after_partial(self, env: Env, httpd, nghttpx, proto): + curl = CurlClient(env=env) + auth = env.authority_for(env.domain1, proto) + # Server promises more bytes than it sends, then resets. + partial = f'https://{auth}/curltest/tweak?id=0&chunks=1&chunk_size=100&body_error=reset' + ok = f'https://{auth}/data.json' + r = curl.http_download(urls=[partial, ok], alpn_proto=proto, extra_args=[ + '--retry', '0', + ]) + # First transfer fails (partial/reset); second succeeds on a new connection. + assert len(r.stats) == 2, r.dump_logs() + assert r.stats[0]['exitcode'] != 0, r.dump_logs() + assert r.stats[1].get('http_code') == 200, r.dump_logs() + # Both transfers must open their own connection. + assert r.stats[0]['num_connects'] == 1, r.dump_logs() + assert r.stats[1]['num_connects'] == 1, r.dump_logs() + assert r.total_connects == 2, r.dump_logs() + + # HTTP uses PROTOPT_CREDSPERREQUEST: Basic credentials are per request, so + # different -u values still reuse the idle connection (unlike NTLM/Negotiate + # which bind credentials onto the connection). + @pytest.mark.parametrize("proto", ['http/1.1']) + def test_12_04_reuse_different_basic_credentials(self, env: Env, httpd, nghttpx, proto): + curl = CurlClient(env=env) + url1 = f'https://{env.authority_for(env.domain1, proto)}/data.json?cred=1' + url2 = f'https://{env.authority_for(env.domain1, proto)}/data.json?cred=2' + r = curl.http_download(urls=[url1, url2], alpn_proto=proto, url_options={ + url1: ['-u', 'user1:password1'], + url2: ['-u', 'user2:password2'], + }) + assert len(r.stats) == 2, r.dump_logs() + assert r.stats[0].get('http_code') == 200, r.dump_logs() + assert r.stats[1].get('http_code') == 200, r.dump_logs() + assert r.stats[0]['num_connects'] == 1, r.dump_logs() + assert r.stats[1]['num_connects'] == 0, r.dump_logs() + assert r.total_connects == 1, r.dump_logs() + + # Different target hostnames must not reuse even if they resolve to the + # same address (Curl_peer_same_destination matches hostname + port). + @pytest.mark.parametrize("proto", ['http/1.1']) + def test_12_05_no_reuse_different_host(self, env: Env, httpd, nghttpx, proto): + curl = CurlClient(env=env) + # domain1 and domain2 are both served by the same httpd instance. + url1 = f'https://{env.authority_for(env.domain1, proto)}/data.json' + url2 = f'https://{env.authority_for(env.domain2, proto)}/data.json' + r = curl.http_download(urls=[url1, url2], alpn_proto=proto) + r.check_response(count=2, http_status=200) + assert r.stats[0]['num_connects'] == 1, r.dump_logs() + assert r.stats[1]['num_connects'] == 1, r.dump_logs() + assert r.total_connects == 2, r.dump_logs() + + # Positive control: same host reuses one connection. + @pytest.mark.parametrize("proto", ['http/1.1']) + def test_12_06_reuse_same_host(self, env: Env, httpd, nghttpx, proto): + curl = CurlClient(env=env) + url1 = f'https://{env.authority_for(env.domain1, proto)}/data.json?a=1' + url2 = f'https://{env.authority_for(env.domain1, proto)}/data.json?a=2' + r = curl.http_download(urls=[url1, url2], alpn_proto=proto) + assert len(r.stats) == 2, r.dump_logs() + assert r.stats[0].get('http_code') == 200, r.dump_logs() + assert r.stats[1].get('http_code') == 200, r.dump_logs() + assert r.stats[0]['num_connects'] == 1, r.dump_logs() + assert r.stats[1]['num_connects'] == 0, r.dump_logs() + assert r.total_connects == 1, r.dump_logs() From 5b10939ef77a3ab7941eefb4a1e2d03cf0d74121 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 29 Jun 2026 16:12:10 +0200 Subject: [PATCH 0605/1062] mbedtls: replace `memset()` with `psa_hash_operation_init()` To initialize hash contexts. Ref: https://arm-software.github.io/psa-api/crypto/1.1/api/ops/hashes.html#c.psa_hash_operation_init Follow-up to 3a305831d1a9d10b2bfd4fa3939ed41275fee7f7 #19077 Closes #22220 --- lib/md5.c | 5 +++-- lib/sha256.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/md5.c b/lib/md5.c index 1f1b4f8ad610..9f995c7393d0 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -128,8 +128,9 @@ typedef psa_hash_operation_t my_md5_ctx; static CURLcode my_md5_init(void *ctx) { - memset(ctx, 0, sizeof(my_md5_ctx)); - if(psa_hash_setup(ctx, PSA_ALG_MD5) != PSA_SUCCESS) + psa_hash_operation_t *pctx = (psa_hash_operation_t *)ctx; + *pctx = psa_hash_operation_init(); + if(psa_hash_setup(pctx, PSA_ALG_MD5) != PSA_SUCCESS) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } diff --git a/lib/sha256.c b/lib/sha256.c index 38c9b9bc1b3b..670532a32558 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -148,8 +148,9 @@ typedef psa_hash_operation_t my_sha256_ctx; static CURLcode my_sha256_init(void *ctx) { - memset(ctx, 0, sizeof(my_sha256_ctx)); - if(psa_hash_setup(ctx, PSA_ALG_SHA_256) != PSA_SUCCESS) + psa_hash_operation_t *pctx = (psa_hash_operation_t *)ctx; + *pctx = psa_hash_operation_init(); + if(psa_hash_setup(pctx, PSA_ALG_SHA_256) != PSA_SUCCESS) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } From 6fbb65126dde3cba931ba393240b2294ea78f725 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 30 Jun 2026 00:06:41 +0200 Subject: [PATCH 0606/1062] openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` To match rest of code, use the modern variant and avoid an unnecessary internal reset/cleanup. Closes #22222 --- lib/vtls/openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 3962df160306..ed401fe0d863 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5451,7 +5451,7 @@ static CURLcode ossl_sha256sum(const unsigned char *input, mdctx = EVP_MD_CTX_new(); if(!mdctx) return CURLE_OUT_OF_MEMORY; - if(!EVP_DigestInit(mdctx, EVP_sha256())) { + if(!EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL)) { result = CURLE_FAILED_INIT; goto out; } From c0cae1eba4b2c08fad635b7066b9bacb7a030242 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 30 Jun 2026 00:22:02 +0200 Subject: [PATCH 0607/1062] formdata: replace stray camelcase local function names Replace with names using the style in the rest of codebase. Also move the object to left-hand side and action to the right-hand side. Closes #22223 --- lib/formdata.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/formdata.c b/lib/formdata.c index 823f4bf590ad..0c81d8c781c9 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -46,7 +46,7 @@ struct Curl_easy; /*************************************************************************** * - * AddHttpPost() + * httppost_add() * * Adds an HttpPost structure to the list, if parent_post is given becomes * a subpost of parent_post instead of a direct list element. @@ -54,10 +54,10 @@ struct Curl_easy; * Returns newly allocated HttpPost on success and NULL if malloc failed. * ***************************************************************************/ -static struct curl_httppost *AddHttpPost(struct FormInfo *src, - struct curl_httppost *parent_post, - struct curl_httppost **httppost, - struct curl_httppost **last_post) +static struct curl_httppost *httppost_add(struct FormInfo *src, + struct curl_httppost *parent_post, + struct curl_httppost **httppost, + struct curl_httppost **last_post) { struct curl_httppost *post; size_t namelength = src->namelength; @@ -103,7 +103,7 @@ static struct curl_httppost *AddHttpPost(struct FormInfo *src, } /* Allocate and initialize a new FormInfo structure. */ -static struct FormInfo *NewFormInfo(void) +static struct FormInfo *forminfo_new(void) { struct FormInfo *form_info = curlx_calloc(1, sizeof(struct FormInfo)); @@ -118,7 +118,7 @@ static struct FormInfo *NewFormInfo(void) } /* Replace the target field data by a dynamic copy of it. */ -static CURLcode FormInfoCopyField(struct bufref *field, size_t len) +static CURLcode forminfo_copyfield(struct bufref *field, size_t len) { const char *value = Curl_bufref_ptr(field); CURLcode result = CURLE_OK; @@ -134,12 +134,12 @@ static CURLcode FormInfoCopyField(struct bufref *field, size_t len) /*************************************************************************** * - * AddFormInfo() + * forminfo_add() * * Adds a FormInfo structure to the list presented by parent. * ***************************************************************************/ -static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent) +static void forminfo_add(struct FormInfo *form_info, struct FormInfo *parent) { form_info->flags |= HTTPPOST_FILENAME; @@ -164,7 +164,7 @@ static void free_formlist(struct FormInfo *ptr) /*************************************************************************** * - * FormAdd() + * formadd_check() * * Stores a formpost parameter and builds the appropriate linked list. * @@ -212,9 +212,9 @@ static void free_formlist(struct FormInfo *ptr) * ***************************************************************************/ -static CURLFORMcode FormAddCheck(struct FormInfo *first_form, - struct curl_httppost **httppost, - struct curl_httppost **last_post) +static CURLFORMcode formadd_check(struct FormInfo *first_form, + struct curl_httppost **httppost, + struct curl_httppost **last_post) { const char *prevtype = NULL; struct FormInfo *form = NULL; @@ -263,16 +263,16 @@ static CURLFORMcode FormAddCheck(struct FormInfo *first_form, if(!(form->flags & HTTPPOST_PTRNAME)) { /* Note that there is small risk that form->name is NULL here if the app passed in a bad combo, so we check for that. */ - if(FormInfoCopyField(&form->name, form->namelength)) + if(forminfo_copyfield(&form->name, form->namelength)) return CURL_FORMADD_MEMORY; } if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | HTTPPOST_CALLBACK))) { - if(FormInfoCopyField(&form->value, (size_t)form->contentslength)) + if(forminfo_copyfield(&form->value, (size_t)form->contentslength)) return CURL_FORMADD_MEMORY; } - post = AddHttpPost(form, post, httppost, last_post); + post = httppost_add(form, post, httppost, last_post); if(!post) return CURL_FORMADD_MEMORY; @@ -297,7 +297,7 @@ static void free_chain(struct curl_httppost *c) } } -static CURLFORMcode FormAdd(struct curl_httppost **httppost, +static CURLFORMcode formadd(struct curl_httppost **httppost, struct curl_httppost **last_post, va_list params) { struct FormInfo *first_form, *curr, *form = NULL; @@ -318,7 +318,7 @@ static CURLFORMcode FormAdd(struct curl_httppost **httppost, /* * We need to allocate the first struct to fill in. */ - first_form = NewFormInfo(); + first_form = forminfo_new(); if(!first_form) return CURL_FORMADD_MEMORY; @@ -436,14 +436,14 @@ static CURLFORMcode FormAdd(struct curl_httppost **httppost, if(Curl_bufref_ptr(&curr->value)) { if(curr->flags & HTTPPOST_FILENAME) { if(avalue) { - form = NewFormInfo(); + form = forminfo_new(); if(!form || Curl_bufref_memdup0(&form->value, avalue, strlen(avalue))) { curlx_free(form); retval = CURL_FORMADD_MEMORY; } else { - AddFormInfo(form, curr); + forminfo_add(form, curr); curr = form; form = NULL; } @@ -512,14 +512,14 @@ static CURLFORMcode FormAdd(struct curl_httppost **httppost, if(Curl_bufref_ptr(&curr->contenttype)) { if(curr->flags & HTTPPOST_FILENAME) { if(avalue) { - form = NewFormInfo(); + form = forminfo_new(); if(!form || Curl_bufref_memdup0(&form->contenttype, avalue, strlen(avalue))) { curlx_free(form); retval = CURL_FORMADD_MEMORY; } else { - AddFormInfo(form, curr); + forminfo_add(form, curr); curr = form; form = NULL; } @@ -566,7 +566,7 @@ static CURLFORMcode FormAdd(struct curl_httppost **httppost, } if(!retval) - retval = FormAddCheck(first_form, &newchain, &lastnode); + retval = formadd_check(first_form, &newchain, &lastnode); if(retval) /* On error, free allocated fields for all nodes of the FormInfo linked @@ -611,7 +611,7 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost, va_list arg; CURLFORMcode form; va_start(arg, last_post); - form = FormAdd(httppost, last_post, arg); + form = formadd(httppost, last_post, arg); va_end(arg); return form; } From acebf346fe401f64bb3e393058c3004fdc9d94c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Tue, 30 Jun 2026 21:07:17 +0100 Subject: [PATCH 0608/1062] schannel: shut off experimental TLS 1.3 support for Win 10 - Change minimum Windows version for TLS 1.3 support to Windows Server 2022. Prior to this change Windows Server 2022 was already used as the minimum version in some but not all of the checks for TLS 1.3 support. Ref: https://github.com/curl/curl/pull/21719#issuecomment-4520234306 Ref: https://github.com/curl/curl/issues/21702 Signed-off-by: Emmanuel Ugwu Closes https://github.com/curl/curl/pull/22231 --- lib/vtls/schannel.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 72aedabc7c62..d322c755e88d 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -593,11 +593,10 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; - /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as - long as the user did not set a legacy algorithm list - (CURLOPT_SSL_CIPHER_LIST). */ + /* We support TLS 1.3 starting in Windows Server 2022 or later + * (OS build 20348) */ if(!conn_config->cipher_list && - curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { SCH_CREDENTIALS credentials = { 0 }; From 8b6b0d9ee78b57b771ed3fc81e40e5a0a943f6c5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 30 Jun 2026 23:58:38 +0200 Subject: [PATCH 0609/1062] tool_cb_hdr: de-duplicate filename setter Closes #22232 --- src/tool_cb_hdr.c | 69 +++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 9b3a33aefbc2..918fe1907e12 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -282,6 +282,32 @@ static size_t save_etag(const char *etag_h, const char *endp, return 0; /* ok */ } +static bool set_filename(struct OutStruct *outs, + struct per_transfer *per, char *filename) +{ + if(outs->stream) { + /* indication of problem, get out! */ + curlx_free(filename); + return FALSE; + } + if(outs->alloc_filename) + curlx_safefree(outs->filename); + + if(per->config->output_dir) { + char *f = curl_maprintf("%s/%s", per->config->output_dir, filename); + curlx_free(filename); + if(!f) + return FALSE; + outs->filename = curlx_strdup(f); + curl_free(f); + if(!outs->filename) + return FALSE; + } + else + outs->filename = filename; + return TRUE; +} + /* * This function sets the filename where output shall be written when curl * options --remote-name (-O) and --remote-header-name (-J) have been @@ -303,27 +329,8 @@ static size_t content_disposition(const char *str, const char *end, if(p < end) { /* as a precaution */ char *filename = parse_filename(p, cb - (p - str), 0); if(filename) { - if(outs->stream) { - /* indication of problem, get out! */ - curlx_free(filename); + if(!set_filename(outs, per, filename)) return CURL_WRITEFUNC_ERROR; - } - if(outs->alloc_filename) - curlx_safefree(outs->filename); - - if(per->config->output_dir) { - char *f = curl_maprintf("%s/%s", per->config->output_dir, - filename); - curlx_free(filename); - if(!f) - return CURL_WRITEFUNC_ERROR; - outs->filename = curlx_strdup(f); - curl_free(f); - if(!outs->filename) - return CURL_WRITEFUNC_ERROR; - } - else - outs->filename = filename; outs->alloc_filename = TRUE; outs->is_cd_filename = TRUE; /* set to avoid clobbering existing files by default */ @@ -358,28 +365,8 @@ static size_t content_disposition(const char *str, const char *end, len = cb - (size_t)(p - str); filename = parse_filename(p, len, ';'); if(filename) { - if(outs->stream) { - /* indication of problem, get out! */ - curlx_free(filename); + if(!set_filename(outs, per, filename)) return CURL_WRITEFUNC_ERROR; - } - if(outs->alloc_filename) - curlx_safefree(outs->filename); - - if(per->config->output_dir) { - char *f = curl_maprintf("%s/%s", per->config->output_dir, - filename); - curlx_free(filename); - if(!f) - return CURL_WRITEFUNC_ERROR; - outs->filename = curlx_strdup(f); - curl_free(f); - if(!outs->filename) - return CURL_WRITEFUNC_ERROR; - } - else - outs->filename = filename; - outs->is_cd_filename = TRUE; outs->regular_file = TRUE; outs->fopened = FALSE; From adf843fbbd1161ca65ea7faa8d589a19a7d7e707 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 1 Jul 2026 10:38:29 +0200 Subject: [PATCH 0610/1062] GHA/curl-for-win: upload snapshot Windows curl tool binary These are generated for every PR and master pushes. Retain for 3 days. Size is 1.8MB zipped per artifact. (Takes 1 second extra job time.) Ref: #22162 Ref: https://github.com/curl/curl-for-win/commit/1b0e3569b393c31572a5895ed65ae6f60a8cba55?w=1 Closes #22234 --- .github/workflows/curl-for-win.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index f2048052c7e7..68efd53a385f 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -148,6 +148,9 @@ jobs: name: 'Windows llvm (x64)' runs-on: ubuntu-26.04 timeout-minutes: 10 + env: + CW_PKG_NODELETE: '1' + CW_PKG_FLATTEN: '1' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -171,6 +174,12 @@ jobs: "${OCI_IMAGE_DEBIAN}" \ sh -c ./_ci-linux-debian.sh + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: 'curl-windows-snapshot-tool' + retention-days: 3 + path: curl-*-*-*/curl* + win-gcc-zlibold-x64: name: 'Windows gcc zlib-classic (x64)' runs-on: ubuntu-26.04 From 5bf1ce64f5349e1c8ba309da04a742ec9d7d973d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Jul 2026 09:50:34 +0200 Subject: [PATCH 0611/1062] curl_ws_meta.md: polish and better vocabulary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Hendrik Hübner Closes #22233 --- docs/libcurl/curl_ws_meta.md | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/libcurl/curl_ws_meta.md b/docs/libcurl/curl_ws_meta.md index 978520afd8d0..7fb2551a5ed3 100644 --- a/docs/libcurl/curl_ws_meta.md +++ b/docs/libcurl/curl_ws_meta.md @@ -29,9 +29,9 @@ const struct curl_ws_frame *curl_ws_meta(CURL *curl); # DESCRIPTION -When the write callback (CURLOPT_WRITEFUNCTION(3)) is invoked on -received WebSocket traffic, curl_ws_meta(3) can be called from within -the callback to provide additional information about the current frame. +When the write callback (CURLOPT_WRITEFUNCTION(3)) is invoked on received +WebSocket traffic, curl_ws_meta(3) can be called from within the callback to +provide additional information about the current frame. This function only works from within the callback, and only when receiving WebSocket data. @@ -41,6 +41,14 @@ what transfer the question is about, but as there is no such pointer provided to the callback by libcurl itself, applications that want to use curl_ws_meta(3) need to pass it on to the callback on its own. +WebSocket messages are split into *frames*. A WebSocket message can be made up +of an arbitrary number of frames. Each WebSocket frame payload can be up to +2^63-1 bytes. When libcurl delivers WebSocket data, it splits each frame into +*chunks*; each chunk is therefore part of a frame, or at most a full frame. + +Each callback delivers data for a single chunk that is then part of a single +frame. + # struct curl_ws_frame ~~~c @@ -64,13 +72,15 @@ See the list below. ## `offset` -When this chunk is a continuation of frame data already delivered, this is -the offset into the final frame data where this piece belongs to. +When this chunk is a continuation of frame data already delivered in a +previous callback, this is the offset into the complete frame payload where +this chunk's data belongs. ## `bytesleft` -If this is not a complete fragment, the *bytesleft* field informs about how -many additional bytes are expected to arrive before this fragment is complete. +When this is not a complete frame nor the last chunk for a frame, the +*bytesleft* field informs about how many additional bytes are expected to +arrive before this frame is complete. ## `len` @@ -118,12 +128,12 @@ libcurl does not verify that the payload is valid UTF-8. Can only occur in conjunction with CURLWS_TEXT or CURLWS_BINARY. -This is not the final fragment of the message, it implies that there is -another fragment coming as part of the same message. The application must -reassemble the fragments to receive the complete message. +This is not the final *frame* of the message, it implies that there is another +*frame* coming as part of the same message. The application must reassemble +the frames to receive the complete message. -Only a single fragmented message can be transmitted at a time, but it may -be interrupted by CURLWS_CLOSE, CURLWS_PING or CURLWS_PONG frames. +Only a single multi-frame message can be transmitted at a time, but it may be +interrupted by CURLWS_CLOSE, CURLWS_PING or CURLWS_PONG frames. # %PROTOCOLS% From a55dfa5e3564c16d26ea63092bddefbb28777815 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 1 Jul 2026 17:14:12 +0200 Subject: [PATCH 0612/1062] GHA/windows: exclude `diffutils` for 32-bit builds It's not longer offered by MSYS2. Bug: https://github.com/curl/curl/pull/22239#issuecomment-4856772868 Ref: https://github.com/msys2/MINGW-packages/commit/b13c6d3af1dde801a7d2e16e6daa7e4b5a21cc36 Ref: c4e776cafa22533fe8a6113a39f6a9f624e8c467 #17103 Closes #22242 --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4bf2ff5c4ac3..0e593d043bd6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -344,7 +344,7 @@ jobs: install: >- mingw-w64-${{ matrix.env }}-cc mingw-w64-${{ matrix.env }}-${{ matrix.build }} ${{ matrix.build == 'autotools' && 'make' || '' }} - mingw-w64-${{ matrix.env }}-diffutils + ${{ matrix.sys != 'mingw32' && format('mingw-w64-{0}-diffutils', matrix.env) || '' }} mingw-w64-${{ matrix.env }}-libpsl ${{ matrix.install }} From 0e7030f0e17f6d593e3cf6ebb44022cdd1931ff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:56:42 +0000 Subject: [PATCH 0613/1062] GHA: bump GitHub Actions - updates `actions/cache` from 5.0.5 to 6.1.0 - updates `actions/checkout` from 6.0.2 to 7.0.0 - updates `github/codeql-action/analyze` from 4.36.0 to 4.36.2 - updates `github/codeql-action/init` from 4.36.0 to 4.36.2 - updates `msys2/setup-msys2` from 2.31.1 to 2.32.0 Closes #22239 --- .github/workflows/checkdocs.yml | 6 +-- .github/workflows/checksrc.yml | 12 ++--- .github/workflows/checkurls.yml | 2 +- .github/workflows/codeql.yml | 12 ++--- .github/workflows/configure-vs-cmake.yml | 6 +-- .github/workflows/curl-for-win.yml | 12 ++--- .github/workflows/distcheck.yml | 10 ++-- .github/workflows/http3-linux.yml | 60 ++++++++++++------------ .github/workflows/linux-old.yml | 2 +- .github/workflows/linux.yml | 30 ++++++------ .github/workflows/macos.yml | 8 ++-- .github/workflows/non-native.yml | 12 ++--- .github/workflows/windows.yml | 30 ++++++------ 13 files changed, 101 insertions(+), 101 deletions(-) diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 446aa4668f03..1b1c01915d7c 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -43,7 +43,7 @@ jobs: name: 'proselint' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: name: 'pyspelling' runs-on: ubuntu-26.04 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -124,7 +124,7 @@ jobs: name: 'synopsis, man-examples' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 405e66af457b..213aa94c2af7 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -37,7 +37,7 @@ jobs: name: 'checksrc' runs-on: ubuntu-slim steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -48,7 +48,7 @@ jobs: name: 'spellcheck, linters, REUSE' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -97,7 +97,7 @@ jobs: name: 'pytype' runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -128,7 +128,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install pmccabe - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -151,7 +151,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install libxml2-utils - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -167,7 +167,7 @@ jobs: timeout-minutes: 2 run: /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/checkurls.yml b/.github/workflows/checkurls.yml index da5a99b861a4..466d645a7fee 100644 --- a/.github/workflows/checkurls.yml +++ b/.github/workflows/checkurls.yml @@ -30,7 +30,7 @@ jobs: name: 'linkcheck' runs-on: ubuntu-slim steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ea701495384c..c1fb8f6b92fc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -44,18 +44,18 @@ jobs: permissions: security-events: write # To create/update security events steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -82,7 +82,7 @@ jobs: libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev libwolfssl-dev /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -92,7 +92,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: cpp build-mode: manual @@ -139,4 +139,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 2c2042381001..940456e7c9cf 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -44,7 +44,7 @@ jobs: name: 'Linux' runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -102,7 +102,7 @@ jobs: - name: 'toolchain versions' run: echo '::group::brew packages installed'; ls -l /opt/homebrew/opt; echo '::endgroup::' - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -154,7 +154,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 68efd53a385f..4678b68d201b 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 5 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' @@ -100,7 +100,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' @@ -131,7 +131,7 @@ jobs: env: CW_JOBS: '4' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' @@ -152,7 +152,7 @@ jobs: CW_PKG_NODELETE: '1' CW_PKG_FLATTEN: '1' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' @@ -185,7 +185,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false path: 'curl' diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index e3d37902d702..7235b0a0b39c 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -214,7 +214,7 @@ jobs: timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -230,7 +230,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -268,7 +268,7 @@ jobs: matrix: image: [ubuntu-26.04-arm, macos-latest, windows-2022] steps: - - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 if: ${{ contains(matrix.image, 'windows') }} with: msystem: mingw64 @@ -307,7 +307,7 @@ jobs: printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-macos-universal/CMake.app/Contents/bin/cmake > ~/old-cmake-path.txt fi - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index c6cafaafcc39..03bf8aba984e 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -72,7 +72,7 @@ jobs: steps: - name: 'cache awslc' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-awslc env: cache-name: cache-awslc @@ -81,7 +81,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'cache boringssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-boringssl env: cache-name: cache-boringssl @@ -90,7 +90,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - name: 'cache nettle' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nettle env: cache-name: cache-nettle @@ -99,7 +99,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NETTLE_VERSION }} - name: 'cache gnutls' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-gnutls env: cache-name: cache-gnutls @@ -108,7 +108,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} - name: 'cache libressl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-libressl env: cache-name: cache-libressl @@ -117,7 +117,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'cache openssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openssl-http3-no-deprecated env: cache-name: cache-openssl-http3-no-deprecated @@ -126,7 +126,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - name: 'cache openssl-prev' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openssl-prev-http3 env: cache-name: cache-openssl-prev-http3 @@ -135,7 +135,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - name: 'cache wolfssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -144,7 +144,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'cache nghttp3' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -153,7 +153,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP3_VERSION }} - name: 'cache ngtcp2' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -163,7 +163,7 @@ jobs: ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" - name: 'cache ngtcp2 boringssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -172,7 +172,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} - name: 'cache ngtcp2 openssl-prev' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2-openssl-prev env: cache-name: cache-ngtcp2-openssl-prev @@ -181,7 +181,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - name: 'cache nghttp2' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -191,7 +191,7 @@ jobs: ${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }}" - name: 'cache h2o' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-h2o env: cache-name: cache-h2o @@ -594,7 +594,7 @@ jobs: - name: 'cache awslc' if: ${{ contains(matrix.build.name, 'awslc') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-awslc env: cache-name: cache-awslc @@ -605,7 +605,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-boringssl env: cache-name: cache-boringssl @@ -616,7 +616,7 @@ jobs: - name: 'cache nettle' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nettle env: cache-name: cache-nettle @@ -627,7 +627,7 @@ jobs: - name: 'cache gnutls' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-gnutls env: cache-name: cache-gnutls @@ -638,7 +638,7 @@ jobs: - name: 'cache libressl' if: ${{ contains(matrix.build.name, 'libressl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-libressl env: cache-name: cache-libressl @@ -648,7 +648,7 @@ jobs: fail-on-cache-miss: true - name: 'cache openssl' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openssl-http3-no-deprecated env: cache-name: cache-openssl-http3-no-deprecated @@ -659,7 +659,7 @@ jobs: - name: 'cache openssl-prev' if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openssl-prev-http3 env: cache-name: cache-openssl-prev-http3 @@ -670,7 +670,7 @@ jobs: - name: 'cache wolfssl' if: ${{ contains(matrix.build.name, 'wolfssl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -680,7 +680,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp3' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -690,7 +690,7 @@ jobs: fail-on-cache-miss: true - name: 'cache ngtcp2' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -702,7 +702,7 @@ jobs: - name: 'cache ngtcp2 boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -713,7 +713,7 @@ jobs: - name: 'cache ngtcp2 openssl-prev' if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ngtcp2-openssl-prev env: cache-name: cache-ngtcp2-openssl-prev @@ -723,7 +723,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp2' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -733,7 +733,7 @@ jobs: fail-on-cache-miss: true - name: 'cache h2o' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-h2o env: cache-name: cache-h2o @@ -744,7 +744,7 @@ jobs: - name: 'cache quiche' if: ${{ contains(matrix.build.name, 'quiche') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-quiche env: cache-name: cache-quiche @@ -770,7 +770,7 @@ jobs: # lib dir # /home/runner/quiche/boringssl/lib - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 46fa9acffde5..2f4d8ee81e21 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -90,7 +90,7 @@ jobs: sha256sum "${fn}".tar*.gz | tee /dev/stderr | grep -qwF -- "${CMAKE_SHA256}" && tar -xf "${fn}".tar*.gz && rm -f "${fn}".tar*.gz mv "cmake-${CMAKE_VERSION}-Linux-x86_64" cmake - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index eab56c180c09..70fc045fbda9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -517,7 +517,7 @@ jobs: - name: 'cache libressl (c-arm)' if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-libressl-c-arm env: cache-name: cache-libressl-c-arm @@ -538,7 +538,7 @@ jobs: - name: 'cache libressl (filc)' if: ${{ contains(matrix.build.install_steps, 'libressl-filc') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-libressl-filc env: cache-name: cache-libressl-filc @@ -560,7 +560,7 @@ jobs: - name: 'cache nghttp2 (filc)' if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-nghttp2-filc env: cache-name: cache-nghttp2-filc @@ -583,7 +583,7 @@ jobs: - name: 'cache wolfssl (all-arm)' if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-wolfssl-all-arm env: cache-name: cache-wolfssl-all-arm @@ -606,7 +606,7 @@ jobs: - name: 'cache wolfssl (opensslextra-intel)' # does support `OPENSSL_COEXIST` if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-wolfssl-opensslextra-intel env: cache-name: cache-wolfssl-opensslextra-intel @@ -629,7 +629,7 @@ jobs: - name: 'cache wolfssl (opensslextra-arm)' # does support `OPENSSL_COEXIST` if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-wolfssl-opensslextra-arm env: cache-name: cache-wolfssl-opensslextra-arm @@ -652,7 +652,7 @@ jobs: - name: 'cache mbedtls (latest-intel)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-mbedtls-latest-intel env: cache-name: cache-mbedtls-latest-intel @@ -676,7 +676,7 @@ jobs: - name: 'cache mbedtls (latest-arm)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-mbedtls-latest-arm env: cache-name: cache-mbedtls-latest-arm @@ -700,7 +700,7 @@ jobs: - name: 'cache mbedtls (prev)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-mbedtls-prev env: cache-name: cache-mbedtls-prev @@ -724,7 +724,7 @@ jobs: - name: 'cache openldap (static)' if: ${{ contains(matrix.build.install_steps, 'openldap-static') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openldap-static env: cache-name: cache-openldap-static @@ -745,7 +745,7 @@ jobs: - name: 'cache openssl (thread sanitizer)' if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-openssl-tsan env: cache-name: cache-openssl-tsan @@ -764,7 +764,7 @@ jobs: - name: 'cache awslc' if: ${{ contains(matrix.build.install_steps, 'awslc') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-awslc env: cache-name: cache-awslc @@ -785,7 +785,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.install_steps, 'boringssl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-boringssl env: cache-name: cache-boringssl @@ -807,7 +807,7 @@ jobs: - name: 'cache rustls' if: ${{ contains(matrix.build.install_steps, 'rustls') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-rustls env: cache-name: cache-rustls @@ -842,7 +842,7 @@ jobs: source /opt/intel/oneapi/setvars.sh printenv >> "$GITHUB_ENV" - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 685a533c507f..b1a457dcdfbd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -114,7 +114,7 @@ jobs: - name: 'cache libressl' if: ${{ contains(matrix.build.install_steps, 'libressl') }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-libressl env: cache-name: cache-libressl @@ -139,7 +139,7 @@ jobs: cmake --build . cmake --install . --verbose - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -438,7 +438,7 @@ jobs: echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages installed'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -697,7 +697,7 @@ jobs: echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 621605a45ebd..102cf87366e0 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -112,7 +112,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -250,7 +250,7 @@ jobs: fail-fast: false steps: - name: 'cache compiler' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-compiler with: path: ~/opt/amiga @@ -268,7 +268,7 @@ jobs: --location --proto-redir =https "https://github.com/jens-maus/amissl/releases/download/${AMISSL_VERSION}/AmiSSL-${AMISSL_VERSION}-SDK.lha" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${AMISSL_SHA256}" && 7z x -bd -y pkg.bin && rm -f pkg.bin - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -371,7 +371,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -472,7 +472,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 install libfl2 - name: 'cache compiler (djgpp)' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-compiler with: path: ~/djgpp @@ -493,7 +493,7 @@ jobs: https://www.delorie.com/pub/djgpp/current/v2tk/zlb13b.zip --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF f3d2fa8129e7591c7e79074306d8ab91a70ec172cc01baedeae74992285dd3a3 && unzip -q pkg.bin && rm -f pkg.bin - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0e593d043bd6..89753963c094 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -51,7 +51,7 @@ jobs: image: [windows-11-arm, windows-2022] # Cannot share cache between arm and intel: https://github.com/actions/cache/issues/1622 steps: - name: 'cache test prereqs (stunnel)' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-stunnel with: path: C:\my-stunnel @@ -114,7 +114,7 @@ jobs: libnghttp2-devel ${{ matrix.install }} - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -191,7 +191,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-stunnel with: path: C:\my-stunnel @@ -321,7 +321,7 @@ jobs: install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false steps: - - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 if: ${{ matrix.sys == 'msys' }} with: msystem: ${{ matrix.sys }} @@ -337,7 +337,7 @@ jobs: libpsl-devel ${{ matrix.install }} - - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 if: ${{ matrix.sys != 'msys' }} with: msystem: ${{ matrix.sys }} @@ -348,7 +348,7 @@ jobs: mingw-w64-${{ matrix.env }}-libpsl ${{ matrix.install }} - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -462,7 +462,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-stunnel with: path: C:\my-stunnel @@ -628,7 +628,7 @@ jobs: chkprefill: '' # Set it once to silence actionlint fail-fast: false steps: - - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: msystem: ${{ matrix.sys }} release: false @@ -640,7 +640,7 @@ jobs: ${{ matrix.install }} - name: 'cache compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-compiler with: path: D:\my-cache @@ -661,7 +661,7 @@ jobs: pwd sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${MATRIX_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin && ls -l - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -724,7 +724,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-stunnel with: path: C:\my-stunnel @@ -794,7 +794,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -973,7 +973,7 @@ jobs: fail-fast: false steps: - - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: msystem: ${{ matrix.arch == 'arm64' && 'clangarm64' || 'ucrt64' }} release: ${{ contains(matrix.image, 'arm') }} @@ -995,7 +995,7 @@ jobs: timeout-minutes: 45 run: vcpkg x-set-installed ${MATRIX_INSTALL_VCPKG} --triplet="${MATRIX_ARCH}-${MATRIX_PLAT}" - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -1122,7 +1122,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-stunnel with: path: C:\my-stunnel From 2c97912d7cbe83d874eff096133a103a66a3fced Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Wed, 1 Jul 2026 09:03:29 -0700 Subject: [PATCH 0614/1062] INFRASTRUCTURE.md: fix typo --- docs/INFRASTRUCTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index a4f8b5843356..1679223609ce 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -90,7 +90,7 @@ thus take up to 20 minutes until it takes effect on the origin server. The content on `curl.dev` is available and managed at https://github.com/curl/curl.dev/ -The content on `everything-curl.dev` is available and managed at +The content on `everything.curl.dev` is available and managed at https://github.com/curl/everything-curl/ The machine hosting the website contents for these three sites is owned by From fecff6e610aff725654c39f1ffb9e990869d4445 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Jul 2026 17:02:36 +0200 Subject: [PATCH 0615/1062] TODO: Support WebSocket text frames with command line tool Closes #21997 Closes #22240 --- docs/TODO.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/TODO.md b/docs/TODO.md index 15d6c02ba0e2..501ca2aa7689 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -431,6 +431,19 @@ LDAPS not possible with macOS and Windows with Certificate-Based Authentication [curl issue 9641](https://github.com/curl/curl/issues/9641) +# WebSocket + +## Support text frames with command line tool + +libcurl defaults to using binary WebSocket frames, which makes some servers +not work as they require text. We should make it possible to tell the tool to +use text frames. + +[curl issue 21997](https://github.com/curl/curl/issues/21997) + +An abandoned attempt to add support for this exists in [PR +22093](https://github.com/curl/curl/pull/22093). + # SMB ## Support modern versions From 417189f360a168bca93bf5f8581cd40f2293c2f3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Jul 2026 17:06:46 +0200 Subject: [PATCH 0616/1062] TODO: drop all SMB ideas SMB is about to be removed, no point in adding new SMB things now. Closes #22241 --- docs/TODO.md | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 501ca2aa7689..2838b0b0d978 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -444,31 +444,6 @@ use text frames. An abandoned attempt to add support for this exists in [PR 22093](https://github.com/curl/curl/pull/22093). -# SMB - -## Support modern versions - -curl only supports version 1, which barely anyone is using anymore. - -## File listing support - -Add support for listing the contents of an SMB share. The output should -probably be the same as/similar to FTP. - -## Honor file timestamps - -The timestamp of the transferred file should reflect that of the original -file. - -## Use NTLMv2 - -Currently the SMB authentication uses NTLMv1. - -## Create remote directories - -Support for creating remote directories when uploading a file to a directory -that does not exist on the server, like `--ftp-create-dirs`. - # FILE ## Directory listing on non-POSIX From 75c2c881af3201bc35d582eb4304e9b8ce3436ea Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 1 Jul 2026 10:54:21 +0200 Subject: [PATCH 0617/1062] openssl+sectrust: fix session reuse When TLS is verified via Apple SecTrust, openssl internal state is a verification faiure (that is how it works, we use sectrust when openssl fails to verify and natice ca store is enabled). OpenSSL stores this verification status inside its TLS session objects. On reuse, we see an unverified session and do not reuse it for a verified connect attempt. While this is a performance penalty for most connections, it *fails* on ftps:// transfers where servers expect session reuse on DATA connections. Fix this by remembering that TLS and a session was verified by sectrust and allow reuse of such sessions for new connect attempts that also use sectrust. Closes #22235 --- lib/vquic/cf-ngtcp2-cmn.c | 4 +-- lib/vtls/openssl.c | 53 +++++++++++++++++++++++---------------- lib/vtls/openssl.h | 6 ++++- lib/vtls/vtls_scache.h | 1 + lib/vtls/vtls_spack.c | 7 ++++++ 5 files changed, 47 insertions(+), 24 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 419c52009acd..a86960c91cbf 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -649,8 +649,8 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) quic_tp_len = (size_t)tplen; } #endif - Curl_ossl_add_session(cf, data, ctx->ssl_peer.scache_key, ssl_sessionid, - SSL_version(ssl), "h3", quic_tp, quic_tp_len); + Curl_ossl_add_session(cf, data, &ctx->tls.ossl, ctx->ssl_peer.scache_key, + ssl_sessionid, "h3", quic_tp, quic_tp_len); } return 0; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index ed401fe0d863..cd8d43523a5c 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2632,9 +2632,9 @@ static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ossl_ctx *octx, const char *ssl_peer_key, SSL_SESSION *session, - int ietf_tls_id, const char *alpn, unsigned char *quic_tp, size_t quic_tp_len) @@ -2651,6 +2651,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, size_t der_session_size; unsigned char *der_session_ptr; size_t earlydata_max = 0; + int ietf_tls_id = SSL_version(octx->ssl); der_session_size = i2d_SSL_SESSION(session, NULL); if(der_session_size == 0) { @@ -2688,6 +2689,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, earlydata_max, qtp_clone, quic_tp_len, &sc_session); der_session_buf = NULL; /* took ownership of sdata */ +#ifdef USE_APPLE_SECTRUST + sc_session->sectrust_verified = octx->sectrust_verified; +#endif if(!result) { result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); /* took ownership of `sc_session` */ @@ -2708,8 +2712,9 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); struct ssl_connect_data *connssl = cf->ctx; - Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid, - SSL_version(ssl), connssl->negotiated.alpn, NULL, 0); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + Curl_ossl_add_session(cf, data, octx, connssl->peer.scache_key, + ssl_sessionid, connssl->negotiated.alpn, NULL, 0); } return 0; } @@ -3353,7 +3358,12 @@ static CURLcode ossl_init_session_and_alpns( } else { if(conn_cfg->verifypeer && - (SSL_get_verify_result(octx->ssl) != X509_V_OK)) { + (SSL_get_verify_result(octx->ssl) != X509_V_OK) +#ifdef USE_APPLE_SECTRUST + /* if sectrust is used and verified the session before */ + && (!ssl_config->native_ca_store || !scs->sectrust_verified) +#endif + ) { /* Session was from unverified connection, cannot reuse here */ SSL_set_session(octx->ssl, NULL); infof(data, "SSL session not peer verified, not reusing"); @@ -3362,6 +3372,9 @@ static CURLcode ossl_init_session_and_alpns( infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); octx->reused_session = TRUE; +#ifdef USE_APPLE_SECTRUST + octx->sectrust_session = scs->sectrust_verified; +#endif infof(data, "SSL verify result: %lx", (unsigned long)SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA @@ -4671,13 +4684,13 @@ static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf, static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, struct Curl_easy *data, struct ossl_ctx *octx, - struct ssl_peer *peer, - bool *pverified) + struct ssl_peer *peer) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ossl_certs_ctx chain; CURLcode result; + octx->sectrust_verified = FALSE; memset(&chain, 0, sizeof(chain)); chain.sk = SSL_get_peer_cert_chain(octx->ssl); chain.num_certs = chain.sk ? sk_X509_num(chain.sk) : 0; @@ -4689,8 +4702,11 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; } else { - /* when session was reused, there is no peer cert chain */ - *pverified = FALSE; + /* When session was reused, there is no peer cert chain. + * We trust it if it came from a SecTrust verified TLS. */ + CURL_TRC_CF(data, cf, "session reused, sectrust_session=%d", + octx->sectrust_session); + octx->sectrust_verified = (bool)octx->sectrust_session; return CURLE_OK; } } @@ -4717,12 +4733,12 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, if(!result && ocsp_missing && conn_config->verifystatus && !octx->reused_session) { /* verified, but OCSP stapling is required and server sent none */ - *pverified = TRUE; + octx->sectrust_verified = TRUE; failf(data, "No OCSP response received"); return CURLE_SSL_INVALIDCERTSTATUS; } } - *pverified = !result; + octx->sectrust_verified = !result; return result; } #endif /* USE_APPLE_SECTRUST */ @@ -4739,9 +4755,6 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, long ossl_verify; X509 *server_cert; bool verified = FALSE; -#if !defined(OPENSSL_NO_OCSP) && defined(USE_APPLE_SECTRUST) - bool sectrust_verified = FALSE; -#endif if(data->set.ssl.certinfo && !octx->reused_session) { /* asked to gather certificate info. Reused sessions do not have cert @@ -4789,15 +4802,13 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, /* we verify using Apple SecTrust *unless* OpenSSL already verified. * This may happen if the application intercepted the OpenSSL callback * and installed its own. */ - result = ossl_apple_verify(cf, data, octx, peer, &verified); + result = ossl_apple_verify(cf, data, octx, peer); if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) goto out; /* unexpected error */ - if(verified) { + if(octx->sectrust_verified) { infof(data, "SSL certificate verified via Apple SecTrust."); ssl_config->certverifyresult = X509_V_OK; -#ifndef OPENSSL_NO_OCSP - sectrust_verified = TRUE; -#endif + verified = TRUE; } } #endif @@ -4816,9 +4827,9 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, #ifndef OPENSSL_NO_OCSP if(conn_config->verifystatus && #ifdef USE_APPLE_SECTRUST - !sectrust_verified && /* already verified via apple sectrust, cannot - * verifystate via OpenSSL in that case as it - * does not have the trust anchors */ + !octx->sectrust_verified && /* already verified via sectrust, cannot + * verifystate via OpenSSL in that case as it + * does not have the trust anchors */ #endif !octx->reused_session) { /* do not do this after Session ID reuse */ diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 53ab60528f4b..1daeef50dfbe 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -129,6 +129,10 @@ struct ossl_ctx { BIT(x509_store_setup); /* x509 store has been set up */ BIT(store_is_empty); /* no certs/paths/blobs in x509 store */ BIT(reused_session); /* session-ID was reused for this */ +#ifdef USE_APPLE_SECTRUST + BIT(sectrust_verified); /* peer was verified by sectrust */ + BIT(sectrust_session); /* session from sectrust verified peer */ +#endif }; size_t Curl_ossl_version(char *buffer, size_t size); @@ -182,9 +186,9 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, */ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ossl_ctx *octx, const char *ssl_peer_key, SSL_SESSION *session, - int ietf_tls_id, const char *alpn, unsigned char *quic_tp, size_t quic_tp_len); diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index effb1d8f96af..fc4eb9ef51eb 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -131,6 +131,7 @@ struct Curl_ssl_session { const unsigned char *quic_tp; /* Optional QUIC transport param bytes */ size_t quic_tp_len; /* number of bytes in quic_tp */ struct Curl_llist_node list; /* internal storage handling */ + BIT(sectrust_verified); /* session comes from sectrust verified TLS */ }; /* Create a `session` instance. Does NOT need locking. diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index 9cbdecc90185..e572e923f542 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -38,6 +38,7 @@ #define CURL_SPACK_ALPN 0x05 #define CURL_SPACK_EARLYDATA 0x06 #define CURL_SPACK_QUICTP 0x07 +#define CURL_SPACK_SECTRUST 0x08 static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b) { @@ -218,6 +219,9 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, if(!result) result = spack_enc32(buf, (uint32_t)s->earlydata_max); } + if(!result && s->sectrust_verified) { + result = spack_enc8(buf, CURL_SPACK_SECTRUST); + } if(!result && s->quic_tp && s->quic_tp_len) { result = spack_enc8(buf, CURL_SPACK_QUICTP); if(!result) @@ -303,6 +307,9 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, goto out; s->valid_until = (curl_off_t)val64; break; + case CURL_SPACK_SECTRUST: + s->sectrust_verified = TRUE; + break; default: /* unknown tag */ result = CURLE_READ_ERROR; goto out; From 3507b74dbaa32aab6b38da36da43e29d4fe4790f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Jul 2026 23:15:02 +0200 Subject: [PATCH 0618/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 60 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 66aac9052dff..2ef9aa52679d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.21.1 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1490 - Contributors: 3734 + Authors: 1493 + Contributors: 3741 This release includes the following changes: @@ -13,29 +13,48 @@ This release includes the following changes: This release includes the following bugfixes: o autotools: minor fixes and improvements [33] - o build: enable thread-safe `getaddrinfo()` for OpenBSD, and other tidy-ups [35] + o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] + o build: drop superfluous `STDC_HEADERS` macro [51] + o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] + o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] o cmake: flatten build tree, tidy up base dir variables [12] - o cmake: replace deprecated `remove` command with `rm` and pass arg safely [11] + o cmake: minor improvements to `cmake_uninstall.in.cmake` [54] + o cmake: replace `remove` command with `rm` and pass arg safely [11] o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] o conncache: connection alive checks intervals [20] - o curl-functions.m4: verify working RFC 4291 zero prefixed IPv4 - mapped format [41] + o content_encoding: give a clear error on multi-member gzip [46] + o CREDENTIALS.md: remove comment about emtpy user/pass [50] + o curl_ws_meta.md: polish and better vocabulary [19] + o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] + o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] o gopher: reject CR and LF in the selector [1] o http: trim custom header name before the Authorization drop [17] o INSTALL.md: add building-from-source overview section [29] o ldap: support insecure mode for Windows native LDAP [3] o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] + o lib: fix 'ns' -> 'us' in trace messages [57] + o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] + o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] + o openldap: handle Curl_sasl_continue() returns better [45] + o openssl+sectrust: fix session reuse [4] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] + o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] + o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] o runtests: restore `-k` option and actively process as no-op [32] + o sasl: fix zero-length response encoding [36] + o schannel: shut off experimental TLS 1.3 support for Win 10 [25] o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] + o smtp: reject CR and LF in the envelope address [37] o sws: allow connection-monitor to log all disconnects [2] o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] o tool: do not flush on out-null [38] o tool: init progress bar on demand [39] + o tool_cb_hdr: de-duplicate filename setter [24] o tool_operate: remove call to abort() [23] o urlapi: do not keep an internal port string [31] o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] @@ -60,16 +79,19 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Alhuda Khan, alhudz, Dan Fandrich, Daniel Stenberg, Ray Satiro, - renovate[bot], Roger Leigh, Sam James, Samuel Dainard, Stefan Eissing, - stze on hackerone, Viktor Szakats - (12 contributors) + Alhuda Khan, Bigtang on hackerone, Dan Fandrich, Daniel Stenberg, + dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, + Hendrik Hübner, HwangRock, Memduh Çelik, Patrick Monnerat, Ray Satiro, + renovate[bot], Roger Leigh, Sam James, Samuel Dainard, + smaeljaish on hackerone, Stefan Eissing, stze on hackerone, Viktor Szakats + (21 contributors) References to bug reports and discussions on issues: [1] = https://curl.se/bug/?i=22116 [2] = https://curl.se/bug/?i=22158 [3] = https://curl.se/bug/?i=22152 + [4] = https://curl.se/bug/?i=22235 [5] = https://curl.se/bug/?i=22190 [6] = https://curl.se/bug/?i=22195 [7] = https://curl.se/bug/?i=22198 @@ -80,17 +102,35 @@ References to bug reports and discussions on issues: [12] = https://curl.se/bug/?i=22192 [15] = https://curl.se/bug/?i=22187 [17] = https://curl.se/bug/?i=22178 + [19] = https://curl.se/bug/?i=22233 [20] = https://curl.se/bug/?i=22169 [22] = https://curl.se/bug/?i=22179 [23] = https://curl.se/bug/?i=22182 + [24] = https://curl.se/bug/?i=22232 + [25] = https://curl.se/bug/?i=22231 + [27] = https://curl.se/bug/?i=22222 + [28] = https://curl.se/bug/?i=22220 [29] = https://curl.se/bug/?i=22113 [31] = https://curl.se/bug/?i=22167 [32] = https://curl.se/bug/?i=22100 [33] = https://curl.se/bug/?i=22154 [34] = https://curl.se/bug/?i=22150 [35] = https://curl.se/bug/?i=22148 + [36] = https://curl.se/bug/?i=22218 + [37] = https://curl.se/bug/?i=22119 [38] = https://curl.se/bug/?i=22165 [39] = https://curl.se/bug/?i=22164 [40] = https://curl.se/bug/?i=22136 - [41] = https://curl.se/bug/?i=22136 [43] = https://curl.se/bug/?i=22112 + [44] = https://curl.se/bug/?i=22217 + [45] = https://curl.se/bug/?i=22213 + [46] = https://curl.se/bug/?i=22156 + [47] = https://curl.se/bug/?i=22219 + [48] = https://curl.se/bug/?i=22211 + [49] = https://curl.se/bug/?i=22214 + [50] = https://curl.se/bug/?i=22212 + [51] = https://curl.se/bug/?i=22206 + [52] = https://curl.se/bug/?i=22205 + [54] = https://curl.se/bug/?i=22201 + [56] = https://curl.se/bug/?i=22170 + [57] = https://curl.se/bug/?i=22200 From 03f9751585112cdc3c5f0a370217f08c05782de9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 2 Jul 2026 03:06:45 +0200 Subject: [PATCH 0619/1062] GHA: update dependency wolfSSL/wolfssl to v5.9.2, apply upstream patch Apply upstream patch to fix a regression in 5.9.2. Refs: https://github.com/wolfSSL/wolfssl/issues/10790 https://github.com/wolfSSL/wolfssl/pull/10793 https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8 Ref: #22175 (local mitigation attempt) Fixes #22160 Closes #22204 --- .github/workflows/http3-linux.yml | 5 ++++- .github/workflows/linux.yml | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 03bf8aba984e..4b20eaea972a 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -54,7 +54,7 @@ env: # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com QUICHE_VERSION: 0.29.2 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.9.1 + WOLFSSL_VERSION: 5.9.2 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com NGHTTP3_VERSION: 1.17.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com @@ -327,6 +327,9 @@ jobs: cd ~ git clone --quiet --depth 1 --branch "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl cd wolfssl + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ + https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch + patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-all --enable-quic \ --disable-benchmark --disable-crypttests --disable-examples diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 70fc045fbda9..95313c7661b1 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -56,7 +56,7 @@ env: # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com RUSTLS_VERSION: 0.15.3 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.9.1 + WOLFSSL_VERSION: 5.9.2 jobs: linux: @@ -598,6 +598,9 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ + https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch + patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-all \ --enable-tls13 --enable-harden --enable-all \ @@ -621,6 +624,9 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ + https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch + patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ @@ -644,6 +650,9 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ + https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch + patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ From d3a7e571571d6d63039c5d75180f3bcfc4a632ce Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 Jul 2026 09:43:58 +0200 Subject: [PATCH 0620/1062] lib: add "Curl_" prefix to two global functions To make it clear what they are and according to our styleguide. - Curl_cf_ip_happy_insert_after - Curl_ftp_conns_match Found with: $ nm lib/.libs/libcurl.a | grep ' T ' | grep -vi ' curl' Closes #22245 --- lib/cf-ip-happy.c | 14 +++++++------- lib/cf-ip-happy.h | 14 +++++++------- lib/cf-setup.c | 6 +++--- lib/ftp.c | 2 +- lib/ftp.h | 3 ++- lib/url.c | 2 +- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 963ccea94d78..91ff029f6f68 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -1031,13 +1031,13 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, return result; } -CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - struct Curl_peer *origin, - struct Curl_peer *peer, - uint8_t transport_peer, - struct Curl_peer *tunnel_peer, - uint8_t tunnel_transport) +CURLcode Curl_cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct Curl_cfilter *cf; CURLcode result; diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index dd9b29ac8f6a..88de0c32454b 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -60,13 +60,13 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, * With a `tunnel_peer` present, the filter will be used to proxy tunnel * to it and the tunnel will use `tunnel_transport`. */ -CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - struct Curl_peer *origin, - struct Curl_peer *peer, - uint8_t transport_peer, - struct Curl_peer *tunnel_peer, - uint8_t tunnel_transport); +CURLcode Curl_cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_ip_happy; diff --git a/lib/cf-setup.c b/lib/cf-setup.c index 119294a89a4d..1b96d682b47f 100644 --- a/lib/cf-setup.c +++ b/lib/cf-setup.c @@ -202,9 +202,9 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, } #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ - result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer, - first_transport, - tunnel_peer, ctx->transport); + result = Curl_cf_ip_happy_insert_after(cf, data, first_origin, first_peer, + first_transport, + tunnel_peer, ctx->transport); if(result) { CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result); return result; diff --git a/lib/ftp.c b/lib/ftp.c index 05d17064a08b..8b4d9b663ba2 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -4449,7 +4449,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, return result; } -bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) +bool Curl_ftp_conns_match(struct connectdata *needle, struct connectdata *conn) { struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN); struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); diff --git a/lib/ftp.h b/lib/ftp.h index 257f5958267c..3ce02c87932f 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -28,7 +28,8 @@ #ifndef CURL_DISABLE_FTP extern const struct Curl_protocol Curl_protocol_ftp; -bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn); +bool Curl_ftp_conns_match(struct connectdata *needle, + struct connectdata *conn); typedef enum { FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ diff --git a/lib/url.c b/lib/url.c index a95e3581b456..a7e7b4d06d44 100644 --- a/lib/url.c +++ b/lib/url.c @@ -942,7 +942,7 @@ static bool url_match_proto_config(struct connectdata *conn, #endif #ifndef CURL_DISABLE_FTP else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) { - if(!ftp_conns_match(m->needle, conn)) + if(!Curl_ftp_conns_match(m->needle, conn)) return FALSE; } #endif From c7f67be0100967d041450285a30b2bec6cbfad64 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 Jul 2026 09:07:05 +0200 Subject: [PATCH 0621/1062] vssh: keyfile use cleanups - make the libssh backend do the same fallback for private and public key filename as libssh2 already did and is documented behavior. This now uses a common function. - set the file names already in setup_connection if public key auth is requested, so that the connection reuse functions can use the information when checking for existing connections to reuse - rename the oddly named struct fields 'rsa' to 'priv_key' and 'rsa_pub' to 'pub_key' to better reflect their purposes Fixes #22243 Closes #22244 --- lib/url.c | 4 +- lib/vssh/libssh.c | 22 ++++------ lib/vssh/libssh2.c | 101 +++++---------------------------------------- lib/vssh/ssh.h | 4 +- lib/vssh/vssh.c | 84 +++++++++++++++++++++++++++++++++++++ lib/vssh/vssh.h | 4 ++ 6 files changed, 112 insertions(+), 107 deletions(-) diff --git a/lib/url.c b/lib/url.c index a7e7b4d06d44..164aef797a2b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -681,8 +681,8 @@ static bool ssh_config_matches(struct connectdata *one, sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); - return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && - Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); + return sshc1 && sshc2 && Curl_safecmp(sshc1->priv_key, sshc2->priv_key) && + Curl_safecmp(sshc1->pub_key, sshc2->pub_key); } #endif diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 93be7ca21f4a..ea21e19289cf 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -832,8 +832,7 @@ static int myssh_in_AUTHLIST(struct Curl_easy *data, /* For public key auth we need either the private key or CURLSSH_AUTH_AGENT. */ if((sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) && - (data->set.str[STRING_SSH_PRIVATE_KEY] || - (data->set.ssh_auth_types & CURLSSH_AUTH_AGENT))) { + (sshc->priv_key || (data->set.ssh_auth_types & CURLSSH_AUTH_AGENT))) { myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); infof(data, "Authentication using SSH public key file"); } @@ -863,7 +862,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, /* Two choices, (1) private key was given on CMD, * (2) use the "default" keys. */ - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + if(sshc->priv_key) { if(sshc->pubkey && !data->set.ssl.primary.key_passwd) { rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) @@ -875,13 +874,11 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, } } - rc = ssh_pki_import_privkey_file(data-> - set.str[STRING_SSH_PRIVATE_KEY], + rc = ssh_pki_import_privkey_file(sshc->priv_key, data->set.ssl.primary.key_passwd, NULL, NULL, &sshc->privkey); if(rc != SSH_OK) { - failf(data, "Could not load private key file %s", - data->set.str[STRING_SSH_PRIVATE_KEY]); + failf(data, "Could not load private key file %s", sshc->priv_key); rc = myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); return rc; } @@ -1916,8 +1913,8 @@ static void sshc_cleanup(struct ssh_conn *sshc) sshc->pubkey = NULL; } - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); + curlx_safefree(sshc->pub_key); + curlx_safefree(sshc->priv_key); curlx_safefree(sshc->quote_path1); curlx_safefree(sshc->quote_path2); curlx_dyn_free(&sshc->readdir_buf); @@ -2547,7 +2544,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; + return Curl_ssh_setup_pkey(data, sshc); } static Curl_recv scp_recv, sftp_recv; @@ -2655,9 +2652,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) sshc->privkey = NULL; sshc->pubkey = NULL; - if(data->set.str[STRING_SSH_PUBLIC_KEY]) { - rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], - &sshc->pubkey); + if(sshc->pub_key) { + rc = ssh_pki_import_pubkey_file(sshc->pub_key, &sshc->pubkey); if(rc != SSH_OK) { failf(data, "Could not load public key file"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index a3d0adf3b284..5f1353303359 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1094,8 +1094,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, return CURLE_OK; } -static CURLcode ssh_state_pkey_init(struct Curl_easy *data, - struct ssh_conn *sshc) +static void ssh_state_pkey_init(struct Curl_easy *data, struct ssh_conn *sshc) { /* * Check the supported auth types in the order I feel is most secure @@ -1105,88 +1104,13 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && strstr(sshc->authlist, "publickey")) { - bool out_of_memory = FALSE; - - sshc->rsa_pub = sshc->rsa = NULL; - - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - sshc->rsa = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); - if(!sshc->rsa) - out_of_memory = TRUE; - } - else { - /* To ponder about: should really the lib be messing about with the - HOME environment variable etc? */ - char *home = curl_getenv("HOME"); - curlx_struct_stat sbuf; - - /* If no private key file is specified, try some common paths. */ - if(home) { - /* Try ~/.ssh first. */ - sshc->rsa = curl_maprintf("%s/.ssh/id_rsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - sshc->rsa = curl_maprintf("%s/.ssh/id_dsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(curlx_stat(sshc->rsa, &sbuf)) { - curlx_safefree(sshc->rsa); - } - } - curlx_free(home); - } - if(!out_of_memory && !sshc->rsa) { - /* Nothing found; try the current dir. */ - sshc->rsa = curlx_strdup("id_rsa"); - if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - sshc->rsa = curlx_strdup("id_dsa"); - if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - /* Out of guesses. Set to the empty string to avoid - * surprising info messages. */ - sshc->rsa = curlx_strdup(""); - } - } - } - } - - /* - * Unless the user explicitly specifies a public key file, let - * libssh2 extract the public key from the private key file. - * This is done by passing sshc->rsa_pub = NULL. - */ - if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] && - /* treat empty string the same way as NULL */ - data->set.str[STRING_SSH_PUBLIC_KEY][0]) { - sshc->rsa_pub = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); - if(!sshc->rsa_pub) - out_of_memory = TRUE; - } - - if(out_of_memory || !sshc->rsa) { - curlx_safefree(sshc->rsa); - curlx_safefree(sshc->rsa_pub); - myssh_to(data, sshc, SSH_SESSION_FREE); - return CURLE_OUT_OF_MEMORY; - } - - sshc->passphrase = data->set.ssl.primary.key_passwd; - if(!sshc->passphrase) - sshc->passphrase = ""; - - if(sshc->rsa_pub) - infof(data, "SSH: trying public key file '%s'", sshc->rsa_pub); - infof(data, "SSH: trying private key file '%s'", sshc->rsa); - + if(sshc->pub_key) + infof(data, "SSH: trying public key file '%s'", sshc->pub_key); + infof(data, "SSH: trying private key file '%s'", sshc->priv_key); myssh_to(data, sshc, SSH_AUTH_PKEY); } - else { + else myssh_to(data, sshc, SSH_AUTH_PASS_INIT); - } - return CURLE_OK; } static CURLcode sftp_quote_stat(struct Curl_easy *data, @@ -1559,14 +1483,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, user, curlx_uztoui(strlen(user)), - sshc->rsa_pub, - sshc->rsa, sshc->passphrase); + sshc->pub_key, + sshc->priv_key, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); - if(rc == 0) { sshc->authed = TRUE; infof(data, "SSH: authenticated via publickey"); @@ -2626,8 +2547,8 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, DEBUGASSERT(!sshc->kh); DEBUGASSERT(!sshc->ssh_agent); - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); + curlx_safefree(sshc->pub_key); + curlx_safefree(sshc->priv_key); curlx_safefree(sshc->quote_path1); curlx_safefree(sshc->quote_path2); curlx_safefree(sshc->homedir); @@ -2968,7 +2889,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_AUTH_PKEY_INIT: - result = ssh_state_pkey_init(data, sshc); + ssh_state_pkey_init(data, sshc); break; case SSH_AUTH_PKEY: @@ -3343,7 +3264,7 @@ static CURLcode ssh_setup_connection(struct Curl_easy *data, if(Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; + return Curl_ssh_setup_pkey(data, sshc); } static Curl_recv scp_recv, sftp_recv; diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index de76c8a253d0..a299c3c4764c 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -144,8 +144,8 @@ struct ssh_conn { /* common */ const char *passphrase; /* pass-phrase to use */ - char *rsa_pub; /* strdup'ed public key file */ - char *rsa; /* strdup'ed private key file */ + char *pub_key; /* strdup'ed public key file */ + char *priv_key; /* strdup'ed private key file */ sshstate state; /* always use ssh.c:state() to change state! */ sshstate nextstate; /* the state to goto after stopping */ struct curl_slist *quote_item; /* for the quote option */ diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c index 9c82d0a88465..ac0217182907 100644 --- a/lib/vssh/vssh.c +++ b/lib/vssh/vssh.c @@ -32,6 +32,7 @@ #include "escape.h" #include "select.h" /* for Curl_pollset_change() */ #include "url.h" /* for Curl_conn_meta_get() */ +#include "curlx/fopen.h" #ifdef CURLVERBOSE const char *Curl_ssh_statename(sshstate state) @@ -361,4 +362,87 @@ CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps) return CURLE_OK; } +CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) +{ + char *home = NULL; + if(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) { + sshc->pub_key = sshc->priv_key = NULL; + + if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + sshc->priv_key = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + if(!sshc->priv_key) + goto fail; + } + else { + /* To ponder about: should really the lib be messing about with the HOME + environment variable etc? */ + curlx_struct_stat sbuf; + home = curl_getenv("HOME"); + + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->priv_key = curl_maprintf("%s/.ssh/id_rsa", home); + if(!sshc->priv_key) + goto fail; + else if(curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + sshc->priv_key = curl_maprintf("%s/.ssh/id_dsa", home); + if(!sshc->priv_key) + goto fail; + else if(curlx_stat(sshc->priv_key, &sbuf)) { + curlx_safefree(sshc->priv_key); + } + } + curlx_safefree(home); + } + if(!sshc->priv_key) { + /* Nothing found; try the current dir. */ + sshc->priv_key = curlx_strdup("id_rsa"); + if(sshc->priv_key && curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + sshc->priv_key = curlx_strdup("id_dsa"); + if(sshc->priv_key && curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->priv_key = curlx_strdup(""); + } + } + } + } + + /* + * Unless the user explicitly specifies a public key file, let the SSH + * library extract the public key from the private key file. This is done + * by passing sshc->pub_key = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY] && + /* treat empty string the same way as NULL */ + data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + sshc->pub_key = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->pub_key) + goto fail; + } + + sshc->passphrase = data->set.ssl.primary.key_passwd; + if(!sshc->passphrase) + sshc->passphrase = ""; + + if(sshc->pub_key) + infof(data, "SSH: public key file '%s'", sshc->pub_key); + if(sshc->priv_key) + infof(data, "SSH: private key file '%s'", sshc->priv_key); + else + infof(data, "SSH: public key auth without private key set!"); + } + return CURLE_OK; + +fail: + curlx_safefree(home); + curlx_safefree(sshc->priv_key); + curlx_safefree(sshc->pub_key); + return CURLE_OUT_OF_MEMORY; +} + #endif /* USE_SSH */ diff --git a/lib/vssh/vssh.h b/lib/vssh/vssh.h index 89fe41e0ae6e..4c505c453a3e 100644 --- a/lib/vssh/vssh.h +++ b/lib/vssh/vssh.h @@ -29,6 +29,8 @@ #include "urldata.h" +struct ssh_conn; + CURLcode Curl_getworkingpath(struct Curl_easy *data, const char *homedir, char **path); @@ -40,5 +42,7 @@ CURLcode Curl_ssh_range(struct Curl_easy *data, curl_off_t *startp, curl_off_t *sizep); CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps); +CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc); + #endif /* USE_SSH */ #endif /* HEADER_CURL_VSSH_VSSH_H */ From 6ab52f7c8951747b0379e63a440ca37cca658467 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 2 Jul 2026 13:11:35 +0200 Subject: [PATCH 0622/1062] openssl+sectrust: move session verified set into result check Pointed out by Codex Security Closes #22248 --- lib/vtls/openssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index cd8d43523a5c..10e3946bdfe3 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2689,10 +2689,10 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, earlydata_max, qtp_clone, quic_tp_len, &sc_session); der_session_buf = NULL; /* took ownership of sdata */ + if(!result) { #ifdef USE_APPLE_SECTRUST - sc_session->sectrust_verified = octx->sectrust_verified; + sc_session->sectrust_verified = octx->sectrust_verified; #endif - if(!result) { result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); /* took ownership of `sc_session` */ } From 0a7ec0ea4d2f1b3255fb9f7a8a81320ed8c54d76 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Wed, 1 Jul 2026 10:52:29 -0400 Subject: [PATCH 0623/1062] ci: retain curl-for-win artifacts for a longer time - Retain for 5 days because 3 days seems too short. Follow-up to adf843fb. Closes https://github.com/curl/curl/pull/22238 --- .github/workflows/curl-for-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 4678b68d201b..6c65e8ca8746 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -177,7 +177,7 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: 'curl-windows-snapshot-tool' - retention-days: 3 + retention-days: 5 path: curl-*-*-*/curl* win-gcc-zlibold-x64: From 84ecfb3ecc793c0087ed9700bdc2a1475ab53ff9 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 2 Jul 2026 12:04:18 +0200 Subject: [PATCH 0624/1062] FTP: fix TLS session reuse on the data connection FTP servers using SSL can be configured to check TLS session reuse on the DATA connection. They hand out a new session on every CONTROL connect and require to see the client using exactly that one when up-/downloading on DATA. This means: 1. We have to configure the SSL filter on the DATA connection with exactly the same peers. 2. We have to remember the SSL session on the CONTROL connection - separately from the session cache. The SSL filter on the DATA connection then looks for a session at the CONTROL filter and, if present, uses that. Tests: Enable `require_ssl_reuse` in our pytest setup for vsftpd. This reproduces the problem reported in #22225 and verifies the fix. Skip ftp+SSL pytests for rustls, as we have no possibility to reuse sessions in that backend. Schannel: we do not run these tests with the backend. I expect it has similar problems but am not able to verify. Reported-by: Laurent Sabourin Fixes #22225 Closes #22246 --- lib/cf-setup.c | 15 ++- lib/vquic/cf-ngtcp2-cmn.c | 7 +- lib/vtls/gtls.c | 133 ++++++++++++++------- lib/vtls/gtls.h | 6 +- lib/vtls/mbedtls.c | 60 +++++++++- lib/vtls/openssl.c | 190 ++++++++++++++++++------------ lib/vtls/openssl.h | 3 +- lib/vtls/vtls.c | 58 +++++---- lib/vtls/vtls_int.h | 8 ++ lib/vtls/vtls_scache.c | 50 +++++++- lib/vtls/vtls_scache.h | 4 + lib/vtls/wolfssl.c | 77 ++++++++---- lib/vtls/wolfssl.h | 3 +- tests/http/conftest.py | 4 +- tests/http/test_31_vsftpds.py | 36 ++++-- tests/http/test_32_ftps_vsftpd.py | 19 ++- tests/http/testenv/vsftpd.py | 2 +- 17 files changed, 485 insertions(+), 190 deletions(-) diff --git a/lib/cf-setup.c b/lib/cf-setup.c index 1b96d682b47f..b073989a4e34 100644 --- a/lib/cf-setup.c +++ b/lib/cf-setup.c @@ -273,12 +273,15 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, else #endif { - /* Another FTP quirk: when adding SSL verification, to a DATA - * connection, always verify against the control's origin */ - struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); - struct Curl_peer *peer = - Curl_conn_get_destination(cf->conn, cf->sockindex); - result = Curl_cf_ssl_insert_after(cf, data, origin, peer); + /* FTP is a bitch. Wherever we really connect to on the DATA + * (secondary) connection, many servers require TLS sessions reuse + * to prove they are talking to the same client. + * For the TLS session lookup to work, we need to instantiate + * the SSL filter with the same peers as FIRSTSOCKET. See #22225 + * Meaning: cf->sockindex does not matter here. */ + result = Curl_cf_ssl_insert_after(cf, data, + Curl_conn_get_origin(cf->conn, FIRSTSOCKET), + Curl_conn_get_destination(cf->conn, FIRSTSOCKET)); } if(result) { CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index a86960c91cbf..9d47a574a615 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -650,7 +650,7 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) } #endif Curl_ossl_add_session(cf, data, &ctx->tls.ossl, ctx->ssl_peer.scache_key, - ssl_sessionid, "h3", quic_tp, quic_tp_len); + ssl_sessionid, "h3", quic_tp, quic_tp_len, NULL); } return 0; } @@ -721,7 +721,8 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, quic_tp_len = (size_t)tplen; } (void)Curl_gtls_cache_session(cf, data, ctx->ssl_peer.scache_key, - session, 0, "h3", quic_tp, quic_tp_len); + session, 0, "h3", quic_tp, quic_tp_len, + NULL); break; } default: @@ -760,7 +761,7 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) } (void)Curl_wssl_cache_session(cf, data, ctx->ssl_peer.scache_key, session, wolfSSL_version(ssl), - "h3", quic_tp, quic_tp_len); + "h3", quic_tp, quic_tp_len, NULL); } } return 0; diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 1a7a4a1e5bbc..8cc9c53f1c13 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -720,14 +720,17 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, curl_off_t valid_until, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len) + size_t quic_tp_len, + struct Curl_ssl_session **psession) { - struct Curl_ssl_session *sc_session; + struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL; unsigned char *sdata, *qtp_clone = NULL; size_t sdata_len = 0; size_t earlydata_max = 0; CURLcode result = CURLE_OK; + if(psession) + *psession = NULL; if(!Curl_ssl_scache_use(cf, data)) return CURLE_OK; @@ -766,10 +769,20 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, qtp_clone, quic_tp_len, &sc_session); /* call took ownership of `sdata` and `qtp_clone` */ + if(!result && psession && /* return a duplicate if asked for and FTP */ + (cf->conn->scheme->family == CURLPROTO_FTP)) + result = Curl_ssl_session_dup(sc_session, &sc_dup); if(!result) { result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); /* took ownership of `sc_session` */ + sc_session = NULL; } + if(!result && psession) { + *psession = sc_dup; + sc_dup = NULL; + } + Curl_ssl_session_destroy(sc_session); + Curl_ssl_session_destroy(sc_dup); return result; } #endif @@ -780,9 +793,17 @@ static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, gnutls_session_t session) { struct ssl_connect_data *connssl = cf->ctx; - return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key, - session, 0, connssl->negotiated.alpn, - NULL, 0); + struct Curl_ssl_session *scs = NULL; + CURLcode result; + + result = Curl_gtls_cache_session(cf, data, connssl->peer.scache_key, + session, 0, connssl->negotiated.alpn, + NULL, 0, &scs); + if(!result) { + Curl_ssl_session_destroy(connssl->session); + connssl->session = scs; + } + return result; } static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, @@ -1104,6 +1125,55 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, } #endif +static CURLcode gtls_apply_session( + struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + Curl_gtls_init_session_reuse_cb *sess_reuse_cb, + struct Curl_ssl_session *scs, + bool *pwas_setup) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + int rc; + + if(scs && scs->sdata && scs->sdata_len && + (!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) { + /* we got a cached session, use it! */ + + result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx); + if(result) + goto out; + *pwas_setup = TRUE; + + rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len); + if(rc < 0) + infof(data, "SSL session not accepted by GnuTLS, continuing without"); + else { + infof(data, "SSL reusing session with ALPN '%s'", + scs->alpn ? scs->alpn : "-"); + if(ssl_config->earlydata && scs->alpn && + !cf->conn->bits.connect_only) { + bool do_early_data = FALSE; + if(sess_reuse_cb) { + result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data); + if(result) + goto out; + } + if(do_early_data) { + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + Curl_alpn_restrict_to(alpns, scs->alpn); + } + } + } + } +out: + return result; +} + CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1115,57 +1185,40 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, Curl_gtls_init_session_reuse_cb *sess_reuse_cb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *scs = NULL; gnutls_datum_t gtls_alpns[ALPN_ENTRIES_MAX]; size_t gtls_alpns_count = 0; bool gtls_session_setup = FALSE; struct alpn_spec alpns; CURLcode result = CURLE_OK; - int rc; DEBUGASSERT(gctx); Curl_alpn_copy(&alpns, alpns_requested); + if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) { + /* FTP is a bitch. On TLS secured transfers, it is a common server + * option to require the client to use the SAME TLS session as on + * the control connection or it fails the request. See #22225. */ + scs = Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET); + if(scs) { + result = gtls_apply_session(gctx, cf, data, peer, &alpns, + sess_reuse_cb, scs, >ls_session_setup); + scs = NULL; + if(!result && gtls_session_setup) { + CURL_TRC_CF(data, cf, "applied SSL session from control connection"); + } + } + } /* This might be a reconnect, so we check for a session ID in the cache to speed up things. We need to do this before constructing the GnuTLS session since we need to set flags depending on the kind of reuse. */ - if(conn_config->cache_session && !conn_config->verifystatus) { + if(!gtls_session_setup && conn_config->cache_session && + !conn_config->verifystatus) { result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); if(result) goto out; - - if(scs && scs->sdata && scs->sdata_len && - (!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) { - /* we got a cached session, use it! */ - - result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx); - if(result) - goto out; - gtls_session_setup = TRUE; - - rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len); - if(rc < 0) - infof(data, "SSL session not accepted by GnuTLS, continuing without"); - else { - infof(data, "SSL reusing session with ALPN '%s'", - scs->alpn ? scs->alpn : "-"); - if(ssl_config->earlydata && scs->alpn && - !cf->conn->bits.connect_only) { - bool do_early_data = FALSE; - if(sess_reuse_cb) { - result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); - if(result) - goto out; - } - if(do_early_data) { - /* We only try the ALPN protocol the session used before, - * otherwise we might send early data for the wrong protocol */ - Curl_alpn_restrict_to(&alpns, scs->alpn); - } - } - } - } + result = gtls_apply_session(gctx, cf, data, peer, &alpns, + sess_reuse_cb, scs, >ls_session_setup); } if(!gtls_session_setup) { diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 4c662003eb82..72aec29e32a1 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -105,7 +105,8 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, struct ssl_peer *peer, const char *pinned_key); -/* Extract TLS session and place in cache, if configured. */ +/* Extract TLS session and place in cache, if configured. Return + * a copy of the session if desired. */ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -113,7 +114,8 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, curl_off_t valid_until, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len); + size_t quic_tp_len, + struct Curl_ssl_session **psession); /* Report properties of a successful handshake */ void Curl_gtls_report_handshake(struct Curl_easy *data, struct gtls_ctx *gctx); diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 785a71f66e4e..b2ab84425075 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -786,6 +786,38 @@ static CURLcode mbed_load_crl(struct Curl_cfilter *cf, return CURLE_OK; } +static bool mbed_apply_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_session *sc_session) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + + if(sc_session && sc_session->sdata && sc_session->sdata_len) { + mbedtls_ssl_session session; + int ret; + + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_session_load(&session, sc_session->sdata, + sc_session->sdata_len); + if(ret) { + failf(data, "SSL session error loading: -0x%x", (unsigned int)-ret); + } + else { + ret = mbedtls_ssl_set_session(&backend->ssl, &session); + if(ret) + failf(data, "SSL session error setting: -0x%x", (unsigned int)-ret); + } + mbedtls_ssl_session_free(&session); + if(!ret) { + infof(data, "SSL reusing session ID"); + return TRUE; + } + } + return FALSE; +} + static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -795,6 +827,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); int ret; + bool session_applied = FALSE; CURLcode result; char errorbuf[128]; @@ -899,8 +932,22 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); #endif + if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) { + /* FTP is a bitch. On TLS secured transfers, it is a common server + * option to require the client to use the SAME TLS session as on + * the control connection or it fails the request. See #22225. */ + struct Curl_ssl_session *scs = + Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET); + if(scs) { + if(mbed_apply_session(cf, data, scs)) { + CURL_TRC_CF(data, cf, "applied SSL session from control connection"); + session_applied = TRUE; + } + } + } + /* Check if there is a cached ID we can/should use here! */ - if(Curl_ssl_scache_use(cf, data)) { + if(!session_applied && Curl_ssl_scache_use(cf, data)) { struct Curl_ssl_session *sc_session = NULL; CURLcode sresult = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, &sc_session); @@ -1196,13 +1243,22 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, connssl->negotiated.alpn, 0, 0, &sc_session); sdata = NULL; /* call took ownership */ - if(!result) + if(!result && /* return a duplicate if asked for and FTP */ + (cf->conn->scheme->family == CURLPROTO_FTP)) { + Curl_ssl_session_destroy(connssl->session); + result = Curl_ssl_session_dup(sc_session, &connssl->session); + } + + if(!result) { result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key, sc_session); + sc_session = NULL; + } out: if(msession_alloced) mbedtls_ssl_session_free(&session); + Curl_ssl_session_destroy(sc_session); curlx_free(sdata); return result; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 10e3946bdfe3..a6ed5a78b0b7 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2637,17 +2637,20 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, SSL_SESSION *session, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len) + size_t quic_tp_len, + struct Curl_ssl_session **psession) { + struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL; unsigned char *der_session_buf = NULL; unsigned char *qtp_clone = NULL; CURLcode result = CURLE_OK; + if(psession) + *psession = NULL; if(!cf || !data) goto out; if(Curl_ssl_scache_use(cf, data)) { - struct Curl_ssl_session *sc_session = NULL; size_t der_session_size; unsigned char *der_session_ptr; size_t earlydata_max = 0; @@ -2689,17 +2692,29 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, earlydata_max, qtp_clone, quic_tp_len, &sc_session); der_session_buf = NULL; /* took ownership of sdata */ - if(!result) { #ifdef USE_APPLE_SECTRUST + if(!result) sc_session->sectrust_verified = octx->sectrust_verified; #endif + if(!result && psession && /* return a duplicate if asked for and FTP */ + (cf->conn->scheme->family == CURLPROTO_FTP)) { + result = Curl_ssl_session_dup(sc_session, &sc_dup); + } + if(!result) { result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); /* took ownership of `sc_session` */ + sc_session = NULL; } } out: curlx_free(der_session_buf); + if(!result && psession) { + *psession = sc_dup; + sc_dup = NULL; + } + Curl_ssl_session_destroy(sc_session); + Curl_ssl_session_destroy(sc_dup); return result; } @@ -2713,8 +2728,14 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) struct Curl_easy *data = CF_DATA_CURRENT(cf); struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct Curl_ssl_session *session = NULL; Curl_ossl_add_session(cf, data, octx, connssl->peer.scache_key, - ssl_sessionid, connssl->negotiated.alpn, NULL, 0); + ssl_sessionid, connssl->negotiated.alpn, NULL, + 0, &session); + if(session) { /* remember current TLS session */ + Curl_ssl_session_destroy(connssl->session); + connssl->session = session; + } } return 0; } @@ -3319,6 +3340,82 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, return result; } +static bool ossl_apply_session( + struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + Curl_ossl_init_session_reuse_cb *sess_reuse_cb, + struct Curl_ssl_session *scs) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); + const unsigned char *der_sessionid = scs->sdata; + size_t der_sessionid_size = scs->sdata_len; + SSL_SESSION *ssl_session = NULL; + + /* If OpenSSL does not accept the session from the cache, this + * is not an error. We continue without it. */ + ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, + (long)der_sessionid_size); + if(ssl_session) { + if(!SSL_set_session(octx->ssl, ssl_session)) { + VERBOSE(char error_buffer[256]); + infof(data, "SSL: SSL_set_session not accepted, " + "continuing without: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + } + else { + if(conn_cfg->verifypeer && + (SSL_get_verify_result(octx->ssl) != X509_V_OK) +#ifdef USE_APPLE_SECTRUST + /* if sectrust is used and verified the session before */ + && (!ssl_config->native_ca_store || !scs->sectrust_verified) +#endif + ) { + /* Session was from unverified connection, cannot reuse here */ + SSL_set_session(octx->ssl, NULL); + infof(data, "SSL session not peer verified, not reusing"); + } + else { + infof(data, "SSL reusing session with ALPN '%s'", + scs->alpn ? scs->alpn : "-"); + octx->reused_session = TRUE; +#ifdef USE_APPLE_SECTRUST + octx->sectrust_session = scs->sectrust_verified; +#endif + infof(data, "SSL verify result: %lx", + (unsigned long)SSL_get_verify_result(octx->ssl)); +#ifdef HAVE_OPENSSL_EARLYDATA + if(ssl_config->earlydata && scs->alpn && + SSL_SESSION_get_max_early_data(ssl_session) && + !cf->conn->bits.connect_only && + (SSL_version(octx->ssl) == TLS1_3_VERSION)) { + bool do_early_data = FALSE; + if(sess_reuse_cb) + (void)sess_reuse_cb(cf, data, alpns, scs, &do_early_data); + if(do_early_data) { + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + Curl_alpn_restrict_to(alpns, scs->alpn); + } + } +#else + (void)alpns; + (void)ssl_config; + (void)sess_reuse_cb; +#endif + } + } + SSL_SESSION_free(ssl_session); + } + else { + infof(data, "SSL session not accepted by OpenSSL, continuing without"); + } + return (bool)octx->reused_session; +} + static CURLcode ossl_init_session_and_alpns( struct ossl_ctx *octx, struct Curl_cfilter *cf, @@ -3327,7 +3424,6 @@ static CURLcode ossl_init_session_and_alpns( const struct alpn_spec *alpns_requested, Curl_ossl_init_session_reuse_cb *sess_reuse_cb) { - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); struct alpn_spec alpns; CURLcode result; @@ -3335,78 +3431,26 @@ static CURLcode ossl_init_session_and_alpns( Curl_alpn_copy(&alpns, alpns_requested); octx->reused_session = FALSE; - if(Curl_ssl_scache_use(cf, data) && !conn_cfg->verifystatus) { + + if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) { + /* FTP is a bitch. On TLS secured transfers, it is a common server + * option to require the client to use the SAME TLS session as on + * the control connection or it fails the request. See #22225. */ + struct Curl_ssl_session *scs = + Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET); + if(scs) { + if(ossl_apply_session(octx, cf, data, &alpns, sess_reuse_cb, scs)) + CURL_TRC_CF(data, cf, "applied SSL session from control connection"); + } + } + + if(!octx->reused_session && + Curl_ssl_scache_use(cf, data) && !conn_cfg->verifystatus) { struct Curl_ssl_session *scs = NULL; result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); if(!result && scs && scs->sdata && scs->sdata_len) { - const unsigned char *der_sessionid = scs->sdata; - size_t der_sessionid_size = scs->sdata_len; - SSL_SESSION *ssl_session = NULL; - - /* If OpenSSL does not accept the session from the cache, this - * is not an error. We continue without it. */ - ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, - (long)der_sessionid_size); - if(ssl_session) { - if(!SSL_set_session(octx->ssl, ssl_session)) { - VERBOSE(char error_buffer[256]); - infof(data, "SSL: SSL_set_session not accepted, " - "continuing without: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - } - else { - if(conn_cfg->verifypeer && - (SSL_get_verify_result(octx->ssl) != X509_V_OK) -#ifdef USE_APPLE_SECTRUST - /* if sectrust is used and verified the session before */ - && (!ssl_config->native_ca_store || !scs->sectrust_verified) -#endif - ) { - /* Session was from unverified connection, cannot reuse here */ - SSL_set_session(octx->ssl, NULL); - infof(data, "SSL session not peer verified, not reusing"); - } - else { - infof(data, "SSL reusing session with ALPN '%s'", - scs->alpn ? scs->alpn : "-"); - octx->reused_session = TRUE; -#ifdef USE_APPLE_SECTRUST - octx->sectrust_session = scs->sectrust_verified; -#endif - infof(data, "SSL verify result: %lx", - (unsigned long)SSL_get_verify_result(octx->ssl)); -#ifdef HAVE_OPENSSL_EARLYDATA - if(ssl_config->earlydata && scs->alpn && - SSL_SESSION_get_max_early_data(ssl_session) && - !cf->conn->bits.connect_only && - (SSL_version(octx->ssl) == TLS1_3_VERSION)) { - bool do_early_data = FALSE; - if(sess_reuse_cb) { - result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); - if(result) { - SSL_SESSION_free(ssl_session); - return result; - } - } - if(do_early_data) { - /* We only try the ALPN protocol the session used before, - * otherwise we might send early data for the wrong protocol */ - Curl_alpn_restrict_to(&alpns, scs->alpn); - } - } -#else - (void)ssl_config; - (void)sess_reuse_cb; -#endif - } - } - SSL_SESSION_free(ssl_session); - } - else { - infof(data, "SSL session not accepted by OpenSSL, continuing without"); - } + (void)ossl_apply_session(octx, cf, data, &alpns, sess_reuse_cb, scs); } Curl_ssl_scache_return(cf, data, peer->scache_key, scs); } diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 1daeef50dfbe..dfac78c21e57 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -191,7 +191,8 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, SSL_SESSION *session, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len); + size_t quic_tp_len, + struct Curl_ssl_session **psession); /* * Get the server cert, verify it and show it, etc., only call failf() if diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index ed2008a604f2..02dbb9f7c7e9 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -877,19 +877,6 @@ void Curl_ssl_peer_cleanup(struct ssl_peer *peer) peer->type = CURL_SSL_PEER_DNS; } -static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - if(connssl) { - connssl->ssl_impl->close(cf, data); - connssl->state = ssl_connection_none; - connssl->connecting_state = ssl_connect_1; - connssl->prefs_checked = FALSE; - Curl_ssl_peer_cleanup(&connssl->peer); - } - cf->connected = FALSE; -} - static ssl_peer_type get_peer_type(const char *hostname) { if(hostname && hostname[0]) { @@ -957,13 +944,18 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - CF_DATA_RESTORE(cf, save); - cf_ctx_free(cf->ctx); - cf->ctx = NULL; + struct ssl_connect_data *connssl = cf->ctx; + if(connssl) { + connssl->ssl_impl->close(cf, data); + connssl->state = ssl_connection_none; + connssl->connecting_state = ssl_connect_1; + connssl->prefs_checked = FALSE; + Curl_ssl_peer_cleanup(&connssl->peer); + Curl_ssl_session_destroy(connssl->session); + cf_ctx_free(connssl); + cf->ctx = NULL; + } + cf->connected = FALSE; } static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, @@ -1776,4 +1768,30 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, return result; } +struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data, + const struct Curl_cftype *cft, + int sockindex) +{ + if(data->conn && +#ifndef CURL_DISABLE_PROXY + ((cft == &Curl_cft_ssl) || (cft == &Curl_cft_ssl_proxy))) { +#else + (cft == &Curl_cft_ssl)) { +#endif + struct Curl_cfilter *cf1 = data->conn->cfilter[sockindex]; + for(; cf1; cf1 = cf1->next) { + /* A tunneling proxy does not offer end2end encryption, even if + * it does SSL itself (e.g. QUIC H3 proxy) */ + if(cf1->cft == cft) + break; + } + if(cf1) { + struct ssl_connect_data *connssl = cf1->ctx; + if(connssl) + return connssl->session; + } + } + return NULL; +} + #endif /* USE_SSL */ diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index fa5da6e4d595..b3ef76af1e2d 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -116,6 +116,7 @@ struct ssl_connect_data { const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ + struct Curl_ssl_session *session; /* TLS session in use or NULL */ struct curltime handshake_done; /* time when handshake finished */ struct { char *alpn; /* ALPN value or NULL */ @@ -203,6 +204,13 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, struct alpn_spec *alpns, struct Curl_ssl_session *scs, bool *do_early_data, bool early_data_allowed); + +/* Retrieve the SSL session held at the filter of type `cft` at + * data's connection at `sockindex` or NULL if not found/available. */ +struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data, + const struct Curl_cftype *cft, + int sockindex); + #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 98beeac0368e..a55394bc7809 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -44,7 +44,7 @@ #include "curl_trc.h" #include "curl_sha256.h" #include "rand.h" - +#include "curlx/strdup.h" /* a peer+tls-config we cache sessions for */ struct Curl_ssl_scache_peer { @@ -456,6 +456,54 @@ void Curl_ssl_session_destroy(struct Curl_ssl_session *s) } } +CURLcode Curl_ssl_session_dup(struct Curl_ssl_session *src, + struct Curl_ssl_session **pdest) +{ + struct Curl_ssl_session *dest = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if(!src || !pdest) + return CURLE_BAD_FUNCTION_ARGUMENT; + *pdest = NULL; + + dest = curlx_calloc(1, sizeof(*dest)); + if(!dest) + goto out; + + dest->ietf_tls_id = src->ietf_tls_id; + dest->valid_until = src->valid_until; + dest->earlydata_max = src->earlydata_max; + dest->sectrust_verified = src->sectrust_verified; + if(src->sdata_len) { + dest->sdata = curlx_memdup(src->sdata, src->sdata_len); + if(!dest->sdata) + goto out; + dest->sdata_len = src->sdata_len; + } + if(src->quic_tp_len) { + dest->quic_tp = curlx_memdup(src->quic_tp, src->quic_tp_len); + if(!dest->quic_tp) + goto out; + dest->quic_tp_len = src->quic_tp_len; + } + if(src->alpn) { + dest->alpn = curlx_strdup(src->alpn); + if(!dest->alpn) + goto out; + } + result = CURLE_OK; + +out: + if(!result) + *pdest = dest; + else { + *pdest = NULL; + if(dest) + Curl_ssl_session_destroy(dest); + } + return result; +} + static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) { Curl_llist_destroy(&peer->sessions, NULL); diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index fc4eb9ef51eb..5fd9d3c05481 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -157,6 +157,10 @@ CURLcode Curl_ssl_session_create2(void *sdata, size_t sdata_len, unsigned char *quic_tp, size_t quic_tp_len, struct Curl_ssl_session **psession); +/* Duplicate an ssl session */ +CURLcode Curl_ssl_session_dup(struct Curl_ssl_session *src, + struct Curl_ssl_session **pdest); + /* Destroy a `session` instance. Can be called with NULL. * Does NOT need locking. */ void Curl_ssl_session_destroy(struct Curl_ssl_session *s); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 09a8494edd9b..3084219f8762 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -397,14 +397,17 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, int ietf_tls_id, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len) + size_t quic_tp_len, + struct Curl_ssl_session **pscs) { CURLcode result = CURLE_OK; - struct Curl_ssl_session *sc_session = NULL; + struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL; unsigned char *sdata = NULL, *sdata_ptr, *qtp_clone = NULL; unsigned int sdata_len; unsigned int earlydata_max = 0; + if(pscs) + *pscs = NULL; if(!session) goto out; @@ -446,13 +449,23 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, earlydata_max, qtp_clone, quic_tp_len, &sc_session); sdata = NULL; /* took ownership of sdata */ + if(!result && pscs && /* return a duplicate if asked for and FTP */ + (cf->conn->scheme->family == CURLPROTO_FTP)) + result = Curl_ssl_session_dup(sc_session, &sc_dup); if(!result) { result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); /* took ownership of `sc_session` */ + sc_session = NULL; } out: curlx_free(sdata); + if(!result && pscs) { + *pscs = sc_dup; + sc_dup = NULL; + } + Curl_ssl_session_destroy(sc_session); + Curl_ssl_session_destroy(sc_dup); return result; } @@ -465,12 +478,17 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) if(cf && session) { struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct Curl_ssl_session *scs = NULL; DEBUGASSERT(connssl); DEBUGASSERT(data); if(connssl && data) { (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key, session, wolfSSL_version(ssl), - connssl->negotiated.alpn, NULL, 0); + connssl->negotiated.alpn, NULL, 0, &scs); + if(scs) { + Curl_ssl_session_destroy(connssl->session); + connssl->session = scs; + } } } return 0; @@ -498,36 +516,35 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, connssl->earlydata_max); } -static CURLcode wssl_setup_session( +static bool wssl_apply_session( struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wss, struct alpn_spec *alpns, - const char *ssl_peer_key, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) + Curl_wssl_init_session_reuse_cb *sess_reuse_cb, + struct Curl_ssl_session *scs) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct Curl_ssl_session *scs = NULL; - CURLcode result; + CURLcode result = CURLE_OK; + WOLFSSL_SESSION *session = NULL; + bool success = FALSE; - result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &scs); - if(!result && scs && scs->sdata && scs->sdata_len && + if(scs && scs->sdata && scs->sdata_len && (!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) { - WOLFSSL_SESSION *session; /* wolfSSL changes the passed pointer for whatever reasons, yikes */ const unsigned char *sdata = scs->sdata; session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)scs->sdata_len); if(session) { int ret = wolfSSL_set_session(wss->ssl, session); if(ret != WOLFSSL_SUCCESS) { - Curl_ssl_session_destroy(scs); - scs = NULL; infof(data, "cached session not accepted (%d), " "removing from cache", ret); + goto out; } else { infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); + success = TRUE; if(ssl_config->earlydata && !cf->conn->bits.connect_only && !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) { @@ -535,7 +552,6 @@ static CURLcode wssl_setup_session( if(sess_reuse_cb) { result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data); if(result) { - wolfSSL_SESSION_free(session); goto out; } } @@ -554,15 +570,14 @@ static CURLcode wssl_setup_session( #endif } } - wolfSSL_SESSION_free(session); } else { failf(data, "could not decode previous session"); } } out: - Curl_ssl_scache_return(cf, data, ssl_peer_key, scs); - return result; + wolfSSL_SESSION_free(session); + return success; } static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, @@ -1151,6 +1166,9 @@ static CURLcode wssl_init_ssl_handle( unsigned char transport, Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { + struct Curl_ssl_session *scs = NULL; + bool session_applied = FALSE; + /* Let's make an SSL structure */ wctx->ssl = wolfSSL_new(wctx->ssl_ctx); if(!wctx->ssl) { @@ -1170,11 +1188,30 @@ static CURLcode wssl_init_ssl_handle( (void)transport; #endif + if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) { + /* FTP is a bitch. On TLS secured transfers, it is a common server + * option to require the client to use the SAME TLS session as on + * the control connection or it fails the request. See #22225. */ + scs = Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET); + if(scs) { + session_applied = wssl_apply_session(cf, data, wctx, alpns, + sess_reuse_cb, scs); + if(session_applied) + CURL_TRC_CF(data, cf, "applied SSL session from control connection"); + scs = NULL; + } + } + /* Check if there is a cached ID we can/should use here! */ - if(Curl_ssl_scache_use(cf, data)) { + if(!session_applied && Curl_ssl_scache_use(cf, data)) { /* Set session from cache if there is one */ - (void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key, - sess_reuse_cb); + CURLcode result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); + if(!result && scs) { + if(wssl_apply_session(cf, data, wctx, alpns, sess_reuse_cb, scs)) + Curl_ssl_scache_return(cf, data, peer->scache_key, scs); + else + Curl_ssl_session_destroy(scs); + } } #ifdef HAVE_ALPN diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index 2490bf3e9cff..2016a0069f0e 100644 --- a/lib/vtls/wolfssl.h +++ b/lib/vtls/wolfssl.h @@ -88,7 +88,8 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, int ietf_tls_id, const char *alpn, unsigned char *quic_tp, - size_t quic_tp_len); + size_t quic_tp_len, + struct Curl_ssl_session **pscs); #endif CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 225d63fe6d19..31a3d55b6487 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -106,9 +106,7 @@ def httpd(env) -> Generator[Httpd, None, None]: @pytest.fixture(scope="session") def nghttpx(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]: nghttpx = NghttpxQuic(env=env) - if nghttpx.exists(): - if not nghttpx.supports_h3() and env.have_h3_curl(): - log.warning("nghttpx does not support QUIC, but curl does") + if nghttpx.exists() and nghttpx.supports_h3() and env.have_h3_curl(): nghttpx.clear_logs() assert nghttpx.initial_start() yield nghttpx diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index ac4c154c4722..18f93dede457 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -37,6 +37,10 @@ @pytest.mark.skipif(condition=not Env.has_vsftpd(), reason="missing vsftpd") +@pytest.mark.skipif(condition=Env.curl_uses_lib('rustls-ffi'), + reason="rustls does not support TLS session reuse") +@pytest.mark.skipif(condition=Env.curl_uses_lib('libressl'), + reason="libressl fails on TLS session reuse") class TestVsFTPD: SUPPORTS_SSL = True @@ -101,31 +105,39 @@ def test_31_02_download_1(self, env: Env, vsftpds: VsFTPD, docname): self.check_downloads(curl, srcfile, count) r.check_stats_timelines() - @pytest.mark.parametrize("docname", [ - 'data-1k', 'data-1m', 'data-10m' + @pytest.mark.parametrize("docname,count,secure", [ + ['data-1k', 10, True], + ['data-1m', 5, True], + ['data-1m', 5, False], + ['data-10m', 2,True] ]) - def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname): + def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) srcfile = os.path.join(vsftpds.docs_dir, f'{docname}') - count = 10 url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]' - r = curl.ftp_ssl_get(urls=[url], with_stats=True) + xargs = [] + if not secure: + xargs.append('--insecure') + r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=xargs) r.check_stats(count=count, http_status=226) self.check_downloads(curl, srcfile, count) assert r.total_connects == count + 1, 'should reuse the control conn' r.check_stats_timelines() - @pytest.mark.parametrize("docname", [ - 'data-1k', 'data-1m', 'data-10m' + @pytest.mark.parametrize("docname,count,secure", [ + ['data-1k', 10, True], + ['data-1m', 5, True], + ['data-1m', 5, False], + ['data-10m', 2,True] ]) - def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname): + def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) srcfile = os.path.join(vsftpds.docs_dir, f'{docname}') - count = 10 url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]' - r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=[ - '--parallel' - ]) + xargs = ['--parallel'] + if not secure: + xargs.append('--insecure') + r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=xargs) r.check_stats(count=count, http_status=226) self.check_downloads(curl, srcfile, count) assert r.total_connects > count + 1, 'should have used several control conns' diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index a466d130b6dc..df1258c2d210 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -37,6 +37,10 @@ @pytest.mark.skipif(condition=not Env.has_vsftpd(), reason="missing vsftpd") +@pytest.mark.skipif(condition=Env.curl_uses_lib('rustls-ffi'), + reason="rustls does not support TLS session reuse") +@pytest.mark.skipif(condition=Env.curl_uses_lib('libressl'), + reason="libressl fails on TLS session reuse") class TestFtpsVsFTPD: SUPPORTS_SSL = True @@ -101,15 +105,20 @@ def test_32_02_download_1(self, env: Env, vsftpds: VsFTPD, docname): self.check_downloads(curl, srcfile, count) r.check_stats_timelines() - @pytest.mark.parametrize("docname", [ - 'data-1k', 'data-1m', 'data-10m' + @pytest.mark.parametrize("docname,count,secure", [ + ['data-1k', 10, True], + ['data-1m', 5, True], + ['data-1m', 5, False], + ['data-10m', 2,True] ]) - def test_32_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname): + def test_32_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) srcfile = os.path.join(vsftpds.docs_dir, f'{docname}') - count = 10 url = f'ftps://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]' - r = curl.ftp_get(urls=[url], with_stats=True) + xargs = [] + if not secure: + xargs.append('--insecure') + r = curl.ftp_get(urls=[url], with_stats=True, extra_args=xargs) r.check_stats(count=count, http_status=226) self.check_downloads(curl, srcfile, count) assert r.total_connects == count + 1, 'should reuse the control conn' diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index 6c2d1db8c5f9..daaf904f1e4e 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -217,7 +217,7 @@ def _write_config(self): f'rsa_cert_file={creds.cert_file}', f'rsa_private_key_file={creds.pkey_file}', # require_ssl_reuse=YES means ctrl and data connection need to use the same session - 'require_ssl_reuse=NO', + 'require_ssl_reuse=YES', ]) if self._ssl_implicit: conf.extend([ From 0fff69d514aea3879cd8dabb767c571527d6b60b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 3 Jul 2026 00:18:35 +0200 Subject: [PATCH 0625/1062] configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows Syncing it with CMake/FindWolfSSL. `-lm` is not needed on Windows. As of mingw-w64 14.0.0 it's offered as a dummy library; in such case it wasn't causing an actual issue. `-lcryp32` is necessary when linking wolfSSL statically. Ref: #22249 Closes #22251 --- m4/curl-wolfssl.m4 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index a18659605e77..29ae6b0c6cfd 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -76,6 +76,8 @@ if test "$OPT_WOLFSSL" != "no"; then if test "$curl_cv_apple" = "yes"; then addlib="$addlib -framework Security -framework CoreFoundation" + elif test "$curl_cv_native_windows" = "yes"; then + addlib="$addlib -lcrypt32" else addlib="$addlib -lm" fi From 2e2c45831c058f1753701dd8bf043973bab136ed Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 3 Jul 2026 00:51:12 +0200 Subject: [PATCH 0626/1062] GHA/windows: fix MSYS2 UWP job name Also: - whitespace tidy-up. - tried building with MSYS2 wolfSSL, but still not compatible with curl. Ref: https://packages.msys2.org/base/mingw-w64-wolfssl Ref: #22251 Follow-up to 923db3515d3f3a707fd4cad6f05f9538899536d7 #18116 Closes #22252 --- .github/workflows/windows.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 89753963c094..c3e988cb60a3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -254,23 +254,23 @@ jobs: config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-proxy --enable-ntlm', install: 'openssl-devel libssh2-devel' } - { name: 'default', - build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: 'skiprun' , + build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver --enable-ntlm', install: 'openssl-devel libssh2-devel' } - { name: 'default', - build: 'cmake' , sys: 'msys' , env: 'x86_64' , tflags: '' , + build: 'cmake' , sys: 'msys' , env: 'x86_64' , tflags: '', config: '-DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON', install: 'openssl-devel libssh2-devel' } - { name: 'default R', - build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '' , + build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '', config: '--with-openssl --enable-ntlm', install: 'openssl-devel libssh2-devel' } # MinGW - { name: 'default', - build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , + build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver --enable-static --without-zlib', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2' } - { name: 'c-ares U', - build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '' , + build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '', config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --enable-static --disable-shared --enable-ca-native --enable-ntlm', install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-nghttp3 mingw-w64-ucrt-x86_64-libssh2' } - { name: 'schannel c-ares U', type: 'Debug', @@ -292,31 +292,31 @@ jobs: # Holds true after CVE-2025-14821 mitigations in 0.12.0. # https://github.com/curl/curl-for-win/blob/471a065705a16c61a343b15d3e4ef195e2df2f9e/libssh.sh#L6-L94 - { name: 'gnutls libssh', type: 'Debug', openssh: 'OpenSSH-Windows', - build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: '' , + build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: '', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_GNUTLS=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DCURL_ENABLE_NTLM=ON', install: 'mingw-w64-clang-x86_64-gnutls mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh unzip' } - { name: 'schannel R', type: 'Release', image: 'windows-11-arm', - build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun' , + build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_DROP_UNUSED=ON', install: 'mingw-w64-clang-aarch64-libssh2' } - { name: 'openssl', type: 'Release', chkprefill: '_chkprefill', - build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun' , + build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DUSE_PROXY_HTTP3=ON', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2' } - - { name: 'schannel', type: 'Release', test: 'uwp', - build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , + - { name: 'openssl', type: 'Release', test: 'uwp', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', install: 'mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-libssh2' } # { name: 'schannel', type: 'Release', test: 'uwp', - # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , + # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', # config: '--without-debug --with-schannel --disable-static', # install: 'mingw-w64-ucrt-x86_64-libssh2' } - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', - build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , + build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2' } - { name: 'MultiSSL R', type: 'Release', - build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun' , + build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_SCHANNEL=ON -DENABLE_ARES=ON -DENABLE_UNICODE=ON', install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false From 7bfdc6f44ae0b2e93d9f12597ee55dde62535eeb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 Jul 2026 22:38:11 +0200 Subject: [PATCH 0627/1062] setopt: return OK earlier for the deprecated h2 dep options No need to have them checked twice. Follow-up to bfbff7852f050232edd3e5 Closes #22250 --- lib/setopt.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index eb9ff2e39679..bfaeb728ae66 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1513,14 +1513,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, break; } -#ifdef USE_HTTP2 - case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: - /* not doing stream dependencies any longer, but accept options - * for backward compatibility */ - break; -#endif - default: return CURLE_UNKNOWN_OPTION; } @@ -2906,10 +2898,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_MIMEPOST: /* curl_mime * */ case CURLOPT_STDERR: /* FILE * */ case CURLOPT_SHARE: /* CURLSH * */ - case CURLOPT_STREAM_DEPENDS: /* CURL * */ - case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ case CURLOPT_CURLU: /* CURLU * */ return setopt_pointers(data, option, param); + case CURLOPT_STREAM_DEPENDS: /* CURL * */ + case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ + return CURLE_OK; default: break; } From 2d38f44121b4cea687dbc4e16a97941065f1a794 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 Jul 2026 09:31:00 +0200 Subject: [PATCH 0628/1062] vquic: add Curl_ prefix to some global functions Closes #22254 --- lib/vquic/cf-ngtcp2-cmn.c | 28 +++++++++---------- lib/vquic/cf-ngtcp2-proxy.c | 4 +-- lib/vquic/cf-ngtcp2.c | 6 ++-- lib/vquic/cf-quiche.c | 26 ++++++++--------- lib/vquic/vquic.c | 56 +++++++++++++++++++------------------ lib/vquic/vquic_int.h | 55 +++++++++++++++++++----------------- 6 files changed, 90 insertions(+), 85 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 9d47a574a615..c1f9ae40ce99 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -139,7 +139,7 @@ void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx) { if(ctx && ctx->initialized) { Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); + Curl_vquic_ctx_free(&ctx->q); Curl_bufcp_free(&ctx->stream_bufcp); curlx_dyn_free(&ctx->scratch); Curl_uint32_hash_destroy(&ctx->streams); @@ -921,7 +921,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->qlogfd = qfd; /* -1 if failure above */ quic_settings(ctx, data, pktx); - result = vquic_ctx_init(data, &ctx->q); + result = Curl_vquic_ctx_init(data, &ctx->q); if(result) return result; @@ -1228,7 +1228,7 @@ CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { CURL_TRC_CF(data, cf, "shutdown, flushing egress"); - result = vquic_flush(cf, data, &ctx->q); + result = Curl_vquic_flush(cf, data, &ctx->q); if(result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); result = CURLE_OK; @@ -1296,7 +1296,7 @@ void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, io_ctx->cf = cf; io_ctx->data = data; ngtcp2_path_storage_zero(&io_ctx->ps); - vquic_ctx_set_time(&ctx->q, pnow); + Curl_vquic_ctx_set_time(&ctx->q, pnow); io_ctx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } @@ -1308,7 +1308,7 @@ void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, struct cf_ngtcp2_ctx *ctx = cf->ctx; const struct curltime *pnow = Curl_pgrs_now(data); - vquic_ctx_update_time(&ctx->q, pnow); + Curl_vquic_ctx_update_time(&ctx->q, pnow); pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } @@ -1484,7 +1484,7 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, ngtcp2_path_storage_zero(&pktx->ps); } - result = vquic_flush(cf, data, &ctx->q); + result = Curl_vquic_flush(cf, data, &ctx->q); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -1532,11 +1532,11 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, } else if(nread > gsolen || (gsolen > path_max_payload_size && nread != gsolen)) { - /* The added packet is a PMTUD *or* the one(s) before the - * added were PMTUD and the last one is smaller. - * Flush the buffer before the last add. */ - result = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); + /* The added packet is a PMTUD *or* the one(s) before the added were + * PMTUD and the last one is smaller. Flush the buffer before the last + * add. */ + result = Curl_vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -1558,7 +1558,7 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, /* time to send */ CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); - result = vquic_send(cf, data, &ctx->q, gsolen); + result = Curl_vquic_send(cf, data, &ctx->q, gsolen); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -1649,8 +1649,8 @@ CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, if(ctx->q.sockfd != CURL_SOCKET_BAD) { /* Direct UDP socket (via happy eyeballs) */ CURL_TRC_CF(data, cf, "progress_ingress(socket)"); - return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts, &rctx); + return Curl_vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts, &rctx); } else { /* Tunneled QUIC (CONNECT-UDP through proxy) */ diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index fd2840890f86..7a7fe417c38a 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -674,13 +674,13 @@ static ssize_t cf_h3_proxy_recv_closed_stream(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " "but we did not want a body anyway, ignore error 0x%" PRIx64 " %s", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); nread = 0; goto out; } failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 " %s)", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index be1a3257c327..f03ec06a4506 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -457,12 +457,12 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " "but we did not want a body anyway, ignore error 0x%" PRIx64 " %s", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); return CURLE_OK; } failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 " %s)", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { @@ -947,7 +947,7 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->ssl_peer); - vquic_ctx_free(&ctx->q); + Curl_vquic_ctx_free(&ctx->q); if(ctx->h3conn) { nghttp3_conn_del(ctx->h3conn); ctx->h3conn = NULL; diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 31a3957ec372..479fbe3b637c 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -135,7 +135,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->ssl_peer); - vquic_ctx_free(&ctx->q); + Curl_vquic_ctx_free(&ctx->q); Curl_uint32_hash_destroy(&ctx->streams); curlx_dyn_free(&ctx->h1hdr); Curl_bufq_free(&ctx->writebuf); @@ -726,8 +726,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, rctx.data = data; rctx.pkts = 0; - result = vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_quiche_recv_pkts, &rctx); + result = Curl_vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_quiche_recv_pkts, &rctx); if(result) return result; @@ -796,7 +796,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, } } - result = vquic_flush(cf, data, &ctx->q); + result = Curl_vquic_flush(cf, data, &ctx->q); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -818,7 +818,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, if(result != CURLE_AGAIN) return result; /* Nothing more to add, flush and leave */ - result = vquic_send(cf, data, &ctx->q, gsolen); + result = Curl_vquic_send(cf, data, &ctx->q, gsolen); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -831,7 +831,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, ++pkt_count; if(nread < gsolen || pkt_count >= MAX_PKT_BURST) { - result = vquic_send(cf, data, &ctx->q, gsolen); + result = Curl_vquic_send(cf, data, &ctx->q, gsolen); if(result) { if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); @@ -874,12 +874,12 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%" PRIu64 "] error after response headers, " "but we did not want a body anyway, ignore error 0x%" PRIx64 " %s", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); return CURLE_OK; } failf(data, "HTTP/3 stream %" PRIu64 " reset by server (error 0x%" PRIx64 " %s)", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); + Curl_vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", stream->id, (int)result); @@ -903,7 +903,7 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, *pnread = 0; (void)buf; (void)blen; - vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); + Curl_vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!stream) return CURLE_RECV_ERROR; @@ -1114,7 +1114,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; *pnwritten = 0; - vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); + Curl_vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); result = cf_process_ingress(cf, data); if(result) @@ -1272,7 +1272,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); DEBUGASSERT(ctx->initialized); - result = vquic_ctx_init(data, &ctx->q); + result = Curl_vquic_ctx_init(data, &ctx->q); if(result) return result; @@ -1397,7 +1397,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, return CURLE_OK; } - vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); + Curl_vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!ctx->qconn) { result = cf_quiche_ctx_open(cf, data); @@ -1479,7 +1479,7 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, int err; ctx->shutdown_started = TRUE; - vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); + Curl_vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); if(err) { CURL_TRC_CF(data, cf, "error %d adding shutdown packet, " diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index dfb8346c1ad3..4cfd3fbac427 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -75,8 +75,8 @@ void Curl_quic_ver(char *p, size_t len) #endif } -CURLcode vquic_ctx_init(struct Curl_easy *data, - struct cf_quic_ctx *qctx) +CURLcode Curl_vquic_ctx_init(struct Curl_easy *data, + struct cf_quic_ctx *qctx) { Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, BUFQ_OPT_SOFT_LIMIT); @@ -95,24 +95,24 @@ CURLcode vquic_ctx_init(struct Curl_easy *data, } } #endif - vquic_ctx_set_time(qctx, Curl_pgrs_now(data)); + Curl_vquic_ctx_set_time(qctx, Curl_pgrs_now(data)); return CURLE_OK; } -void vquic_ctx_free(struct cf_quic_ctx *qctx) +void Curl_vquic_ctx_free(struct cf_quic_ctx *qctx) { Curl_bufq_free(&qctx->sendbuf); } -void vquic_ctx_set_time(struct cf_quic_ctx *qctx, - const struct curltime *pnow) +void Curl_vquic_ctx_set_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow) { qctx->last_op = *pnow; } -void vquic_ctx_update_time(struct cf_quic_ctx *qctx, - const struct curltime *pnow) +void Curl_vquic_ctx_update_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow) { qctx->last_op = *pnow; } @@ -333,8 +333,8 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, return result; } -CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx) +CURLcode Curl_vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx) { const unsigned char *buf; size_t blen, sent; @@ -380,24 +380,26 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_OK; } -CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen) +CURLcode Curl_vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen) { qctx->gsolen = gsolen; - return vquic_flush(cf, data, qctx); + return Curl_vquic_flush(cf, data, qctx); } -CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen, - size_t tail_len, size_t tail_gsolen) +CURLcode Curl_vquic_send_tail_split(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen, + size_t tail_len, size_t tail_gsolen) { DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len); qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; qctx->split_gsolen = gsolen; qctx->gsolen = tail_gsolen; - CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", + CURL_TRC_CF(data, cf, "Curl_vquic_send_tail_split: " + "[%zu gso=%zu][%zu gso=%zu]", qctx->split_len, qctx->split_gsolen, tail_len, qctx->gsolen); - return vquic_flush(cf, data, qctx); + return Curl_vquic_flush(cf, data, qctx); } #if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) @@ -435,7 +437,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkts_cb *recv_cb, void *userp) + Curl_vquic_recv_pkts_cb *recv_cb, void *userp) { #if defined(__linux__) && defined(UDP_GRO) #define MMSG_NUM 16 @@ -541,7 +543,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkts_cb *recv_cb, void *userp) + Curl_vquic_recv_pkts_cb *recv_cb, void *userp) { struct iovec msg_iov; struct msghdr msg; @@ -624,7 +626,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkts_cb *recv_cb, void *userp) + Curl_vquic_recv_pkts_cb *recv_cb, void *userp) { uint8_t buf[64 * 1024]; int bufsize = (int)sizeof(buf); @@ -685,11 +687,11 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ -CURLcode vquic_recv_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkts_cb *recv_cb, void *userp) +CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + Curl_vquic_recv_pkts_cb *recv_cb, void *userp) { CURLcode result; #ifdef HAVE_SENDMMSG @@ -885,7 +887,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, } #ifdef CURLVERBOSE -const char *vquic_h3_err_str(uint64_t error_code) +const char *Curl_vquic_h3_err_str(uint64_t error_code) { if(error_code <= UINT_MAX) { switch((unsigned int)error_code) { diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index db5183d43115..7d56ccdeb16e 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -53,9 +53,9 @@ typedef enum { } vquic_h3_error; #ifdef CURLVERBOSE -const char *vquic_h3_err_str(uint64_t error_code); +const char *Curl_vquic_h3_err_str(uint64_t error_code); #else -#define vquic_h3_err_str(x) "" +#define Curl_vquic_h3_err_str(x) "" #endif /* CURLVERBOSE */ struct cf_quic_ctx { @@ -80,37 +80,40 @@ struct cf_quic_ctx { #define H3_STREAM_CTX(ctx, data) \ ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) -CURLcode vquic_ctx_init(struct Curl_easy *data, - struct cf_quic_ctx *qctx); -void vquic_ctx_free(struct cf_quic_ctx *qctx); +CURLcode Curl_vquic_ctx_init(struct Curl_easy *data, + struct cf_quic_ctx *qctx); +void Curl_vquic_ctx_free(struct cf_quic_ctx *qctx); -void vquic_ctx_set_time(struct cf_quic_ctx *qctx, - const struct curltime *pnow); +void Curl_vquic_ctx_set_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow); -void vquic_ctx_update_time(struct cf_quic_ctx *qctx, - const struct curltime *pnow); +void Curl_vquic_ctx_update_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow); -CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen); +CURLcode Curl_vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen); -CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen, - size_t tail_len, size_t tail_gsolen); +CURLcode Curl_vquic_send_tail_split(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen, + size_t tail_len, size_t tail_gsolen); -CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx); +CURLcode Curl_vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx); -typedef CURLcode vquic_recv_pkts_cb(const unsigned char *buf, size_t buflen, - size_t gso_size, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp); +typedef CURLcode Curl_vquic_recv_pkts_cb(const unsigned char *buf, + size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp); -CURLcode vquic_recv_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkts_cb *recv_cb, void *userp); +CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + Curl_vquic_recv_pkts_cb *recv_cb, + void *userp); #ifdef USE_NGTCP2 struct ngtcp2_mem; From 2213e4bff09428cd7d822daa3dba0e34a8d16578 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 Jul 2026 13:10:50 +0200 Subject: [PATCH 0629/1062] THANKS: add Gary from the curl 4.6 release notes --- docs/THANKS | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/THANKS b/docs/THANKS index a70bf7527dd1..0f3722d0b1f6 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -1204,6 +1204,7 @@ gaoxingwang on github Garrett Holmstrom Garrett Squire Gary Maxwell +Gary W. Swearingen Gaurav Malhotra Gautam Kachroo Gautam Mani From 3dd6a87a7feaecbb66555b11a390b31bb4513b9f Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Fri, 3 Jul 2026 10:39:08 -0400 Subject: [PATCH 0630/1062] wolfssl: fix build for wolfssl without bio chain support - Do not mix declarations and code. (ISO C90 build error) Ref: https://github.com/curl/curl/pull/22252#issuecomment-4871171567 Closes https://github.com/curl/curl/pull/22255 --- lib/vtls/wolfssl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 3084219f8762..e6ee960117cc 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1539,15 +1539,17 @@ static CURLcode wssl_connect_step1(struct Curl_cfilter *cf, wolfSSL_set_bio(wssl->ssl, bio, bio); } #else /* !USE_BIO_CHAIN */ - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - if(sockfd > INT_MAX) { - failf(data, "SSL: socket value too large"); - return CURLE_SSL_CONNECT_ERROR; - } - /* pass the raw socket into the SSL layer */ - if(!wolfSSL_set_fd(wssl->ssl, (int)sockfd)) { - failf(data, "SSL: wolfSSL_set_fd failed"); - return CURLE_SSL_CONNECT_ERROR; + { + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + if(sockfd > INT_MAX) { + failf(data, "SSL: socket value too large"); + return CURLE_SSL_CONNECT_ERROR; + } + /* pass the raw socket into the SSL layer */ + if(!wolfSSL_set_fd(wssl->ssl, (int)sockfd)) { + failf(data, "SSL: wolfSSL_set_fd failed"); + return CURLE_SSL_CONNECT_ERROR; + } } #endif /* USE_BIO_CHAIN */ From 09bfd2c645eb89a98d2ff777503dc79249394062 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 Jul 2026 17:49:03 +0200 Subject: [PATCH 0631/1062] THANKS: remove duplicate, fix typo --- docs/THANKS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/THANKS b/docs/THANKS index 0f3722d0b1f6..59ed8abba81f 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -2854,7 +2854,6 @@ Rene Bernhardt Rene Rebe renjian on hackerone renovate[bot] -renovate[bot] RepoRascal on hackerone Reuven Wachtfogel RevaliQaQ on github @@ -2971,7 +2970,7 @@ Rolland Dudemaine Romain Coltel Romain Fliedel Romain Geissler -romamik om github +romamik on github Roman Koifman Roman Mamedov Roman Zharkov From d23fa6e4b6b960298a0a8683b9055e9ee9b9503b Mon Sep 17 00:00:00 2001 From: Joel Depooter Date: Fri, 3 Jul 2026 12:39:13 -0700 Subject: [PATCH 0632/1062] CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization' instead of 'Authentication' Closes #22256 --- docs/libcurl/opts/CURLOPT_UNRESTRICTED_AUTH.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_UNRESTRICTED_AUTH.md b/docs/libcurl/opts/CURLOPT_UNRESTRICTED_AUTH.md index 2dc15ca6b014..37a9fe56110d 100644 --- a/docs/libcurl/opts/CURLOPT_UNRESTRICTED_AUTH.md +++ b/docs/libcurl/opts/CURLOPT_UNRESTRICTED_AUTH.md @@ -36,12 +36,12 @@ when following locations, even when the host changes. This option is meaningful only when setting CURLOPT_FOLLOWLOCATION(3). Further, when this option is not used or set to **0L**, libcurl does not send -custom nor internally generated `Authentication:` or `Cookie:` headers on +custom nor internally generated `Authorization:` or `Cookie:` headers on requests done to other hosts than the one used for the initial URL. Another host means that one or more of hostname, protocol scheme or port number changed. -By default, libcurl only sends `Authentication:` or explicitly set `Cookie:` +By default, libcurl only sends `Authorization:` or explicitly set `Cookie:` headers to the initial host as given in the original URL, to avoid leaking username + password to other sites. From b8ceb430be43edf009836345ec4d9e1b9b3fa108 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 Jul 2026 23:32:08 +0200 Subject: [PATCH 0633/1062] cd2nroff: stricter checks for asterisks for italics The nroff (man page) version of the markdown docs could get asterisks mistreated as emphasis - seen in the CURLOPT_WRITEDATA manpage. The regex now more stricter requires a word character to follow an italics starting asterisk and there needs to be a word character or a closing parenthesis before the italics ending asterisk. Also: fix CURLINFO_CERTINFO.md which used wrong italics - it was not rendered correctly on GitHub and with other generic markdown parsers. Reported-by: Christian Ullrich Fixes #22257 Closes #22260 --- docs/libcurl/opts/CURLINFO_CERTINFO.md | 2 +- scripts/cd2nroff | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/libcurl/opts/CURLINFO_CERTINFO.md b/docs/libcurl/opts/CURLINFO_CERTINFO.md index 0882cbccd181..4d95ad7509b3 100644 --- a/docs/libcurl/opts/CURLINFO_CERTINFO.md +++ b/docs/libcurl/opts/CURLINFO_CERTINFO.md @@ -33,7 +33,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CERTINFO, # DESCRIPTION -Pass a pointer to a *struct curl_certinfo ** and it is set to point to a +Pass a pointer to a `struct curl_certinfo *` and it is set to point to a struct that holds info about the server's certificate chain, assuming you had CURLOPT_CERTINFO(3) enabled when the request was made. diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 23fc4285cbe1..3f5a447929c8 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -421,7 +421,7 @@ sub single { # **bold** $d =~ s/\*\*(\S.*?)\*\*/\\fB$1\\fP/g; # *italics* - $d =~ s/\*(\S.*?)\*/\\fI$1\\fP/g; + $d =~ s/\*(\w.*?[\w)])\*/\\fI$1\\fP/g; my $back = $d; From d169ad68faa5ed5ba2375e7502307a6262466d88 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Oct 2025 01:40:44 +0200 Subject: [PATCH 0634/1062] gssapi: add support for Apple GSS Framework Add support for the native GSS Framework on Apple targets, which is Apple's fork of Heimdal. This option allows to drop Apple's deprecated MIT Kerberos compatibility shim "mit-krb5/1.7-prerelease". Source code uses different headers, other than that no source changes are necessary. You can enable by configuring with: autotools: `--enable-gssapi-apple` CMake: `-DCURL_USE_GSSAPI=ON -DCURL_GSS_FLAVOR=Apple` These options are experimental, and may receive breaking updates till the GSS backend selection logic settles, for Apple and also for the GNU and MIT implementation. Version string: ``` curl 8.21.0-DEV (aarch64-apple-darwin25.4.0) libcurl/8.21.0-DEV OpenSSL/3.6.2 zlib/1.2.12 AppleIDN AppleGSS OpenLDAP/2.4.28/Apple ``` Also: - drop in-source deprecation warning suppressions when using AppleGSS. - GHA/macos: enable Apple GSS in CI jobs. Supported by: iOS 5.0+, iPadOS 5.0+, Mac Catalyst 13.0+, macOS 10.14+, visionOS 1.0+ Ref: https://developer.apple.com/documentation/gss Ref: #19109 Closes #22052 --- .github/workflows/macos.yml | 10 +- CMake/FindGSS.cmake | 242 ++++++++++++++++++---------------- CMake/curl-config.in.cmake | 2 +- CMakeLists.txt | 11 +- configure.ac | 251 +++++++++++++++++++++--------------- docs/INSTALL-CMAKE.md | 1 + lib/curl_config-cmake.h.in | 3 + lib/curl_gssapi.c | 4 +- lib/socks_gssapi.c | 4 +- lib/urldata.h | 5 +- lib/vauth/krb5_gssapi.c | 4 +- lib/vauth/spnego_gssapi.c | 4 +- lib/version.c | 4 +- 13 files changed, 308 insertions(+), 237 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b1a457dcdfbd..21e2617c9369 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,7 +39,7 @@ permissions: {} # - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) -# - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime) +# - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime), GSS Framework env: CURL_CI: github @@ -328,12 +328,12 @@ jobs: -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - - name: 'LibreSSL openldap krb5 c-ares +examples' + - name: 'LibreSSL openldap AppleGSS c-ares +examples' compiler: clang install: libressl krb5 openldap generate: >- - -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DENABLE_ARES=ON -DCURL_USE_GSSAPI=ON - -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 + -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DENABLE_ARES=ON + -DCURL_USE_GSSAPI=ON -DCURL_GSS_FLAVOR=Apple -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include -DLDAP_LIBRARY=/opt/homebrew/opt/openldap/lib/libldap.dylib -DLDAP_LBER_LIBRARY=/opt/homebrew/opt/openldap/lib/liblber.dylib @@ -737,6 +737,7 @@ jobs: -DUSE_NGHTTP2=OFF -DUSE_LIBIDN2=OFF \ -DCURL_USE_LIBPSL=OFF -DCURL_USE_LIBSSH2=OFF \ -DUSE_APPLE_IDN=ON -DUSE_APPLE_SECTRUST=ON \ + -DCURL_USE_GSSAPI=ON -DCURL_GSS_FLAVOR=Apple \ ${options} else export CFLAGS @@ -756,6 +757,7 @@ jobs: --without-nghttp2 --without-libidn2 \ --without-libpsl \ --with-apple-idn --with-apple-sectrust \ + --enable-gssapi-apple \ ${options} fi diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index a49d529d8154..46d768699de6 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -23,6 +23,10 @@ ########################################################################### # Find the GSS Kerberos library # +# This module accepts optional COMPONENTS to control the GSS library flavor: +# +# - Apple +# # Input variables: # # - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) @@ -32,7 +36,7 @@ # - `GSS_FOUND`: System has GSS. # - `GSS_VERSION`: Version of GSS. # - `CURL::gss`: GSS library target. -# - `INTERFACE_CURL_GSS_FLAVOR`: Custom property. "GNU" or "MIT" if detected. +# - `INTERFACE_CURL_GSS_FLAVOR`: Custom property. "Apple", "GNU" or "MIT" if detected. set(_gnu_modname "gss") set(_mit_modname "mit-krb5-gssapi") @@ -46,8 +50,12 @@ set(_gss_root_hints "${GSS_ROOT_DIR}" "$ENV{GSS_ROOT_DIR}") set(_gss_CFLAGS "") set(_gss_LIBRARY_DIRS "") +if(NOT APPLE AND GSS_FIND_COMPONENTS STREQUAL "Apple") + set(GSS_FIND_COMPONENTS "") +endif() + # Try to find library using system pkg-config if user did not specify root dir -if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") +if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}" AND NOT GSS_FIND_COMPONENTS STREQUAL "Apple") if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) pkg_search_module(_gss ${_mit_modname} ${_gnu_modname}) @@ -60,136 +68,146 @@ if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") endif() if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional approach. - find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin" HINTS ${_gss_root_hints} - NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) - # If not found in user-supplied directories, maybe system knows better - find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin") - - if(_gss_configure_script) - - set(_gss_INCLUDE_DIRS "") - set(_gss_LIBRARIES "") - - execute_process(COMMAND ${_gss_configure_script} "--cflags" "gssapi" - OUTPUT_VARIABLE _gss_cflags_raw - RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}") - - if(NOT _gss_configure_failed) # 0 means success - # Should also work in an odd case when multiple directories are given. - string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw) - string(REGEX REPLACE " +-(I)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") - string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") - - foreach(_flag IN LISTS _gss_cflags_raw) - if(_flag MATCHES "^-I") - string(REGEX REPLACE "^-I" "" _flag "${_flag}") - list(APPEND _gss_INCLUDE_DIRS "${_flag}") - else() - list(APPEND _gss_CFLAGS "${_flag}") - endif() - endforeach() - endif() + if(GSS_FIND_COMPONENTS STREQUAL "Apple") + find_path(_gss_INCLUDE_DIRS NAMES "GSS/gssapi.h" PATH_SUFFIXES "include") + find_library(_gss_LIBRARIES NAMES "GSS") - execute_process(COMMAND ${_gss_configure_script} "--libs" "gssapi" - OUTPUT_VARIABLE _gss_lib_flags - RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}") - - if(NOT _gss_configure_failed) # 0 means success - # This script gives us libraries and link directories. - string(STRIP "${_gss_lib_flags}" _gss_lib_flags) - string(REGEX REPLACE " +-(L|l)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") - string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") - - foreach(_flag IN LISTS _gss_lib_flags) - if(_flag MATCHES "^-l") - string(REGEX REPLACE "^-l" "" _flag "${_flag}") - list(APPEND _gss_LIBRARIES "${_flag}") - elseif(_flag MATCHES "^-L") - string(REGEX REPLACE "^-L" "" _flag "${_flag}") - list(APPEND _gss_LIBRARY_DIRS "${_flag}") - endif() - endforeach() + if(_gss_INCLUDE_DIRS AND _gss_LIBRARIES) + message(STATUS "Found AppleGSS: ${_gss_INCLUDE_DIRS}") + set(_gss_flavor "Apple") endif() + else() + find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin" HINTS ${_gss_root_hints} + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) + # If not found in user-supplied directories, maybe system knows better + find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin") - execute_process(COMMAND ${_gss_configure_script} "--version" - OUTPUT_VARIABLE _gss_version - RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE) - - # Older versions may not have the "--version" parameter. In this case we do not care. - if(_gss_configure_failed) - set(_gss_version 0) - else() - # Strip prefix string to leave the version number only - string(REPLACE "Kerberos 5 release " "" _gss_version "${_gss_version}") - endif() + if(_gss_configure_script) - execute_process(COMMAND ${_gss_configure_script} "--vendor" - OUTPUT_VARIABLE _gss_vendor - RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE) + set(_gss_INCLUDE_DIRS "") + set(_gss_LIBRARIES "") - # Older versions may not have the "--vendor" parameter. In this case we do not care. - if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal") - set(_gss_flavor "MIT") # assume a default, should not really matter - endif() + execute_process(COMMAND ${_gss_configure_script} "--cflags" "gssapi" + OUTPUT_VARIABLE _gss_cflags_raw + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}") - else() # Either there is no config script or we are on a platform that does not provide one (Windows?) + if(NOT _gss_configure_failed) # 0 means success + # Should also work in an odd case when multiple directories are given. + string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw) + string(REGEX REPLACE " +-(I)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") + string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") - find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include" "inc") + foreach(_flag IN LISTS _gss_cflags_raw) + if(_flag MATCHES "^-I") + string(REGEX REPLACE "^-I" "" _flag "${_flag}") + list(APPEND _gss_INCLUDE_DIRS "${_flag}") + else() + list(APPEND _gss_CFLAGS "${_flag}") + endif() + endforeach() + endif() - if(_gss_INCLUDE_DIRS) # We have found something - set(_gss_libdir_suffixes "") + execute_process(COMMAND ${_gss_configure_script} "--libs" "gssapi" + OUTPUT_VARIABLE _gss_lib_flags + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}") - cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}") - check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) - cmake_pop_check_state() + if(NOT _gss_configure_failed) # 0 means success + # This script gives us libraries and link directories. + string(STRIP "${_gss_lib_flags}" _gss_lib_flags) + string(REGEX REPLACE " +-(L|l)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") + string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") - if(_gss_have_mit_headers) - set(_gss_flavor "MIT") - if(WIN32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _gss_libdir_suffixes "lib/AMD64") - set(_gss_libname "gssapi64") - else() - list(APPEND _gss_libdir_suffixes "lib/i386") - set(_gss_libname "gssapi32") + foreach(_flag IN LISTS _gss_lib_flags) + if(_flag MATCHES "^-l") + string(REGEX REPLACE "^-l" "" _flag "${_flag}") + list(APPEND _gss_LIBRARIES "${_flag}") + elseif(_flag MATCHES "^-L") + string(REGEX REPLACE "^-L" "" _flag "${_flag}") + list(APPEND _gss_LIBRARY_DIRS "${_flag}") endif() - else() - list(APPEND _gss_libdir_suffixes "lib" "lib64") # those suffixes are not checked for HINTS - set(_gss_libname "gssapi_krb5") - endif() + endforeach() endif() - else() - find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include") - if(_gss_INCLUDE_DIRS) - set(_gss_flavor "GNU") - set(_gss_pc_requires ${_gnu_modname}) - set(_gss_libname "gss") + execute_process(COMMAND ${_gss_configure_script} "--version" + OUTPUT_VARIABLE _gss_version + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Older versions may not have the "--version" parameter. In this case we do not care. + if(_gss_configure_failed) + set(_gss_version 0) + else() + # Strip prefix string to leave the version number only + string(REPLACE "Kerberos 5 release " "" _gss_version "${_gss_version}") endif() - endif() - # If we have headers, look up libraries - if(_gss_flavor) - set(_gss_libdir_hints ${_gss_root_hints}) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) - cmake_path(GET _gss_INCLUDE_DIRS PARENT_PATH _gss_calculated_potential_root) + execute_process(COMMAND ${_gss_configure_script} "--vendor" + OUTPUT_VARIABLE _gss_vendor + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Older versions may not have the "--vendor" parameter. In this case we do not care. + if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal") + set(_gss_flavor "MIT") # assume a default, should not really matter + endif() + + else() # Either there is no config script or we are on a platform that does not provide one (Windows?) + + find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include" "inc") + + if(_gss_INCLUDE_DIRS) # We have found something + set(_gss_libdir_suffixes "") + + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}") + check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) + cmake_pop_check_state() + + if(_gss_have_mit_headers) + set(_gss_flavor "MIT") + if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _gss_libdir_suffixes "lib/AMD64") + set(_gss_libname "gssapi64") + else() + list(APPEND _gss_libdir_suffixes "lib/i386") + set(_gss_libname "gssapi32") + endif() + else() + list(APPEND _gss_libdir_suffixes "lib" "lib64") # those suffixes are not checked for HINTS + set(_gss_libname "gssapi_krb5") + endif() + endif() else() - get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY) + find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include") + + if(_gss_INCLUDE_DIRS) + set(_gss_flavor "GNU") + set(_gss_pc_requires ${_gnu_modname}) + set(_gss_libname "gss") + endif() endif() - list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) - find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes}) + # If we have headers, look up libraries + if(_gss_flavor) + set(_gss_libdir_hints ${_gss_root_hints}) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(GET _gss_INCLUDE_DIRS PARENT_PATH _gss_calculated_potential_root) + else() + get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY) + endif() + list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) + + find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes}) + endif() endif() endif() if(NOT _gss_flavor) - message(FATAL_ERROR "MIT or GNU GSS is required") + message(FATAL_ERROR "MIT, GNU or Apple GSS is required") endif() else() if(_gss_MODULE_NAME STREQUAL _gnu_modname) @@ -199,7 +217,7 @@ else() set(_gss_flavor "MIT") set(_gss_pc_requires ${_mit_modname}) else() - message(FATAL_ERROR "MIT or GNU GSS is required") + message(FATAL_ERROR "MIT, GNU or Apple GSS is required") endif() message(STATUS "Found GSS/${_gss_flavor} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") endif() diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake index b4300ca2108c..e782cb5e9274 100644 --- a/CMake/curl-config.in.cmake +++ b/CMake/curl-config.in.cmake @@ -74,7 +74,7 @@ if("@USE_ARES@") list(APPEND _curl_libs CURL::cares) endif() if("@HAVE_GSSAPI@") - find_dependency(GSS MODULE) + find_dependency(GSS MODULE COMPONENTS "@GSS_FLAVOR@") list(APPEND _curl_libs CURL::gss) endif() if("@USE_BACKTRACE@") diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aa412fc98a7..8fe4822265c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1345,14 +1345,18 @@ option(CURL_USE_GSSAPI "Use GSSAPI implementation" OFF) mark_as_advanced(CURL_USE_GSSAPI) if(CURL_USE_GSSAPI) - find_package(GSS MODULE) + set(CURL_GSS_FLAVOR "" CACHE STRING "Set preferred GSSAPI implementation ('Apple'). Default: MIT, then GNU (experimental)") + + find_package(GSS MODULE COMPONENTS "${CURL_GSS_FLAVOR}") set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) list(APPEND CURL_LIBS CURL::gss) - get_target_property(_gss_flavor CURL::gss INTERFACE_CURL_GSS_FLAVOR) - if(_gss_flavor STREQUAL "GNU") + get_target_property(GSS_FLAVOR CURL::gss INTERFACE_CURL_GSS_FLAVOR) + if(GSS_FLAVOR STREQUAL "Apple") + set(HAVE_GSSAPPLE 1) + elseif(GSS_FLAVOR STREQUAL "GNU") set(HAVE_GSSGNU 1) elseif(GSS_VERSION) # MIT set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"") @@ -2388,6 +2392,7 @@ if(NOT CURL_DISABLE_INSTALL) # CURL_SUPPORTED_PROTOCOLS_LIST # CURL_USE_CMAKECONFIG # CURL_USE_PKGCONFIG + # GSS_FLAVOR # HAVE_BROTLI # HAVE_GSSAPI # HAVE_LIBIDN2 diff --git a/configure.ac b/configure.ac index a8bf9703fa71..f376793bff96 100644 --- a/configure.ac +++ b/configure.ac @@ -1914,6 +1914,27 @@ AC_ARG_WITH(gssapi, ] ) +want_gss_apple="no" +if test "$curl_cv_apple" = "yes"; then + AC_MSG_CHECKING([whether to use Apple GSS Framework for GSS-API]) + AC_ARG_ENABLE(gssapi-apple, +AS_HELP_STRING([--enable-gssapi-apple],[Enable Apple GSS Framework (experimental)]) +AS_HELP_STRING([--disable-gssapi-apple],[Disable Apple GSS Framework (experimental) (default)]), [ + case "$enableval" in + yes) + want_gss="yes" + want_gss_apple="yes" + AC_MSG_RESULT([yes]) + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac + ], + AC_MSG_RESULT([no]) + ) +fi + : ${KRB5CONFIG:="$GSSAPI_ROOT/bin/krb5-config"} save_CPPFLAGS="$CPPFLAGS" @@ -1921,130 +1942,146 @@ AC_MSG_CHECKING([if GSS-API support is requested]) if test "$want_gss" = "yes"; then AC_MSG_RESULT(yes) - if test "$GSSAPI_ROOT" != "/usr"; then - CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) + if test "$want_gss_apple" = "yes"; then + AC_CHECK_HEADER(GSS/gssapi.h, [ + AC_DEFINE(HAVE_GSSAPPLE, 1, [if you have Apple GSS Framework]) + ],[ + AC_MSG_ERROR([--enable-gssapi-apple was specified, but GSS/gssapi.h was not found]) + ]) else - CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) - fi - if test -z "$GSSAPI_INCS"; then - if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then - GSSAPI_INCS=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --cflags gssapi` - elif test "$PKGCONFIG" != "no"; then - GSSAPI_INCS=`$PKGCONFIG --cflags mit-krb5-gssapi` - elif test -f "$KRB5CONFIG"; then - GSSAPI_INCS=`$KRB5CONFIG --cflags gssapi` - elif test "$GSSAPI_ROOT" != "yes"; then - GSSAPI_INCS="-I$GSSAPI_ROOT/include" + if test "$GSSAPI_ROOT" != "/usr"; then + CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) + else + CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) + fi + if test -z "$GSSAPI_INCS"; then + if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then + GSSAPI_INCS=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --cflags gssapi` + elif test "$PKGCONFIG" != "no"; then + GSSAPI_INCS=`$PKGCONFIG --cflags mit-krb5-gssapi` + elif test -f "$KRB5CONFIG"; then + GSSAPI_INCS=`$KRB5CONFIG --cflags gssapi` + elif test "$GSSAPI_ROOT" != "yes"; then + GSSAPI_INCS="-I$GSSAPI_ROOT/include" + fi fi - fi - CPPFLAGS="$CPPFLAGS $GSSAPI_INCS" + CPPFLAGS="$CPPFLAGS $GSSAPI_INCS" - AC_CHECK_HEADER(gss.h, - [ - dnl found in the given dirs - AC_DEFINE(HAVE_GSSGNU, 1, [if you have GNU GSS]) - gnu_gss=yes - ], - [ - dnl not found, check for MIT - AC_CHECK_HEADERS( - [gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h], - [], - [not_mit=1]) - if test "$not_mit" = "1"; then - dnl MIT not found - AC_MSG_ERROR([MIT or GNU GSS library required, but not found]) - fi - ] - ) + AC_CHECK_HEADER(gss.h, + [ + dnl found in the given dirs + AC_DEFINE(HAVE_GSSGNU, 1, [if you have GNU GSS]) + gnu_gss=yes + ], + [ + dnl not found, check for MIT + AC_CHECK_HEADERS( + [gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h], + [], + [not_mit=1]) + if test "$not_mit" = "1"; then + dnl MIT not found + AC_MSG_ERROR([MIT or GNU GSS library required, but not found]) + fi + ] + ) + fi else AC_MSG_RESULT(no) fi if test "$want_gss" = "yes"; then AC_DEFINE(HAVE_GSSAPI, 1, [if you have GSS-API libraries]) HAVE_GSSAPI=1 - curl_gss_msg="enabled (MIT Kerberos)" - link_pkgconfig='' - - if test -n "$gnu_gss"; then - curl_gss_msg="enabled (GNU GSS)" - LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" - LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" - LIBS="-lgss $LIBS" - link_pkgconfig=1 - elif test -z "$GSSAPI_LIB_DIR"; then - if test "$curl_cv_apple" = "yes"; then - LIBS="-lgssapi_krb5 -lresolv $LIBS" - else - if test "$GSSAPI_ROOT" != "/usr"; then - CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) + + if test "$want_gss_apple" = "yes"; then + curl_gss_msg="enabled (Apple GSS)" + GSS_LDFLAGS='-framework GSS' + LDFLAGS="$LDFLAGS $GSS_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $GSS_LDFLAGS" + else + curl_gss_msg="enabled (MIT Kerberos)" + link_pkgconfig='' + + if test -n "$gnu_gss"; then + curl_gss_msg="enabled (GNU GSS)" + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" + LIBS="-lgss $LIBS" + link_pkgconfig=1 + elif test -z "$GSSAPI_LIB_DIR"; then + if test "$curl_cv_apple" = "yes"; then + LIBS="-lgssapi_krb5 -lresolv $LIBS" else - CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) + if test "$GSSAPI_ROOT" != "/usr"; then + CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) + else + CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) + fi + if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then + dnl krb5-config does not have --libs-only-L or similar, put everything + dnl into LIBS + gss_libs=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --libs gssapi` + LIBS="$gss_libs $LIBS" + elif test "$PKGCONFIG" != "no"; then + gss_libs=`$PKGCONFIG --libs mit-krb5-gssapi` + LIBS="$gss_libs $LIBS" + link_pkgconfig=1 + elif test -f "$KRB5CONFIG"; then + dnl krb5-config does not have --libs-only-L or similar, put everything + dnl into LIBS + gss_libs=`$KRB5CONFIG --libs gssapi` + LIBS="$gss_libs $LIBS" + link_pkgconfig=1 + else + case $host in + *-hp-hpux*) + gss_libname="gss" + ;; + *) + gss_libname="gssapi" + ;; + esac + + if test "$GSSAPI_ROOT" != "yes"; then + LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff" + LDFLAGSPC="$LDFLAGSPC -L$GSSAPI_ROOT/lib$libsuff" + LIBS="-l$gss_libname $LIBS" + else + LIBS="-l$gss_libname $LIBS" + fi + fi fi + gss_version="" if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then - dnl krb5-config does not have --libs-only-L or similar, put everything - dnl into LIBS - gss_libs=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --libs gssapi` - LIBS="$gss_libs $LIBS" + gss_version=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --version | $SED 's/Kerberos 5 release //'` elif test "$PKGCONFIG" != "no"; then - gss_libs=`$PKGCONFIG --libs mit-krb5-gssapi` - LIBS="$gss_libs $LIBS" - link_pkgconfig=1 + gss_version=`$PKGCONFIG --modversion mit-krb5-gssapi` elif test -f "$KRB5CONFIG"; then - dnl krb5-config does not have --libs-only-L or similar, put everything - dnl into LIBS - gss_libs=`$KRB5CONFIG --libs gssapi` - LIBS="$gss_libs $LIBS" - link_pkgconfig=1 - else - case $host in - *-hp-hpux*) - gss_libname="gss" - ;; - *) - gss_libname="gssapi" - ;; - esac - - if test "$GSSAPI_ROOT" != "yes"; then - LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff" - LDFLAGSPC="$LDFLAGSPC -L$GSSAPI_ROOT/lib$libsuff" - LIBS="-l$gss_libname $LIBS" - else - LIBS="-l$gss_libname $LIBS" - fi + gss_version=`$KRB5CONFIG --version | $SED 's/Kerberos 5 release //'` + fi + if test -n "$gss_version"; then + AC_MSG_NOTICE([GSS-API MIT Kerberos version detected: $gss_version]) + AC_DEFINE_UNQUOTED([CURL_KRB5_VERSION], ["$gss_version"], [MIT Kerberos version]) fi - fi - gss_version="" - if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then - gss_version=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --version | $SED 's/Kerberos 5 release //'` - elif test "$PKGCONFIG" != "no"; then - gss_version=`$PKGCONFIG --modversion mit-krb5-gssapi` - elif test -f "$KRB5CONFIG"; then - gss_version=`$KRB5CONFIG --version | $SED 's/Kerberos 5 release //'` - fi - if test -n "$gss_version"; then - AC_MSG_NOTICE([GSS-API MIT Kerberos version detected: $gss_version]) - AC_DEFINE_UNQUOTED([CURL_KRB5_VERSION], ["$gss_version"], [MIT Kerberos version]) - fi - else - LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" - LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" - case $host in - *-hp-hpux*) - LIBS="-lgss $LIBS" - ;; - *) - LIBS="-lgssapi $LIBS" - ;; - esac - fi - if test -n "$link_pkgconfig"; then - if test -n "$gnu_gss"; then - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gss" else - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mit-krb5-gssapi" + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" + case $host in + *-hp-hpux*) + LIBS="-lgss $LIBS" + ;; + *) + LIBS="-lgssapi $LIBS" + ;; + esac + fi + if test -n "$link_pkgconfig"; then + if test -n "$gnu_gss"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gss" + else + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mit-krb5-gssapi" + fi fi fi else diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 52fc596cdb2c..d1e5251d4f26 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -346,6 +346,7 @@ Details via CMake - `CURL_BROTLI`: Use brotli (`ON`, `OFF` or `AUTO`). Default: `AUTO` - `CURL_USE_CMAKECONFIG`: Enable detecting dependencies via CMake Config. Default: `ON` for MSVC (except under vcpkg), if not cross-compiling. (experimental) +- `CURL_GSS_FLAVOR`: Set preferred GSSAPI implementation (`Apple`). Default: MIT, then GNU (experimental) - `CURL_USE_GNUTLS`: Enable GnuTLS for SSL/TLS. Default: `OFF` - `CURL_USE_GSASL`: Use libgsasl. Default: `OFF` - `CURL_USE_GSSAPI`: Use GSSAPI implementation. Default: `OFF` diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index add3b26aadf2..caedb552ad59 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -321,6 +321,9 @@ /* if you have the gssapi libraries */ #cmakedefine HAVE_GSSAPI 1 +/* if you have Apple GSS */ +#cmakedefine HAVE_GSSAPPLE 1 + /* if you have the GNU gssapi libraries */ #cmakedefine HAVE_GSSGNU 1 diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 07a6c1e7ed80..fbc3c262f166 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -55,7 +55,7 @@ #define CURL_ALIGN8 #endif -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -440,7 +440,7 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, } #endif /* CURLVERBOSE */ -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic pop #endif diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index e10899c7e745..9a5ad62f5c5e 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -35,7 +35,7 @@ #include "socks.h" #include "curlx/strdup.h" -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -593,7 +593,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return result; } -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic pop #endif diff --git a/lib/urldata.h b/lib/urldata.h index 8a716d559025..89ca31a0f7ca 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -98,7 +98,10 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #include "cf-socket.h" #ifdef HAVE_GSSAPI -# ifdef HAVE_GSSGNU +# ifdef HAVE_GSSAPPLE +# include +# include +# elif defined(HAVE_GSSGNU) # include # elif defined(HAVE_GSSAPI_H) # include diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index 738ce9a744a8..41c47c055d3b 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -33,7 +33,7 @@ #include "curl_gssapi.h" #include "curl_trc.h" -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -318,7 +318,7 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) } } -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic pop #endif diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 869a27fdc983..1483b1f1e5b6 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -32,7 +32,7 @@ #include "curl_gssapi.h" #include "curl_trc.h" -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -288,7 +288,7 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) nego->havemultiplerequests = FALSE; } -#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) && !defined(HAVE_GSSAPPLE) #pragma GCC diagnostic pop #endif diff --git a/lib/version.c b/lib/version.c index 299caee9fb7d..94d6c0bf43ac 100644 --- a/lib/version.c +++ b/lib/version.c @@ -242,7 +242,9 @@ char *curl_version(void) src[i++] = gsasl_buf; #endif #ifdef HAVE_GSSAPI -#ifdef HAVE_GSSGNU +#ifdef HAVE_GSSAPPLE + curl_msnprintf(gss_buf, sizeof(gss_buf), "AppleGSS"); +#elif defined(HAVE_GSSGNU) curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", GSS_VERSION); #elif defined(CURL_KRB5_VERSION) curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", CURL_KRB5_VERSION); From 69ac867f98fb438c242687c36526e2a090371d3d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 4 Jul 2026 12:19:47 +0200 Subject: [PATCH 0635/1062] TLS: drop support for TLS-SRP The options will simply do nothing. Closes #21965 --- CMakeLists.txt | 20 -- configure.ac | 33 --- docs/CIPHERS.md | 5 +- docs/DEPRECATE.md | 9 +- docs/INSTALL-CMAKE.md | 3 - docs/INSTALL.md | 1 - docs/cmdline-opts/proxy-tlsauthtype.md | 2 + docs/cmdline-opts/proxy-tlspassword.md | 2 + docs/cmdline-opts/proxy-tlsuser.md | 2 + docs/cmdline-opts/tlsauthtype.md | 2 + docs/cmdline-opts/tlspassword.md | 2 + docs/cmdline-opts/tlsuser.md | 2 + docs/cmdline-opts/version.md | 3 - docs/internals/TLS-SESSIONS.md | 11 +- docs/libcurl/curl_easy_setopt.md | 16 +- docs/libcurl/curl_easy_ssls_export.md | 3 +- .../opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md | 4 + .../opts/CURLOPT_PROXY_TLSAUTH_TYPE.md | 4 + .../opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md | 4 + docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md | 6 + docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md | 6 + docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md | 6 + docs/libcurl/symbols-in-versions | 18 +- docs/tests/FILEFORMAT.md | 2 - include/curl/curl.h | 18 +- lib/curl_config-cmake.h.in | 9 - lib/curl_setup.h | 2 +- lib/setopt.c | 15 +- lib/urldata.h | 8 - lib/version.c | 3 - lib/vtls/gtls.c | 93 +------- lib/vtls/gtls.h | 11 - lib/vtls/openssl.c | 25 -- lib/vtls/vtls_config.c | 27 +-- lib/vtls/vtls_config.h | 4 - lib/vtls/vtls_scache.c | 45 +--- m4/curl-gnutls.m4 | 12 - m4/curl-openssl.m4 | 22 -- scripts/spacecheck.pl | 1 - src/config2setopts.c | 37 +-- src/tool_cfgable.c | 6 - src/tool_cfgable.h | 6 - src/tool_getparam.c | 59 +---- src/tool_libinfo.c | 2 - src/tool_libinfo.h | 1 - tests/certs/Makefile.am | 4 +- tests/certs/Makefile.inc | 4 - tests/certs/srp-verifier-conf | 3 - tests/certs/srp-verifier-db | 2 - tests/data/DISABLED | 4 - tests/data/Makefile.am | 4 +- tests/data/data320.html | 19 -- tests/data/test320 | 55 ----- tests/data/test321 | 33 --- tests/data/test322 | 33 --- tests/data/test323 | 33 --- tests/data/test324 | 33 --- tests/runtests.pl | 23 -- tests/serverhelp.pm | 11 +- tests/servers.pm | 223 +----------------- tests/test1165.pl | 2 +- tests/test1177.pl | 1 + tests/unit/unit3304.c | 6 +- 63 files changed, 133 insertions(+), 932 deletions(-) delete mode 100644 tests/certs/srp-verifier-conf delete mode 100644 tests/certs/srp-verifier-db delete mode 100644 tests/data/data320.html delete mode 100644 tests/data/test320 delete mode 100644 tests/data/test321 delete mode 100644 tests/data/test322 delete mode 100644 tests/data/test323 delete mode 100644 tests/data/test324 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fe4822265c9..1c49e3cb37d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,8 +421,6 @@ mark_as_advanced(CURL_ENABLE_EXPORT_TARGET) option(CURL_DISABLE_ALTSVC "Disable alt-svc support" OFF) mark_as_advanced(CURL_DISABLE_ALTSVC) -option(CURL_DISABLE_SRP "Disable TLS-SRP support" OFF) -mark_as_advanced(CURL_DISABLE_SRP) option(CURL_DISABLE_COOKIES "Disable cookies support" OFF) mark_as_advanced(CURL_DISABLE_COOKIES) option(CURL_DISABLE_BASIC_AUTH "Disable Basic authentication" OFF) @@ -936,15 +934,6 @@ if(CURL_USE_GNUTLS) set(_valid_default_ssl_backend TRUE) endif() set(_curl_ca_bundle_supported TRUE) - - if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP) - cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::gnutls) - # In GnuTLS 3.8.0 (2023-02-10) and upper, this check always succeeds. - # Detecting actual TLS-SRP support needs poking the API at runtime. - check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP) - cmake_pop_check_state() - endif() endif() if(CURL_USE_RUSTLS) @@ -1012,7 +1001,6 @@ macro(curl_openssl_check_exists) cmake_push_check_state() if(USE_OPENSSL) list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DOPENSSL_SUPPRESS_DEPRECATED") # for SSL_CTX_set_srp_username deprecated since 3.0.0 if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) endif() @@ -1077,9 +1065,6 @@ if(USE_OPENSSL) if(NOT DEFINED HAVE_SSL_SET0_WBIO) curl_openssl_check_exists("SSL_set0_wbio" HAVE_SSL_SET0_WBIO) endif() - if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP) - curl_openssl_check_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP) - endif() endif() option(USE_HTTPSRR "Enable HTTPS RR support (experimental)" OFF) @@ -1204,10 +1189,6 @@ if(USE_PROXY_HTTP3) endif() endif() -if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) - set(USE_TLS_SRP 1) -endif() - if(NOT CURL_DISABLE_LDAP) if(WIN32 AND NOT WINDOWS_STORE) option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) @@ -2062,7 +2043,6 @@ curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) curl_add_if("NTLM" CURL_ENABLE_NTLM AND (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) -curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) curl_add_if("proxy-HTTP3" USE_PROXY_HTTP3) diff --git a/configure.ac b/configure.ac index f376793bff96..2b8a0e0f0c9e 100644 --- a/configure.ac +++ b/configure.ac @@ -174,7 +174,6 @@ dnl initialize all the info variables curl_zstd_msg="no (--with-zstd)" curl_gss_msg="no (--with-gssapi)" curl_gsasl_msg="no (--with-gsasl)" - curl_tls_srp_msg="no (--enable-tls-srp)" curl_res_msg="blocking (--enable-ares / --enable-threaded-resolver)" curl_ipv6_msg="no (--enable-ipv6)" curl_unix_sockets_msg="no (--enable-unix-sockets)" @@ -4584,33 +4583,6 @@ AS_HELP_STRING([--disable-ntlm],[Disable NTLM support]), AC_MSG_RESULT(no) ) -dnl ************************************************************ -dnl disable TLS-SRP authentication -dnl -AC_MSG_CHECKING([whether to enable TLS-SRP authentication]) -AC_ARG_ENABLE(tls-srp, -AS_HELP_STRING([--enable-tls-srp],[Enable TLS-SRP authentication]) -AS_HELP_STRING([--disable-tls-srp],[Disable TLS-SRP authentication]), -[ case "$enableval" in - no) - AC_MSG_RESULT(no) - want_tls_srp=no - ;; - *) - AC_MSG_RESULT(yes) - want_tls_srp=yes - ;; - esac ], - AC_MSG_RESULT(yes) - want_tls_srp=yes -) - -if test "$want_tls_srp" = "yes" && (test "$HAVE_GNUTLS_SRP" = "1" || test "$HAVE_OPENSSL_SRP" = "1"); then - AC_DEFINE(USE_TLS_SRP, 1, [Use TLS-SRP authentication]) - USE_TLS_SRP=1 - curl_tls_srp_msg="enabled" -fi - dnl ************************************************************ dnl disable Unix domain sockets support dnl @@ -5279,10 +5251,6 @@ if test "$CURL_ENABLE_NTLM" = "1"; then fi fi -if test "$USE_TLS_SRP" = "1"; then - SUPPORT_FEATURES="$SUPPORT_FEATURES TLS-SRP" -fi - if test "$USE_NGHTTP2" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP2" fi @@ -5558,7 +5526,6 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: zstd: ${curl_zstd_msg} GSS-API: ${curl_gss_msg} GSASL: ${curl_gsasl_msg} - TLS-SRP: ${curl_tls_srp_msg} resolver: ${curl_res_msg} IPv6: ${curl_ipv6_msg} Unix sockets: ${curl_unix_sockets_msg} diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 0b70bda7d9e1..0c1c3db666ce 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -224,9 +224,8 @@ the following way: * When the set string starts with '+', '-' or '!' it is *appended* to the priority string libcurl itself generates (separated by ':'). This initial - priority depends other settings such as CURLOPT_SSLVERSION(3), - CURLOPT_TLSAUTH_USERNAME(3) (for SRP) or if HTTP/3 (QUIC) - is being negotiated. + priority depends other settings such as CURLOPT_SSLVERSION(3) or if HTTP/3 + (QUIC) is being negotiated. * Otherwise, the set string fully *replaces* the libcurl generated one. While giving full control to the application, the set priority needs to provide for everything the transfer may need to negotiate. Example: if diff --git a/docs/DEPRECATE.md b/docs/DEPRECATE.md index fe00189182bb..62822375291a 100644 --- a/docs/DEPRECATE.md +++ b/docs/DEPRECATE.md @@ -12,14 +12,6 @@ email the as soon as possible and explain to us why this is a problem for you and how your use case cannot be satisfied properly using a workaround. -## TLS-SRP Authentication - -Transport Layer Security Secure Remote Password is a TLS feature that does not -work with TLS 1.3 or QUIC and is virtually unused by curl users and in -general. - -TLS-SRP support gets removed in August 2026. - ## drop SMB support The SMB protocol has weak security and is rarely used these days. @@ -83,3 +75,4 @@ Local crypto gets removed in October 2026. - SMB (became opt-in in 8.20.0) - NTLM (became opt-in in 8.20.0) - c-ares < 1.16.0 (removed in 8.20.0) +- TLS-SRP (removed in 8.22.0) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index d1e5251d4f26..c95ce3366bd7 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -299,7 +299,6 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_SHUFFLE_DNS`: Disable shuffle DNS feature. Default: `OFF` - `CURL_DISABLE_SMTP`: Disable SMTP. Default: `OFF` - `CURL_DISABLE_SOCKETPAIR`: Disable use of socketpair for curl_multi_poll(). Default: `OFF` -- `CURL_DISABLE_SRP`: Disable TLS-SRP support. Default: `OFF` - `CURL_DISABLE_TELNET`: Disable Telnet. Default: `OFF` - `CURL_DISABLE_TFTP`: Disable TFTP. Default: `OFF` - `CURL_DISABLE_TYPECHECK`: Disable curl_easy_setopt()/curl_easy_getinfo() type checking. Default: `OFF` @@ -516,11 +515,9 @@ the parent project, ideally in the "extra" find package redirect file: Available variables: - `HAVE_DES_ECB_ENCRYPT`: `DES_ecb_encrypt` present in OpenSSL (or fork). -- `HAVE_GNUTLS_SRP`: `gnutls_srp_verifier` present in GnuTLS. - `HAVE_LDAP_INIT_FD`: `ldap_init_fd` present in LDAP library. - `HAVE_LDAP_URL_PARSE`: `ldap_url_parse` present in LDAP library. - `HAVE_MBEDTLS_DES_CRYPT_ECB`: `mbedtls_des_crypt_ecb` present in mbedTLS <4. -- `HAVE_OPENSSL_SRP`: `SSL_CTX_set_srp_username` present in OpenSSL (or fork). - `HAVE_QUICHE_CONN_SET_QLOG_FD`: `quiche_conn_set_qlog_fd` present in quiche. - `HAVE_RUSTLS_SUPPORTED_HPKE`: `rustls_supported_hpke` present in Rustls (unused if Rustls is detected via `pkg-config`). diff --git a/docs/INSTALL.md b/docs/INSTALL.md index f98cdb11b01d..546f3edf1aed 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -634,7 +634,6 @@ disabling support for some features (run `./configure --help` to see them all): - `--disable-proxy` (HTTP and SOCKS proxies) - `--disable-socketpair` (socketpair for asynchronous name resolving) - `--disable-threaded-resolver` (threaded name resolver) -- `--disable-tls-srp` (Secure Remote Password authentication for TLS) - `--disable-unix-sockets` (Unix sockets) - `--disable-verbose` (eliminates debugging strings and error code strings) - `--disable-versioned-symbols` (versioned symbols) diff --git a/docs/cmdline-opts/proxy-tlsauthtype.md b/docs/cmdline-opts/proxy-tlsauthtype.md index 84becc149e99..24fa449fd03e 100644 --- a/docs/cmdline-opts/proxy-tlsauthtype.md +++ b/docs/cmdline-opts/proxy-tlsauthtype.md @@ -17,6 +17,8 @@ Example: # `--proxy-tlsauthtype` +Deprecated option. This has no functionality since 8.22.0. + Set TLS authentication type with HTTPS proxy. The only supported option is `SRP`, for TLS-SRP (RFC 5054). This option works only if the underlying libcurl is built with TLS-SRP support. diff --git a/docs/cmdline-opts/proxy-tlspassword.md b/docs/cmdline-opts/proxy-tlspassword.md index 63c252156627..5acc5e31a907 100644 --- a/docs/cmdline-opts/proxy-tlspassword.md +++ b/docs/cmdline-opts/proxy-tlspassword.md @@ -16,6 +16,8 @@ Example: # `--proxy-tlspassword` +Deprecated option. This has no functionality since 8.22.0. + Set password to use with the TLS authentication method specified with --proxy-tlsauthtype when using HTTPS proxy. Requires that --proxy-tlsuser is set. diff --git a/docs/cmdline-opts/proxy-tlsuser.md b/docs/cmdline-opts/proxy-tlsuser.md index 610a2169b885..549ab398157c 100644 --- a/docs/cmdline-opts/proxy-tlsuser.md +++ b/docs/cmdline-opts/proxy-tlsuser.md @@ -16,6 +16,8 @@ Example: # `--proxy-tlsuser` +Deprecated option. This has no functionality since 8.22.0. + Set username for use for HTTPS proxy with the TLS authentication method specified with --proxy-tlsauthtype. Requires that --proxy-tlspassword also is set. diff --git a/docs/cmdline-opts/tlsauthtype.md b/docs/cmdline-opts/tlsauthtype.md index 10eccb29adc4..ce8acd52d271 100644 --- a/docs/cmdline-opts/tlsauthtype.md +++ b/docs/cmdline-opts/tlsauthtype.md @@ -16,6 +16,8 @@ Example: # `--tlsauthtype` +Deprecated option. This has no functionality since 8.22.0. + Set TLS authentication type. Currently, the only supported option is `SRP`, for TLS-SRP (RFC 5054). If --tlsuser and --tlspassword are specified but --tlsauthtype is not, then this option defaults to `SRP`. This option works diff --git a/docs/cmdline-opts/tlspassword.md b/docs/cmdline-opts/tlspassword.md index 6a1bb23a1523..1fd46288187b 100644 --- a/docs/cmdline-opts/tlspassword.md +++ b/docs/cmdline-opts/tlspassword.md @@ -16,6 +16,8 @@ Example: # `--tlspassword` +Deprecated option. This has no functionality since 8.22.0. + Set password to use with the TLS authentication method specified with --tlsauthtype. Requires that --tlsuser is set. diff --git a/docs/cmdline-opts/tlsuser.md b/docs/cmdline-opts/tlsuser.md index 7f4636fa0a2e..568f3f4917bd 100644 --- a/docs/cmdline-opts/tlsuser.md +++ b/docs/cmdline-opts/tlsuser.md @@ -16,6 +16,8 @@ Example: # `--tlsuser` +Deprecated option. This has no functionality since 8.22.0. + Set username for use with the TLS authentication method specified with --tlsauthtype. Requires that --tlspassword also is set. diff --git a/docs/cmdline-opts/version.md b/docs/cmdline-opts/version.md index 35e9cf237af6..f17181a029c4 100644 --- a/docs/cmdline-opts/version.md +++ b/docs/cmdline-opts/version.md @@ -121,9 +121,6 @@ This build supports TLS session export/import, like with the --ssl-sessions. ## `SSPI` SSPI is supported. -## `TLS-SRP` -SRP (Secure Remote Password) authentication is supported for TLS. - ## `Unicode` Unicode support on Windows. diff --git a/docs/internals/TLS-SESSIONS.md b/docs/internals/TLS-SESSIONS.md index c3bf7038b447..fdca13ab1187 100644 --- a/docs/internals/TLS-SESSIONS.md +++ b/docs/internals/TLS-SESSIONS.md @@ -55,9 +55,7 @@ Examples: Different configurations produce different keys which is what curl needs when handling SSL session tickets. -One important thing: peer keys do not contain confidential information. If you -configure a client certificate or SRP authentication with username/password, -these are not part of the peer key. +One important thing: peer keys do not contain confidential information. Peer keys carry the hostnames you use curl for. They *do* leak the privacy of your communication. We recommend to *not* persist peer keys for this reason. @@ -76,10 +74,9 @@ its own peer_key and calls into the cache. The cache then looks for a ticket with exactly this peer_key. Peer keys between proxy SSL filters and SSL filters talking through a tunnel differ, as they talk to different peers. -If the connection filter wants to use a client certificate or SRP -authentication, the cache checks those as well. If the cache peer carries -client cert or SRP auth, the connection filter must have those with the same -values (and vice versa). +If the connection filter wants to use a client certificate, the cache checks +those as well. If the cache peer carries client certs, the connection filter +must have those with the same values (and vice versa). On a match, the connection filter gets the session ticket and feeds that to the TLS implementation which, on accepting it, tries to resume it for a diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 5f75596cff60..44d2132577b0 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -867,15 +867,18 @@ Proxy TLS 1.3 cipher suites to use. See CURLOPT_PROXY_TLS13_CIPHERS(3) ## CURLOPT_PROXY_TLSAUTH_PASSWORD -Proxy TLS authentication password. See CURLOPT_PROXY_TLSAUTH_PASSWORD(3) +**Deprecated option**. Proxy TLS authentication password. See +CURLOPT_PROXY_TLSAUTH_PASSWORD(3) ## CURLOPT_PROXY_TLSAUTH_TYPE -Proxy TLS authentication methods. See CURLOPT_PROXY_TLSAUTH_TYPE(3) +**Deprecated option**. Proxy TLS authentication methods. See +CURLOPT_PROXY_TLSAUTH_TYPE(3) ## CURLOPT_PROXY_TLSAUTH_USERNAME -Proxy TLS authentication username. See CURLOPT_PROXY_TLSAUTH_USERNAME(3) +**Deprecated option**. Proxy TLS authentication username. See +CURLOPT_PROXY_TLSAUTH_USERNAME(3) ## CURLOPT_PROXY_TRANSFER_MODE @@ -1244,15 +1247,16 @@ TLS 1.3 cipher suites to use. See CURLOPT_TLS13_CIPHERS(3) ## CURLOPT_TLSAUTH_PASSWORD -TLS authentication password. See CURLOPT_TLSAUTH_PASSWORD(3) +**Deprecated option**. TLS authentication password. See CURLOPT_TLSAUTH_PASSWORD(3) ## CURLOPT_TLSAUTH_TYPE -TLS authentication methods. See CURLOPT_TLSAUTH_TYPE(3) +**Deprecated option**. TLS authentication methods. See CURLOPT_TLSAUTH_TYPE(3) ## CURLOPT_TLSAUTH_USERNAME -TLS authentication username. See CURLOPT_TLSAUTH_USERNAME(3) +**Deprecated option**. TLS authentication username. See +CURLOPT_TLSAUTH_USERNAME(3) ## CURLOPT_TRAILERDATA diff --git a/docs/libcurl/curl_easy_ssls_export.md b/docs/libcurl/curl_easy_ssls_export.md index 1eb634fb5887..cb516254a25c 100644 --- a/docs/libcurl/curl_easy_ssls_export.md +++ b/docs/libcurl/curl_easy_ssls_export.md @@ -60,8 +60,7 @@ persisted: either **session_key** or **shamc** and always **sdata**. All other parameters are informative, e.g. allow the callback to act only on specific session tickets. -Note that SSL sessions that involve a client certificate or SRP -username/password are not exported. +Note that SSL sessions that involve a client certificate are not exported. # Export Function Parameter diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md index 17acca6761d6..5ef82f02d3a9 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md @@ -47,6 +47,10 @@ previous ones. Set it to NULL to disable its use again. NULL +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md index d1badf4be565..33b5075abd2c 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md @@ -54,6 +54,10 @@ options. blank +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md index c4db7aa81526..816a1063f7dc 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md @@ -47,6 +47,10 @@ previous ones. Set it to NULL to disable its use again. NULL +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md index 89dd79ebc946..fba7af2374de 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md @@ -30,6 +30,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TLSAUTH_PASSWORD, char *pwd); # DESCRIPTION +Deprecated option. It serves no purpose anymore. + Pass a char pointer as parameter, which should point to the null-terminated password to use for the TLS authentication method specified with the CURLOPT_TLSAUTH_TYPE(3) option. Requires that the CURLOPT_TLSAUTH_USERNAME(3) @@ -47,6 +49,10 @@ This feature relies on TLS-SRP which does not work with TLS 1.3. NULL +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md index 35ae6bf4674c..9b2bbe0bf54d 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md @@ -29,6 +29,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TLSAUTH_TYPE, char *type); # DESCRIPTION +Deprecated option. It serves no purpose anymore. + Pass a pointer to a null-terminated string as parameter. The string should be the method of the TLS authentication. Supported method is "SRP". @@ -51,6 +53,10 @@ TLS-SRP does not work with TLS 1.3. blank +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md index b1d352e80e95..01ea52d35e42 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md @@ -29,6 +29,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TLSAUTH_USERNAME, char *user); # DESCRIPTION +Deprecated option. It serves no purpose anymore. + Pass a char pointer as parameter, which should point to the null-terminated username to use for the TLS authentication method specified with the CURLOPT_TLSAUTH_TYPE(3) option. Requires that the CURLOPT_TLSAUTH_PASSWORD(3) @@ -46,6 +48,10 @@ This feature relies on TLS-SRP which does not work with TLS 1.3. NULL +# DEPRECATED + +This option was deprecated in 8.22.0. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 5bad9a98425d..faa3b5494696 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -151,8 +151,8 @@ CURL_TIMECOND_IFMODSINCE 7.9.7 CURL_TIMECOND_IFUNMODSINCE 7.9.7 CURL_TIMECOND_LASTMOD 7.9.7 CURL_TIMECOND_NONE 7.9.7 -CURL_TLSAUTH_NONE 7.21.4 -CURL_TLSAUTH_SRP 7.21.4 +CURL_TLSAUTH_NONE 7.21.4 8.22.0 +CURL_TLSAUTH_SRP 7.21.4 8.22.0 CURL_TRAILERFUNC_ABORT 7.64.0 CURL_TRAILERFUNC_OK 7.64.0 CURL_UPKEEP_INTERVAL_DEFAULT 7.62.0 @@ -184,7 +184,7 @@ CURL_VERSION_SPNEGO 7.10.8 CURL_VERSION_SSL 7.10 CURL_VERSION_SSPI 7.13.2 CURL_VERSION_THREADSAFE 7.84.0 -CURL_VERSION_TLSAUTH_SRP 7.21.4 +CURL_VERSION_TLSAUTH_SRP 7.21.4 8.22.0 CURL_VERSION_UNICODE 7.72.0 CURL_VERSION_UNIX_SOCKETS 7.40.0 CURL_VERSION_ZSTD 7.72.0 @@ -791,9 +791,9 @@ CURLOPT_PROXY_SSLKEY_BLOB 7.71.0 CURLOPT_PROXY_SSLKEYTYPE 7.52.0 CURLOPT_PROXY_SSLVERSION 7.52.0 CURLOPT_PROXY_TLS13_CIPHERS 7.61.0 -CURLOPT_PROXY_TLSAUTH_PASSWORD 7.52.0 -CURLOPT_PROXY_TLSAUTH_TYPE 7.52.0 -CURLOPT_PROXY_TLSAUTH_USERNAME 7.52.0 +CURLOPT_PROXY_TLSAUTH_PASSWORD 7.52.0 8.22.0 +CURLOPT_PROXY_TLSAUTH_TYPE 7.52.0 8.22.0 +CURLOPT_PROXY_TLSAUTH_USERNAME 7.52.0 8.22.0 CURLOPT_PROXY_TRANSFER_MODE 7.18.0 CURLOPT_PROXYAUTH 7.10.7 CURLOPT_PROXYHEADER 7.37.0 @@ -901,9 +901,9 @@ CURLOPT_TIMEOUT_MS 7.16.2 CURLOPT_TIMEVALUE 7.1 CURLOPT_TIMEVALUE_LARGE 7.59.0 CURLOPT_TLS13_CIPHERS 7.61.0 -CURLOPT_TLSAUTH_PASSWORD 7.21.4 -CURLOPT_TLSAUTH_TYPE 7.21.4 -CURLOPT_TLSAUTH_USERNAME 7.21.4 +CURLOPT_TLSAUTH_PASSWORD 7.21.4 8.22.0 +CURLOPT_TLSAUTH_TYPE 7.21.4 8.22.0 +CURLOPT_TLSAUTH_USERNAME 7.21.4 8.22.0 CURLOPT_TRAILERDATA 7.64.0 CURLOPT_TRAILERFUNCTION 7.64.0 CURLOPT_TRANSFER_ENCODING 7.21.6 diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index b19cd7f87f96..bd7ed82465e2 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -445,8 +445,6 @@ What server(s) this test case requires/uses. Available servers: - `https` - `https-proxy` - `https-mtls` -- `httptls+srp` -- `httptls+srp-ipv6` - `http-unix` - `imap` - `mqtt` diff --git a/include/curl/curl.h b/include/curl/curl.h index 7e59e43c4d2a..adc0c90e7259 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1863,13 +1863,16 @@ typedef enum { CURLOPT(CURLOPT_RESOLVE, CURLOPTTYPE_SLISTPOINT, 203), /* Set a username for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204, + 8.22.0, "Support was removed"), /* Set a password for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205, + 8.22.0, "Support was removed"), /* Set authentication type for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206, + 8.22.0, "Support was removed"), /* Set to 1 to enable the "TE:" header in HTTP requests to ask for compressed transfer-encoded responses. Set to 0 to disable the use of TE: @@ -2030,13 +2033,16 @@ typedef enum { CURLOPT(CURLOPT_PROXY_SSLVERSION, CURLOPTTYPE_VALUES, 250), /* Set a username for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 251), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, + 251, 8.22.0, "Support was removed"), /* Set a password for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 252), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, + 252, 8.22.0, "Support was removed"), /* Set authentication type for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253, + 8.22.0, "Support was removed"), /* name of the file keeping your private SSL-certificate for proxy */ CURLOPT(CURLOPT_PROXY_SSLCERT, CURLOPTTYPE_STRINGPOINT, 254), diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index caedb552ad59..fbafbc088a65 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -770,15 +770,6 @@ ${SIZEOF_TIME_T_CODE} /* to enable Apple OS-native certificate verification */ #cmakedefine USE_APPLE_SECTRUST 1 -/* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */ -#cmakedefine HAVE_OPENSSL_SRP 1 - -/* Define to 1 if GnuTLS has the gnutls_srp_verifier function. */ -#cmakedefine HAVE_GNUTLS_SRP 1 - -/* Define to 1 to enable TLS-SRP support. */ -#cmakedefine USE_TLS_SRP 1 - /* Define to 1 to query for HTTPSRR when using DoH */ #cmakedefine USE_HTTPSRR 1 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 065fd04dde3f..5a5d8dbf1f35 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1577,7 +1577,7 @@ typedef struct sockaddr_un { #ifdef USE_OPENSSL /* OpenSSL 3 marks these functions deprecated but we have no replacements (yet) so tell the compiler to not warn for them: - - DES_* (for NTLM), SSL_CTX_set_srp_* (for TLS-SRP) + - DES_* (for NTLM) - EVP_PKEY_get1_RSA, MD5_*, RSA_flags, RSA_free (auto-skipped for OpenSSL built with no-deprecated) */ # define OPENSSL_SUPPRESS_DEPRECATED diff --git a/lib/setopt.c b/lib/setopt.c index bfaeb728ae66..c6215e3e9150 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2404,26 +2404,13 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, s->rtp_out = ptr; break; #endif /* !CURL_DISABLE_RTSP */ -#ifdef USE_TLS_SRP case CURLOPT_TLSAUTH_USERNAME: - return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME], ptr); case CURLOPT_TLSAUTH_PASSWORD: - return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD], ptr); case CURLOPT_TLSAUTH_TYPE: - if(ptr && !curl_strequal(ptr, "SRP")) - result = CURLE_BAD_FUNCTION_ARGUMENT; - break; -#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: - return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME_PROXY], ptr); case CURLOPT_PROXY_TLSAUTH_PASSWORD: - return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); case CURLOPT_PROXY_TLSAUTH_TYPE: - if(ptr && !curl_strequal(ptr, "SRP")) - result = CURLE_BAD_FUNCTION_ARGUMENT; - break; -#endif -#endif + return CURLE_NOT_BUILT_IN; #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTSREADDATA: s->hsts_read_userp = ptr; diff --git a/lib/urldata.h b/lib/urldata.h index 89ca31a0f7ca..508ab729c144 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -835,14 +835,6 @@ enum dupstring { #ifndef CURL_DISABLE_SMTP STRING_MAIL_FROM, STRING_MAIL_AUTH, -#endif -#ifdef USE_TLS_SRP - STRING_TLSAUTH_USERNAME, /* TLS auth */ - STRING_TLSAUTH_PASSWORD, /* TLS auth */ -#ifndef CURL_DISABLE_PROXY - STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth */ - STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth */ -#endif #endif STRING_BEARER, /* , if used */ #ifdef USE_UNIX_SOCKETS diff --git a/lib/version.c b/lib/version.c index 94d6c0bf43ac..33dbe3c43697 100644 --- a/lib/version.c +++ b/lib/version.c @@ -521,9 +521,6 @@ static const struct feat features_table[] = { #ifdef GLOBAL_INIT_IS_THREADSAFE FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE), #endif -#ifdef USE_TLS_SRP - FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), -#endif #if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), #endif diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 8cc9c53f1c13..423179172f1e 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -313,10 +313,6 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) #define GNUTLS_CIPHERS "NORMAL:%PROFILE_MEDIUM:-ARCFOUR-128:" \ "-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it errors out if SRP is - requested in the priority string, so treat it specially - */ -#define GNUTLS_SRP "+SRP" #define QUIC_PRIORITY \ "NORMAL:%PROFILE_MEDIUM:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:" \ @@ -846,19 +842,6 @@ static CURLcode gtls_set_priority(struct Curl_cfilter *cf, curlx_dyn_init(&buf, 4096); -#ifdef USE_GNUTLS_SRP - if(conn_config->username) { - /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS disables TLS 1.3 support. */ - result = curlx_dyn_add(&buf, priority); - if(!result) - result = curlx_dyn_add(&buf, ":" GNUTLS_SRP); - if(result) - goto out; - priority = curlx_dyn_ptr(&buf); - } -#endif - if(conn_config->cipher_list) { if((conn_config->cipher_list[0] == '+') || (conn_config->cipher_list[0] == '-') || @@ -915,33 +898,6 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; -#ifdef USE_GNUTLS_SRP - if(config->username && Curl_auth_allowed_to_host(data)) { - infof(data, "Using TLS-SRP username: %s", config->username); - - rc = gnutls_srp_allocate_client_credentials(>ls->srp_client_cred); - if(rc == GNUTLS_E_UNIMPLEMENTED_FEATURE) { - failf(data, "GnuTLS: TLS-SRP support not built in: %s", - gnutls_strerror(rc)); - return CURLE_NOT_BUILT_IN; - } - else if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_allocate_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_OUT_OF_MEMORY; - } - - rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred, - config->username, - config->password); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_set_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } -#endif - ssl_config->certverifyresult = 0; /* Initialize TLS session as a client */ @@ -1056,25 +1012,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, } } -#ifdef USE_GNUTLS_SRP - /* put the credentials to the current session */ - if(config->username) { - rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP, - gtls->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - else -#endif - { - rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, - gtls->shared_creds->creds); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } + rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, + gtls->shared_creds->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; } if(config->verifystatus) { @@ -1728,21 +1670,10 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, if(config->verifypeer || config->verifyhost || config->issuercert) { -#ifdef USE_GNUTLS_SRP - if(ssl_config->primary.username && !config->verifypeer && - gnutls_cipher_get(session)) { - /* no peer cert, but auth is ok if we have SRP user and cipher and no - peer verify */ - } - else { -#endif - failf(data, "failed to get server cert"); - *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; - result = CURLE_PEER_FAILED_VERIFICATION; - goto out; -#ifdef USE_GNUTLS_SRP - } -#endif + failf(data, "failed to get server cert"); + *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; } infof(data, " common name: WARNING could not obtain"); } @@ -2275,12 +2206,6 @@ static void gtls_close(struct Curl_cfilter *cf, if(backend->gtls.shared_creds) { Curl_gtls_shared_creds_free(&backend->gtls.shared_creds); } -#ifdef USE_GNUTLS_SRP - if(backend->gtls.srp_client_cred) { - gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); - backend->gtls.srp_client_cred = NULL; - } -#endif } static CURLcode gtls_recv(struct Curl_cfilter *cf, diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 72aec29e32a1..0ad9a7e68c72 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -31,14 +31,6 @@ #include "curlx/timeval.h" -#ifdef HAVE_GNUTLS_SRP -/* the function exists */ -#ifdef USE_TLS_SRP -/* the functionality is not disabled */ -#define USE_GNUTLS_SRP -#endif -#endif - struct Curl_easy; struct Curl_cfilter; struct alpn_spec; @@ -64,9 +56,6 @@ void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds); struct gtls_ctx { gnutls_session_t session; struct gtls_shared_creds *shared_creds; -#ifdef USE_GNUTLS_SRP - gnutls_srp_client_credentials_t srp_client_cred; -#endif CURLcode io_result; /* result of last IO cfilter operation */ BIT(sent_shutdown); }; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index a6ed5a78b0b7..406037bcc33f 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3935,31 +3935,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } #endif -#if defined(HAVE_OPENSSL_SRP) && defined(USE_TLS_SRP) - if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { - char * const ssl_username = ssl_config->primary.username; - char * const ssl_password = ssl_config->primary.password; - infof(data, "Using TLS-SRP username: %s", ssl_username); - - if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) { - failf(data, "Unable to set SRP username"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) { - failf(data, "failed setting SRP password"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!conn_config->cipher_list) { - infof(data, "Setting cipher list SRP"); - - if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) { - failf(data, "failed setting SRP cipher list"); - return CURLE_SSL_CIPHER; - } - } - } -#endif /* HAVE_OPENSSL_SRP && USE_TLS_SRP */ - /* OpenSSL always tries to verify the peer. By setting the failure mode * to NONE, we allow the connect to complete, regardless of the outcome. * We then explicitly check the result and may try alternatives like diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index 4a9b69654b3c..dfc1549c10b0 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -133,10 +133,6 @@ void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc) curlx_safefree(sslc->key); curlx_safefree(sslc->key_type); curlx_safefree(sslc->key_passwd); -#ifdef USE_TLS_SRP - curlx_safefree(sslc->username); - curlx_safefree(sslc->password); -#endif sslc->deep_copy = FALSE; } } @@ -160,10 +156,6 @@ static bool match_ssl_primary_config(struct Curl_easy *data, Curl_safecmp(c1->CAfile, c2->CAfile) && Curl_safecmp(c1->issuercert, c2->issuercert) && Curl_safecmp(c1->clientcert, c2->clientcert) && -#ifdef USE_TLS_SRP - !Curl_timestrcmp(c1->username, c2->username) && - !Curl_timestrcmp(c1->password, c2->password) && -#endif curl_strequal(c1->cipher_list, c2->cipher_list) && curl_strequal(c1->cipher_list13, c2->cipher_list13) && curl_strequal(c1->curves, c2->curves) && @@ -219,18 +211,13 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, CLONE_STRING(curves); CLONE_STRING(signature_algorithms); CLONE_STRING(CRLfile); - /* SSL credentials: client certificate, SRP auth */ + /* SSL credentials: client certificate */ CLONE_STRING(clientcert); CLONE_STRING(cert_type); CLONE_STRING(key); CLONE_STRING(key_type); CLONE_STRING(key_passwd); CLONE_BLOB(key_blob); -#ifdef USE_TLS_SRP - CLONE_STRING(username); - CLONE_STRING(password); -#endif - return TRUE; } @@ -307,10 +294,6 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; sslc->primary.clientcert = data->set.str[STRING_CERT]; sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#endif } else { sslc->primary.pinned_key = NULL; @@ -321,10 +304,6 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, sslc->primary.key_passwd = NULL; sslc->primary.clientcert = NULL; sslc->primary.key_blob = NULL; -#ifdef USE_TLS_SRP - sslc->primary.username = NULL; - sslc->primary.password = NULL; -#endif } #ifndef CURL_DISABLE_PROXY @@ -370,10 +349,6 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif #endif /* CURL_DISABLE_PROXY */ return CURLE_OK; diff --git a/lib/vtls/vtls_config.h b/lib/vtls/vtls_config.h index 44e691dd267e..4372743fa2f7 100644 --- a/lib/vtls/vtls_config.h +++ b/lib/vtls/vtls_config.h @@ -47,10 +47,6 @@ struct ssl_primary_config { struct curl_blob *ca_info_blob; struct curl_blob *issuercert_blob; struct curl_blob *key_blob; -#ifdef USE_TLS_SRP - char *username; /* TLS username (for, e.g., SRP) */ - char *password; /* TLS password (for, e.g., SRP) */ -#endif char *curves; /* list of curves to use */ uint32_t version_max; /* max supported version the client wants to use */ uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index a55394bc7809..3ba97222858c 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -51,8 +51,6 @@ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ char *clientcert; char *key_passwd; - char *srp_username; - char *srp_password; struct Curl_llist sessions; void *sobj; /* object instance or NULL */ Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */ @@ -321,13 +319,6 @@ static CURLcode ssl_peer_key_build(struct ssl_primary_config *ssl, result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local); if(result) goto out; -#ifdef USE_TLS_SRP - if(ssl->username || ssl->password) { - result = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(result) - goto out; - } -#endif if(!tls_id || !tls_id[0]) { result = CURLE_FAILED_INIT; @@ -516,10 +507,6 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) peer->sobj_free = NULL; curlx_safefree(peer->clientcert); curlx_safefree(peer->key_passwd); -#ifdef USE_TLS_SRP - curlx_safefree(peer->srp_username); - curlx_safefree(peer->srp_password); -#endif curlx_safefree(peer->ssl_peer_key); peer->age = 0; peer->hmac_set = FALSE; @@ -545,7 +532,6 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) * imported using only the salt+hmac * - the peer key is global, e.g. carrying no relative paths */ peer->exportable = (!peer->clientcert && !peer->key_passwd && - !peer->srp_username && !peer->srp_password && (!peer->ssl_peer_key || cf_ssl_peer_key_is_global(peer->ssl_peer_key))); } @@ -554,8 +540,6 @@ static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, const char *ssl_peer_key, const char *clientcert, const char *key_passwd, - const char *srp_username, - const char *srp_password, const unsigned char *salt, const unsigned char *hmac) { @@ -587,17 +571,6 @@ static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, if(!peer->key_passwd) goto out; } - if(srp_username) { - peer->srp_username = curlx_strdup(srp_username); - if(!peer->srp_username) - goto out; - } - if(srp_password) { - peer->srp_password = curlx_strdup(srp_password); - if(!peer->srp_password) - goto out; - } - cf_ssl_cache_peer_update(peer); result = CURLE_OK; out: @@ -730,21 +703,12 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, if(!conn_config) { if(peer->clientcert || peer->key_passwd) return FALSE; -#ifdef USE_TLS_SRP - if(peer->srp_username || peer->srp_password) - return FALSE; -#endif return TRUE; } else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) return FALSE; if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd)) return FALSE; -#ifdef USE_TLS_SRP - if(Curl_timestrcmp(peer->srp_username, conn_config->username) || - Curl_timestrcmp(peer->srp_password, conn_config->password)) - return FALSE; -#endif return TRUE; } @@ -869,11 +833,6 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; const char *kpasswd = conn_config ? conn_config->key_passwd : NULL; - const char *username = NULL, *password = NULL; -#ifdef USE_TLS_SRP - username = conn_config ? conn_config->username : NULL; - password = conn_config ? conn_config->password : NULL; -#endif if(!ccert && conn_config && conn_config->cert_blob) { /* when using a client cert blob, create a name for it */ curl_msnprintf(buffer, sizeof(buffer), @@ -881,7 +840,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ } result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd, - username, password, NULL, NULL); + NULL, NULL); if(result) goto out; /* all ready */ @@ -1259,7 +1218,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data, peer = cf_ssl_get_free_peer(scache); if(peer) { result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL, - NULL, NULL, salt, hmac); + salt, hmac); if(result) goto out; } diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index 3eab1c5d3192..9da5cf6bd5c6 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -199,17 +199,5 @@ if test "$GNUTLS_ENABLED" = "1"; then if test "$USE_GNUTLS_NETTLE" = "1"; then LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE nettle" fi - - dnl --- - dnl We require GnuTLS with SRP support. - dnl - dnl In GnuTLS 3.8.0 (2023-02-10) and upper, this check always succeeds. - dnl Detecting actual TLS-SRP support needs poking the API at runtime. - dnl --- - AC_CHECK_LIB(gnutls, gnutls_srp_verifier, - [ - AC_DEFINE(HAVE_GNUTLS_SRP, 1, [if you have the function gnutls_srp_verifier]) - HAVE_GNUTLS_SRP=1 - ]) fi ]) diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 6ac2c9d2bfa3..26c9eb3fbb18 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -356,28 +356,6 @@ if test "$OPENSSL_ENABLED" = "1"; then AC_MSG_RESULT([no]) ]) - dnl --- - dnl We require OpenSSL with SRP support. - dnl --- - AC_MSG_CHECKING([for SRP support in OpenSSL]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #ifndef OPENSSL_SUPPRESS_DEPRECATED - #define OPENSSL_SUPPRESS_DEPRECATED - #endif - #include - ]],[[ - SSL_CTX_set_srp_username(NULL, NULL); - SSL_CTX_set_srp_password(NULL, NULL); - ]]) - ],[ - AC_MSG_RESULT([yes]) - AC_DEFINE(HAVE_OPENSSL_SRP, 1, [if you have the functions SSL_CTX_set_srp_username and SSL_CTX_set_srp_password]) - HAVE_OPENSSL_SRP=1 - ],[ - AC_MSG_RESULT([no]) - ]) - dnl --- dnl Whether the OpenSSL configuration is loaded automatically dnl --- diff --git a/scripts/spacecheck.pl b/scripts/spacecheck.pl index 416408e56ebe..18543dfd9a02 100755 --- a/scripts/spacecheck.pl +++ b/scripts/spacecheck.pl @@ -54,7 +54,6 @@ '^renovate\.json$', '^docs/DISTROS\.md$', '^projects/Windows/tmpl/.+\.vcxproj$', - '^tests/certs/srp-verifier-', '^tests/data/test', ); diff --git a/src/config2setopts.c b/src/config2setopts.c index f27783d9fa45..d7be234557bc 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -786,27 +786,6 @@ static CURLcode proxy_setopts(struct OperationConfig *config, CURL *curl) return result; } -static CURLcode tls_srp_setopts(struct OperationConfig *config, CURL *curl) -{ - CURLcode result = CURLE_OK; - if(config->tls_username) - MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); - if(config->tls_password) - MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password); - if(config->tls_authtype) - MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_TYPE, config->tls_authtype); - if(config->proxy_tls_username) - MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, - config->proxy_tls_username); - if(config->proxy_tls_password) - MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, - config->proxy_tls_password); - if(config->proxy_tls_authtype) - MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_TYPE, - config->proxy_tls_authtype); - return result; -} - static CURLcode setopt_post(struct OperationConfig *config, CURL *curl) { CURLcode result = CURLE_OK; @@ -982,12 +961,6 @@ static CURLcode protocol_setopts(struct OperationConfig *config, #endif } - if(feature_tls_srp) { - result = tls_srp_setopts(config, curl); - if(setopt_bad(result)) - return result; - } - return result; } @@ -1071,6 +1044,16 @@ static CURLcode misc_setopts(struct OperationConfig *config, CURL *curl) if(config->proto_redir_present) MY_SETOPT_STR(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); + my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); + my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); + + if(config->gssapi_delegation) + my_setopt_long(curl, CURLOPT_GSSAPI_DELEGATION, config->gssapi_delegation); + + MY_SETOPT_STR(curl, CURLOPT_MAIL_AUTH, config->mail_auth); + MY_SETOPT_STR(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid); + my_setopt_long(curl, CURLOPT_SASL_IR, config->sasl_ir); + if(config->unix_socket_path) { if(config->abstract_unix_socket) MY_SETOPT_STR(curl, CURLOPT_ABSTRACT_UNIX_SOCKET, diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 3feb3e782471..b0a5990177bd 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -81,12 +81,6 @@ static void free_config_fields(struct OperationConfig *config) curlx_safefree(config->range); curlx_safefree(config->userpwd); - curlx_safefree(config->tls_username); - curlx_safefree(config->tls_password); - curlx_safefree(config->tls_authtype); - curlx_safefree(config->proxy_tls_username); - curlx_safefree(config->proxy_tls_password); - curlx_safefree(config->proxy_tls_authtype); curlx_safefree(config->proxyuserpwd); curlx_safefree(config->proxy); diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 0f8bc6fe0824..e21c3c5b2d75 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -77,12 +77,6 @@ struct OperationConfig { char *dns_ipv6_addr; /* dot notation */ char *userpwd; char *login_options; - char *tls_username; - char *tls_password; - char *tls_authtype; - char *proxy_tls_username; - char *proxy_tls_password; - char *proxy_tls_authtype; char *proxyuserpwd; char *proxy; char *noproxy; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index a039ad1cb897..9d34ffad205c 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -264,9 +264,11 @@ static const struct LongShort aliases[] = { {"proxy-ssl-auto-client-cert", ARG_BOOL|ARG_TLS, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT}, {"proxy-tls13-ciphers", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLS13_CIPHERS}, - {"proxy-tlsauthtype", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLSAUTHTYPE}, - {"proxy-tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_TLSPASSWORD}, - {"proxy-tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_TLSUSER}, + {"proxy-tlsauthtype", ARG_STRG|ARG_TLS|ARG_DEPR, ' ', C_PROXY_TLSAUTHTYPE}, + {"proxy-tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR|ARG_DEPR, ' ', + C_PROXY_TLSPASSWORD}, + {"proxy-tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR|ARG_DEPR, ' ', + C_PROXY_TLSUSER}, {"proxy-tlsv1", ARG_NONE|ARG_TLS, ' ', C_PROXY_TLSV1}, {"proxy-user", ARG_STRG|ARG_CLEAR, 'U', C_PROXY_USER}, {"proxy1.0", ARG_STRG, ' ', C_PROXY1_0}, @@ -338,9 +340,9 @@ static const struct LongShort aliases[] = { {"tls-earlydata", ARG_BOOL|ARG_TLS, ' ', C_TLS_EARLYDATA}, {"tls-max", ARG_STRG|ARG_TLS, ' ', C_TLS_MAX}, {"tls13-ciphers", ARG_STRG|ARG_TLS, ' ', C_TLS13_CIPHERS}, - {"tlsauthtype", ARG_STRG|ARG_TLS, ' ', C_TLSAUTHTYPE}, - {"tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_TLSPASSWORD}, - {"tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_TLSUSER}, + {"tlsauthtype", ARG_STRG|ARG_TLS|ARG_DEPR, ' ', C_TLSAUTHTYPE}, + {"tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR|ARG_DEPR, ' ', C_TLSPASSWORD}, + {"tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR|ARG_DEPR, ' ', C_TLSUSER}, {"tlsv1", ARG_NONE|ARG_TLS, '1', C_TLSV1}, {"tlsv1.0", ARG_NONE|ARG_TLS, ' ', C_TLSV1_0}, {"tlsv1.1", ARG_NONE|ARG_TLS, ' ', C_TLSV1_1}, @@ -2787,55 +2789,12 @@ static ParameterError opt_string(struct OperationConfig *config, case C_HOSTPUBSHA256: /* --hostpubsha256 */ err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); break; - case C_TLSUSER: /* --tlsuser */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->tls_username, nextarg, DENY_BLANK); - break; - case C_TLSPASSWORD: /* --tlspassword */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->tls_password, nextarg, ALLOW_BLANK); - break; - case C_TLSAUTHTYPE: /* --tlsauthtype */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else { - err = getstr(&config->tls_authtype, nextarg, DENY_BLANK); - if(!err && config->tls_authtype && strcmp(config->tls_authtype, "SRP")) - err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ - } - break; case C_PINNEDPUBKEY: /* --pinnedpubkey */ err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK); break; case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */ err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK); break; - case C_PROXY_TLSUSER: /* --proxy-tlsuser */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK); - break; - case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK); - break; - case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else { - err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK); - if(!err && config->proxy_tls_authtype && - strcmp(config->proxy_tls_authtype, "SRP")) - err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ - } - break; case C_PROXY_CERT_TYPE: /* --proxy-cert-type */ err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK); break; @@ -3069,6 +3028,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ *usedarg = consumearg; /* mark it as used */ } if(a->desc & ARG_DEPR) { + if(a->desc & ARG_CLEAR) + cleanarg(CURL_UNCONST(nextarg)); opt_depr(a); break; } diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 9aee23428090..2eec25f0835d 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -75,7 +75,6 @@ bool feature_ntlm = FALSE; bool feature_ntlm_wb = FALSE; bool feature_spnego = FALSE; bool feature_ssl = FALSE; -bool feature_tls_srp = FALSE; bool feature_zstd = FALSE; bool feature_ech = FALSE; bool feature_ssls_export = FALSE; @@ -112,7 +111,6 @@ static struct feature_name_presentp { { "SSPI", NULL, CURL_VERSION_SSPI }, { "SSLS-EXPORT", &feature_ssls_export, 0 }, { "threadsafe", NULL, CURL_VERSION_THREADSAFE }, - { "TLS-SRP", &feature_tls_srp, CURL_VERSION_TLSAUTH_SRP }, { "Unicode", NULL, CURL_VERSION_UNICODE }, { "UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS }, { "zstd", &feature_zstd, CURL_VERSION_ZSTD }, diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index e8c3517a9b2a..481db1e58e1c 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -58,7 +58,6 @@ extern bool feature_ntlm; extern bool feature_ntlm_wb; extern bool feature_spnego; extern bool feature_ssl; -extern bool feature_tls_srp; extern bool feature_zstd; extern bool feature_ech; extern bool feature_ssls_export; diff --git a/tests/certs/Makefile.am b/tests/certs/Makefile.am index a88f47417515..071855d3a6e0 100644 --- a/tests/certs/Makefile.am +++ b/tests/certs/Makefile.am @@ -23,10 +23,10 @@ ########################################################################### AUTOMAKE_OPTIONS = foreign -# Get CERTCONFIG_CA, CERTCONFIGS, GENERATEDCERTS, SRPFILES variables +# Get CERTCONFIG_CA, CERTCONFIGS, GENERATEDCERTS variables include Makefile.inc -EXTRA_DIST = $(CERTCONFIG_CA) $(CERTCONFIGS) $(SRPFILES) CMakeLists.txt \ +EXTRA_DIST = $(CERTCONFIG_CA) $(CERTCONFIGS) CMakeLists.txt \ genserv.pl CLEANFILES = $(GENERATEDCERTS) diff --git a/tests/certs/Makefile.inc b/tests/certs/Makefile.inc index 06516a09d5d0..9c0d5ff3ec56 100644 --- a/tests/certs/Makefile.inc +++ b/tests/certs/Makefile.inc @@ -82,7 +82,3 @@ GENERATEDCERTS = \ test-localhost0h.pem \ test-localhost0h.pub.der \ test-localhost0h.pub.pem - -SRPFILES = \ - srp-verifier-conf \ - srp-verifier-db diff --git a/tests/certs/srp-verifier-conf b/tests/certs/srp-verifier-conf deleted file mode 100644 index 67825ceabd9f..000000000000 --- a/tests/certs/srp-verifier-conf +++ /dev/null @@ -1,3 +0,0 @@ -1:Ewl2hcjiutMd3Fu2lgFnUXWSc67TVyy2vwYCKoS9MLsrdJVT9RgWTCuEqWJrfB6uE3LsE9GkOlaZabS7M29sj5TnzUqOLJMjiwEzArfiLr9WbMRANlF68N5AVLcPWvNx6Zjl3m5Scp0BzJBz9TkgfhzKJZ.WtP3Mv/67I/0wmRZ:2 -2:dUyyhxav9tgnyIg65wHxkzkb7VIPh4o0lkwfOKiPp4rVJrzLRYVBtb76gKlaO7ef5LYGEw3G.4E0jbMxcYBetDy2YdpiP/3GWJInoBbvYHIRO9uBuxgsFKTKWu7RnR7yTau/IrFTdQ4LY/q.AvoCzMxV0PKvD9Odso/LFIItn8PbTov3VMn/ZEH2SqhtpBUkWtmcIkEflhX/YY/fkBKfBbe27/zUaKUUZEUYZ2H2nlCL60.JIPeZJSzsu/xHDVcx:2 -3:2iQzj1CagQc/5ctbuJYLWlhtAsPHc7xWVyCPAKFRLWKADpASkqe9djWPFWTNTdeJtL8nAhImCn3Sr/IAdQ1FrGw0WvQUstPx3FO9KNcXOwisOQ1VlL.gheAHYfbYyBaxXL.NcJx9TUwgWDT0hRzFzqSrdGGTN3FgSTA1v4QnHtEygNj3eZ.u0MThqWUaDiP87nqha7XnT66bkTCkQ8.7T8L4KZjIImrNrUftedTTBi.WCi.zlrBxDuOM0da0JbUkQlXqvp0yvJAPpC11nxmmZOAbQOywZGmu9nhZNuwTlxjfIro0FOdthaDTuZRL9VL7MRPUDo/DQEyW.d4H.UIlzp:2 diff --git a/tests/certs/srp-verifier-db b/tests/certs/srp-verifier-db deleted file mode 100644 index 2f851a342dc6..000000000000 --- a/tests/certs/srp-verifier-db +++ /dev/null @@ -1,2 +0,0 @@ -jsmith:34fPk7u.w3R/M1k2sQ9F.04GZqLKAsqDn44CHGu7ML0M8VWwu1p79OLxi6jRhSNdSM46Kx9GRVyJLXz7eok53..A6X5p3NdnMSYX8WwYrDmuseHDr.eua7gjd04S4EoY4ZuKix2.WGAsMTwk86AmTvcqyzqsH7GDhGOHEhjP5zs:lTjBBoK04K9vTKiL10rI/:1 -alice:3IIP1g1HDTN6VEUr8DUkMleocoC1cpuFZnmunDaGhMyIsw8LAwCc7ZapWaC66gZSyis4ezSuCqvhsJdwdc.0es2UrH6PBkBQflcQDuC.dEpjhWgAcH2Dw.2qU.E0ApQzLkcKOjXMQ2R6jMBL14kEUPjjHS3aa16yB.Afj3bNPdf:1JxU4GkweUEii6.b0grkzU:1 diff --git a/tests/data/DISABLED b/tests/data/DISABLED index 60dca3ca6a64..d4c90a0df913 100644 --- a/tests/data/DISABLED +++ b/tests/data/DISABLED @@ -27,10 +27,6 @@ # per line. # Lines starting with '#' letters are treated as comments. # -# Uses SRP to "a server not supporting it" but modern stunnel versions -# silently accept it and remain happy -323 -# 594 836 882 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a47680d6dd17..1df0a8053c8f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -60,7 +60,7 @@ test280 test281 test282 test283 test284 test285 test286 test287 test288 \ test289 test290 test291 test292 test293 test294 test295 test296 test297 \ test298 test299 test300 test301 test302 test303 test304 test305 test306 \ test307 test308 test309 test310 test311 test312 test313 test314 test315 \ -test316 test317 test318 test319 test320 test321 test322 test323 test324 \ +test316 test317 test318 test319 \ test325 test326 test327 test328 test329 test330 test331 test332 test333 \ test334 test335 test336 test337 test338 test339 test340 test341 test342 \ test343 test344 test345 test346 test347 test348 test349 test350 test351 \ @@ -292,7 +292,7 @@ test3400 test3401 \ \ test4000 test4001 -EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 data320.html \ +EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ data1400.c data1401.c data1402.c data1403.c data1404.c data1405.c data1406.c \ data1407.c data1420.c data1465.c data1481.c \ diff --git a/tests/data/data320.html b/tests/data/data320.html deleted file mode 100644 index 307bb87c473c..000000000000 --- a/tests/data/data320.html +++ /dev/null @@ -1,19 +0,0 @@ - -

This is GnuTLS

- -

Session ID: 003030000100000001000000000000000030330001000000B062410001000000

-
If your browser supports session resuming, then you should see the same session ID, when you press the reload button.
-

Connected as user 'jsmith'.

-

- - - - - -

Protocol version:TLS1.2
Key Exchange:SRP
CompressionNULL
CipherAES-NNN-CBC
MACSHA1
CiphersuiteSRP_SHA_AES_NNN_CBC_SHA1
-


Your HTTP header was:

Host: localhost:9011%CR
-User-Agent: curl-test-suite%CR
-Accept: */*%CR
-%CR
-

- diff --git a/tests/data/test320 b/tests/data/test320 deleted file mode 100644 index b9a8cce9a51d..000000000000 --- a/tests/data/test320 +++ /dev/null @@ -1,55 +0,0 @@ - - - - -HTTPS -HTTP GET -TLS-SRP - - - -# Server-side - - -%includetext %SRCDIR/data/data%TESTNUMBER.html% - - - -# Client-side - - -httptls+srp - - -TLS-SRP - - -simple TLS-SRP HTTPS GET, check user in response - - ---insecure --tlsauthtype SRP --tlsuser jsmith --tlspassword abc -A curl-test-suite https://%HOSTIP:%HTTPTLSPORT - - - -# Verify data after the test has been "shot" - - -HTTP/1.0 200 OK -Content-type: text/html - -FINE -User-Agent: curl-test-suite -Accept: */* - - - -s/^%LTp%GTConnected as user 'jsmith'.*/FINE/ -s/Protocol version:.*[0-9]// -s/GNUTLS/GnuTLS/ -s/(AES[-_])\d\d\d([-_]CBC)/$1NNN$2/ -s/^%LT.*\n// -s/^\n// - - - - diff --git a/tests/data/test321 b/tests/data/test321 deleted file mode 100644 index ca258c078d79..000000000000 --- a/tests/data/test321 +++ /dev/null @@ -1,33 +0,0 @@ - - - - -HTTPS -TLS-SRP - - - -# Client-side - - -httptls+srp - - -TLS-SRP - - -TLS-SRP with bad username and password - - ---insecure --tlsauthtype SRP --tlsuser baduser --tlspassword badpass https://%HOSTIP:%HTTPTLSPORT - - - -# Verify data after the test has been "shot" - - -35 - - - - diff --git a/tests/data/test322 b/tests/data/test322 deleted file mode 100644 index e0aca6b28eda..000000000000 --- a/tests/data/test322 +++ /dev/null @@ -1,33 +0,0 @@ - - - - -HTTPS -TLS-SRP - - - -# Client-side - - -httptls+srp - - -TLS-SRP - - -TLS-SRP with bad password - - ---insecure --tlsauthtype SRP --tlsuser jsmith --tlspassword badpass https://%HOSTIP:%HTTPTLSPORT - - - -# Verify data after the test has been "shot" - - -35 - - - - diff --git a/tests/data/test323 b/tests/data/test323 deleted file mode 100644 index ba09253c0f17..000000000000 --- a/tests/data/test323 +++ /dev/null @@ -1,33 +0,0 @@ - - - - -HTTPS -TLS-SRP - - - -# Client-side - - -https - - -TLS-SRP - - -TLS-SRP to non-TLS-SRP server - - ---insecure --tlsauthtype SRP --tlsuser jsmith --tlspassword badpass https://%HOSTIP:%HTTPSPORT/want/%TESTNUMBER - - - -# Verify data after the test has been "shot" - - -35 - - - - diff --git a/tests/data/test324 b/tests/data/test324 deleted file mode 100644 index d85fecb9b33d..000000000000 --- a/tests/data/test324 +++ /dev/null @@ -1,33 +0,0 @@ - - - - -HTTPS -TLS-SRP - - - -# Client-side - - -httptls+srp - - -TLS-SRP - - -TLS-SRP with server cert checking - - # no --insecure ---tlsauthtype SRP --tlsuser jsmith --tlspassword abc https://%HOSTIP:%HTTPTLSPORT/want/fails - - - -# Verify data after the test has been "shot" - - -60 - - - - diff --git a/tests/runtests.pl b/tests/runtests.pl index 999b8ebb01b4..5c544fd01ce6 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -697,8 +697,6 @@ sub checksystemfeatures { $feature{"Kerberos"} = $feat =~ /Kerberos/i; # SPNEGO enabled $feature{"SPNEGO"} = $feat =~ /SPNEGO/i; - # TLS-SRP enabled - $feature{"TLS-SRP"} = $feat =~ /TLS-SRP/i; # PSL enabled $feature{"PSL"} = $feat =~ /PSL/i; # alt-svc enabled @@ -739,27 +737,6 @@ sub checksystemfeatures { $feature{"HTTPSRR"} = $feat =~ /HTTPSRR/; $feature{"ECH"} = $feat =~ /ECH/; } - # - # Test harness currently uses a non-stunnel server in order to - # run HTTP TLS-SRP tests required when curl is built with https - # protocol support and TLS-SRP feature enabled. For convenience - # 'httptls' may be included in the test harness protocols array - # to differentiate this from classic stunnel based 'https' test - # harness server. - # - if($feature{"TLS-SRP"}) { - my $add_httptls; - for(@protocols) { - if($_ =~ /^https(-ipv6|)$/) { - $add_httptls = 1; - last; - } - } - if($add_httptls && (! grep /^httptls$/, @protocols)) { - push @protocols, 'httptls'; - push @protocols, 'httptls-ipv6'; - } - } } if($torture) { diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 66c971b49953..ac451cb04b7f 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -67,10 +67,9 @@ use testutil qw( our $logfile; # server log filename, for logmsg #*************************************************************************** -# For convenience, test harness uses 'https' and 'httptls' literals as -# values for 'proto' variable in order to differentiate different servers. -# 'https' literal is used for stunnel based https test servers, and 'httptls' -# is used for non-stunnel https test servers. +# For convenience, test harness uses 'https' literal as values for 'proto' +# variable in order to differentiate different servers. 'https' literal is +# used for stunnel based https test servers. #********************************************************************** # logmsg is general message logging subroutine for our test servers. @@ -105,7 +104,7 @@ sub serverfactors { $ipvnum = ($4 && ($4 =~ /6$/)) ? 6 : 4; } elsif($server =~ - /^(dns|tftp|sftp|socks|ssh|rtsp|gopher|httptls)(\d*)(-ipv6|)$/) { + /^(dns|tftp|sftp|socks|ssh|rtsp|gopher)(\d*)(-ipv6|)$/) { $proto = $1; $idnum = ($2 && ($2 > 1)) ? $2 : 1; $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4; @@ -124,7 +123,7 @@ sub servername_str { $proto = uc($proto) if($proto); die "unsupported protocol: '$proto'" unless($proto && - ($proto =~ /^(((DNS|FTP|HTTP|HTTP\/2|HTTP\/3|IMAP|POP3|GOPHER|SMTP|HTTPS-MTLS)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|HTTPTLS|DICT|SMB|SMBS|TELNET|MQTT|MQTTS))$/)); + ($proto =~ /^(((DNS|FTP|HTTP|HTTP\/2|HTTP\/3|IMAP|POP3|GOPHER|SMTP|HTTPS-MTLS)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|DICT|SMB|SMBS|TELNET|MQTT|MQTTS))$/)); $ipver = (not $ipver) ? 'ipv4' : lc($ipver); die "unsupported IP version: '$ipver'" unless($ipver && diff --git a/tests/servers.pm b/tests/servers.pm index 474a12c377e7..7aff9be429b4 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -96,7 +96,6 @@ use sshhelp qw( find_sshd find_ssh find_sftp - find_httptlssrv sshversioninfo ); @@ -125,7 +124,6 @@ my $sshderror; # for socks server, ssh daemon version error my %doesntrun; # servers that do not work, identified by pidfile my %PORT = (nolisten => 47); # port we use for a local non-listening service my $server_response_maxtime = 13; -my $httptlssrv = find_httptlssrv(); my %run; # running server my %runcert; # cert file currently in use by an SSL running server my $CLIENTIP = "127.0.0.1"; # address which curl uses for incoming connections @@ -236,7 +234,7 @@ sub init_serverpidfile_hash { } } } - for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'httptls', + for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'dict', 'smb', 'smbs', 'telnet', 'mqtt', 'mqtts', 'https-mtls', 'dns')) { for my $ipvnum ((4, 6)) { @@ -301,10 +299,6 @@ sub serverfortest { my $server = "${1}"; my $lnrest = "${2}"; my $tlsext; - if($server =~ /^(httptls)(\+)(ext|srp)(\d*)(-ipv6|)$/) { - $server = "${1}${4}${5}"; - $tlsext = uc("TLS-${3}"); - } my @lprotocols = @protocols; @@ -789,88 +783,6 @@ sub verifysftp { return $verified; } -####################################################################### -# Verify that the non-stunnel HTTP TLS extensions capable server that runs -# on $ip, $port is our server. This also implies that we can speak with it, -# as there might be occasions when the server runs fine but we cannot talk -# to it ("Failed to connect to ::1: Cannot assign requested address") -# -sub verifyhttptls { - my ($proto, $ipvnum, $idnum, $ip, $port) = @_; - my $server = servername_id($proto, $ipvnum, $idnum); - my $pidfile = server_pidfilename("$LOGDIR/$PIDDIR", $proto, $ipvnum, - $idnum); - - my $verifyout = "$LOGDIR/". - servername_canon($proto, $ipvnum, $idnum) .'_verify.out'; - unlink($verifyout) if(-f $verifyout); - - my $verifylog = "$LOGDIR/". - servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; - unlink($verifylog) if(-f $verifylog); - - my $flags = "--max-time $server_response_maxtime "; - $flags .= "--output $verifyout "; - $flags .= "--verbose "; - $flags .= "--globoff "; - $flags .= "--insecure "; - $flags .= "--tlsauthtype SRP "; - $flags .= "--tlsuser jsmith "; - $flags .= "--tlspassword abc "; - if($proxy_address) { - $flags .= getexternalproxyflags(); - } - $flags .= "\"https://$ip:$port/verifiedserver\""; - - my $cmd = exerunner() . "$VCURL $flags 2>$verifylog"; - - # verify if our/any server is running on this port - logmsg "RUN: $cmd\n" if($verbose); - my $res = runclient($cmd); - - $res >>= 8; # rotate the result - if($res & 128) { - logmsg "RUN: curl command died with a coredump\n"; - return -1; - } - - if($res && $verbose) { - logmsg "RUN: curl command returned $res\n"; - if(open(my $file, "<", $verifylog)) { - while(my $string = <$file>) { - logmsg "RUN: $string" if($string !~ /^([ \t]*)$/); - } - close($file); - } - } - - my $data; - if(open(my $file, "<", $verifyout)) { - while(my $string = <$file>) { - $data .= $string; - } - close($file); - } - - my $pid = 0; - if($data && ($data =~ /(GNUTLS|GnuTLS)/) && ($pid = processexists($pidfile))) { - if($pid < 0) { - logmsg "RUN: $server server has died after starting up\n"; - } - return $pid; - } - elsif($res == 6) { - # curl: (6) Could not resolve hostname '::1' - logmsg "RUN: failed to resolve host (https://$ip:$port/verifiedserver)\n"; - return -1; - } - elsif($data || ($res && ($res != 7))) { - logmsg "RUN: Unknown server on our $server port: $port ($res)\n"; - return -1; - } - return $pid; -} - ####################################################################### # For verifying mqtt and socks # @@ -1012,10 +924,9 @@ sub verifytelnet { # particular can take a long time to start if it needs to generate # keys on a slow or loaded host. # -# For convenience, test harness uses 'https' and 'httptls' literals +# For convenience, test harness uses 'https' literal # as values for 'proto' variable in order to differentiate different -# servers. 'https' literal is used for stunnel based https test servers, -# and 'httptls' is used for non-stunnel https test servers. +# servers. 'https' literal is used for stunnel based https test servers. # my %protofunc = ('http' => \&verifyhttp, @@ -1038,7 +949,6 @@ my %protofunc = ('http' => \&verifyhttp, 'socks' => \&verifypid, 'socks5unix' => \&verifypid, 'gopher' => \&verifyhttp, - 'httptls' => \&verifyhttptls, 'dict' => \&verifyftp, 'smb' => \&verifysmb, 'telnet' => \&verifytelnet); @@ -1367,66 +1277,6 @@ sub runhttpsserver { return (0+!$httpspid, $httpspid, $pid2, $port); } -####################################################################### -# start the non-stunnel HTTP TLS extensions capable server -# -sub runhttptlsserver { - my ($verb, $ipv6) = @_; - my $proto = "httptls"; - my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? $HOST6IP : $HOSTIP; - my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; - my $idnum = 1; - - if(!$httptlssrv) { - return (4, 0, 0); - } - - my $server = servername_id($proto, $ipvnum, $idnum); - - my $pidfile = $serverpidfile{$server}; - - # do not retry if the server does not work - if($doesntrun{$pidfile}) { - return (2, 0, 0, 0); - } - - my $pid = processexists($pidfile); - if($pid > 0) { - stopserver($server, $pid); - } - unlink($pidfile) if(-f $pidfile); - - my $srvrname = servername_str($proto, $ipvnum, $idnum); - my $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); - - my $flags = ""; - $flags .= "--http "; - $flags .= "--debug 1 " if($debugprotocol); - $flags .= "--priority NORMAL:+SRP "; - $flags .= "--srppasswd $srcdir/certs/srp-verifier-db "; - $flags .= "--srppasswdconf $srcdir/certs/srp-verifier-conf"; - - my $port = getfreeport($ipvnum); - my $allflags = "--port $port $flags"; - my $cmd = "$httptlssrv $allflags > $logfile 2>&1"; - my ($httptlspid, $pid2) = startnew($cmd, $pidfile, 10, 1); - - if($httptlspid <= 0 || !pidexists($httptlspid)) { - # it is NOT alive - stopserver($server, $pid2); - $doesntrun{$pidfile} = 1; - $httptlspid = $pid2 = 0; - logmsg "RUN: failed to start the $srvrname server\n"; - return (3, 0, 0, 0); - } - $doesntrun{$pidfile} = 0; - - if($verb) { - logmsg "RUN: $srvrname server PID $httptlspid port $port\n"; - } - return (0+!$httptlspid, $httptlspid, $pid2, $port); -} - ####################################################################### # start the pingpong server (FTP, POP3, IMAP, SMTP) # @@ -2372,27 +2222,6 @@ sub responsive_dns_server { return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); } -####################################################################### -# Single shot non-stunnel HTTP TLS extensions capable server -# responsiveness test. This should only be used to verify that a -# server present in %run hash is still functional -# -sub responsive_httptls_server { - my ($verb, $ipv6) = @_; - my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; - my $proto = "httptls"; - my $port = protoport($proto); - my $ip = $HOSTIP; - my $idnum = 1; - - if($ipvnum == 6) { - $port = protoport("httptls6"); - $ip = $HOST6IP; - } - - return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); -} - ####################################################################### # startservers() starts all the named servers # @@ -2831,50 +2660,6 @@ sub startservers { $run{'https-proxy'} = "$pid $pid2"; } } - elsif($what eq "httptls") { - if(!$httptlssrv) { - # for now, we cannot run http TLS-EXT tests without gnutls-serv - return ("no gnutls-serv (with SRP support)", 4); - } - if($run{'httptls'} && - !responsive_httptls_server($verbose, "IPv4")) { - if(stopserver('httptls')) { - return ("failed stopping unresponsive HTTPTLS server", 3); - } - } - if(!$run{'httptls'}) { - ($serr, $pid, $pid2, $PORT{'httptls'}) = - runhttptlsserver($verbose, "IPv4"); - if($pid <= 0) { - return ("failed starting HTTPTLS server (gnutls-serv)", $serr); - } - logmsg sprintf("* pid httptls => %d %d\n", $pid, $pid2) - if($verbose); - $run{'httptls'} = "$pid $pid2"; - } - } - elsif($what eq "httptls-ipv6") { - if(!$httptlssrv) { - # for now, we cannot run http TLS-EXT tests without gnutls-serv - return ("no gnutls-serv", 4); - } - if($run{'httptls-ipv6'} && - !responsive_httptls_server($verbose, "ipv6")) { - if(stopserver('httptls-ipv6')) { - return ("failed stopping unresponsive HTTPTLS-IPv6 server", 3); - } - } - if(!$run{'httptls-ipv6'}) { - ($serr, $pid, $pid2, $PORT{"httptls6"}) = - runhttptlsserver($verbose, "ipv6"); - if($pid <= 0) { - return ("failed starting HTTPTLS-IPv6 server (gnutls-serv)", $serr); - } - logmsg sprintf("* pid httptls-ipv6 => %d %d\n", $pid, $pid2) - if($verbose); - $run{'httptls-ipv6'} = "$pid $pid2"; - } - } elsif($what eq "dns") { if($run{'dns'} && !responsive_dns_server("", $verbose)) { @@ -3137,7 +2922,7 @@ sub subvariables { 'FTP', 'FTP6', 'FTPS', 'GOPHER', 'GOPHER6', 'GOPHERS', 'HTTP', 'HTTP6', 'HTTPS', 'HTTPS-MTLS', - 'HTTPSPROXY', 'HTTPTLS', 'HTTPTLS6', + 'HTTPSPROXY', 'HTTP2', 'HTTP2TLS', 'HTTP3', 'IMAP', 'IMAP6', 'IMAPS', diff --git a/tests/test1165.pl b/tests/test1165.pl index ef6b34f32856..99979dff06b4 100755 --- a/tests/test1165.pl +++ b/tests/test1165.pl @@ -72,7 +72,7 @@ sub scanconf_cmake { while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { my ($sym) = ($1); - if(not $sym =~ /^(CURL_DISABLE_INSTALL|CURL_DISABLE_SRP|CURL_DISABLE_TYPECHECK)$/) { + if(not $sym =~ /^(CURL_DISABLE_INSTALL|CURL_DISABLE_TYPECHECK)$/) { $hashr->{$sym} = 1; } } diff --git a/tests/test1177.pl b/tests/test1177.pl index cf5bd44fa744..bca20fb37d43 100755 --- a/tests/test1177.pl +++ b/tests/test1177.pl @@ -66,6 +66,7 @@ } close($s); $sourcename{'NTLM_WB'}++; # deprecated, fake its presence in code +$sourcename{'TLS-SRP'}++; # deprecated, fake its presence in code for my $h (keys %headerversion) { if(!$manversion{$h}) { diff --git a/tests/unit/unit3304.c b/tests/unit/unit3304.c index bb2bd77b8613..7336aa731bda 100644 --- a/tests/unit/unit3304.c +++ b/tests/unit/unit3304.c @@ -26,8 +26,8 @@ * Verifies that Curl_ssl_peer_key_build() produces distinct keys when two * handles differ only on key, key_type or cert_type. key_passwd is NOT * embedded in the peer key; it is compared separately at session lookup via - * cf_ssl_scache_match_auth(), following the same pattern as SRP - * credentials. */ + * cf_ssl_scache_match_auth(). + */ #include "unitcheck.h" #include "urldata.h" @@ -88,7 +88,7 @@ static CURLcode test_unit3304(const char *arg) curlx_safefree(key2); /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison - * via cf_ssl_scache_match_auth(), same as SRP credentials. */ + * via cf_ssl_scache_match_auth(). */ fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1), "peer key build failed"); ssl.key_passwd = NULL; From ecaa344a7a17874ff0c6e5717489e8f9033c4d5c Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Wed, 1 Jul 2026 15:11:15 +0530 Subject: [PATCH 0636/1062] url: reject control codes in credentials set via CURLOPT Closes #22236 --- lib/url.c | 12 +++++++++++- tests/data/Makefile.am | 2 +- tests/data/test2114 | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2114 diff --git a/lib/url.c b/lib/url.c index 164aef797a2b..dd4e33c9d17c 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1398,7 +1398,6 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, #define hsts_upgrade(x, y, z, a) CURLE_OK #endif -#ifndef CURL_DISABLE_NETRC static bool str_has_ctrl(const char *input) { if(input) { @@ -1412,6 +1411,7 @@ static bool str_has_ctrl(const char *input) return FALSE; } +#ifndef CURL_DISABLE_NETRC /* * Override the login details from the URL with that in the CURLOPT_USERPWD * option or a .netrc file, if applicable. @@ -1538,6 +1538,16 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) CREDS_OPTION, &newcreds); if(result) goto out; + if(newcreds && + !(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) && + (str_has_ctrl(Curl_creds_user(newcreds)) || + str_has_ctrl(Curl_creds_passwd(newcreds)))) { + /* if the protocol cannot handle control codes in credentials, make + sure there are none */ + failf(data, "control code detected in credentials"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } } /* Extract credentials from the URL only if there are none OR diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 1df0a8053c8f..4ef4f55f9587 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 test2110 test2113 \ +test2108 test2109 test2110 test2113 test2114 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2114 b/tests/data/test2114 new file mode 100644 index 000000000000..fe8bc1cf987b --- /dev/null +++ b/tests/data/test2114 @@ -0,0 +1,43 @@ + + + + +FTP + + + +# Server-side + + + +# Client-side + + +ftp + + +FTP username with embedded CRLF is rejected + +# read the credentials from a config file so the raw CR LF reaches curl intact +# regardless of how the platform passes command line arguments + +user = "anonymous\r\nSTOR /%TESTNUMBER" + + +ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -K %LOGDIR/test%TESTNUMBER.config + + + +# Verify data after the test has been "shot" + +# 43 - CURLE_BAD_FUNCTION_ARGUMENT + +43 + +# the credentials are rejected before connecting so the injected STOR command +# must never reach the server + + + + + From e0222c00293573e83d85fb5dfc43d67b4b837dda Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 4 Jul 2026 17:20:13 +0200 Subject: [PATCH 0637/1062] test2114: fix reading from stdin The test provides an invalid username, but no ':' with password, so that the curl tool tries to read the password from stdin. That makes the test hang, also in CI. The check for invalid characters is happening when the CURLOPT_USERPWD is applied to the URL, so when the transfer is started. Maybe the check should happen also in setopt already. The test hangs because tool_paramhlp.c:548 `checkpasswd()` does not see a ':' and tries to read the password from stdin. Fix: add a ':' in the tests invalid user setting, so that the read does not happen. Follow-up to ecaa344a7a17874ff0c6e5 Closes #22264 --- tests/data/test2114 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test2114 b/tests/data/test2114 index fe8bc1cf987b..eaeefa6a520b 100644 --- a/tests/data/test2114 +++ b/tests/data/test2114 @@ -21,7 +21,7 @@ FTP username with embedded CRLF is rejected # read the credentials from a config file so the raw CR LF reaches curl intact # regardless of how the platform passes command line arguments -user = "anonymous\r\nSTOR /%TESTNUMBER" +user = "anonymous\r\nSTOR: /%TESTNUMBER" ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -K %LOGDIR/test%TESTNUMBER.config From 791f5b25d32fa4598a629d25b03a152faefc0f75 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 Jul 2026 08:55:44 +0200 Subject: [PATCH 0638/1062] setopt: error for CURLOPT_SHARE when easy handle is used Changing the share while driving would be complicated and error-prone. URL: https://curl.se/mail/lib-2026-07/0000.html Closes #22253 --- lib/setopt.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index c6215e3e9150..a95a11ef0e73 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1449,6 +1449,26 @@ static CURLcode setopt_mimepost(struct Curl_easy *data, curl_mime *mimep) #endif /* !CURL_DISABLE_MIME */ #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ +static CURLcode setopt_share(struct Curl_easy *data, struct Curl_share *set) +{ + CURLcode result; + + if(data->conn) { + /* As this handle already has a connection attached, changing share now + would be complicated and error-prone */ + infof(data, "Cannot change share object while in use"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + else { + /* disconnect from old share, if any and possible */ + result = Curl_share_easy_unlink(data); + if(!result && GOOD_SHARE_HANDLE(set)) + /* use new share if it set */ + result = Curl_share_easy_link(data, set); + } + return result; +} + /* assorted pointer type arguments */ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, va_list param) @@ -1496,22 +1516,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, if(!s->err) s->err = stderr; break; - case CURLOPT_SHARE: { - struct Curl_share *set = va_arg(param, struct Curl_share *); - - /* disconnect from old share, if any and possible */ - result = Curl_share_easy_unlink(data); - if(result) - return result; - - /* use new share if it set */ - if(GOOD_SHARE_HANDLE(set)) { - result = Curl_share_easy_link(data, set); - if(result) - return result; - } - break; - } + case CURLOPT_SHARE: + return setopt_share(data, va_arg(param, struct Curl_share *)); default: return CURLE_UNKNOWN_OPTION; From 6841d59af67345493c0a692a6200bc4d8a82c5fd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 02:40:48 +0000 Subject: [PATCH 0639/1062] GHA: update dependency pizlonator/fil-c to v0.681 Note: Renovate strips pre-existing quotes from the YAML version value. This breaks the bump next time its version is ending with zero(s). Ref: 78267398e5a2ce347a6729926c68272b2b6fbed5 #22143 #22142 Closes #22267 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 95313c7661b1..8b5adf1b6eab 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: '0.680' + FIL_C_VERSION: 0.681 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com From 6e5f94cd5dad9764641d1718b36e79907c68caf4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:43:40 +0000 Subject: [PATCH 0640/1062] GHA: update dependency Mbed-TLS/mbedtls to v4.2.0 Ref: #21178 Closes #22271 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8b5adf1b6eab..e4b51b7d9f48 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -43,7 +43,7 @@ env: # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com - MBEDTLS_VERSION: 4.0.0 + MBEDTLS_VERSION: 4.2.0 # manually bumped MBEDTLS_PREV_VERSION: 3.6.5 MBEDTLS_PREV_SHA256: 4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8 From 7183bec8fe7b4efcae3b0354dc5bf6de54384190 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 6 Jul 2026 11:24:31 +0200 Subject: [PATCH 0641/1062] GHA/macos: ignore test 311 results for Homebrew wolfSSL Homebrew is distributing v5.9.2 at the time of this commit, which contains known regression failing to parse the server certificate. The fix has been merged upstream. Till it makes it into a release and Homebrew, ignore test results for 311. Refs: https://github.com/wolfSSL/wolfssl/pull/10793 https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8 Follow-up to 03f9751585112cdc3c5f0a370217f08c05782de9 #22204 #22175 #22160 Closes #22269 --- .github/workflows/macos.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 21e2617c9369..8e8af395fba0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -343,6 +343,7 @@ jobs: install: brotli wolfssl zstd install_steps: pytest generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + tflags: '~311' # fails on wolfSSL v5.9.2, fixed in upcoming release - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 From 06a42cd8bbf237626b6e0c67f592e380cd359e86 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 9 Jul 2026 18:04:18 +0200 Subject: [PATCH 0642/1062] GHA/linux: bump 3.x mbedTLS to 3.6.7 Follow-up to 6e5f94cd5dad9764641d1718b36e79907c68caf4 #22271 Closes #22285 --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e4b51b7d9f48..6936bcb1fd88 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -45,8 +45,8 @@ env: # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com MBEDTLS_VERSION: 4.2.0 # manually bumped - MBEDTLS_PREV_VERSION: 3.6.5 - MBEDTLS_PREV_SHA256: 4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8 + MBEDTLS_PREV_VERSION: 3.6.7 + MBEDTLS_PREV_SHA256: a7e8bcbec0e6f761b4af24f25677626b35f762f68eef79c08677a363212d11f6 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 # handled in renovate.json From 50c244c74d775979e2771d128fd90affe7193b49 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 9 Jul 2026 20:09:24 +0200 Subject: [PATCH 0643/1062] tests: skip test 311 for wolfSSL 5.9.2 To fix this for everyone running curl tests. Also: drop workarounds for CI. Refs: https://github.com/wolfSSL/wolfssl/pull/10793 https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8 Ref: https://github.com/curl/curl/pull/22269#issuecomment-4892203666 Follow-up to 7183bec8fe7b4efcae3b0354dc5bf6de54384190 #22269 Follow-up to 03f9751585112cdc3c5f0a370217f08c05782de9 #22204 Closes #22286 --- .github/workflows/http3-linux.yml | 3 --- .github/workflows/linux.yml | 9 --------- .github/workflows/macos.yml | 1 - tests/data/test311 | 1 + tests/runtests.pl | 3 +++ 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 4b20eaea972a..1851d06a9494 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -327,9 +327,6 @@ jobs: cd ~ git clone --quiet --depth 1 --branch "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl cd wolfssl - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ - https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch - patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-all --enable-quic \ --disable-benchmark --disable-crypttests --disable-examples diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6936bcb1fd88..76adeca87414 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -598,9 +598,6 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ - https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch - patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-all \ --enable-tls13 --enable-harden --enable-all \ @@ -624,9 +621,6 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ - https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch - patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ @@ -650,9 +644,6 @@ jobs: --location --proto-redir =https "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 30 --retry 6 --retry-connrefused \ - https://github.com/wolfSSL/wolfssl/commit/7dd269fc52228cbc854b9f0ed8c3938b95d8a2c8.diff --output pkg.patch - patch -p1 -i pkg.patch ./autogen.sh ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8e8af395fba0..21e2617c9369 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -343,7 +343,6 @@ jobs: install: brotli wolfssl zstd install_steps: pytest generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON - tflags: '~311' # fails on wolfSSL v5.9.2, fixed in upcoming release - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 diff --git a/tests/data/test311 b/tests/data/test311 index 292aa4d859c9..848cbedb45de 100644 --- a/tests/data/test311 +++ b/tests/data/test311 @@ -17,6 +17,7 @@ PEM certificate SSL local-http +!wolfssl-5.9.2 https test-localhost0h.pem diff --git a/tests/runtests.pl b/tests/runtests.pl index 5c544fd01ce6..b35d6b536897 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -589,6 +589,9 @@ sub checksystemfeatures { elsif($libcurl =~ /\swolfssl\b/i) { $feature{"wolfssl"} = 1; $feature{"SSLpinning"} = 1; + if($libcurl =~ /\swolfssl\/5\.9\.2\b/i) { + $feature{"wolfssl-5.9.2"} = 1; + } } elsif($libcurl =~ /\s(AWS-LC|BoringSSL)\b/i) { # OpenSSL compatible API From d054f386dd77ed02f6d7513e0464e414948d93df Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 3 Jul 2026 22:37:58 +0200 Subject: [PATCH 0644/1062] GHA/windows: enable wolfSSL in two MSYS2 mingw-w64 jobs Number of tests went to 1919 (from 1912). Runs 30s faster. Thanks-to: Christoph Reiter Ref: https://github.com/msys2/MINGW-packages/commit/5c995ac09830ac84bcfff58e6fcf032c42f4b20f Ref: #22252 Closes #22259 --- .github/workflows/windows.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c3e988cb60a3..267a2a108a37 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -269,10 +269,10 @@ jobs: build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver --enable-static --without-zlib', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2' } - - { name: 'c-ares U', + - { name: 'wolfssl c-ares U', build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '', - config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --enable-static --disable-shared --enable-ca-native --enable-ntlm', - install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-nghttp3 mingw-w64-ucrt-x86_64-libssh2' } + config: '--enable-debug --with-wolfssl --enable-windows-unicode --enable-ares --enable-static --disable-shared --enable-ca-native --enable-ntlm', + install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-nghttp3 mingw-w64-ucrt-x86_64-libssh2 mingw-w64-ucrt-x86_64-wolfssl' } - { name: 'schannel c-ares U', type: 'Debug', build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: '--min=1720', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON -DCURL_DROP_UNUSED=ON', @@ -313,8 +313,8 @@ jobs: # install: 'mingw-w64-ucrt-x86_64-libssh2' } - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', - install: 'mingw-w64-x86_64-libssh2' } + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_WOLFSSL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', + install: 'mingw-w64-x86_64-libssh2 mingw-w64-x86_64-wolfssl' } - { name: 'MultiSSL R', type: 'Release', build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_SCHANNEL=ON -DENABLE_ARES=ON -DENABLE_UNICODE=ON', @@ -341,6 +341,7 @@ jobs: if: ${{ matrix.sys != 'msys' }} with: msystem: ${{ matrix.sys }} + update: ${{ contains(matrix.install, 'wolfssl') && 'true' || 'false' }} install: >- mingw-w64-${{ matrix.env }}-cc mingw-w64-${{ matrix.env }}-${{ matrix.build }} ${{ matrix.build == 'autotools' && 'make' || '' }} From 5c5334f831c29fc993b5689c2265655297a848b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 01:41:13 +0000 Subject: [PATCH 0645/1062] GHA: update dependency awslabs/aws-lc to v5.1.0 Closes #22288 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 1851d06a9494..26db16250c4b 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -37,7 +37,7 @@ env: CURL_TEST_MIN: 1850 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 5.0.0 + AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 76adeca87414..1dda819a57c5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -35,7 +35,7 @@ env: CURL_TEST_MIN: 1660 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 5.0.0 + AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260616.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com From b093d884475b75d266ee2e1693c596db0416536e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 26 Jun 2026 22:57:40 +0200 Subject: [PATCH 0646/1062] tidy-up: typos, comment nits Closes #22294 --- RELEASE-NOTES | 2 +- docs/INFRASTRUCTURE.md | 2 +- docs/TODO.md | 12 ++++++------ docs/VULN-DISCLOSURE-POLICY.md | 2 +- docs/cmdline-opts/MANPAGE.md | 4 ++-- docs/internals/CREDENTIALS.md | 6 +++--- docs/tests/FILEFORMAT.md | 2 +- lib/altsvc.c | 2 +- lib/asyn-thrdd.c | 2 +- lib/asyn.h | 4 ++-- lib/cf-dns.c | 2 +- lib/curl_sha512_256.c | 2 +- lib/curl_trc.c | 2 +- lib/curlx/inet_pton.c | 25 +++++++++++++------------ lib/ftp.c | 4 ++-- lib/hostip4.c | 2 +- lib/http.c | 4 ++-- lib/peer.h | 10 +++++----- lib/setopt.c | 4 ++-- lib/socks.c | 2 +- lib/vquic/cf-ngtcp2-cmn.c | 8 ++++---- lib/vssh/libssh.c | 2 +- lib/vtls/openssl.c | 2 +- projects/OS400/os400sys.c | 10 +++++----- projects/vms/config_h.com | 2 +- scripts/verify-release | 2 +- tests/http/test_06_eyeballs.py | 4 ++-- tests/http/test_10_proxy.py | 8 ++++---- tests/http/test_20_websockets.py | 2 +- tests/http/test_21_resolve.py | 12 ++++++------ tests/http/test_60_h3_proxy.py | 2 +- tests/libtest/cli_hx_download.c | 2 +- tests/secureserver.pl | 2 +- tests/unit/unit1658.c | 12 ++++++------ tests/unit/unit2600.c | 16 ++++++++-------- 35 files changed, 91 insertions(+), 90 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 2ef9aa52679d..e886c14cc168 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -26,7 +26,7 @@ This release includes the following bugfixes: o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] o conncache: connection alive checks intervals [20] o content_encoding: give a clear error on multi-member gzip [46] - o CREDENTIALS.md: remove comment about emtpy user/pass [50] + o CREDENTIALS.md: remove comment about empty user/pass [50] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 1679223609ce..ddcd1289d960 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -191,7 +191,7 @@ chat about curl and related topics. This done in the `#curl` channel on the `libra.chat` IRC network. **Daniel Stenberg** (`bagder`) is registered owner of the channel. We do not run any IRC servers or services ourselves. -`curelbot` is a service in the channel that shows details about GitHub issues +`curlbot` is a service in the channel that shows details about GitHub issues and pull requests when publicly mentioned using #[number]. The bot is run by user `TheAssassin`. diff --git a/docs/TODO.md b/docs/TODO.md index 2838b0b0d978..aa6611a0c66e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -57,7 +57,7 @@ to share data in more powerful ways. ## updated DNS server while running If `/etc/resolv.conf` gets updated while a program using libcurl is running, it -is may cause name resolves to fail unless `res_init()` is called. We should +may cause name resolves to fail unless `res_init()` is called. We should consider calling `res_init()` + retry once unconditionally on all name resolve failures to mitigate against this. Firefox works like that. Note that Windows does not have `res_init()` or an alternative. @@ -269,7 +269,7 @@ This is not detailed in any FTP specification. ## Passive transfer could try other IP addresses -When doing FTP operations through a proxy at localhost, the reported spotted +When doing FTP operations through a proxy at localhost, the reporter spotted that curl only tried to connect once to the proxy, while it had multiple addresses and a failed connect on one address should make it try the next. @@ -284,8 +284,8 @@ See [curl issue 1508](https://github.com/curl/curl/issues/1508) When curl receives a body response from a CONNECT request to a proxy, it always reads and ignores it. It would make some users happy if curl instead -optionally would be able to make that responsible available. Via a new -callback? Through some other means? +optionally would be able to make that response available. Via a new callback? +Through some other means? See [curl issue 9513](https://github.com/curl/curl/issues/9513) @@ -763,7 +763,7 @@ backed up from those that are either not ready or have not changed. Downloads in progress are neither ready to be backed up, nor should they be opened by a different process. Only after a download has been completed it is -sensible to include it in any integer snapshot or backup of the system. +sensible to include it in any incremental snapshot or backup of the system. See [curl issue 3354](https://github.com/curl/curl/issues/3354) @@ -825,7 +825,7 @@ one, which then could make curl decide to rather retry the transfer on that URL only instead of the original operation to the original URL. Perhaps extra emphasized if the original transfer is a large POST that -redirects to a separate GET, and that GET is what gets the 529 +redirects to a separate GET, and that GET is what gets the 429 See [curl issue 5462](https://github.com/curl/curl/issues/5462) diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index 847579edb521..2ae863d10516 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -363,7 +363,7 @@ A *legacy dependency* is here defined as: - there are modern versions of equivalent or better functionality offered and in common use -## weak algorithms required for functionality +## Weak algorithms required for functionality curl supports several algorithms that are considered weak, like DES and MD5. These algorithms are still not curl security vulnerabilities or security diff --git a/docs/cmdline-opts/MANPAGE.md b/docs/cmdline-opts/MANPAGE.md index 1e4facd954cf..972fc9a51bb2 100644 --- a/docs/cmdline-opts/MANPAGE.md +++ b/docs/cmdline-opts/MANPAGE.md @@ -114,12 +114,12 @@ During rendering, the generator expands them as follows: ## Generate -`managen mainpage [list of markdown option file names]` +`managen mainpage [list of markdown option filenames]` This command outputs a single huge nroff file, meant to become `curl.1`. The full curl man page. -`managen ascii [list of markdown option file names]` +`managen ascii [list of markdown option filenames]` This command outputs a single text file, meant to become `curl.txt`. The full curl man page in text format, used to build `tool_hugehelp.c`. diff --git a/docs/internals/CREDENTIALS.md b/docs/internals/CREDENTIALS.md index a99b0fa31774..5f4aa97885ab 100644 --- a/docs/internals/CREDENTIALS.md +++ b/docs/internals/CREDENTIALS.md @@ -12,7 +12,7 @@ Authorization credentials are kept in `struct Curl_creds`. This contains: * `passwd`: the password, maybe the empty string * `sasl_authzid`: the SASL `authz` value, maybe the empty string * `oauth_bearer`: the OAUTH bearer token, maybe the empty string -* `source`: where the credentials from +* `source`: where the credentials come from * `refcount`: a reference counter to link/unlink `creds` With reference counting, `creds` can be linked in several places. @@ -54,12 +54,12 @@ password and `netrc` is consulted as well (when built in). ### `conn->creds` Once `data->state.creds` is known, the connection credentials are -determined. For protocols that tie authorization to everything send +determined. For protocols that tie authorization to everything sent on a connection (protocols without flag `PROTOPT_CREDSPERREQUEST`), `conn->creds` is linked to `data->state.creds`. Only connections carrying the same credentials may be reused. -Protocol with flag `PROTOPT_CREDSPERREQUEST` leave `conn->creds` empty, +Protocols with flag `PROTOPT_CREDSPERREQUEST` leave `conn->creds` empty, as connections for such protocols may be reused with different credentials. diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index bd7ed82465e2..5ded0d343f94 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -421,7 +421,7 @@ issue. Commands for the test DNS server. -- `A: [dotted ipv4 address]` - set IPv4 address to return +- `A: [dotted IPv4 address]` - set IPv4 address to return - `AAAA: [numerical IPv6 address]` - set IPv6 address to return, with or without `[]` diff --git a/lib/altsvc.c b/lib/altsvc.c index 6b8b2617f9f3..11c75d45dfd3 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -531,7 +531,7 @@ static bool altsvc_parse_dest(const char **pp, /* quoted string, with hostname or just :port ? */ if(curlx_str_single(pp, ':')) { /* is hostname:port ? */ - if(curlx_str_single(pp, '[')) { /* DNS hostname/ipv4 */ + if(curlx_str_single(pp, '[')) { /* DNS hostname/IPv4 */ if(curlx_str_until(pp, dsthost, MAX_ALTSVC_HOSTLEN, ':')) { infof(data, "Bad alt-svc hostname, ignoring."); return FALSE; diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index aeadec11a4de..dc7c7b0418a0 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -618,7 +618,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, return result; #ifdef CURLRES_IPV6 - /* Do not start an AAAA query for an ipv4 address when + /* Do not start an AAAA query for an IPv4 address when * we will start an A query for it. */ if((async->dns_queries & CURL_DNSQ_AAAA) && !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { diff --git a/lib/asyn.h b/lib/asyn.h index ed5093365439..7df9b681ba84 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -137,8 +137,8 @@ struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - struct async_thrdd_item *res_A; /* ipv4 result */ - struct async_thrdd_item *res_AAAA; /* ipv6 result */ + 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; diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 631c987be262..6388d65a9e31 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -219,7 +219,7 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, return TRUE; #ifdef USE_CURL_ASYNC else { - /* We want AAAA answer as we prefer ipv6. If a sub-filter desires + /* We want AAAA answer as we prefer IPv6. If a sub-filter desires * HTTPS-RR, we check for that query as well. */ uint8_t wanted_answers = CURL_DNSQ_AAAA; if(Curl_conn_cf_wants_httpsrr(cf, data)) diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 0ce669603ea5..d0c81e7a5528 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -650,7 +650,7 @@ static CURLcode Curl_sha512_256_update(void *context, if(length == 0) return CURLE_OK; /* Shortcut, do nothing */ - /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1)) + /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE - 1)) equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */ bytes_have = (unsigned int)(ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); ctx->count += length; diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 87082f031bc8..91b180f908a9 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -545,7 +545,7 @@ static struct trc_feat_def trc_feats[] = { { &Curl_trc_feat_ssls, TRC_CT_NETWORK }, #endif #ifdef USE_SSH - { &Curl_trc_feat_ssh, TRC_CT_PROTOCOL }, + { &Curl_trc_feat_ssh, TRC_CT_PROTOCOL }, #endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) { &Curl_trc_feat_ws, TRC_CT_PROTOCOL }, diff --git a/lib/curlx/inet_pton.c b/lib/curlx/inet_pton.c index c8f63a1413c6..b3e806602a01 100644 --- a/lib/curlx/inet_pton.c +++ b/lib/curlx/inet_pton.c @@ -187,18 +187,19 @@ static int inet_pton6(const char *src, unsigned char *dst) return 1; } -/* int inet_pton(af, src, dst) - * convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * return: - * 1 if the address was valid for the specified address family - * 0 if the address was not valid (`dst' is untouched in this case) - * -1 if some other error occurred (`dst' is untouched in this case, too) - * notice: - * On Windows we store the error in the thread errno, not - * in the Winsock error code. This is to avoid losing the - * actual last Winsock error. When this function returns - * -1, check errno not SOCKERRNO. +/* + * Convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * + * Return: + * 1 if the address was valid for the specified address family + * 0 if the address was not valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * + * On Windows we store the error in the thread errno, not in the Winsock error + * code. This is to avoid losing the actual last Winsock error. When this + * function returns NULL, check errno not SOCKERRNO. + * * author: * Paul Vixie, 1996. */ diff --git a/lib/ftp.c b/lib/ftp.c index 8b4d9b663ba2..c4b1b5fcfd95 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -929,11 +929,11 @@ static CURLcode ftp_port_parse_string(struct Curl_easy *data, #ifdef USE_IPV6 struct sockaddr_in6 * const sa6 = (void *)ss; #endif - /* either ipv6 or (ipv4|domain|interface):port(-range) */ + /* either IPv6 or (ipv4|domain|interface):port(-range) */ addrlen = ip_end - string_ftpport; #ifdef USE_IPV6 if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { - /* ipv6 */ + /* IPv6 */ addrlen = strlen(string_ftpport); ip_end = NULL; /* this got no port ! */ } diff --git a/lib/hostip4.c b/lib/hostip4.c index c8cc4dc340a0..b4566c5baada 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -91,7 +91,7 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, !defined(CURLRES_AMIGA) /* - * Curl_ipv4_resolve_r() - ipv4 thread-safe resolver function. + * Curl_ipv4_resolve_r() - IPv4 thread-safe resolver function. * * This is used for both synchronous and asynchronous resolver builds, * implying that only thread-safe code and function calls may be used. diff --git a/lib/http.c b/lib/http.c index ef2bd8f96316..9af3a7051470 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2054,9 +2054,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) } else { /* This is the HTTP Host: header, so we want - * - for ipv6 origins: "[ipv6-address]" where the ipv6 address is + * - for IPv6 origins: "[ipv6-address]" where the IPv6 address is * found in origin->hostname, stripped of zoneid/scopeid. - * - the (IDN converted) origin->hostname (DNS name or ipv4) otherwise. + * - the (IDN converted) origin->hostname (DNS name or IPv4) otherwise. * Note: zoneid/scopeid only applies to local routing and has no * meaning on the remote HTTP server (eg. would confuse it). */ bool ipv6 = (bool)data->state.origin->ipv6; diff --git a/lib/peer.h b/lib/peer.h index f6ed00cd485e..a0a3054cd844 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -33,9 +33,9 @@ struct Curl_scheme; struct Curl_peer { const struct Curl_scheme *scheme; /* url scheme */ char *hostname; /* normalized hostname (IDN decoded when supported) */ - char *zoneid; /* NULL or ipv6 zone identifier */ + char *zoneid; /* NULL or IPv6 zone identifier */ uint32_t refcount; /* created with 1, freed when dropping to 0 */ - uint32_t scopeid; /* != 0, ipv6 scope to use */ + uint32_t scopeid; /* != 0, IPv6 scope to use */ uint16_t port; BIT(unix_socket); /* hostname is a UDS path without the prefix */ BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */ @@ -47,12 +47,12 @@ struct Curl_peer { * - `peer->user_hostname` is the passed `hostname` * - `peer->hostname` is the normalized `hostname` via * + IDN conversion if it has non-ASCII characters - * + stripping of surrounding '[]' for URL formatted ipv6 addresses + * + stripping of surrounding '[]' for URL formatted IPv6 addresses * + the path alone in case of a unix domain socket, e.g. hostname * starts with CURL_PEER_UDS_PREFIX and is longer * Scans for IPv6 addresses even without surrounding '[]'. - * - `zoneid` ipv6 zone identifier or NULL - * - `scopeid` ipv6 scopeid of zoneid, when known. + * - `zoneid` IPv6 zone identifier or NULL + * - `scopeid` IPv6 scopeid of zoneid, when known. */ CURLcode Curl_peer_create(struct Curl_easy *data, const struct Curl_scheme *scheme, diff --git a/lib/setopt.c b/lib/setopt.c index a95a11ef0e73..9a87e748d2a6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2054,7 +2054,7 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, * If the encoding is set to "" we use an Accept-Encoding header that * encompasses all the encodings we support. * If the encoding is set to NULL we do not send an Accept-Encoding header - * and ignore an received Content-Encoding header. + * and ignore any received Content-Encoding header. * */ if(ptr && !*ptr) { @@ -2077,7 +2077,7 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, */ result = Curl_setstropt(&s->str[STRING_AWS_SIGV4], ptr); /* - * Basic been set by default it need to be unset here + * Basic has been set by default; it needs to be unset here. */ if(s->str[STRING_AWS_SIGV4]) s->httpauth = CURLAUTH_AWS_SIGV4; diff --git a/lib/socks.c b/lib/socks.c index 6c458c505d2f..ab3663e88628 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -361,7 +361,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, return CURLPX_SEND_REQUEST; } else { - /* No ipv4 address resolved */ + /* No IPv4 address resolved */ failf(data, "SOCKS4 connection to %s not supported", sx->dest->hostname); return CURLPX_RESOLVE_HOST; } diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index c1f9ae40ce99..1e0b5def65d2 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -876,10 +876,10 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, *do_early_data = TRUE; } } - else { /* h3_conn_init set, assume done */ - ctx->use_earlydata = TRUE; - cf->connected = TRUE; - *do_early_data = TRUE; + else { /* init_h3_conn_cb not set, assume done */ + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; } } #else /* not supported in the TLS backend */ diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index ea21e19289cf..3d1203d3b655 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2582,7 +2582,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - /* For ipv6 origins, use the `user_hostname` that has the "[]" enclosure. + /* For IPv6 origins, use the `user_hostname` that has the "[]" enclosure. * Otherwise, use `hostname` that is IDN converted. */ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->origin->ipv6 ? diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 406037bcc33f..094fc3a374f1 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2144,7 +2144,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, else if(dNSName || iPAddress) { const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : (peer->type == CURL_SSL_PEER_IPV4) ? - "ipv4 address" : "ipv6 address"; + "IPv4 address" : "IPv6 address"; infof(data, " subjectAltName does not match %s %s", tname, peer->origin->user_hostname); failf(data, "SSL: no alternative certificate subject name matches " diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index fc7ae3c6122f..207eb297bb79 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -57,11 +57,11 @@ #include "os400sys.h" -/** -*** QADRT OS/400 ASCII runtime defines only the most used procedures, but a -*** lot of them are not supported. This module implements ASCII wrappers for -*** those that are used by libcurl, but not defined by QADRT. -**/ +/* + * QADRT OS/400 ASCII runtime defines only the most used procedures, but a + * lot of them are not supported. This module implements ASCII wrappers for + * those that are used by libcurl, but not defined by QADRT. + */ #pragma convert(0) /* Restore EBCDIC. */ diff --git a/projects/vms/config_h.com b/projects/vms/config_h.com index 2de76ad7318a..a19bc1affa1f 100644 --- a/projects/vms/config_h.com +++ b/projects/vms/config_h.com @@ -20,7 +20,7 @@ $! $! The config_vms.h will be invoked by the resulting config.h file. $! $! This procedure knows about the DEC C RTL on the system it is on. -$! Future versions may be handle the GNV, the OpenVMS porting library, +$! Future versions may handle the GNV, the OpenVMS porting library, $! and others. $! $! This procedure may not guess the options correctly for all architectures, diff --git a/scripts/verify-release b/scripts/verify-release index 920303aec009..f0339d306ff6 100755 --- a/scripts/verify-release +++ b/scripts/verify-release @@ -65,7 +65,7 @@ rm -rf _tarballs/* # checksum the original tarball to compare with later sha256sum -- "$tarball" >_tarballs/checksum -# extract version number from file name +# extract version number from filename tarver=$(echo "$tarball" | sed 's/curl-\([0-9.]*\)\..*/\1/') # extract the version from the official header file diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index a423524fa5ef..d883822d54a0 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -103,13 +103,13 @@ def test_06_12_stats_fail_tcp(self, env: Env, httpd, nghttpx): # check timers when trying 3 unresponsive addresses @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), - reason='curl lacks ipv6 support') + reason='curl lacks IPv6 support') @pytest.mark.skipif(condition=not Env.curl_has_feature('AsynchDNS'), reason='curl lacks async DNS support') @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") def test_06_13_timers(self, env: Env): curl = CurlClient(env=env) - # ipv6 0100::/64 is supposed to go into the void (rfc6666) + # IPv6 0100::/64 is supposed to go into the void (rfc6666) r = curl.http_download(urls=['https://xxx.invalid/'], extra_args=[ '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', '--connect-timeout', '1', diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index 5843c30b08f1..7ef77465bc54 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -392,9 +392,9 @@ def test_10_14_proxys_ip_addr(self, env: Env, httpd, proto): r.check_response(count=1, http_status=200, protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1') - # download via http: ipv4 proxy (no tunnel) using IP address, IPv6 only + # download via http: IPv4 proxy (no tunnel) using IP address, IPv6 only @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), - reason='no ipv6 support') + reason='no IPv6 support') def test_10_15_proxy_ip_addr(self, env: Env, httpd): proto = 'http/1.1' curl = CurlClient(env=env, force_resolv=False) @@ -406,9 +406,9 @@ def test_10_15_proxy_ip_addr(self, env: Env, httpd): r.check_exit_code(0) r.check_response(count=1, http_status=200, protocol='HTTP/1.1') - # download via http: ipv6 proxy (no tunnel) using IP address, IPv4 only + # download via http: IPv6 proxy (no tunnel) using IP address, IPv4 only @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), - reason='no ipv6 support') + reason='no IPv6 support') def test_10_16_proxy_ip_addr(self, env: Env, httpd): proto = 'http/1.1' curl = CurlClient(env=env, force_resolv=False) diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 0a86e6439b1e..a3f5e3a3c07c 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -211,7 +211,7 @@ def test_20_09_data_empty(self, env: Env, ws_echo, model): r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url]) r.check_exit_code(0) - # use ws:// url with HTTP proxy, check that it tunnels automatically + # use ws:// URL with HTTP proxy, check that it tunnels automatically def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): curl = CurlClient(env=env) url = f'ws://127.0.0.1:{env.ws_port}/' diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index 8f2937f42f1e..a50997409d01 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -51,7 +51,7 @@ def _class_scope(self, env, httpd): indir = httpd.docs_dir env.make_data_file(indir=indir, fname="data-0k", fsize=0) - # use .invalid host name that should never resolv + # use .invalid hostname that should never resolv def test_21_01_resolv_invalid_one(self, env: Env, httpd, nghttpx): count = 1 run_env = os.environ.copy() @@ -62,7 +62,7 @@ def test_21_01_resolv_invalid_one(self, env: Env, httpd, nghttpx): r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) - # use .invalid host name, one after the other + # use .invalid hostname, one after the other @pytest.mark.parametrize("delay_ms", [1, 50]) def test_21_02_resolv_invalid_serial(self, env: Env, delay_ms, httpd, nghttpx): count = 10 @@ -74,7 +74,7 @@ def test_21_02_resolv_invalid_serial(self, env: Env, delay_ms, httpd, nghttpx): r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) - # use .invalid host name, parallel + # use .invalid hostname, parallel @pytest.mark.parametrize("delay_ms", [1, 50]) def test_21_03_resolv_invalid_parallel(self, env: Env, delay_ms, httpd, nghttpx): count = 20 @@ -88,7 +88,7 @@ def test_21_03_resolv_invalid_parallel(self, env: Env, delay_ms, httpd, nghttpx) r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) - # resolve first url with ipv6 only and fail that, resolve second + # resolve first URL with IPv6 only and fail that, resolve second # with ipv*, should succeed. def test_21_04_resolv_inv_v6(self, env: Env, httpd): count = 2 @@ -100,7 +100,7 @@ def test_21_04_resolv_inv_v6(self, env: Env, httpd): pytest.skip(f'example client not built: {client.name}') dfiles = [client.download_file(i) for i in range(count)] self._clean_files(dfiles) - # let the first URL resolve via ipv6 only, which we force to fail + # let the first URL resolve via IPv6 only, which we force to fail r = client.run(args=[ '-n', f'{count}', '-6', '-C', env.ca.cert_file, url ]) @@ -108,7 +108,7 @@ def test_21_04_resolv_inv_v6(self, env: Env, httpd): assert not os.path.exists(dfiles[0]) assert os.path.exists(dfiles[1]) - # use .invalid host name, parallel, single resolve thread + # use .invalid hostname, parallel, single resolve thread @pytest.mark.skipif(condition=not Env.curl_resolv_threaded(), reason="no threaded resolver") def test_21_05_resolv_single_thread(self, env: Env, httpd, nghttpx): count = 10 diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 9382ef70d9d2..29630e5b07fc 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -552,7 +552,7 @@ def test_60_14_udp_tunnel_capsule_absent( # _require_available(h2o_proxy=h2o_proxy) # curl = CurlClient(env=env, timeout=15) # url = f"https://localhost:{h2o_proxy.port}/data.json" - # # ipv6 0100::/64 is supposed to go into the void (rfc6666) + # # IPv6 0100::/64 is supposed to go into the void (rfc6666) # xargs = [ # '--proxy', 'https://xxx.invalid/', # '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index b472ed5691b4..f964067f1550 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -287,7 +287,7 @@ static void usage_hx_download(const char *msg) " -r :: resolve information\n" " -T number max concurrent connections total\n" " -V http_version (http/1.1, h2, h3) http version to use\n" - " -6 use ipv6 for resolving the FIRST URL\n" + " -6 use IPv6 for resolving the FIRST URL\n" ); } diff --git a/tests/secureserver.pl b/tests/secureserver.pl index 5025d47b4b8b..5aa82f4a0f00 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -262,7 +262,7 @@ sub exit_signal_handler { $socketopt = "-O a:SO_REUSEADDR=1"; } # TODO: we do not use $host_ip in this old version. I find - # no documentation how to. But maybe ipv6 is not available anyway? + # no documentation how to. But maybe IPv6 is not available anyway? $cmd = "\"$stunnel\" -p $certfile -P $pidfile "; $cmd .= "-d $accept_port -r $target_port -f -D $loglevel "; $cmd .= ($socketopt) ? "$socketopt " : ""; diff --git a/tests/unit/unit1658.c b/tests/unit/unit1658.c index 1491e9338f96..49859c69c8d6 100644 --- a/tests/unit/unit1658.c +++ b/tests/unit/unit1658.c @@ -355,7 +355,7 @@ static CURLcode test_unit1658(const char *arg) "r:43|" }, { - "alpn + two ipv4 addresses", + "alpn + two IPv4 addresses", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x01" /* RR (1 == ALPN) */ @@ -370,7 +370,7 @@ static CURLcode test_unit1658(const char *arg) "r:0|p:16|.|alpn:10|ipv4:192.168.0.1|ipv4:192.168.0.2|" }, { - "alpn + two ipv4 addresses in wrong order", + "alpn + two IPv4 addresses in wrong order", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x04" /* RR (4 == Ipv4hints) */ @@ -385,7 +385,7 @@ static CURLcode test_unit1658(const char *arg) "r:8|" }, { - "alpn + ipv4 address with wrong size", + "alpn + IPv4 address with wrong size", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x01" /* RR (1 == ALPN) */ @@ -399,7 +399,7 @@ static CURLcode test_unit1658(const char *arg) "r:43|" }, { - "alpn + one ipv6 address", + "alpn + one IPv6 address", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x01" /* RR (1 == ALPN) */ @@ -413,7 +413,7 @@ static CURLcode test_unit1658(const char *arg) "r:0|p:16|.|alpn:10|ipv6:fe80:dabb:c1ff:fea3:8a22:1234:5678:9123|" }, { - "alpn + one ipv6 address with wrong size", + "alpn + one IPv6 address with wrong size", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x01" /* RR (1 == ALPN) */ @@ -427,7 +427,7 @@ static CURLcode test_unit1658(const char *arg) "r:43|" }, { - "alpn + two ipv6 addresses", + "alpn + two IPv6 addresses", (const unsigned char *)"\x00\x10" /* 16-bit prio */ "\x00" /* no RNAME */ "\x00\x01" /* RR (1 == ALPN) */ diff --git a/tests/unit/unit2600.c b/tests/unit/unit2600.c index a8affbb9eb51..c77c3a4fe7e2 100644 --- a/tests/unit/unit2600.c +++ b/tests/unit/unit2600.c @@ -268,12 +268,12 @@ static void check_result(const struct test_case *tc, struct test_result *tr) fail(msg); } if(tr->cf4.creations != tc->exp_cf4_creations) { - curl_msprintf(msg, "%d: expected %d ipv4 creations, but got %d", + curl_msprintf(msg, "%d: expected %d IPv4 creations, but got %d", tc->id, tc->exp_cf4_creations, tr->cf4.creations); fail(msg); } if(tr->cf6.creations != tc->exp_cf6_creations) { - curl_msprintf(msg, "%d: expected %d ipv6 creations, but got %d", + curl_msprintf(msg, "%d: expected %d IPv6 creations, but got %d", tc->id, tc->exp_cf6_creations, tr->cf6.creations); fail(msg); } @@ -290,7 +290,7 @@ static void check_result(const struct test_case *tc, struct test_result *tr) fail(msg); } if(tr->cf6.creations && tr->cf4.creations && tc->pref_family) { - /* did ipv4 and ipv6 both, expect the preferred family to start right away + /* did IPv4 and IPv6 both, expect the preferred family to start right away * with the other being delayed by the happy_eyeball_timeout */ struct ai_family_stats *stats1 = !strcmp(tc->pref_family, "v6") ? &tr->cf6 : &tr->cf4; @@ -359,7 +359,7 @@ static void test_connect(CURL *easy, const struct test_case *tc) * - replace the creation of the TCP socket filter with our test filter * - test filter does nothing and reports failure after configured delay * - we feed addresses into the resolve cache to simulate different cases - * - we monitor how many instances of ipv4/v6 attempts are made and when + * - we monitor how many instances of IPv4/IPv6 attempts are made and when * - for mixed families, we expect HAPPY_EYEBALLS_TIMEOUT to trigger * * Max Duration checks needs to be conservative since CI jobs are not @@ -384,20 +384,20 @@ static CURLcode test_unit2600(const char *arg) { 1, TURL, "abc.test:123:192.0.2.1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 1, 0, 200, TC_TMOT, R_FAIL, NULL, 1 }, - /* 1 ipv4, fails after ~200ms, reports COULDNT_CONNECT */ + /* 1 IPv4, fails after ~200ms, reports COULDNT_CONNECT */ { 2, TURL, "abc.test:123:192.0.2.1,192.0.2.2", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 2, 0, 400, TC_TMOT, R_FAIL, NULL, 2 }, - /* 2 ipv4, fails after ~400ms, reports COULDNT_CONNECT */ + /* 2 IPv4, fails after ~400ms, reports COULDNT_CONNECT */ #ifdef USE_IPV6 { 3, TURL, "abc.test:123:::1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 0, 1, 200, TC_TMOT, R_FAIL, NULL, 1 }, - /* 1 ipv6, fails after ~200ms, reports COULDNT_CONNECT */ + /* 1 IPv6, fails after ~200ms, reports COULDNT_CONNECT */ { 4, TURL, "abc.test:123:::1,::2", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 0, 2, 400, TC_TMOT, R_FAIL, NULL, 2 }, - /* 2 ipv6, fails after ~400ms, reports COULDNT_CONNECT */ + /* 2 IPv6, fails after ~400ms, reports COULDNT_CONNECT */ { 5, TURL, "abc.test:123:192.0.2.1,::1", CURL_IPRESOLVE_WHATEVER, CNCT_TMOT, 150, 250, 250, 1, 1, 350, TC_TMOT, R_FAIL, "v6", 2 }, From eb73af253cb37bf3caa2112fff7d02f38cfe49fb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 8 Jul 2026 14:15:10 +0200 Subject: [PATCH 0647/1062] tool: fix memory use in parallel mode The curl tool was creating a new transfer every time it checked if it needed to add one to reach max parallelism. This led to eventually all configured transfers to have easy handles created. Limit the creation again to the ones needed for max parallelism. scorecard.py: set --out-null only for curl versions that support it Closes #22277 --- src/tool_operate.c | 9 ++++++++- tests/http/testenv/curl.py | 5 ++++- tests/http/testenv/env.py | 7 +++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index 8ccd791bf53f..56e3544ca2bb 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1552,7 +1552,13 @@ static CURLcode add_parallel_transfers(CURLM *multi, CURLSH *share, return CURLE_UNKNOWN_OPTION; } - if(nxfers < (curl_off_t)(global->parallel_max * 2)) { + if(all_added >= global->parallel_max) { + /* we are at max parallelism, no need to create more transfers */ + *morep = TRUE; /* pretend there are, we have not checked */ + return CURLE_OK; + } + /* if the list is empty, create one to kickstart the loop */ + if(!transfers) { bool skipped = FALSE; do { result = create_transfer(share, addedp, &skipped); @@ -1560,6 +1566,7 @@ static CURLcode add_parallel_transfers(CURLM *multi, CURLSH *share, return result; } while(skipped); } + /* add transfers until max parallelism is achieved again */ for(per = transfers; per && (all_added < global->parallel_max); per = per->next) { if(per->added || per->skip) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 3071d7dd4aa9..90b7b2885673 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -729,7 +729,10 @@ def http_download(self, urls: List[str], if extra_args is None: extra_args = [] if no_save: - extra_args.extend(['--out-null']) + if self.env.curl_version_at_least('8.16.0'): + extra_args.extend(['--out-null']) + else: + extra_args.extend(['-o', '/dev/null']) else: extra_args.extend(['-o', 'download_#1.data']) if limit_rate: diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 4a18d65ffab6..78ab3abec949 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -512,6 +512,13 @@ def curl_uses_ossl_quic() -> bool: def curl_version_string() -> str: return Env.CONFIG.curl_props["version_string"] + @staticmethod + def curl_version_at_least(min_version) -> bool: + version = Env.curl_version() + return Env.CONFIG.versiontuple(min_version) <= Env.CONFIG.versiontuple( + version + ) + @staticmethod def curl_features_string() -> str: return Env.CONFIG.curl_props["features_string"] From e89d5a1202f3e00918eb0ea465041dbaa78e75c7 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 10 Jul 2026 09:58:08 +0200 Subject: [PATCH 0648/1062] tests: remove test1701 Test 1701 did a HTTP/2 Upgrade in a HTTP/1.1 POST request. This will no longer be supported in the upcoming nghttp2 release for security reasons (Upgrade requests with body a re messy, error prone and can easily lead to connection confusion). As no other server we have supports this, remove the test. Reported-by: firexinghe on github Fixes #22280 Closes #22291 --- tests/data/Makefile.am | 2 +- tests/data/test1701 | 82 ------------------------------------------ 2 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 tests/data/test1701 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 4ef4f55f9587..53a1927146b5 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -223,7 +223,7 @@ test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ test1677 test1680 test1681 test1682 test1683 test1684 \ test1685 test1686 \ \ -test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ +test1700 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ test1720 test1721 test1722 test1723 test1724 test1725 \ \ diff --git a/tests/data/test1701 b/tests/data/test1701 deleted file mode 100644 index 50e782bed037..000000000000 --- a/tests/data/test1701 +++ /dev/null @@ -1,82 +0,0 @@ - - - - -HTTP -HTTP POST -HTTP/2 - - - -# Server-side - - -HTTP/1.1 200 OK -Date: Tue, 09 Nov 2010 14:49:00 GMT -Server: test-server/fake -Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT -ETag: "21025-dc7-39462498" -Accept-Ranges: bytes -Content-Length: 6 -Connection: close -Content-Type: text/html -Funny-head: yesyes - --foo- - - - -# Client-side - - -h2c - - -http/2 - - -HTTP/2 POST with Upgrade: - - -http://%HOSTIP:%HTTP2PORT/%TESTNUMBER --http2 -d "datatosend" - - - -# Verify data after the test has been "shot" - - -^X-Forwarded-Proto:.* -^Via:.* - - -POST /%TESTNUMBER HTTP/1.1 -Host: %HOSTIP:%HTTP2PORT -User-Agent: curl/%VERSION -Accept: */* -Content-Length: 10 -Content-Type: application/x-www-form-urlencoded - -datatosend - - -HTTP/1.1 101 Switching Protocols -Connection: Upgrade -Upgrade: h2c - -HTTP/2 200%SP -date: Tue, 09 Nov 2010 14:49:00 GMT -last-modified: Tue, 13 Jun 2000 12:10:00 GMT -etag: "21025-dc7-39462498" -accept-ranges: bytes -content-length: 6 -content-type: text/html -funny-head: yesyes -via: 1.1 nghttpx - --foo- - - -s/^server: nghttpx.*\r?\n// - - - From b940cb15666560c86d4f443713a621d737a56c60 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 9 Jul 2026 12:45:51 +0200 Subject: [PATCH 0649/1062] ws: pause/unpause write handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Websocket frames need to be written individualy, so that applications can access the frame meta data correctly. This worked until the write function triggered a pause. Remaining frames accumulated in the "out" writer's buffer and on unpaused were written in one chunk. ws decode writer will now stop writing frames when the client writer is paused. To handle the writing of buffered raw data after an unpause, client writers have gotten a new "flush" method. Add pytest test_20_12 with a new client to check handling of pauses and websocket frames. Reported-by: Hendrik Hübner Fixes #22273 Closes #22283 --- lib/content_encoding.c | 6 + lib/cw-out.c | 59 ++++---- lib/cw-pause.c | 18 +-- lib/cw-pause.h | 2 - lib/ftp.c | 1 + lib/headers.c | 1 + lib/http_chunks.c | 1 + lib/sendf.c | 38 +++++- lib/sendf.h | 8 ++ lib/ws.c | 58 +++++++- tests/http/Makefile.am | 3 +- tests/http/test_20_websockets.py | 106 ++++++++++----- tests/http/testenv/env.py | 4 - tests/http/testenv/ws_4frames_server.py | 75 +++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/cli_ws_pause.c | 171 ++++++++++++++++++++++++ 16 files changed, 466 insertions(+), 86 deletions(-) create mode 100755 tests/http/testenv/ws_4frames_server.py create mode 100644 tests/libtest/cli_ws_pause.c diff --git a/lib/content_encoding.c b/lib/content_encoding.c index df62741932b0..a3724d319d69 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -299,6 +299,7 @@ static const struct Curl_cwtype deflate_encoding = { NULL, deflate_do_init, deflate_do_write, + Curl_cwriter_def_flush, deflate_do_close, sizeof(struct zlib_writer) }; @@ -360,6 +361,7 @@ static const struct Curl_cwtype gzip_encoding = { "x-gzip", gzip_do_init, gzip_do_write, + Curl_cwriter_def_flush, gzip_do_close, sizeof(struct zlib_writer) }; @@ -493,6 +495,7 @@ static const struct Curl_cwtype brotli_encoding = { NULL, brotli_do_init, brotli_do_write, + Curl_cwriter_def_flush, brotli_do_close, sizeof(struct brotli_writer) }; @@ -606,6 +609,7 @@ static const struct Curl_cwtype zstd_encoding = { NULL, zstd_do_init, zstd_do_write, + Curl_cwriter_def_flush, zstd_do_close, sizeof(struct zstd_writer) }; @@ -617,6 +621,7 @@ static const struct Curl_cwtype identity_encoding = { "none", Curl_cwriter_def_init, Curl_cwriter_def_write, + Curl_cwriter_def_flush, Curl_cwriter_def_close, sizeof(struct Curl_cwriter) }; @@ -704,6 +709,7 @@ static const struct Curl_cwtype error_writer = { NULL, error_do_init, error_do_write, + Curl_cwriter_def_flush, error_do_close, sizeof(struct Curl_cwriter) }; diff --git a/lib/cw-out.c b/lib/cw-out.c index fad89b392cc8..b38fde455fd4 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -428,6 +428,8 @@ static CURLcode cw_out_write(struct Curl_easy *data, CURLcode result; bool flush_all = !!(type & CLIENTWRITE_EOS); + CURL_TRC_WRITE(data, "[OUT] write %zu bytes, type=%x", + blen, (unsigned int)type); if((type & CLIENTWRITE_BODY) || ((type & CLIENTWRITE_HEADER) && data->set.include_header)) { cw_out_type otype = (!blen && (type & CLIENTWRITE_0LEN)) ? @@ -446,11 +448,40 @@ static CURLcode cw_out_write(struct Curl_easy *data, return CURLE_OK; } +static CURLcode cw_out_do_flush(struct Curl_easy *data, + struct Curl_cwriter *cw_out, + bool flush_all) +{ + struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; + CURLcode result = CURLE_OK; + + if(ctx->errored) + return CURLE_WRITE_ERROR; + if(ctx->paused) + return CURLE_OK; /* not doing it */ + + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) { + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + return result; + } + return result; +} + +static CURLcode cw_out_flush(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + CURL_TRC_WRITE(data, "[OUT] flush"); + return cw_out_do_flush(data, writer, FALSE); +} + const struct Curl_cwtype Curl_cwt_out = { "cw-out", NULL, cw_out_init, cw_out_write, + cw_out_flush, cw_out_close, sizeof(struct cw_out_ctx) }; @@ -468,27 +499,6 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data) return (bool)ctx->paused; } -static CURLcode cw_out_flush(struct Curl_easy *data, - struct Curl_cwriter *cw_out, - bool flush_all) -{ - struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; - CURLcode result = CURLE_OK; - - if(ctx->errored) - return CURLE_WRITE_ERROR; - if(ctx->paused) - return CURLE_OK; /* not doing it */ - - result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); - if(result) { - ctx->errored = TRUE; - cw_out_bufs_free(ctx); - return result; - } - return result; -} - CURLcode Curl_cw_out_unpause(struct Curl_easy *data) { struct Curl_cwriter *cw_out; @@ -499,9 +509,6 @@ CURLcode Curl_cw_out_unpause(struct Curl_easy *data) struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; CURL_TRC_WRITE(data, "[OUT] unpause"); ctx->paused = FALSE; - result = Curl_cw_pause_flush(data); - if(!result) - result = cw_out_flush(data, cw_out, FALSE); } return result; } @@ -514,9 +521,9 @@ CURLcode Curl_cw_out_done(struct Curl_easy *data) cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); if(cw_out) { CURL_TRC_WRITE(data, "[OUT] done"); - result = Curl_cw_pause_flush(data); + result = Curl_client_flush(data); if(!result) - result = cw_out_flush(data, cw_out, TRUE); + result = cw_out_do_flush(data, cw_out, TRUE); } return result; } diff --git a/lib/cw-pause.c b/lib/cw-pause.c index 5561a8d7b7f6..a42f61f4baf9 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -138,6 +138,9 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, *plast = NULL; } } + + if(!result) + result = Curl_cwriter_flush(data, cw_pause->next); return result; } @@ -166,8 +169,6 @@ static CURLcode cw_pause_write(struct Curl_easy *data, if(wlen < blen) wtype &= ~CLIENTWRITE_EOS; result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen); - CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d", - wlen, blen, (unsigned int)wtype, (int)result); if(result) return result; buf += wlen; @@ -210,18 +211,7 @@ const struct Curl_cwtype Curl_cwt_pause = { NULL, cw_pause_init, cw_pause_write, + cw_pause_flush, cw_pause_close, sizeof(struct cw_pause_ctx) }; - -CURLcode Curl_cw_pause_flush(struct Curl_easy *data) -{ - struct Curl_cwriter *cw_pause; - CURLcode result = CURLE_OK; - - cw_pause = Curl_cwriter_get_by_type(data, &Curl_cwt_pause); - if(cw_pause) - result = cw_pause_flush(data, cw_pause); - - return result; -} diff --git a/lib/cw-pause.h b/lib/cw-pause.h index 544cbfa57778..9a41a1c9b1a7 100644 --- a/lib/cw-pause.h +++ b/lib/cw-pause.h @@ -33,6 +33,4 @@ struct Curl_easy; */ extern const struct Curl_cwtype Curl_cwt_pause; -CURLcode Curl_cw_pause_flush(struct Curl_easy *data); - #endif /* HEADER_CURL_CW_PAUSE_H */ diff --git a/lib/ftp.c b/lib/ftp.c index c4b1b5fcfd95..b3da888b44e9 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -445,6 +445,7 @@ static const struct Curl_cwtype ftp_cw_lc = { NULL, Curl_cwriter_def_init, ftp_cw_lc_write, + Curl_cwriter_def_flush, Curl_cwriter_def_close, sizeof(struct ftp_cw_lc_ctx) }; diff --git a/lib/headers.c b/lib/headers.c index b290a9e5b340..5d2101ee881a 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -317,6 +317,7 @@ static const struct Curl_cwtype hds_cw_collect = { NULL, Curl_cwriter_def_init, hds_cw_collect_write, + Curl_cwriter_def_flush, Curl_cwriter_def_close, sizeof(struct hds_cw_collect_ctx) }; diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 9596fc6693a4..93f5bb6c4222 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -468,6 +468,7 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = { NULL, cw_chunked_init, cw_chunked_write, + Curl_cwriter_def_flush, cw_chunked_close, sizeof(struct chunked_writer) }; diff --git a/lib/sendf.c b/lib/sendf.c index 7559f64f84a7..b830995345be 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -134,6 +134,15 @@ CURLcode Curl_cwriter_write(struct Curl_easy *data, return writer->cwt->do_write(data, writer, type, buf, nbytes); } +CURLcode Curl_cwriter_flush(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + if(!writer) { + return CURLE_WRITE_ERROR; + } + return writer->cwt->do_flush(data, writer); +} + CURLcode Curl_cwriter_def_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -149,6 +158,12 @@ CURLcode Curl_cwriter_def_write(struct Curl_easy *data, return Curl_cwriter_write(data, writer->next, type, buf, nbytes); } +CURLcode Curl_cwriter_def_flush(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + return Curl_cwriter_flush(data, writer->next); +} + void Curl_cwriter_def_close(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -298,6 +313,7 @@ static const struct Curl_cwtype cw_download = { NULL, Curl_cwriter_def_init, cw_download_write, + Curl_cwriter_def_flush, Curl_cwriter_def_close, sizeof(struct cw_download_ctx) }; @@ -319,6 +335,7 @@ static const struct Curl_cwtype cw_raw = { NULL, Curl_cwriter_def_init, cw_raw_write, + Curl_cwriter_def_flush, Curl_cwriter_def_close, sizeof(struct Curl_cwriter) }; @@ -401,6 +418,22 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, return result; } +CURLcode Curl_client_flush(struct Curl_easy *data) +{ + CURLcode result; + + if(!data->req.writer_stack) { + result = do_init_writer_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); + } + + result = Curl_cwriter_flush(data, data->req.writer_stack); + CURL_TRC_WRITE(data, "client_flush() -> %d", (int)result); + return result; +} + /* Create an unencoding writer stage using the given handler. */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, @@ -510,7 +543,10 @@ bool Curl_cwriter_is_paused(struct Curl_easy *data) CURLcode Curl_cwriter_unpause(struct Curl_easy *data) { - return Curl_cw_out_unpause(data); + CURLcode result = Curl_cw_out_unpause(data); + if(!result) + result = Curl_cwriter_flush(data, data->req.writer_stack); + return result; } CURLcode Curl_creader_read(struct Curl_easy *data, diff --git a/lib/sendf.h b/lib/sendf.h index 787fe7ff69ba..0cf6245ff673 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -61,6 +61,8 @@ struct Curl_easy; CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, size_t len) WARN_UNUSED_RESULT; +CURLcode Curl_client_flush(struct Curl_easy *data); + /** * Free all resources related to client writing. */ @@ -115,6 +117,8 @@ struct Curl_cwtype { CURLcode (*do_write)(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes); + CURLcode (*do_flush)(struct Curl_easy *data, + struct Curl_cwriter *writer); void (*do_close)(struct Curl_easy *data, struct Curl_cwriter *writer); size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */ @@ -179,6 +183,8 @@ struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, CURLcode Curl_cwriter_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes); +CURLcode Curl_cwriter_flush(struct Curl_easy *data, + struct Curl_cwriter *writer); /** * Return TRUE iff client writer is paused. @@ -201,6 +207,8 @@ CURLcode Curl_cwriter_def_init(struct Curl_easy *data, CURLcode Curl_cwriter_def_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes); +CURLcode Curl_cwriter_def_flush(struct Curl_easy *data, + struct Curl_cwriter *writer); void Curl_cwriter_def_close(struct Curl_easy *data, struct Curl_cwriter *writer); diff --git a/lib/ws.c b/lib/ws.c index 9820c3e4bd26..3082860389bb 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -477,7 +477,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, dec->frame_age = 0; dec->payload_offset = 0; - ws_dec_info(dec, data, "decoded"); + ws_dec_info(dec, data, "head"); return CURLE_OK; } return CURLE_AGAIN; @@ -496,7 +496,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, size_t remain = curlx_sotouz_range(dec->payload_len - dec->payload_offset, 0, SIZE_MAX); - while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { + while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen) && + !Curl_cwriter_is_paused(data)) { if(inlen > remain) inlen = remain; result = write_cb(inbuf, inlen, dec->frame_age, dec->frame_flags, @@ -733,7 +734,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, } } - while(!Curl_bufq_is_empty(&ctx->buf)) { + while(!Curl_bufq_is_empty(&ctx->buf) && !Curl_cwriter_is_paused(data)) { struct ws_cw_dec_ctx pass_ctx; pass_ctx.data = data; pass_ctx.ws = ws; @@ -768,12 +769,63 @@ static CURLcode ws_cw_write(struct Curl_easy *data, return result; } +static CURLcode ws_cw_flush(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + CURLcode result = CURLE_OK; + + CURL_TRC_WRITE(data, "[ws] flush"); + if(!data->set.ws_raw_mode) { + struct ws_cw_ctx *ctx = writer->ctx; + struct websocket *ws; + + /* Frames should be written one by one, else the meta data does + * not fit. Flush the next writer first, so it does not aggregate + * our flushed data with anything it might have buffered. */ + result = Curl_cwriter_flush(data, writer->next); + if(result) + goto out; + + ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] not a websocket transfer"); + return CURLE_FAILED_INIT; + } + + while(!Curl_bufq_is_empty(&ctx->buf) && !Curl_cwriter_is_paused(data)) { + struct ws_cw_dec_ctx pass_ctx; + pass_ctx.data = data; + pass_ctx.ws = ws; + pass_ctx.next_writer = writer->next; + pass_ctx.cw_type = CLIENTWRITE_BODY; + result = ws_dec_pass(&ws->dec, data, &ctx->buf, + ws_cw_dec_next, &pass_ctx); + if(result == CURLE_AGAIN) { + /* insufficient amount of data, keep it for later. + * we pretend to have written all since we have a copy */ + result = CURLE_OK; + goto out; + } + else if(result) { + failf(data, "[WS] decode payload error %d", (int)result); + return result; + } + } + } + +out: + if(!result) + result = Curl_cwriter_flush(data, writer->next); + return result; +} + /* WebSocket payload decoding client writer. */ static const struct Curl_cwtype ws_cw_decode = { "ws-decode", NULL, ws_cw_init, ws_cw_write, + ws_cw_flush, ws_cw_close, sizeof(struct ws_cw_ctx) }; diff --git a/tests/http/Makefile.am b/tests/http/Makefile.am index 6b4b6054ebf7..4f2fad7d02d7 100644 --- a/tests/http/Makefile.am +++ b/tests/http/Makefile.am @@ -38,7 +38,8 @@ TESTENV = \ testenv/ports.py \ testenv/sshd.py \ testenv/vsftpd.py \ - testenv/ws_echo_server.py + testenv/ws_echo_server.py \ + testenv/ws_4frames_server.py EXTRA_DIST = \ CMakeLists.txt \ diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index a3f5e3a3c07c..5ee8b5e6a99e 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -44,13 +44,19 @@ log = logging.getLogger(__name__) -@pytest.mark.skipif(condition=not Env.curl_has_protocol('ws'), - reason='curl lacks ws protocol support') -class TestWebsockets: +class WsServer: - PORT_SPECS = { - 'ws': socket.SOCK_STREAM, - } + def __init__(self, name, env, cmd): + self.name = name + self.env = env + self.run_dir = os.path.join(env.gen_dir, self.name) + self.err_file = os.path.join(self.run_dir, 'stderr') + self._rmrf(self.run_dir) + self._mkpath(self.run_dir) + self.cmd = cmd + self.wsproc = None + self.cerr = None + self.port = 0 def check_alive(self, env, port, timeout=Env.SERVER_TIMEOUT): curl = CurlClient(env=env) @@ -71,42 +77,62 @@ def _rmrf(self, path): if os.path.exists(path): shutil.rmtree(path) - @pytest.fixture(autouse=True, scope='class') - def ws_echo(self, env): - self.run_dir = os.path.join(env.gen_dir, 'ws_echo_server') - err_file = os.path.join(self.run_dir, 'stderr') - self._rmrf(self.run_dir) - self._mkpath(self.run_dir) - self.cmd = os.path.join(env.project_dir, - 'tests/http/testenv/ws_echo_server.py') - self.wsproc = None - self.cerr = None + def startup(self): def startup(ports: Dict[str, int]) -> bool: - wargs = [self.cmd, '--port', str(ports['ws'])] + self.port = ports[self.name] + wargs = [self.cmd, '--port', str(self.port)] log.info(f'start_ {wargs}') self.wsproc = subprocess.Popen(args=wargs, cwd=self.run_dir, stderr=self.cerr, stdout=self.cerr) - if self.check_alive(env, ports['ws']): - env.update_ports(ports) + if self.check_alive(self.env, self.port): + self.env.update_ports(ports) return True log.error(f'not alive {wargs}') self.wsproc.terminate() self.wsproc = None return False - with open(err_file, 'w') as self.cerr: - assert alloc_ports_and_do(TestWebsockets.PORT_SPECS, startup, - env.gen_root, max_tries=3) - assert self.wsproc - yield - self.wsproc.terminate() + self.cerr = open(self.err_file, 'w') + port_spec = { + self.name: socket.SOCK_STREAM + } + assert alloc_ports_and_do(port_spec, startup, + self.env.gen_root, max_tries=3) + assert self.wsproc + + def shutdown(self): + self.wsproc.terminate() + self.cerr.close() + + +@pytest.mark.skipif(condition=not Env.curl_has_protocol('ws'), + reason='curl lacks ws protocol support') +class TestWebsockets: + + @pytest.fixture(autouse=True, scope='class') + def ws_echo(self, env): + cmd = os.path.join(env.project_dir, + 'tests/http/testenv/ws_echo_server.py') + server = WsServer('ws_echo', env, cmd) + server.startup() + yield server + server.shutdown() + + @pytest.fixture(autouse=True, scope='class') + def ws_4frames(self, env): + cmd = os.path.join(env.project_dir, + 'tests/http/testenv/ws_4frames_server.py') + server = WsServer('ws_4frames', env, cmd) + server.startup() + yield server + server.shutdown() def test_20_01_basic(self, env: Env, ws_echo): curl = CurlClient(env=env) - url = f'http://localhost:{env.ws_port}/' + url = f'http://localhost:{ws_echo.port}/' r = curl.http_download(urls=[url]) r.check_response(http_status=426) @@ -115,7 +141,7 @@ def test_20_02_pingpong_small(self, env: Env, ws_echo): client = LocalClient(env=env, name='cli_ws_pingpong') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[url, payload]) r.check_exit_code(0) @@ -125,7 +151,7 @@ def test_20_03_pingpong_too_large(self, env: Env, ws_echo): client = LocalClient(env=env, name='cli_ws_pingpong') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[url, payload]) r.check_exit_code(100) # CURLE_TOO_LARGE @@ -137,7 +163,7 @@ def test_20_04_data_small(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[f'-{model}', '-m', str(1), '-M', str(10), url]) r.check_exit_code(0) @@ -149,7 +175,7 @@ def test_20_05_data_med(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[f'-{model}', '-m', str(120), '-M', str(130), url]) r.check_exit_code(0) @@ -161,7 +187,7 @@ def test_20_06_data_large(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[f'-{model}', '-m', str(65535 - 5), '-M', str(65535 + 5), url]) r.check_exit_code(0) @@ -175,7 +201,7 @@ def test_20_07_data_large_small_recv(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data', run_env=run_env) if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' r = client.run(args=[f'-{model}', '-m', str(65535 - 5), '-M', str(65535 + 5), url]) r.check_exit_code(0) @@ -191,7 +217,7 @@ def test_20_08_data_very_large(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data', run_env=run_env) if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' count = 10 large = 20000 r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url]) @@ -205,7 +231,7 @@ def test_20_09_data_empty(self, env: Env, ws_echo, model): client = LocalClient(env=env, name='cli_ws_data') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{env.ws_port}/' + url = f'ws://localhost:{ws_echo.port}/' count = 10 large = 0 r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url]) @@ -214,7 +240,7 @@ def test_20_09_data_empty(self, env: Env, ws_echo, model): # use ws:// URL with HTTP proxy, check that it tunnels automatically def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): curl = CurlClient(env=env) - url = f'ws://127.0.0.1:{env.ws_port}/' + url = f'ws://127.0.0.1:{ws_echo.port}/' xargs = curl.get_proxy_args(proxys=False) xargs.extend([ '--max-time', '2' @@ -300,3 +326,13 @@ def srv(): assert r.profile, f'{r}' rss2 = r.profile.stats['rss'] / (1024 * 1024) assert (rss1 * 1.1) >= rss2, 'bad memory increase' + + # test frame delivery when pausing + def test_20_12_pause_frames(self, env: Env, ws_4frames): + payload = 127 * "x" + client = LocalClient(env=env, name='cli_ws_pause') + if not client.exists(): + pytest.skip(f'example client not built: {client.name}') + url = f'ws://localhost:{ws_4frames.port}/' + r = client.run(args=[url, payload]) + r.check_exit_code(0) diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 78ab3abec949..8f619271a279 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -862,10 +862,6 @@ def danted(self) -> str: def vsftpd(self) -> str: return self.CONFIG.vsftpd - @property - def ws_port(self) -> int: - return self.CONFIG.ports["ws"] - @property def curl(self) -> str: return self.CONFIG.curl diff --git a/tests/http/testenv/ws_4frames_server.py b/tests/http/testenv/ws_4frames_server.py new file mode 100755 index 000000000000..8d5655aa5cbd --- /dev/null +++ b/tests/http/testenv/ws_4frames_server.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# +import argparse +import asyncio +import logging + +import websockets + +MESSAGES = [ + "Hello 1", + "Hello 2", + "Hello 3", + "Hello 4", +] + + +async def handler(websocket): + peer = websocket.remote_address + print(f"client from {peer[0]}:{peer[1]}", flush=True) + print("handshake complete", flush=True) + + await asyncio.sleep(0.1) + for index, payload in enumerate(MESSAGES, start=1): + await websocket.send(payload) + print(f"sent frame {index}: {payload!r}", flush=True) + # await asyncio.sleep(0.2) + + # await asyncio.sleep(2.0) + print("server done", flush=True) + + +async def main(): + parser = argparse.ArgumentParser(prog='scorecard', description=""" + Run a websocket 4frames server. + """) + parser.add_argument("--port", type=int, + default=9876, help="port to listen on") + args = parser.parse_args() + + logging.basicConfig( + format="%(asctime)s %(message)s", + level=logging.DEBUG, + ) + + print(f"listening on ws://localhost:{args.port}", flush=True) + async with websockets.serve(handler, 'localhost', args.port): + await asyncio.Future() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 64508d1285b8..140ec0bd5819 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -63,6 +63,7 @@ TESTS_C = \ cli_tls_session_reuse.c \ cli_upload_pausing.c \ cli_ws_data.c \ + cli_ws_pause.c \ cli_ws_pingpong.c \ \ lib500.c lib501.c lib502.c lib503.c lib504.c lib505.c lib506.c lib507.c \ diff --git a/tests/libtest/cli_ws_pause.c b/tests/libtest/cli_ws_pause.c new file mode 100644 index 000000000000..b42331400324 --- /dev/null +++ b/tests/libtest/cli_ws_pause.c @@ -0,0 +1,171 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +#include "testtrace.h" + +#ifndef CURL_DISABLE_WEBSOCKETS + +struct test_ws_pause_ctx { + CURL *easy; + int callback_count; + int paused; + int frames; + int errors; + int closed; +}; + +static size_t test_ws_pause_write_cb(char *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct test_ws_pause_ctx *ctx = userdata; + size_t nbytes = size * nmemb; + const struct curl_ws_frame *meta = curl_ws_meta(ctx->easy); + + ctx->callback_count++; + if(!meta) { + curl_mfprintf(stderr, "write_cb: ERROR call #%d with meta=NULL\n", + ctx->callback_count); + ++ctx->errors; + return CURL_WRITEFUNC_ERROR; + } + + ++ctx->frames; + if(meta->len < nbytes) { + curl_mfprintf(stderr, "write_cb: ERROR call #%d more data than current " + "frame, FRAME[flags=0x%x age=%d offset=%" FMT_OFF_T + " bytesleft=%" FMT_OFF_T " len=%zu], bytes=%zu\n", + ctx->callback_count, + (unsigned int)meta->flags, meta->age, + meta->offset, meta->bytesleft, meta->len, nbytes); + ++ctx->errors; + return CURL_WRITEFUNC_ERROR; + } + + if(meta->flags == 0x1) { /* TEXT frame */ + curl_mfprintf(stderr, "write_cb: call #%d FRAME[TEXT age=%d offset=%" + FMT_OFF_T " bytesleft=%" FMT_OFF_T " len=%zu] '%.*s'\n", + ctx->callback_count, meta->age, + meta->offset, meta->bytesleft, meta->len, + (int)nbytes, ptr); + } + else if(meta->flags == 0x8) { + curl_mfprintf(stderr, "write_cb: call #%d FRAME[CLOSE age=%d offset=%" + FMT_OFF_T " bytesleft=%" FMT_OFF_T " len=%zu] bytes=%zu\n", + ctx->callback_count, meta->age, + meta->offset, meta->bytesleft, meta->len, nbytes); + ctx->closed = TRUE; + } + else { + curl_mfprintf(stderr, "write_cb: call #%d FRAME[flags=0x%x age=%d offset=%" + FMT_OFF_T " bytesleft=%" FMT_OFF_T " len=%zu\n", + ctx->callback_count, + (unsigned int)meta->flags, meta->age, + meta->offset, meta->bytesleft, meta->len); + } + + if(ctx->callback_count == 1 || ctx->callback_count == 3) { + ctx->paused = 1; + curl_mfprintf(stderr, "write_cb: call #%d PAUSING\n", ctx->callback_count); + return CURL_WRITEFUNC_PAUSE; + } + return nbytes; +} +#endif /* CURL_DISABLE_WEBSOCKETS */ + +static CURLcode test_cli_ws_pause(const char *URL) +{ +#ifndef CURL_DISABLE_WEBSOCKETS + struct test_ws_pause_ctx ctx; + CURLM *multi; + int still_running = 0; + int msgs_left = 0; + int done = 0; + + memset(&ctx, 0, sizeof(ctx)); + setbuf(stdout, NULL); + + curl_global_init(CURL_GLOBAL_ALL); + + ctx.easy = curl_easy_init(); + multi = curl_multi_init(); + if(!ctx.easy || !multi) { + curl_mfprintf(stderr, "main: ERROR creating easy/multi\n"); + ctx.errors = 1; + goto out; + } + + curl_easy_setopt(ctx.easy, CURLOPT_URL, URL); + curl_easy_setopt(ctx.easy, CURLOPT_WRITEFUNCTION, test_ws_pause_write_cb); + curl_easy_setopt(ctx.easy, CURLOPT_WRITEDATA, &ctx); + curl_easy_setopt(ctx.easy, CURLOPT_VERBOSE, 1L); + + curl_multi_add_handle(multi, ctx.easy); + curl_multi_perform(multi, &still_running); + + while(still_running && !ctx.closed && !ctx.errors) { + + if(ctx.paused) { + curl_mfprintf(stderr, "main: wait and UNPAUSE\n"); + curlx_wait_ms(500); + ctx.paused = 0; + curl_easy_pause(ctx.easy, CURLPAUSE_CONT); + } + + curl_mfprintf(stderr, "main: poll\n"); + curl_multi_poll(multi, NULL, 0, 100, NULL); + curl_mfprintf(stderr, "main: perform\n"); + curl_multi_perform(multi, &still_running); + + while(!done) { + CURLMsg *msg = curl_multi_info_read(multi, &msgs_left); + if(!msg) + break; + if(msg->msg == CURLMSG_DONE) { + curl_mfprintf(stderr, "main: done result=%d (%s)\n", + (int)msg->data.result, + curl_easy_strerror(msg->data.result)); + done = 1; + } + } + } + +out: + if(ctx.easy) { + if(multi) + curl_multi_remove_handle(multi, ctx.easy); + curl_easy_cleanup(ctx.easy); + } + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + + return ctx.errors ? CURLE_WRITE_ERROR : CURLE_OK; + +#else /* !CURL_DISABLE_WEBSOCKETS */ + (void)URL; + curl_mfprintf(stderr, "WebSockets not enabled in libcurl\n"); + return (CURLcode)1; +#endif /* CURL_DISABLE_WEBSOCKETS */ +} From fa246bf7f2b6096732ae9f53408363444b5865fe Mon Sep 17 00:00:00 2001 From: itzTanos29 Date: Fri, 10 Jul 2026 12:57:41 -0400 Subject: [PATCH 0650/1062] CURLOPT_HEADERFUNCTION.md: document folded header unfolding Fixes #22296 Closes #22297 --- docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md index e89ec887f21b..107e9e524c0b 100644 --- a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md @@ -92,10 +92,11 @@ curl_easy_header(3). # LIMITATIONS -libcurl does not unfold HTTP "folded headers" (deprecated since RFC 7230). A -folded header is a header that continues on a subsequent line and starts with -a whitespace. Such folds are passed to the header callback as separate ones, -although strictly they are continuations of the previous lines. +For legacy HTTP/1 "folded headers" (deprecated since RFC 7230), libcurl +unfolds the lines before calling the header callback. A folded header is a +header that continues on a subsequent line and starts with whitespace. The +callback gets the full single header with one whitespace between the lines. +This unfolding is done since 8.18.0. # DEFAULT From 6bad3db3d55161fb6deafee005de0bbde6a795c4 Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Sat, 11 Jul 2026 20:02:33 +0100 Subject: [PATCH 0651/1062] configure: fix misleading error messages Reported-by: Bryan Henderson URL: https://curl.se/mail/lib-2026-07/0010.html Closes #22300 Signed-off-by: Emmanuel Ugwu --- configure.ac | 8 ++++---- m4/curl-openssl.m4 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 2b8a0e0f0c9e..82ded68db4d5 100644 --- a/configure.ac +++ b/configure.ac @@ -1640,7 +1640,7 @@ if test "x$OPT_BROTLI" != "xno"; then if test "x$OPT_BROTLI" != "xoff" && test "$HAVE_BROTLI" != "1"; then - AC_MSG_ERROR([BROTLI libs and/or directories were not found where specified!]) + AC_MSG_ERROR([BROTLI libs and/or directories were not found!]) fi if test "$HAVE_BROTLI" = "1"; then @@ -2354,7 +2354,7 @@ if test "x$OPT_LIBPSL" != "xno"; then ) if test "$USE_LIBPSL" != "1"; then - AC_MSG_ERROR([libpsl libs and/or directories were not found where specified!]) + AC_MSG_ERROR([libpsl libs and/or directories were not found!]) fi fi AM_CONDITIONAL([USE_LIBPSL], [test "$curl_psl_msg" = "enabled"]) @@ -2505,7 +2505,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then if test "x$OPT_LIBSSH2" != "xoff" && test "$USE_LIBSSH2" != "1"; then - AC_MSG_ERROR([libssh2 libs and/or directories were not found where specified!]) + AC_MSG_ERROR([libssh2 libs and/or directories were not found!]) fi if test "$USE_LIBSSH2" = "1"; then @@ -2581,7 +2581,7 @@ elif test "x$OPT_LIBSSH" != "xno"; then if test "x$OPT_LIBSSH" != "xoff" && test "$USE_LIBSSH" != "1"; then - AC_MSG_ERROR([libssh libs and/or directories were not found where specified!]) + AC_MSG_ERROR([libssh libs and/or directories were not found!]) fi if test "$USE_LIBSSH" = "1"; then diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 26c9eb3fbb18..96060f841dbe 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -219,7 +219,7 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" != "1"; then LIBS="$CLEANLIBS" - AC_MSG_ERROR([OpenSSL libs and/or directories were not found where specified!]) + AC_MSG_ERROR([OpenSSL libs and/or directories were not found!]) fi fi From 02214d98f7ae3e7dad099e807c1ed549ef551014 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sat, 4 Jul 2026 20:45:31 +0100 Subject: [PATCH 0652/1062] conncache: apply multi limits to transfers using a shared pool - Determine max host and total connection limits from the transfer's own multi handle and test that it works for shared connections. Prior to this change, since df67269 (precedes 8.13.0), Curl_cpool_check_limits() took the limits from the multi handle that owns the pool. A share-owned pool is not owned by any multi, so the limit check always passed. Prior to df67269 the limits came from the transfer's own multi handle. Fixes https://github.com/curl/curl/issues/22265 Closes https://github.com/curl/curl/pull/22266 --- docs/libcurl/opts/CURLSHOPT_SHARE.md | 5 ++++ lib/conncache.c | 35 +++++++++++++++++++++------- tests/http/test_02_download.py | 14 ++++++----- tests/http/test_19_shutdown.py | 8 +++---- tests/libtest/cli_hx_download.c | 12 ++++++---- 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/docs/libcurl/opts/CURLSHOPT_SHARE.md b/docs/libcurl/opts/CURLSHOPT_SHARE.md index 69b59779ad6b..827ba77ab607 100644 --- a/docs/libcurl/opts/CURLSHOPT_SHARE.md +++ b/docs/libcurl/opts/CURLSHOPT_SHARE.md @@ -79,6 +79,11 @@ in different threads using a shared connection. Note that when you use the multi interface, all easy handles added to the same multi handle share the connection cache by default without using this option. +Connection limits set with CURLMOPT_MAX_HOST_CONNECTIONS(3) and +CURLMOPT_MAX_TOTAL_CONNECTIONS(3) also apply to transfers using a shared +connection cache. Each transfer applies the limits of the multi handle it +runs on to the shared cache. + ## CURL_LOCK_DATA_PSL The Public Suffix List stored in the share object is made available to all diff --git a/lib/conncache.c b/lib/conncache.c index 6fc29be4b4e2..17e8d715b0dc 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -367,6 +367,22 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, return oldest_idle; } +/* Evict an idle connection to make room in the pool. A pool owned by + * a share has no multi that could perform a controlled shutdown of the + * connection; terminate it right away. Otherwise, hand it to the + * transfer's multi for shutdown. Expects the pool to be locked. */ +static void cpool_evict_conn(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn) +{ + if(cpool->share) { + cpool_remove_conn(cpool, conn); + Curl_cshutdn_terminate(cpool->idata, conn, TRUE); + } + else + Curl_conn_terminate(data, conn, FALSE); +} + int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *conn) { @@ -380,9 +396,10 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!cpool) return CPOOL_LIMIT_OK; - if(cpool->idata->multi) { - dest_limit = cpool->idata->multi->max_host_connections; - total_limit = cpool->idata->multi->max_total_connections; + /* multi determines the limits, no matter who owns the pool */ + if(data->multi) { + dest_limit = data->multi->max_host_connections; + total_limit = data->multi->max_total_connections; } if(!dest_limit && !total_limit) @@ -416,13 +433,13 @@ int Curl_cpool_check_limits(struct Curl_easy *data, " from %zu to reach destination limit of %zu", oldest_idle->connection_id, Curl_llist_count(&bundle->conns), dest_limit); - Curl_conn_terminate(cpool->idata, oldest_idle, FALSE); + cpool_evict_conn(cpool, data, oldest_idle); /* in case the bundle was destroyed in disconnect, look it up again */ bundle = cpool_find_bundle(cpool, conn); live = bundle ? Curl_llist_count(&bundle->conns) : 0; } - shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination); + shutdowns = Curl_cshutdn_dest_count(data, conn->destination); } if((live + shutdowns) >= dest_limit) { res = CPOOL_LIMIT_DEST; @@ -431,7 +448,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } if(total_limit) { - shutdowns = Curl_cshutdn_count(cpool->idata); + shutdowns = Curl_cshutdn_count(data); while((cpool->num_conn + shutdowns) >= total_limit) { if(shutdowns) { /* close one connection in shutdown right away, if we can */ @@ -448,9 +465,9 @@ int Curl_cpool_check_limits(struct Curl_easy *data, FMT_OFF_T " from %zu to reach total " "limit of %zu", oldest_idle->connection_id, cpool->num_conn, total_limit); - Curl_conn_terminate(cpool->idata, oldest_idle, FALSE); + cpool_evict_conn(cpool, data, oldest_idle); } - shutdowns = Curl_cshutdn_count(cpool->idata); + shutdowns = Curl_cshutdn_count(data); } if((cpool->num_conn + shutdowns) >= total_limit) { res = CPOOL_LIMIT_TOTAL; @@ -582,7 +599,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); kept = (oldest_idle != conn); if(oldest_idle) { - Curl_conn_terminate(data, oldest_idle, FALSE); + cpool_evict_conn(cpool, data, oldest_idle); } } if(do_lock) diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index c75f932b7218..de2f1a3e0935 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -586,7 +586,8 @@ def test_02_32_earlydata(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) @pytest.mark.parametrize("max_host_conns", [0, 1, 5]) - def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_conns): + @pytest.mark.parametrize("share_connect", [False, True]) + def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_conns, share_connect): if not env.curl_is_debug(): pytest.skip('only works for curl debug builds') if not env.curl_is_verbose(): @@ -601,6 +602,7 @@ def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_co client = LocalClient(name='cli_hx_download', env=env, run_env=run_env) if not client.exists(): pytest.skip(f'example client not built: {client.name}') + extra_args = ['-S'] if share_connect else [] # share connections r = client.run(args=[ '-n', f'{count}', '-m', f'{max_parallel}', @@ -608,8 +610,7 @@ def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_co '-x', # always use a fresh connection '-M', str(max_host_conns), # limit conns per host '-r', f'{env.domain1}:{port}:127.0.0.1', - '-V', proto, url - ]) + ] + extra_args + ['-V', proto, url]) r.check_exit_code(0) srcfile = os.path.join(httpd.docs_dir, docname) self.check_downloads(client, srcfile, count) @@ -625,7 +626,8 @@ def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_co @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) @pytest.mark.parametrize("max_total_conns", [0, 1, 5]) - def test_02_34_max_total_conns(self, env: Env, httpd, nghttpx, proto, max_total_conns): + @pytest.mark.parametrize("share_connect", [False, True]) + def test_02_34_max_total_conns(self, env: Env, httpd, nghttpx, proto, max_total_conns, share_connect): if not env.curl_is_debug(): pytest.skip('only works for curl debug builds') if not env.curl_is_verbose(): @@ -640,6 +642,7 @@ def test_02_34_max_total_conns(self, env: Env, httpd, nghttpx, proto, max_total_ client = LocalClient(name='cli_hx_download', env=env, run_env=run_env) if not client.exists(): pytest.skip(f'example client not built: {client.name}') + extra_args = ['-S'] if share_connect else [] # share connections r = client.run(args=[ '-n', f'{count}', '-m', f'{max_parallel}', @@ -647,8 +650,7 @@ def test_02_34_max_total_conns(self, env: Env, httpd, nghttpx, proto, max_total_ '-x', # always use a fresh connection '-T', str(max_total_conns), # limit total connections '-r', f'{env.domain1}:{port}:127.0.0.1', - '-V', proto, url - ]) + ] + extra_args + ['-V', proto, url]) r.check_exit_code(0) srcfile = os.path.join(httpd.docs_dir, docname) self.check_downloads(client, srcfile, count) diff --git a/tests/http/test_19_shutdown.py b/tests/http/test_19_shutdown.py index 4ed9ad27fb01..f8a948acd661 100644 --- a/tests/http/test_19_shutdown.py +++ b/tests/http/test_19_shutdown.py @@ -185,7 +185,8 @@ def test_19_06_check_shutdown(self, env: Env, httpd, nghttpx, proto): # run connection pressure, many small transfers, not reusing connections, # limited total @pytest.mark.parametrize("proto", ['http/1.1']) - def test_19_07_shutdown_by_curl(self, env: Env, httpd, proto): + @pytest.mark.parametrize("share_connect", [False, True]) + def test_19_07_shutdown_by_curl(self, env: Env, httpd, proto, share_connect): if not env.curl_is_debug(): pytest.skip('only works for curl debug builds') count = 500 @@ -197,15 +198,14 @@ def test_19_07_shutdown_by_curl(self, env: Env, httpd, proto): }) if not client.exists(): pytest.skip(f'example client not built: {client.name}') + extra_args = ['-S'] if share_connect else [] # share connections r = client.run(args=[ '-n', f'{count}', # that many transfers '-C', env.ca.cert_file, '-f', # forbid conn reuse '-m', '10', # max parallel '-T', '5', # max total conns at a time - '-V', proto, - url - ]) + ] + extra_args + ['-V', proto, url]) r.check_exit_code(0) shutdowns = [line for line in r.trace_lines if re.match(r'.*SHUTDOWN] shutdown, done=1', line)] diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index f964067f1550..6ad18e124a94 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -285,6 +285,7 @@ static void usage_hx_download(const char *msg) " -M number max concurrent connections to a host\n" " -P number pause transfer after `number` response bytes\n" " -r :: resolve information\n" + " -S share connections between easy handles\n" " -T number max concurrent connections total\n" " -V http_version (http/1.1, h2, h3) http version to use\n" " -6 use IPv6 for resolving the FIRST URL\n" @@ -314,13 +315,14 @@ static CURLcode test_cli_hx_download(const char *URL) size_t max_host_conns = 0; size_t max_total_conns = 0; int fresh_connect = 0; + int share_connect = 0; char *cafile = NULL; bool first_ipv6 = FALSE; CURLcode result = CURLE_OK; (void)URL; - while((ch = cgetopt(test_argc, test_argv, "aefhm:n:xA:C:F:M:P:r:T:V:6")) + while((ch = cgetopt(test_argc, test_argv, "aefhm:n:xA:C:F:M:P:r:ST:V:6")) != -1) { const char *opt = coptarg; curl_off_t num; @@ -373,6 +375,9 @@ static CURLcode test_cli_hx_download(const char *URL) curlx_free(resolve); resolve = curlx_strdup(coptarg); break; + case 'S': + share_connect = 1; + break; case 'T': if(!curlx_str_number(&opt, &num, LONG_MAX)) max_total_conns = (size_t)num; @@ -430,9 +435,8 @@ static CURLcode test_cli_hx_download(const char *URL) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); -#if 0 - curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); -#endif + if(share_connect) + curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS); From c3f9ef13f62a2f09466cbfec8676eb6fbb361313 Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Thu, 2 Jul 2026 16:23:36 +0530 Subject: [PATCH 0653/1062] mime: reject CR and LF in mail part name and filename Closes #22247 --- lib/mime.c | 10 +++ tests/data/Makefile.am | 2 +- tests/data/test3227 | 20 ++++++ tests/unit/Makefile.inc | 1 + tests/unit/unit3227.c | 133 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/data/test3227 create mode 100644 tests/unit/unit3227.c diff --git a/lib/mime.c b/lib/mime.c index 077e599e43f1..11eb66207de7 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1708,6 +1708,16 @@ static CURLcode add_content_disposition(struct Curl_easy *data, CURLcode result = CURLE_OK; char *name = NULL; char *filename = NULL; + /* The mail (and legacy mime_formescape) strategy quotes the name and + filename with a backslash and has no in-band way to represent a CR or + LF, so one embedded in the value would split the generated header. The + form strategy percent-encodes CR/LF (see escape_string) and is safe. */ + bool backslash = (strategy == MIMESTRATEGY_MAIL) || + (data && data->set.mime_formescape); + if(backslash && + ((part->name && part->name[strcspn(part->name, "\r\n")]) || + (part->filename && part->filename[strcspn(part->filename, "\r\n")]))) + return CURLE_BAD_FUNCTION_ARGUMENT; if(part->name) { name = escape_string(data, part->name, strategy); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 53a1927146b5..e1249588745c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -284,7 +284,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ -test3223 test3224 test3225 test3226 \ +test3223 test3224 test3225 test3226 test3227 \ \ test3300 test3301 test3302 test3303 test3304 test3305 \ \ diff --git a/tests/data/test3227 b/tests/data/test3227 new file mode 100644 index 000000000000..cd9d74b30f8d --- /dev/null +++ b/tests/data/test3227 @@ -0,0 +1,20 @@ + + + + +unittest +mime +SMTP + + + +# Client-side + + +unittest + + +reject CR and LF in mail mime part name and filename + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index d1fec804ee6a..c292e7a1985c 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -48,4 +48,5 @@ TESTS_C = \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ + unit3227.c \ unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c unit3400.c diff --git a/tests/unit/unit3227.c b/tests/unit/unit3227.c new file mode 100644 index 000000000000..42d068e99db2 --- /dev/null +++ b/tests/unit/unit3227.c @@ -0,0 +1,133 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" +#include "urldata.h" +#include "mime.h" + +#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ + !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)) + +/* does any generated part header carry a raw CR or LF, i.e. a split line? */ +static bool headers_have_ctrl(const curl_mimepart *part) +{ + const struct curl_slist *h; + for(h = part->curlheaders; h; h = h->next) + if(h->data[strcspn(h->data, "\r\n")]) + return TRUE; + return FALSE; +} + +static CURLcode t3227_setup(void) +{ + CURLcode result = CURLE_OK; + global_init(CURL_GLOBAL_ALL); + return result; +} + +static void t3227_stop(CURL *easy, curl_mime *mime) +{ + curl_mime_free(mime); + if(easy) + curl_easy_cleanup(easy); + curl_global_cleanup(); +} + +static CURLcode test_unit3227(const char *arg) +{ + CURL *easy = NULL; + curl_mime *mime = NULL; + curl_mimepart *part; + + UNITTEST_BEGIN(t3227_setup()) + + easy = curl_easy_init(); + abort_unless(easy, "curl_easy_init()"); + mime = curl_mime_init(easy); + abort_unless(mime, "curl_mime_init()"); + + /* A CR or LF in the mail-part name or filename would split the generated + Content-Disposition header. The mail strategy must reject it. */ + + part = curl_mime_addpart(mime); + abort_unless(part, "curl_mime_addpart()"); + curl_mime_data(part, "x", 1); + curl_mime_filename(part, "a\r\nX-Injected: 1"); + fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL, + MIMESTRATEGY_MAIL) == + CURLE_BAD_FUNCTION_ARGUMENT, + "CRLF filename accepted for mail"); + + part = curl_mime_addpart(mime); + abort_unless(part, "curl_mime_addpart()"); + curl_mime_data(part, "x", 1); + curl_mime_name(part, "a\nb"); + fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL, + MIMESTRATEGY_MAIL) == + CURLE_BAD_FUNCTION_ARGUMENT, + "LF name accepted for mail"); + + part = curl_mime_addpart(mime); + abort_unless(part, "curl_mime_addpart()"); + curl_mime_data(part, "x", 1); + curl_mime_filename(part, "a\rb"); + fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL, + MIMESTRATEGY_MAIL) == + CURLE_BAD_FUNCTION_ARGUMENT, + "CR filename accepted for mail"); + + /* A regular name and filename are accepted and produce a clean header. */ + part = curl_mime_addpart(mime); + abort_unless(part, "curl_mime_addpart()"); + curl_mime_data(part, "x", 1); + curl_mime_name(part, "field"); + curl_mime_filename(part, "readme.txt"); + fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL, + MIMESTRATEGY_MAIL) == CURLE_OK, + "plain mail part rejected"); + fail_if(headers_have_ctrl(part), "clean mail header split"); + + /* The HTTP form strategy percent-encodes CR/LF, so it stays accepted and + produces no split line. */ + part = curl_mime_addpart(mime); + abort_unless(part, "curl_mime_addpart()"); + curl_mime_data(part, "x", 1); + curl_mime_filename(part, "a\r\nX-Injected: 1"); + fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL, + MIMESTRATEGY_FORM) == CURLE_OK, + "CRLF filename rejected for form"); + fail_if(headers_have_ctrl(part), "form header split despite encoding"); + + UNITTEST_END(t3227_stop(easy, mime)) +} + +#else /* mime disabled */ + +static CURLcode test_unit3227(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + puts("nothing to do when mime is disabled"); + UNITTEST_END_SIMPLE +} + +#endif From 94947509868bab0219945567632b519416b1dfb4 Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Sun, 12 Jul 2026 17:31:44 +0530 Subject: [PATCH 0654/1062] ftp: reject control bytes in ACCT and alternative-to-user A CR or LF in the CURLOPT_FTP_ACCOUNT or CURLOPT_FTP_ALTERNATIVE_TO_USER string split the control-channel command line and smuggled a second FTP command. Reject a byte below 0x20 in both values before the command is built. Closes #22301 --- lib/ftp.c | 51 ++++++++++++++++++++++++++++++------------ tests/data/Makefile.am | 2 +- tests/data/test2115 | 46 +++++++++++++++++++++++++++++++++++++ tests/data/test2116 | 45 +++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 tests/data/test2115 create mode 100644 tests/data/test2116 diff --git a/lib/ftp.c b/lib/ftp.c index b3da888b44e9..f3c07d672296 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2927,6 +2927,20 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data, return result; } +/* A value that becomes part of an FTP control command must not carry a + control byte: a CR or LF would end the command line and let a second + command be smuggled onto the control connection. */ +static bool ftp_has_ctrl(const char *string) +{ + const unsigned char *s = (const unsigned char *)string; + while(*s) { + if(*s < 0x20) + return TRUE; + s++; + } + return FALSE; +} + /* for USER and PASS responses */ static CURLcode ftp_state_user_resp(struct Curl_easy *data, struct ftp_conn *ftpc, @@ -2949,16 +2963,20 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = ftp_state_loggedin(data, ftpc); } else if(ftpcode == 332) { - if(data->set.str[STRING_FTP_ACCOUNT]) { - result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", - data->set.str[STRING_FTP_ACCOUNT]); - if(!result) - ftp_state(data, ftpc, FTP_ACCT); - } - else { + const char *account = data->set.str[STRING_FTP_ACCOUNT]; + if(!account) { failf(data, "ACCT requested but none available"); result = CURLE_LOGIN_DENIED; } + else if(ftp_has_ctrl(account)) { + failf(data, "Control byte in FTP account"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", account); + if(!result) + ftp_state(data, ftpc, FTP_ACCT); + } } else { /* All other response codes, like: @@ -2966,14 +2984,19 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, 530 User ... access denied (the server denies to log the specified user) */ - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && - !ftpc->ftp_trying_alternative) { + const char *alt = data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]; + if(alt && !ftpc->ftp_trying_alternative) { /* Ok, USER failed. Let's try the supplied command. */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", - data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); - if(!result) { - ftpc->ftp_trying_alternative = TRUE; - ftp_state(data, ftpc, FTP_USER); + if(ftp_has_ctrl(alt)) { + failf(data, "Control byte in FTP alternative-to-user command"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "%s", alt); + if(!result) { + ftpc->ftp_trying_alternative = TRUE; + ftp_state(data, ftpc, FTP_USER); + } } } else { diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index e1249588745c..45df54464959 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 test2110 test2113 test2114 \ +test2108 test2109 test2110 test2113 test2114 test2115 test2116 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2115 b/tests/data/test2115 new file mode 100644 index 000000000000..d9c8b0513724 --- /dev/null +++ b/tests/data/test2115 @@ -0,0 +1,46 @@ + + + + +FTP +ACCT + + +# Server-side + + +REPLY PASS 332 please provide account name + + + +# Client-side + + +ftp + + +FTP --ftp-account with embedded CRLF is rejected + +# read the account from a config file so the raw CR LF reaches curl intact + +ftp-account = "one\r\nDELE %TESTNUMBER" + + +ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -K %LOGDIR/test%TESTNUMBER.config + + + +# Verify data after the test has been "shot" + +# 43 - CURLE_BAD_FUNCTION_ARGUMENT + +43 + +# the account is rejected before ACCT is built, so the injected DELE command +# must never reach the server + +USER anonymous +PASS ftp@example.com + + + diff --git a/tests/data/test2116 b/tests/data/test2116 new file mode 100644 index 000000000000..8ecd679b4a9e --- /dev/null +++ b/tests/data/test2116 @@ -0,0 +1,45 @@ + + + + +FTP +--ftp-alternative-to-user + + +# Server-side + + +REPLY USER 530 go away + + + +# Client-side + + +ftp + + +FTP --ftp-alternative-to-user with embedded CRLF is rejected + +# read the command from a config file so the raw CR LF reaches curl intact + +ftp-alternative-to-user = "USER me\r\nDELE %TESTNUMBER" + + +ftp://%HOSTIP:%FTPPORT/%TESTNUMBER/ -K %LOGDIR/test%TESTNUMBER.config + + + +# Verify data after the test has been "shot" + +# 43 - CURLE_BAD_FUNCTION_ARGUMENT + +43 + +# the replacement command is rejected before it is sent, so the injected DELE +# command must never reach the server + +USER anonymous + + + From d43d24beb1188d1bcf0cc34450b9af96889b7f9d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 08:57:52 +0200 Subject: [PATCH 0655/1062] configure: remove double check for GnuTLS `GNUTLS_ENABLED` was checked twice in the HTTPS-proxy support block, making the second check redundant. - also fix the "or upper" phrasing which is hard to understand Pointed out by the GitHub AI thing Closes #22307 --- configure.ac | 1 - m4/curl-openssl.m4 | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 82ded68db4d5..185b1beee896 100644 --- a/configure.ac +++ b/configure.ac @@ -5276,7 +5276,6 @@ if test "$CURL_DISABLE_HTTP" != "1"; then test "$GNUTLS_ENABLED" = "1" || test "$RUSTLS_ENABLED" = "1" || test "$SCHANNEL_ENABLED" = "1" || - test "$GNUTLS_ENABLED" = "1" || test "$MBEDTLS_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" AC_MSG_RESULT([yes]) diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 96060f841dbe..d6cb8a67e382 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -289,7 +289,7 @@ if test "x$OPT_OPENSSL" != "xno"; then #endif ]]) ],[],[ - AC_MSG_ERROR([OpenSSL 3.0.0 or upper required.]) + AC_MSG_ERROR([OpenSSL 3.0.0 or later required.]) ]) fi fi From 353d05ef0c82c4e8052affb4e30b56db7349106c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 09:02:43 +0200 Subject: [PATCH 0656/1062] configure: set ldap lib to no by default for non-finds `ldap_lib_ok` is never actually set at the start, so a prior cached autoconf result could lead to an incorrect value. Pointed out by the GitHub AI Closes #22308 --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 185b1beee896..9958bdbe6942 100644 --- a/configure.ac +++ b/configure.ac @@ -2671,6 +2671,9 @@ if test "$CURL_DISABLE_LDAP" != "1" && test "$want_ldap" != "no"; then CURL_CHECK_HEADER_LDAP CURL_CHECK_HEADER_LDAP_SSL + dnl Default to failure; AC_CHECK_LIB success paths set this to yes. + ldap_lib_ok="no" + if test -z "$LDAPLIBNAME"; then if test "$curl_cv_native_windows" = "yes" && test "$curl_cv_winuwp" != "yes"; then dnl Windows uses a single and unique LDAP library name From c3ae9ef8226ac60c5e5915e44a927d4b05daebd9 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sun, 12 Jul 2026 03:58:18 +0100 Subject: [PATCH 0657/1062] hostip: only cache negative resolves for authoritative answers Closes #22302 --- docs/libcurl/libcurl-env-dbg.md | 8 ++- .../libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md | 4 +- lib/asyn-ares.c | 22 ++++-- lib/asyn-thrdd.c | 48 +++++++++++++ lib/asyn.h | 6 ++ lib/doh.c | 23 ++++++- lib/doh.h | 5 +- lib/fake_addrinfo.c | 6 +- lib/hostip.c | 39 +++++++++-- tests/http/test_21_resolve.py | 69 +++++++++++++++++++ tests/http/testenv/dnsd.py | 8 ++- tests/server/dnsd.c | 35 +++++++++- tests/unit/unit1650.c | 2 + 13 files changed, 253 insertions(+), 22 deletions(-) diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index 70d0f12c88bd..5004b8873d46 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -185,7 +185,13 @@ Makes ever threaded resolve experience an initial delay in milliseconds. ## `CURL_DBG_RESOLV_FAIL_DELAY` With a threaded resolver, delay each lookup by the given milliseconds -and give a negative answer. +and fail it, as if a transient resolver failure happened. + +## `CURL_DBG_RESOLV_FAIL_NEGATIVE` + +With a threaded resolver, make a lookup failing via +`CURL_DBG_RESOLV_FAIL_DELAY` count as an authoritative negative answer, +eligible for negative DNS caching, instead of a transient failure. ## `CURL_DBG_RESOLV_FAIL_IPV6` diff --git a/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md b/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md index 5a9b7bbb9af8..893852d3ead9 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md +++ b/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md @@ -53,7 +53,9 @@ libcurl prunes entries from the DNS cache if it exceeds 30,000 entries no matter which timeout value is used. (Added in version 8.1.0) Since curl 8.16.0, failed name resolves are stored in the DNS cache for half -the set timeout period. +the set timeout period. Since curl 8.22.0, this only happens when the +resolver answered that the name does not exist, not on transient or local +resolver failures. # DEFAULT diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index d17a6038e2b9..ef4bbcbcc602 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -308,6 +308,12 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, * CURLE_COULDNT_RESOLVE_* code */ if(!result && !*pdns) { const char *msg = NULL; + /* only an authoritative "does not exist" answer from every query + may be cached as a negative entry, not transient failures like + timeouts or server troubles */ + async->negative_answer = !ares->transient_err && + ((ares->ares_status == ARES_ENOTFOUND) || + (ares->ares_status == ARES_ENODATA)); if(ares->ares_status != ARES_SUCCESS) msg = ares_strerror(ares->ares_status); result = Curl_async_failed(data, async, msg); @@ -569,8 +575,12 @@ static void async_ares_A_cb(void *user_data, int status, int timeouts, ares->res_A = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } - else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ - ares->ares_status = status; + else { + if((status != ARES_ENOTFOUND) && (status != ARES_ENODATA)) + ares->transient_err = TRUE; + if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; + } } #ifdef CURLRES_IPV6 @@ -592,8 +602,12 @@ static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } - else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ - ares->ares_status = status; + else { + if((status != ARES_ENOTFOUND) && (status != ARES_ENODATA)) + ares->transient_err = TRUE; + if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; + } } #endif /* CURLRES_IPV6 */ diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index dc7c7b0418a0..1ee69f83bf03 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -117,9 +117,11 @@ struct async_thrdd_item { uint16_t port; uint8_t transport; uint8_t dns_queries; + BIT(negative); /* resolver answered that the name does not exist */ #ifdef DEBUGBUILD uint32_t delay_ms; uint32_t delay_fail_ms; + BIT(dbg_negative); #endif char hostname[1]; }; @@ -182,6 +184,8 @@ static struct async_thrdd_item *async_thrdd_item_create( item->delay_fail_ms = (uint32_t)l + c; } } + if(getenv("CURL_DBG_RESOLV_FAIL_NEGATIVE")) + item->dbg_negative = TRUE; } #endif @@ -331,6 +335,26 @@ CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, #ifdef HAVE_GETADDRINFO +/* Was the getaddrinfo() failure an authoritative negative answer, + i.e. the resolver responded that the name (or its data) does not + exist? Transient failures like EAI_AGAIN and local troubles must + not count as negative answers. */ +static bool gai_negative(int rc) +{ + switch(rc) { +#ifdef EAI_NONAME + case EAI_NONAME: +#endif +#if defined(EAI_NODATA) && \ + (!defined(EAI_NONAME) || (EAI_NODATA != EAI_NONAME)) + case EAI_NODATA: +#endif + return TRUE; + default: + return FALSE; + } +} + /* Process the item, using Curl_getaddrinfo_ex() */ static void async_thrdd_item_process(void *arg) { @@ -346,6 +370,7 @@ static void async_thrdd_item_process(void *arg) } if(item->delay_fail_ms) { curlx_wait_ms(item->delay_fail_ms); + item->negative = item->dbg_negative; return; } #endif @@ -375,6 +400,7 @@ static void async_thrdd_item_process(void *arg) item->sockerr = SOCKERRNO ? SOCKERRNO : rc; if(item->sockerr == 0) item->sockerr = RESOLVER_ENOMEM; + item->negative = gai_negative(rc); } else { Curl_addrinfo_set_port(item->res, item->port); @@ -394,6 +420,7 @@ static void async_thrdd_item_process(void *arg) } if(item->delay_fail_ms) { curlx_wait_ms(item->delay_fail_ms); + item->negative = item->dbg_negative; return; } #endif @@ -402,6 +429,9 @@ static void async_thrdd_item_process(void *arg) item->sockerr = SOCKERRNO; if(item->sockerr == 0) item->sockerr = RESOLVER_ENOMEM; + /* this resolver cannot tell a transient failure from an + authoritative negative answer, treat it as before */ + item->negative = TRUE; } } @@ -714,6 +744,24 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, return CURLE_AGAIN; Curl_expire_done(data, EXPIRE_ASYNC_NAME); + + /* A failure is an authoritative negative answer, eligible for + negative caching, only when every A/AAAA query performed came + back answering that the name does not exist. A query that + failed transiently or never returned is not an answer. */ + { + const uint8_t ip_queries = + async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA); + bool negative = (async->dns_responses & ip_queries) == ip_queries; + if(thrdd->res_A && (thrdd->res_A->res || !thrdd->res_A->negative)) + negative = FALSE; + if(thrdd->res_AAAA && (thrdd->res_AAAA->res || !thrdd->res_AAAA->negative)) + negative = FALSE; + if(!thrdd->res_A && !thrdd->res_AAAA) + negative = FALSE; + async->negative_answer = negative; + } + if(async->result) { result = async->result; goto out; diff --git a/lib/asyn.h b/lib/asyn.h index 7df9b681ba84..e4bc05e0ddec 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -122,6 +122,8 @@ struct async_ares_ctx { #ifdef USE_HTTPSRR struct Curl_https_rrinfo hinfo; #endif + BIT(transient_err); /* an A/AAAA query failed without the resolver + answering that the name does not exist */ }; void Curl_async_ares_shutdown(struct Curl_easy *data, @@ -251,6 +253,10 @@ struct Curl_resolv_async { BIT(for_proxy); BIT(done); BIT(shutdown); + BIT(negative_answer); /* resolver answered that the name does not + exist. Only such failures may be cached as + negative entries, not transient or local + resolver failures. */ char hostname[1]; }; diff --git a/lib/doh.c b/lib/doh.c index 8b643aa0b428..b1f138f32c32 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -58,12 +58,13 @@ static const char * const errors[] = { "Unexpected CLASS", "No content", "Bad ID", - "Name too long" + "Name too long", + "No such name" }; static const char *doh_strerror(DOHcode code) { - if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG)) + if((code >= DOH_OK) && (code <= DOH_DNS_NXDOMAIN)) return errors[code]; return "bad error code"; } @@ -751,6 +752,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if(!doh || doh[0] || doh[1]) return DOH_DNS_BAD_ID; /* bad ID */ rcode = doh[3] & 0x0f; + if(rcode == 3) + return DOH_DNS_NXDOMAIN; /* name does not exist */ if(rcode) return DOH_DNS_BAD_RCODE; /* bad rcode */ @@ -1248,6 +1251,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, } else if(!dohp->pending) { DOHcode rc[DOH_SLOT_COUNT]; + bool negative = TRUE; int slot; memset(rc, 0, sizeof(rc)); @@ -1262,6 +1266,11 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body), curlx_dyn_len(&p->body), p->dnstype, &de); + /* Failing without an NXDOMAIN answer - a SERVFAIL-class rcode or + an undecodable response - says nothing about the name. Such a + failure must not be cached as a negative entry. */ + if(rc[slot] && (rc[slot] != DOH_DNS_NXDOMAIN)) + negative = FALSE; if(rc[slot]) { CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), doh_type2name(p->dnstype), dohp->host); @@ -1279,8 +1288,13 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, } result = doh2ai(&de, dohp->host, dohp->port, &ai); - if(result) + if(result) { + /* a decoded response without any usable address, e.g. only + CNAME records, is an authoritative "no data" answer */ + if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) + async->negative_answer = TRUE; goto error; + } /* we got a response, create a dns entry. */ dns = Curl_dnscache_mk_entry(data, async->dns_queries, @@ -1314,6 +1328,9 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, *pdns = dns; } /* address processing done */ else { + /* every query failed. Only NXDOMAIN answers for all of them + make this a negative answer, eligible for caching. */ + async->negative_answer = negative; result = async->for_proxy ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } diff --git a/lib/doh.h b/lib/doh.h index cd2aad92544c..f4ecec0881e4 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -39,12 +39,13 @@ typedef enum { DOH_OUT_OF_MEM, /* 5 */ DOH_DNS_RDATA_LEN, /* 6 */ DOH_DNS_MALFORMAT, /* 7 */ - DOH_DNS_BAD_RCODE, /* 8 - no such name */ + DOH_DNS_BAD_RCODE, /* 8 - unsuccessful rcode, not NXDOMAIN */ DOH_DNS_UNEXPECTED_TYPE, /* 9 */ DOH_DNS_UNEXPECTED_CLASS, /* 10 */ DOH_NO_CONTENT, /* 11 */ DOH_DNS_BAD_ID, /* 12 */ - DOH_DNS_NAME_TOO_LONG /* 13 */ + DOH_DNS_NAME_TOO_LONG, /* 13 */ + DOH_DNS_NXDOMAIN /* 14 - no such name */ } DOHcode; typedef enum { diff --git a/lib/fake_addrinfo.c b/lib/fake_addrinfo.c index 6b04b5d2f560..030dc35283e6 100644 --- a/lib/fake_addrinfo.c +++ b/lib/fake_addrinfo.c @@ -42,6 +42,7 @@ void r_freeaddrinfo(struct addrinfo *cahead) struct context { struct ares_addrinfo *addr; + int status; }; static void async_addrinfo_cb(void *userp, int status, int timeouts, @@ -49,6 +50,7 @@ static void async_addrinfo_cb(void *userp, int status, int timeouts, { struct context *ctx = (struct context *)userp; (void)timeouts; + ctx->status = status; if(ARES_SUCCESS == status) { ctx->addr = addr; } @@ -190,8 +192,10 @@ int r_getaddrinfo(const char *node, /* free the old */ ares_freeaddrinfo(ctx.addr); } + else if((ctx.status == ARES_ENOTFOUND) || (ctx.status == ARES_ENODATA)) + rc = EAI_NONAME; /* no such name */ else - rc = EAI_NONAME; /* got nothing */ + rc = EAI_AGAIN; /* failed without an authoritative answer */ /* Cleanup */ ares_destroy(channel); diff --git a/lib/hostip.c b/lib/hostip.c index a18d9a62bb9d..6afe63ff4555 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -452,9 +452,15 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, async->queries_ongoing, async->hostname, async->port); result = CURLE_OK; } - else if(result) { + else if(IS_RESOLV_FAIL(result)) { result = Curl_async_failed(data, async, NULL); } + else if(result) { + /* a local failure, not a resolve answer. Keep the error as it + is so it does not get treated as one. */ + CURL_TRC_DNS(data, "resolve error %d for %s:%u", + (int)result, async->hostname, async->port); + } else { CURL_TRC_DNS(data, "resolve complete for %s:%u", async->hostname, async->port); @@ -528,6 +534,10 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) #endif /* USE_CURL_ASYNC */ +/* Start resolving. `*pnegative` is only meaningful when this returns + a CURLE_COULDNT_RESOLVE_* failure: TRUE when the resolver answered + that the name does not exist, FALSE on transient or local failures + that must not be cached as negative entries. */ static CURLcode hostip_resolv_start(struct Curl_easy *data, uint8_t dns_queries, const char *hostname, @@ -537,7 +547,8 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, timediff_t timeout_ms, bool allowDOH, uint32_t *presolv_id, - struct Curl_dns_entry **pdns) + struct Curl_dns_entry **pdns, + bool *pnegative) { #ifdef USE_CURL_ASYNC struct Curl_resolv_async *async = NULL; @@ -546,6 +557,8 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, size_t hostname_len; CURLcode result = CURLE_OK; + *pnegative = FALSE; + (void)timeout_ms; /* not in all ifdefs */ *presolv_id = 0; *pdns = NULL; @@ -628,8 +641,12 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, if(result) goto out; addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); - if(!addr) + if(!addr) { result = RESOLV_FAIL(for_proxy); + /* the synchronous resolvers do not tell a transient failure from + an authoritative negative answer, treat it as before */ + *pnegative = TRUE; + } #endif out: @@ -657,6 +674,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, data->state.async = async; } else { + *pnegative = !!async->negative_answer; Curl_async_destroy(data, async); } } @@ -678,6 +696,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, size_t hostname_len; CURLcode result = RESOLV_FAIL(for_proxy); bool cache_dns = FALSE; + bool negative = FALSE; (void)timeout_ms; /* not used in all ifdefs */ *presolv_id = 0; @@ -722,14 +741,14 @@ static CURLcode hostip_resolv(struct Curl_easy *data, cache_dns = TRUE; result = hostip_resolv_start(data, dns_queries, hostname, port, transport, for_proxy, timeout_ms, allowDOH, - presolv_id, pdns); + presolv_id, pdns, &negative); } out: if(result && (result != CURLE_AGAIN)) { Curl_dns_entry_unlink(data, pdns); if(IS_RESOLV_FAIL(result)) { - if(cache_dns) + if(cache_dns && negative) Curl_dnscache_add_negative(data, dns_queries, hostname, port); failf(data, "Could not resolve: %s:%u", hostname, port); } @@ -1067,8 +1086,14 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, Curl_dns_entry_unlink(data, pdns); } else if(IS_RESOLV_FAIL(result)) { - Curl_dnscache_add_negative(data, async->dns_queries, - async->hostname, async->port); + /* Only cache the failure when the resolver answered that the + name does not exist. Transient failures, e.g. an unreachable + or overloaded DNS server or local resource shortages, say + nothing about the name and would poison the cache for every + transfer using it. */ + if(async->negative_answer) + Curl_dnscache_add_negative(data, async->dns_queries, + async->hostname, async->port); failf(data, "Could not resolve: %s:%u", async->hostname, async->port); } else if(result) { diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index a50997409d01..e3f0358d741d 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -196,6 +196,75 @@ def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd): r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['remote_ip'] == '::1' + # transient resolve failures must not be cached as negative + # entries: a second lookup of the same name tries again + @pytest.mark.skipif(condition=not Env.curl_resolv_threaded(), reason="no threaded resolver") + def test_21_11_resolv_transient_uncached(self, env: Env, httpd, nghttpx): + count = 2 + delay_ms = 250 + run_env = os.environ.copy() + run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = f'{delay_ms}' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + urls = [f'https://test-again.http.curl.invalid/?id={i}' for i in range(count)] + r = curl.http_download(urls=urls, with_stats=True) + r.check_exit_code(6) + r.check_stats(count=count, http_status=0, exitcode=6) + # not cached as negative: the second transfer resolved again + if env.curl_is_verbose(): + assert not [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + assert r.stats[1]['time_total'] > (delay_ms / 2) / 1000.0, f'{r.stats[1]}' + + # a negative resolve answer is cached: a second lookup of the same + # name fails right away from the cache + @pytest.mark.skipif(condition=not Env.curl_resolv_threaded(), reason="no threaded resolver") + def test_21_12_resolv_negative_cached(self, env: Env, httpd, nghttpx): + count = 2 + delay_ms = 250 + run_env = os.environ.copy() + run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = f'{delay_ms}' + run_env['CURL_DBG_RESOLV_FAIL_NEGATIVE'] = '1' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + urls = [f'https://test-nxdomain.http.curl.invalid/?id={i}' for i in range(count)] + r = curl.http_download(urls=urls, with_stats=True) + r.check_exit_code(6) + r.check_stats(count=count, http_status=0, exitcode=6) + # the second transfer failed right away from the cache entry + if env.curl_is_verbose(): + assert [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + assert r.stats[1]['time_total'] < (delay_ms / 2) / 1000.0, f'{r.stats[1]}' + + # dnsd giving NXDOMAIN for all families: the negative answer is + # cached and a second lookup of the same name uses the cache + @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") + def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd): + count = 2 + dnsd.set_answers(rcode_a=3, rcode_aaaa=3) + run_env = os.environ.copy() + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + urls = [f'https://test-nx.http.curl.invalid/?id={i}' for i in range(count)] + r = curl.http_download(urls=urls, with_stats=True) + r.check_exit_code(6) + r.check_stats(count=count, http_status=0, exitcode=6) + if env.curl_is_verbose(): + assert [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + + # dnsd failing one family with SERVFAIL: not an authoritative + # negative answer, a second lookup of the same name tries again + @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") + def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd): + count = 2 + dnsd.set_answers(rcode_a=2, rcode_aaaa=3) + run_env = os.environ.copy() + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + urls = [f'https://test-sf.http.curl.invalid/?id={i}' for i in range(count)] + r = curl.http_download(urls=urls, with_stats=True) + r.check_exit_code(6) + r.check_stats(count=count, http_status=0, exitcode=6) + if env.curl_is_verbose(): + assert not [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + def _clean_files(self, files): for file in files: if os.path.exists(file): diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index a7dc0885571f..8f6bb3edc801 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -156,7 +156,9 @@ def set_answers(self, addr_a: Optional[List[str]] = None, https: Optional[List[str]] = None, delay_a_ms: int = 0, delay_aaaa_ms: int = 0, - delay_https_ms: int = 0): + delay_https_ms: int = 0, + rcode_a: int = 0, + rcode_aaaa: int = 0): conf = [] if addr_a: conf.extend([f'A: {addr}' for addr in addr_a]) @@ -170,6 +172,10 @@ def set_answers(self, addr_a: Optional[List[str]] = None, conf.append(f'Delay-AAAA: {delay_aaaa_ms}') if delay_https_ms: conf.append(f'Delay-HTTPS: {delay_https_ms}') + if rcode_a: + conf.append(f'Rcode-A: {rcode_a}') + if rcode_aaaa: + conf.append(f'Rcode-AAAA: {rcode_aaaa}') conf.append('\n') with open(self._conf_file, 'w') as fd: fd.write("\n".join(conf)) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index ca89c9c0536e..87f32384eea6 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -327,6 +327,8 @@ static unsigned char ancount_aaaa; static timediff_t a_delay_ms; static timediff_t aaaa_delay_ms; static timediff_t https_delay_ms; +static unsigned char rcode_a; +static unsigned char rcode_aaaa; static int query_id = -1; @@ -444,15 +446,18 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, 0x0, 0x0 /* ARCOUNT */ }; uint16_t ancount = 0; + unsigned char rcode = 0; switch(qtype) { case QTYPE_A: ancount = ancount_a; delay_ms = a_delay_ms; + rcode = rcode_a; break; case QTYPE_AAAA: ancount = ancount_aaaa; delay_ms = aaaa_delay_ms; + rcode = rcode_aaaa; break; case QTYPE_HTTPS: if(httpsrr.dlen) @@ -460,6 +465,8 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, delay_ms = https_delay_ms; break; } + if(rcode) + ancount = 0; resp = curlx_calloc(1, sizeof(*resp)); if(!resp) @@ -478,6 +485,11 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, header[0] = (uint8_t)(id >> 8); header[1] = (uint8_t)(id & 0xff); + if(rcode) { + header[3] = (uint8_t)((header[3] & 0xf0) | (rcode & 0x0f)); + logmsg("[%d] response rcode %u", qid, (unsigned int)rcode); + } + header[6] = (uint8_t)(ancount >> 8); header[7] = (uint8_t)(ancount & 0xff); @@ -491,7 +503,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, switch(qtype) { case QTYPE_A: - for(a = 0; a < ancount_a; a++) { + for(a = 0; !rcode && (a < ancount_a); a++) { const unsigned char *store = ipv4_pref; const char *ip; if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A)) @@ -504,7 +516,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, logmsg("[%d] response A empty", qid); break; case QTYPE_AAAA: - for(a = 0; a < ancount_aaaa; a++) { + for(a = 0; !rcode && (a < ancount_aaaa); a++) { const unsigned char *store = ipv6_pref; const char *ip; if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA)) @@ -659,6 +671,7 @@ static void read_instructions(void) } /* reset defaults */ a_delay_ms = aaaa_delay_ms = https_delay_ms = 0; + rcode_a = rcode_aaaa = 0; blob_reset(&httpsrr); finfo_last = finfo; @@ -721,6 +734,24 @@ static void read_instructions(void) rc = 1; } } + else if(!strncmp("Rcode-A: ", buf, 9)) { + curl_off_t code; + const char *pc = &buf[9]; + rc = 0; + if(!curlx_str_number(&pc, &code, 15)) { + rcode_a = (unsigned char)code; + rc = 1; + } + } + else if(!strncmp("Rcode-AAAA: ", buf, 12)) { + curl_off_t code; + const char *pc = &buf[12]; + rc = 0; + if(!curlx_str_number(&pc, &code, 15)) { + rcode_aaaa = (unsigned char)code; + rc = 1; + } + } else { /* accept empty line */ rc = buf[0] ? 0 : 1; diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index cdc3375d3cd8..4e307474fd5a 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -86,6 +86,8 @@ static CURLcode test_unit1650(const char *arg) CURL_DNS_TYPE_A, DOH_DNS_BAD_ID, NULL }, {"\x00\x00\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 12, CURL_DNS_TYPE_A, DOH_DNS_BAD_RCODE, NULL }, + {"\x00\x00\x00\x03\x00\x01\x00\x01\x00\x01\x00\x01", 12, + CURL_DNS_TYPE_A, DOH_DNS_NXDOMAIN, NULL }, {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x00\x00\x00\x03\x66\x6f\x6f", 16, CURL_DNS_TYPE_A, DOH_DNS_OUT_OF_RANGE, NULL }, {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x00\x00\x00\x03\x66\x6f\x6f\x00", 17, From 34bc5b60bcc13b413116c7eb3635c202ad02f206 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 10 Jul 2026 11:47:48 +0200 Subject: [PATCH 0658/1062] lib: ratelimit timestamps Remove the timestamp passed for ratelimit checks. Let the limit calculation obtain a timestamp when it needs it. Most transfers run without active ratelimits and getting a fresh timestamp is unnecessary. Closes #22292 --- lib/http2.c | 3 +-- lib/multi.c | 5 ++--- lib/progress.c | 10 ++++------ lib/ratelimit.c | 15 +++++++++++++-- lib/ratelimit.h | 9 +++++---- lib/sendf.c | 3 +-- lib/transfer.c | 3 +-- lib/vquic/cf-ngtcp2-proxy.c | 2 +- lib/vquic/cf-ngtcp2.c | 2 +- tests/http/scorecard.py | 2 ++ 10 files changed, 31 insertions(+), 23 deletions(-) diff --git a/lib/http2.c b/lib/http2.c index a820439ef45a..ff3d3d2741fb 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -284,8 +284,7 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf, struct Curl_easy *data) { - curl_off_t avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); + curl_off_t avail = Curl_rlimit_avail(&data->progress.dl.rlimit, NULL); (void)cf; if(avail < CURL_OFF_T_MAX) { /* limit in place */ diff --git a/lib/multi.c b/lib/multi.c index d65c48e2657b..7d0bc0a125c6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -994,11 +994,10 @@ static CURLcode multi_adjust_pollset(struct Curl_easy *data, CURLcode result = CURLE_OK; if(ps->n) { - const struct curltime *pnow = Curl_pgrs_now(data); bool send_blocked, recv_blocked; - recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, pnow) <= 0); - send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, pnow) <= 0); + recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, NULL) <= 0); + send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, NULL) <= 0); if(send_blocked || recv_blocked) { int i; for(i = 0; i <= SECONDARYSOCKET; ++i) { diff --git a/lib/progress.c b/lib/progress.c index 3b3642b9a0b6..72d03edf2de8 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -172,10 +172,8 @@ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, const struct curltime *Curl_pgrs_now(struct Curl_easy *data) { - struct curltime *pnow = data->multi ? - &data->multi->now : &data->progress.now; - curlx_pnow(pnow); - return pnow; + curlx_pnow(&data->progress.now); + return &data->progress.now; } /* @@ -392,7 +390,7 @@ void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta) { if(delta) { data->progress.dl.cur_size += delta; - Curl_rlimit_drain(&data->progress.dl.rlimit, delta, Curl_pgrs_now(data)); + Curl_rlimit_drain(&data->progress.dl.rlimit, delta, NULL); } } @@ -400,7 +398,7 @@ void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta) { if(delta) { data->progress.ul.cur_size += delta; - Curl_rlimit_drain(&data->progress.ul.rlimit, delta, Curl_pgrs_now(data)); + Curl_rlimit_drain(&data->progress.ul.rlimit, delta, NULL); } } diff --git a/lib/ratelimit.c b/lib/ratelimit.c index 97af01c78801..a5efc7c11a38 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -200,9 +200,15 @@ bool Curl_rlimit_is_blocked(struct Curl_rlimit *r) int64_t Curl_rlimit_avail(struct Curl_rlimit *r, const struct curltime *pts) { + struct curltime ts; + if(r->blocked) return 0; else if(r->rate_per_step) { + if(!pts) { + curlx_pnow(&ts); + pts = &ts; + } rlimit_update(r, pts); return r->tokens; } @@ -210,13 +216,18 @@ int64_t Curl_rlimit_avail(struct Curl_rlimit *r, return INT64_MAX; } -void Curl_rlimit_drain(struct Curl_rlimit *r, - size_t tokens, +void Curl_rlimit_drain(struct Curl_rlimit *r, size_t tokens, const struct curltime *pts) { + struct curltime ts; + if(r->blocked || !r->rate_per_step) return; + if(!pts) { + curlx_pnow(&ts); + pts = &ts; + } rlimit_update(r, pts); #if 8 <= SIZEOF_SIZE_T if(tokens > INT64_MAX) { diff --git a/lib/ratelimit.h b/lib/ratelimit.h index 3c3e38b89587..3548f55608ff 100644 --- a/lib/ratelimit.h +++ b/lib/ratelimit.h @@ -90,14 +90,15 @@ bool Curl_rlimit_active(struct Curl_rlimit *r); bool Curl_rlimit_is_blocked(struct Curl_rlimit *r); int64_t Curl_rlimit_per_step(struct Curl_rlimit *r); -/* Return how many tokens are available to spend, may be negative */ +/* Return how many tokens are available to spend, may be negative. + * Pass timestamp or NULL. */ int64_t Curl_rlimit_avail(struct Curl_rlimit *r, const struct curltime *pts); /* Drain tokens from the ratelimit, give an estimate of how many tokens - * remain to be drained in the future (-1 for unknown). */ -void Curl_rlimit_drain(struct Curl_rlimit *r, - size_t tokens, + * remain to be drained in the future (-1 for unknown). + * Pass timestamp or NULL. */ +void Curl_rlimit_drain(struct Curl_rlimit *r, size_t tokens, const struct curltime *pts); /* Block/unblock ratelimiting. A blocked ratelimit has 0 tokens available. */ diff --git a/lib/sendf.c b/lib/sendf.c index b830995345be..efaba08eb4ca 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -1239,8 +1239,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, } if(Curl_rlimit_active(&data->progress.ul.rlimit)) { - curl_off_t ul_avail = Curl_rlimit_avail(&data->progress.ul.rlimit, - Curl_pgrs_now(data)); + curl_off_t ul_avail = Curl_rlimit_avail(&data->progress.ul.rlimit, NULL); if(ul_avail <= 0) { result = CURLE_OK; *eos = FALSE; diff --git a/lib/transfer.c b/lib/transfer.c index 11d36ac42721..2eab7cabad4c 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -250,8 +250,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, bytestoread = xfer_blen; if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) { - curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); + curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit, NULL); #if 0 DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail)); #endif diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 7a7fe417c38a..9a43a443cc9e 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -281,7 +281,7 @@ static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, if(!stream->rx_offset) return; - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, NULL); if(avail <= 0) { /* nothing available, do not extend the rx offset */ CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index f03ec06a4506..9a905a745a46 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -175,7 +175,7 @@ static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, if(!stream->rx_offset) return; - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, NULL); if(avail <= 0) { /* nothing available, do not extend the rx offset */ CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 36947592e28c..2a45450aa40d 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -71,6 +71,8 @@ def fmt_size(cls, val): def fmt_mbs(cls, val): if val is None or val < 0: return '--' + if val >= (1024 * 1024 * 1024): + return f'{val / (1024 * 1024 * 1024):.3g} GB/s' if val >= (1024 * 1024): return f'{val / (1024 * 1024):.3g} MB/s' if val >= 1024: From 7bbcf26edf15d20544a94ff02d566215444357ca Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 10 Jul 2026 12:17:52 +0200 Subject: [PATCH 0659/1062] scripts/contri*: find 'Thanks-to' references Also any other `-to:` alternatives. Co-authored-by: Daniel Stenberg Closes #22293 --- scripts/contributors.sh | 2 +- scripts/contrithanks.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/contributors.sh b/scripts/contributors.sh index a3a11d52432a..f43f91d44615 100755 --- a/scripts/contributors.sh +++ b/scripts/contributors.sh @@ -62,7 +62,7 @@ CURLWWW="${CURLWWW:-../curl-www}" git -C "$CURLWWW" log --pretty=full --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-(by|to)):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ diff --git a/scripts/contrithanks.sh b/scripts/contrithanks.sh index 85f382b755e7..cea98976f540 100755 --- a/scripts/contrithanks.sh +++ b/scripts/contrithanks.sh @@ -62,7 +62,7 @@ tail -n +7 ./docs/THANKS | sed 's/ github/ github/i' > $rand git -C "$CURLWWW" log --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-(by|to)):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ From 30d74402959bd6360e0f491c504552186652f707 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 12:34:23 +0200 Subject: [PATCH 0660/1062] RELEASE-NOTES: synced And bump to 8.22.0 for the pending release --- RELEASE-NOTES | 77 ++++++++++++++++++++++++++++++++++++------ include/curl/curlver.h | 8 ++--- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e886c14cc168..ba6f25d390a8 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,14 +1,16 @@ -curl and libcurl 8.21.1 +curl and libcurl 8.22.0 Public curl releases: 276 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1493 - Contributors: 3741 + Authors: 1494 + Contributors: 3745 This release includes the following changes: + o gssapi: add support for Apple GSS Framework [72] + o TLS: drop support for TLS-SRP [71] This release includes the following bugfixes: @@ -16,6 +18,7 @@ This release includes the following bugfixes: o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] o build: drop superfluous `STDC_HEADERS` macro [51] o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] + o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] @@ -24,24 +27,38 @@ This release includes the following bugfixes: o cmake: replace `remove` command with `rm` and pass arg safely [11] o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] + o configure: fix misleading error messages [42] + o configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows [79] + o configure: remove double check for GnuTLS [21] + o configure: set ldap lib to no by default for non-finds [18] + o conncache: apply multi limits to transfers using a shared pool [41] o conncache: connection alive checks intervals [20] o content_encoding: give a clear error on multi-member gzip [46] o CREDENTIALS.md: remove comment about empty user/pass [50] o curl_ws_meta.md: polish and better vocabulary [19] + o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] + o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization' instead of 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] + o FTP: fix TLS session reuse on the data connection [80] + o ftp: reject control bytes in ACCT and alternative-to-user [26] o gopher: reject CR and LF in the selector [1] + o hostip: only cache negative resolves for authoritative answers [16] o http: trim custom header name before the Authorization drop [17] o INSTALL.md: add building-from-source overview section [29] o ldap: support insecure mode for Windows native LDAP [3] o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] + o lib: add "Curl_" prefix to two global functions [84] o lib: fix 'ns' -> 'us' in trace messages [57] + o lib: ratelimit timestamps [14] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] + o mime: reject CR and LF in mail part name and filename [30] o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] o openldap: handle Curl_sasl_continue() returns better [45] o openssl+sectrust: fix session reuse [4] + o openssl+sectrust: move session verified set into result check [82] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] @@ -49,15 +66,26 @@ This release includes the following bugfixes: o sasl: fix zero-length response encoding [36] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] + o setopt: error for CURLOPT_SHARE when easy handle is used [68] + o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] o sws: allow connection-monitor to log all disconnects [2] o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] + o tests: remove test1701 [58] + o tests: skip test 311 for wolfSSL 5.9.2 [63] + o tidy-up: typos, comment nits [60] o tool: do not flush on out-null [38] + o tool: fix memory use in parallel mode [59] o tool: init progress bar on demand [39] o tool_cb_hdr: de-duplicate filename setter [24] o tool_operate: remove call to abort() [23] + o url: reject control codes in credentials set via CURLOPT [70] o urlapi: do not keep an internal port string [31] + o vquic: add Curl_ prefix to some global functions [76] + o vssh: keyfile use cleanups [83] o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] + o wolfssl: fix build for wolfssl without bio chain support [75] + o ws: pause/unpause write handling [55] This release includes the following known bugs: @@ -72,19 +100,20 @@ Planned upcoming removals include: o local crypto implementations o NTLM o SMB - o TLS-SRP support See https://curl.se/dev/deprecate.html This release would not have looked like this without help, code, reports and advice from friends like these: - Alhuda Khan, Bigtang on hackerone, Dan Fandrich, Daniel Stenberg, - dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, - Hendrik Hübner, HwangRock, Memduh Çelik, Patrick Monnerat, Ray Satiro, - renovate[bot], Roger Leigh, Sam James, Samuel Dainard, - smaeljaish on hackerone, Stefan Eissing, stze on hackerone, Viktor Szakats - (21 contributors) + Alhuda Khan, Bigtang on hackerone, Bryan Henderson, Christian Ullrich, + Christoph Reiter, Dan Fandrich, Daniel Stenberg, dependabot[bot], + ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, + Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, + Laurent Sabourin, Memduh Çelik, Patrick Monnerat, Ray Satiro, renovate[bot], + Roger Leigh, Sam James, Samuel Dainard, smaeljaish on hackerone, + Stefan Eissing, stze on hackerone, Viktor Szakats + (29 contributors) References to bug reports and discussions on issues: @@ -100,17 +129,23 @@ References to bug reports and discussions on issues: [10] = https://curl.se/bug/?i=22191 [11] = https://curl.se/bug/?i=22193 [12] = https://curl.se/bug/?i=22192 + [14] = https://curl.se/bug/?i=22292 [15] = https://curl.se/bug/?i=22187 + [16] = https://curl.se/bug/?i=22302 [17] = https://curl.se/bug/?i=22178 + [18] = https://curl.se/bug/?i=22308 [19] = https://curl.se/bug/?i=22233 [20] = https://curl.se/bug/?i=22169 + [21] = https://curl.se/bug/?i=22307 [22] = https://curl.se/bug/?i=22179 [23] = https://curl.se/bug/?i=22182 [24] = https://curl.se/bug/?i=22232 [25] = https://curl.se/bug/?i=22231 + [26] = https://curl.se/bug/?i=22301 [27] = https://curl.se/bug/?i=22222 [28] = https://curl.se/bug/?i=22220 [29] = https://curl.se/bug/?i=22113 + [30] = https://curl.se/bug/?i=22247 [31] = https://curl.se/bug/?i=22167 [32] = https://curl.se/bug/?i=22100 [33] = https://curl.se/bug/?i=22154 @@ -121,6 +156,8 @@ References to bug reports and discussions on issues: [38] = https://curl.se/bug/?i=22165 [39] = https://curl.se/bug/?i=22164 [40] = https://curl.se/bug/?i=22136 + [41] = https://curl.se/bug/?i=22265 + [42] = https://curl.se/bug/?i=22300 [43] = https://curl.se/bug/?i=22112 [44] = https://curl.se/bug/?i=22217 [45] = https://curl.se/bug/?i=22213 @@ -131,6 +168,26 @@ References to bug reports and discussions on issues: [50] = https://curl.se/bug/?i=22212 [51] = https://curl.se/bug/?i=22206 [52] = https://curl.se/bug/?i=22205 + [53] = https://curl.se/bug/?i=22296 [54] = https://curl.se/bug/?i=22201 + [55] = https://curl.se/bug/?i=22273 [56] = https://curl.se/bug/?i=22170 [57] = https://curl.se/bug/?i=22200 + [58] = https://curl.se/bug/?i=22280 + [59] = https://curl.se/bug/?i=22277 + [60] = https://curl.se/bug/?i=22294 + [63] = https://curl.se/bug/?i=22286 + [68] = https://curl.se/bug/?i=22253 + [70] = https://curl.se/bug/?i=22236 + [71] = https://curl.se/bug/?i=21965 + [72] = https://curl.se/bug/?i=22052 + [73] = https://curl.se/bug/?i=22257 + [74] = https://curl.se/bug/?i=22256 + [75] = https://curl.se/bug/?i=22255 + [76] = https://curl.se/bug/?i=22254 + [77] = https://curl.se/bug/?i=22250 + [79] = https://curl.se/bug/?i=22251 + [80] = https://curl.se/bug/?i=22225 + [82] = https://curl.se/bug/?i=22248 + [83] = https://curl.se/bug/?i=22243 + [84] = https://curl.se/bug/?i=22245 diff --git a/include/curl/curlver.h b/include/curl/curlver.h index d1c295c7f86c..4a673da4668c 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.21.1-DEV" +#define LIBCURL_VERSION "8.22.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 21 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 22 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always follows this syntax: @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081501 +#define LIBCURL_VERSION_NUM 0x081600 /* * This is the date and time when the full source package was created. The From eecc273f6d9a61100fc99898f91f517ab7d9c184 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 18:54:20 +0200 Subject: [PATCH 0661/1062] test1560: allow to build and run without LDAP support Closes #22312 --- tests/data/test1560 | 1 - tests/libtest/lib1560.c | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/data/test1560 b/tests/data/test1560 index 7dcf7fc50475..70de44d31e1e 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -20,7 +20,6 @@ http pop3 smtp imap -ldap dict ftp
diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 82f06a6ab4ec..9fb546bc05aa 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -891,9 +891,11 @@ static const struct urltestcase get_url_list[] = { {"pop3.example.com/path/html", "pop3://pop3.example.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#ifndef CURL_DISABLE_LDAP {"ldap.example.com/path/html", "ldap://ldap.example.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#endif {"imap.example.com/path/html", "imap://imap.example.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, @@ -912,9 +914,11 @@ static const struct urltestcase get_url_list[] = { {"pop3.com/path/html", "pop3://pop3.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#ifndef CURL_DISABLE_LDAP {"ldap.com/path/html", "ldap://ldap.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#endif {"imap.com/path/html", "imap://imap.com/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, @@ -930,9 +934,11 @@ static const struct urltestcase get_url_list[] = { {"pop3/path/html", "http://pop3/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#ifndef CURL_DISABLE_LDAP {"ldap/path/html", "http://ldap/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, +#endif {"imap/path/html", "http://imap/path/html", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, From 87704c45e8123b5dd82e3406536a5154f7d15b41 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:56:23 +0000 Subject: [PATCH 0662/1062] GHA: update dependency google/boringssl to v0.20260713.0 Closes #22311 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 26db16250c4b..8f1f03338f09 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260616.0 + BORINGSSL_VERSION: 0.20260713.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1dda819a57c5..86383de69140 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -37,7 +37,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260616.0 + BORINGSSL_VERSION: 0.20260713.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.681 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com From dfa444f1e59c52b56fb0d16152e610e4c29c7115 Mon Sep 17 00:00:00 2001 From: Collin Funk Date: Sat, 11 Jul 2026 00:47:16 -0700 Subject: [PATCH 0663/1062] unit3214: fix to pass on systems with >=128-bit pointers E.g. on CHERI pointers are 128 bits [1]. This causes the unit3214 test to fail, which was written with more traditional platforms in mind. Here is the output of log/stderr3214: ``` URL: - BAD: struct Curl_easy is 7984 bytes, allowed to be 5370: 2614 bytes too big BAD: struct connectdata is 1408 bytes, allowed to be 1300: 108 bytes too big BAD: struct Curl_multi is 1248 bytes, allowed to be 850: 398 bytes too big BAD: struct curl_httppost is 224 bytes, allowed to be 112: 112 bytes too big BAD: struct curl_slist is 32 bytes, allowed to be 16: 16 bytes too big BAD: struct curl_khkey is 32 bytes, allowed to be 24: 8 bytes too big BAD: struct curl_hstsentry is 48 bytes, allowed to be 40: 8 bytes too big BAD: struct curl_mime is 144 bytes, allowed to be 96: 48 bytes too big BAD: struct curl_mimepart is 592 bytes, allowed to be 440: 152 bytes too big BAD: struct curl_certinfo is 32 bytes, allowed to be 16: 16 bytes too big BAD: struct curl_tlssessioninfo is 32 bytes, allowed to be 16: 16 bytes too big BAD: struct curl_blob is 32 bytes, allowed to be 24: 8 bytes too big BAD: struct CURLMsg is 48 bytes, allowed to be 24: 24 bytes too big BAD: struct curl_header is 80 bytes, allowed to be 48: 32 bytes too big Test ended with result 14 ``` Multiply the allowed size on systems with larger than 64-bit pointers. [1] https://www.cl.cam.ac.uk/research/security/ctsrd/pdfs/20171017a-cheri-poster.pdf Closes #22299 --- tests/unit/unit3214.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/unit/unit3214.c b/tests/unit/unit3214.c index 8f22f53cc64d..191ef2f3f695 100644 --- a/tests/unit/unit3214.c +++ b/tests/unit/unit3214.c @@ -26,6 +26,11 @@ static void checksize(const char *name, size_t size, size_t allowed) { + /* Some platforms, e.g., CHERI, have 128-bit pointers. Allow structs to be + double the size of what we would typically expect. */ + if(sizeof(void *) > 8) + allowed *= sizeof(void *) / 8; + if(size > allowed) { curl_mfprintf(stderr, "BAD: struct %s is %zu bytes, " "allowed to be %zu: %zu bytes too big\n", @@ -39,7 +44,8 @@ static void checksize(const char *name, size_t size, size_t allowed) } } -/* the maximum sizes we allow specific structs to grow to */ +/* The maximum sizes we allow specific structs to grow to. + These sizes were chosen with platforms with 64-bit pointers in mind. */ #define MAX_CURL_EASY 5370 #define MAX_CONNECTDATA 1300 #define MAX_CURL_MULTI 850 From 6546ffeda4f1e81f68db6af721398148b5c74317 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:24:37 +0000 Subject: [PATCH 0664/1062] GHA: update debian:bookworm-slim Docker digest to 7b140f3 Closes #22321 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 85d2f35139c1..6ab1d0dfed4d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:60eac759739651111db372c07be67863818726f754804b8707c90979bda511df +FROM debian:bookworm-slim@sha256:7b140f374b289a7c2befc338f42ebe6441b7ea838a042bbd5acbfca6ec875818 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From bc440a89d47aa8f3a5d02b01985fd7f6fb7e7e0a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 14 Jul 2026 08:52:21 +0200 Subject: [PATCH 0665/1062] mime.c: avoid integer overflow in base64 size calculation Reported-by: xmoezzz on github Fixes #22320 Closes #22322 --- lib/mime.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/mime.c b/lib/mime.c index 11eb66207de7..638831d9c88f 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -419,6 +419,11 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, return cursize; } +/* The maximum input size that does not cause an overflow. */ +#define BASE64_MAX_INPUT_SIZE \ + (((CURL_OFF_T_MAX / (MAX_ENCODED_LINE_LENGTH + 2)) * \ + MAX_ENCODED_LINE_LENGTH / 4) * 3 - 3) + static curl_off_t encoder_base64_size(curl_mimepart *part) { curl_off_t size = part->datasize; @@ -426,6 +431,10 @@ static curl_off_t encoder_base64_size(curl_mimepart *part) if(size <= 0) return size; /* Unknown size or no data. */ + /* Prevent integer overflows */ + if(size > BASE64_MAX_INPUT_SIZE) + return -1; + /* Compute base64 character count. */ size = 4 * (1 + ((size - 1) / 3)); From cb81c5f4e2b51f77e3e913013a236a89bb74f130 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:58:06 +0000 Subject: [PATCH 0666/1062] GHA: update dependency codespell to v2.4.3 Closes #22332 --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index adf6d03ba456..8d1a74af049a 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -3,6 +3,6 @@ # SPDX-License-Identifier: curl cmakelang==0.6.13 -codespell==2.4.2 +codespell==2.4.3 reuse==6.2.0 ruff==0.15.16 From d52c7e78a3531f975591eb1bb1250185a9fd14c2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 14 Jul 2026 19:06:37 +0200 Subject: [PATCH 0667/1062] idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag Also: - curlx: pass this flag to the actual conversion calls, for consistency and robustness. (It's not stricly necessary because the initial call to determine size, with this flag passed, fails already on bad input.) - schannel: unfold `MultiByteToWideChar()` line (formatting). Ref: https://learn.microsoft.com/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar Follow-up to 6694a42aa0e820a6fe1e59d85ff8597b6d768d8d #19798 Closes #22326 --- lib/curlx/fopen.c | 2 +- lib/curlx/multibyte.c | 4 ++-- lib/idn.c | 6 ++++-- lib/vtls/schannel.c | 6 ++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 37ca02671afe..976eff9e2b50 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -68,7 +68,7 @@ static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8) if(str_w_len > 0) { str_w = CURLX_MALLOC(str_w_len * sizeof(wchar_t)); if(str_w) { - if(MultiByteToWideChar(CP_UTF8, 0, + if(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str_utf8, -1, str_w, str_w_len) == 0) { CURLX_FREE(str_w); return NULL; diff --git a/lib/curlx/multibyte.c b/lib/curlx/multibyte.c index 715d2b8dc20d..4ee39d962af0 100644 --- a/lib/curlx/multibyte.c +++ b/lib/curlx/multibyte.c @@ -41,8 +41,8 @@ wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8) if(str_w_len > 0) { str_w = curlx_malloc(str_w_len * sizeof(wchar_t)); if(str_w) { - if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, - str_w_len) == 0) { + if(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + str_utf8, -1, str_w, str_w_len) == 0) { curlx_free(str_w); return NULL; } diff --git a/lib/idn.c b/lib/idn.c index b26f251d975a..943a520eff5e 100644 --- a/lib/idn.c +++ b/lib/idn.c @@ -172,7 +172,8 @@ static CURLcode win32_idn_to_ascii(const char *in, char **out) /* Returned in_w_len includes the null-terminator, which then gets preserved across the calls that follow, ending up terminating the buffer returned to the caller. */ - in_w_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, in_w, IDN_MAX_LENGTH); + in_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + in, -1, in_w, IDN_MAX_LENGTH); if(in_w_len) { wchar_t punycode[IDN_MAX_LENGTH]; int chars = IdnToAscii(0, in_w, in_w_len, punycode, IDN_MAX_LENGTH); @@ -198,7 +199,8 @@ static CURLcode win32_ascii_to_idn(const char *in, char **out) /* Returned in_w_len includes the null-terminator, which then gets preserved across the calls that follow, ending up terminating the buffer returned to the caller. */ - in_w_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, in_w, IDN_MAX_LENGTH); + in_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + in, -1, in_w, IDN_MAX_LENGTH); if(in_w_len) { WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */ int chars = IdnToUnicode(0, in_w, in_w_len, idn, IDN_MAX_LENGTH); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index d322c755e88d..2b8166de1266 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -473,10 +473,8 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, if(pszPassword) { int str_w_len = 0; if(pwd_len > 0) - str_w_len = MultiByteToWideChar(CP_UTF8, - MB_ERR_INVALID_CHARS, - sslc->key_passwd, - (int)pwd_len, + str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + sslc->key_passwd, (int)pwd_len, pszPassword, (int)(pwd_len + 1)); if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) From 5f2a70abe3f780b6a97ac7039b7862bd828302cd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 15 Jul 2026 14:08:18 +0200 Subject: [PATCH 0668/1062] tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) Use `_TEXT()` when interacting with CRT functions (also prefer over synonym `_T()`), `TEXT()` for Win32 functions. Within curl, they mean the same because CRT/Win32 Unicode mode are always enabled in sync. Ref: https://devblogs.microsoft.com/oldnewthing/20040212-00/?p=40643/ Closes #22334 --- lib/curl_sspi.c | 8 ++++---- lib/curlx/fopen.c | 2 +- lib/vtls/schannel.c | 25 +++++++++++++------------ src/tool_main.c | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index 3ea17621b148..b7dcb7d55cc2 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -101,7 +101,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, xcharp_u passwd, dup_passwd; size_t domlen = 0; - domain.const_tchar_ptr = TEXT(""); + domain.const_tchar_ptr = _TEXT(""); /* Initialize the identity */ memset(identity, 0, sizeof(*identity)); @@ -110,9 +110,9 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, if(!useranddomain.tchar_ptr) return CURLE_OUT_OF_MEMORY; - user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('\\')); + user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, _TEXT('\\')); if(!user.const_tchar_ptr) - user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('/')); + user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, _TEXT('/')); if(user.tchar_ptr) { domain.tchar_ptr = useranddomain.tchar_ptr; @@ -121,7 +121,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, } else { user.tchar_ptr = useranddomain.tchar_ptr; - domain.const_tchar_ptr = TEXT(""); + domain.const_tchar_ptr = _TEXT(""); domlen = 0; } diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 976eff9e2b50..c3601af06ac4 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -121,7 +121,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out) *out = NULL; /* skip paths already normalized */ - if(!_tcsncmp(in, _T("\\\\?\\"), 4)) + if(!_tcsncmp(in, _TEXT("\\\\?\\"), 4)) goto cleanup; #ifndef _UNICODE diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 2b8166de1266..6a69237f4e64 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -316,34 +316,35 @@ static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, TCHAR *store_path_start; size_t store_name_len; - sep = _tcschr(path, TEXT('\\')); + sep = _tcschr(path, _TEXT('\\')); if(!sep) return CURLE_SSL_CERTPROBLEM; store_name_len = sep - path; - if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0) + if(_tcsncmp(path, _TEXT("CurrentUser"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER; - else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("LocalMachine"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; - else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("CurrentService"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; - else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("Services"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_SERVICES; - else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("Users"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_USERS; - else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("CurrentUserGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("LocalMachineGroupPolicy"), store_name_len) == + 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), store_name_len) == 0) + else if(_tcsncmp(path, _TEXT("LocalMachineEnterprise"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; else return CURLE_SSL_CERTPROBLEM; store_path_start = sep + 1; - sep = _tcschr(store_path_start, TEXT('\\')); + sep = _tcschr(store_path_start, _TEXT('\\')); if(!sep) return CURLE_SSL_CERTPROBLEM; @@ -351,9 +352,9 @@ static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) return CURLE_SSL_CERTPROBLEM; - *sep = TEXT('\0'); + *sep = _TEXT('\0'); *store_path = curlx_tcsdup(store_path_start); - *sep = TEXT('\\'); + *sep = _TEXT('\\'); if(!*store_path) return CURLE_OUT_OF_MEMORY; diff --git a/src/tool_main.c b/src/tool_main.c index 5ee8fc9ddd96..4ed2f41d580d 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -150,7 +150,7 @@ int main(int argc, char *argv[]) #ifdef _WIN32 /* Undocumented diagnostic option to list the full paths of all loaded modules. This is purposely pre-init. */ - if(argc == 2 && !_tcscmp(argv[1], _T("--dump-module-paths"))) { + if(argc == 2 && !_tcscmp(argv[1], _TEXT("--dump-module-paths"))) { struct curl_slist *item, *head = GetLoadedModulePaths(); for(item = head; item; item = item->next) curl_mprintf("%s\n", item->data); From 899e2edec95b3df01db63ad219d3c5547a3e3ae5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 15 Jul 2026 17:02:21 +0200 Subject: [PATCH 0669/1062] INTERNALS.md: require quiche 0.20.0+ For function `quiche_h3_config_set_max_field_section_size()`. Ref: #22331 Ref: https://github.com/curl/curl/pull/22331#issuecomment-4981647189 Ref: https://github.com/cloudflare/quiche/releases/tag/0.20.0 Closes #22333 --- docs/INTERNALS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index ff59f733e048..244d7c46c330 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -42,6 +42,7 @@ We aim to support these or later versions: - ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) - OpenLDAP 2.0 (2000-08-01) - OpenSSL 3.0.0 (2021-09-07) +- quiche 0.20.0 (2023-12-12) - Windows Vista 6.0 (2006-11-08 - 2012-04-10) - wolfSSL 5.0.0 (2021-11-01) - zlib 1.2.5.2 (2011-12-11) From f369c7ba437962591c8aa9d0519c7b256e7ea5bf Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 15 Jul 2026 12:52:21 +0100 Subject: [PATCH 0670/1062] quiche: set the max field section size quiche 0.29.3 started enforcing a default `SETTINGS_MAX_FIELD_SECTION_SIZE` of 32 KiB, so larger response headers now shut down the whole connection with `CURLE_HTTP3`. curl accepts up to 300 KiB of response headers with every other backend and HTTP version. Tell quiche to allow what curl itself allows. This keeps test_01_11 passing with quiche 0.29.3 and it also advertises our real limit to servers, which the RFC encourages. Ref: 899e2edec95b3df01db63ad219d3c5547a3e3ae5 #22333 Ref: #22329 Ref: #22325 Ref: https://github.com/cloudflare/quiche/commit/9be0e4fa18594a5cca00120f6ae392e217e837e5 Ref: https://github.com/cloudflare/quiche/releases/tag/0.29.3 Closes #22331 --- lib/vquic/cf-quiche.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 479fbe3b637c..b38ab7d3e957 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -40,6 +40,7 @@ #include "connect.h" #include "progress.h" #include "select.h" +#include "http.h" #include "http1.h" #include "sockaddr.h" #include "vquic/vquic.h" @@ -1429,6 +1430,10 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, result = CURLE_OUT_OF_MEMORY; goto out; } + /* quiche 0.29.3+ rejects response headers larger than 32 KiB by + default. Allow as much as curl itself accepts. */ + quiche_h3_config_set_max_field_section_size(ctx->h3config, + MAX_HTTP_RESP_HEADER_SIZE); /* Create a new HTTP/3 connection on the QUIC connection. */ ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config); From 8b5f1809bd0d2c10ac71f3043d3c71a71326bfe1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:00:38 +0000 Subject: [PATCH 0671/1062] GHA: update dependency cloudflare/quiche to v0.29.3 Follow-up to f369c7ba437962591c8aa9d0519c7b256e7ea5bf #22331 Follow-up to 899e2edec95b3df01db63ad219d3c5547a3e3ae5 #22333 Closes #22325 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 8f1f03338f09..a690ecd9aa55 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -52,7 +52,7 @@ env: OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.29.2 + QUICHE_VERSION: 0.29.3 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.2 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com From b8c061c75187fe59dff7ab79022125daa94b5e23 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 14 Jul 2026 01:37:41 +0100 Subject: [PATCH 0672/1062] ngtcp2: let verify failures win over expiry processing errors A flaky CI failure of `test_17_05_bad_ip_addr[h3]` (GnuTLS, event-based) had curl detect the certificate name mismatch yet exit with `CURLE_RECV_ERROR` (56) instead of `CURLE_PEER_FAILED_VERIFICATION` (60). `Curl_cf_ngtcp2_cmn_connect` calls `Curl_cf_ngtcp2_cmn_set_expiry` after its `ctx->tls_vrfy_result` override and returns the error unfiltered, so when the server's final handshake flight happens to be processed by the ingress inside set_expiry, the verify failure surfaces as a generic receive error. This PR makes set_expiry prefer `ctx->tls_vrfy_result` over generic progress errors, as the recv and send paths already do after calling it, and also covers the `cf-ngtcp2-proxy.c` call sites that lack the override. Completes #21712. Seen in https://github.com/curl/curl/actions/runs/29243256619/job/86794119412. Closes #22317 --- lib/vquic/cf-ngtcp2-cmn.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 1e0b5def65d2..e6efd2bd64e6 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -1750,11 +1750,14 @@ CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, return CURLE_SEND_ERROR; } result = Curl_cf_ngtcp2_progress_ingress(cf, data, pktx); - if(result) - return result; - result = Curl_cf_ngtcp2_progress_egress(cf, data, pktx); - if(result) + if(!result) + result = Curl_cf_ngtcp2_progress_egress(cf, data, pktx); + if(result) { + /* a verify failure during ingress must win over generic errors */ + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; return result; + } /* ask again, things might have changed */ expiry = ngtcp2_conn_get_expiry(ctx->qconn); } From 94fab2d210cc60da33da1d7ed862b28a5d970ebf Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 14 Jul 2026 01:37:51 +0100 Subject: [PATCH 0673/1062] test798: force IPv4 to avoid cross-runner port aliasing `test798` is the only test fetching `http://localhost:%HTTPPORT` without `-4`, since it needs the hostname for its folded `domain=localhost` cookie. curl tries `::1` first, but the test HTTP server is IPv4-only. On the BSDs, IPv4 and IPv6 have separate ephemeral port namespaces, so with every test server binding port 0, another parallel runner's IPv6-bound server can hold the same numeric port. curl then connects to the wrong runner's server, which cannot open its own `log/N/test798` and closes without a response, giving exit 52 and an empty `server.input`. This PR fixes this flake by adding `-4` matches what tests 389 and 392 already do. Linux is immune because wildcard IPv6 binds occupy the IPv4 port too. Seen in https://github.com/curl/curl/actions/runs/29229170329/job/86749470571. Closes #22318 --- tests/data/test798 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test798 b/tests/data/test798 index c69461ead714..b3937e571399 100644 --- a/tests/data/test798 +++ b/tests/data/test798 @@ -36,7 +36,7 @@ http HTTP cookies in a folded header -http://localhost:%HTTPPORT/we/want/%TESTNUMBER -b none -c %LOGDIR/jar%TESTNUMBER.txt +-4 http://localhost:%HTTPPORT/we/want/%TESTNUMBER -b none -c %LOGDIR/jar%TESTNUMBER.txt cookies From 0dc22b690dd8dba4048d494f09a50122dd7c0dd4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 14 Jul 2026 09:35:58 +0200 Subject: [PATCH 0674/1062] ssls: fix potential memory leak on import Memory can be leaked when importing corrupted SSL session data that carry the same data fields repeatedly. Fixes #22323 Closes #22324 --- docs/tests/FILEFORMAT.md | 1 + lib/vtls/vtls_scache.c | 6 +- lib/vtls/vtls_scache.h | 4 +- lib/vtls/vtls_spack.c | 10 ++- tests/data/Makefile.am | 2 +- tests/data/test1678 | 21 +++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib1678.c | 163 +++++++++++++++++++++++++++++++++++++ tests/runtests.pl | 1 + 9 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 tests/data/test1678 create mode 100644 tests/libtest/lib1678.c diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 5ded0d343f94..447593d523a7 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -534,6 +534,7 @@ Features testable here are: - `SPNEGO` - `SSL` - `SSLpinning` +- `SSLS-EXPORT` - `SSPI` - `threaded-resolver` - `TLS-SRP` diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 3ba97222858c..c7e1cdb46c4d 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -380,9 +380,9 @@ static void cf_ssl_scache_session_ldestroy(void *udata, void *obj) { struct Curl_ssl_session *s = obj; (void)udata; - curlx_free(CURL_UNCONST(s->sdata)); - curlx_free(CURL_UNCONST(s->quic_tp)); - curlx_free((void *)s->alpn); + curlx_free(s->sdata); + curlx_free(s->quic_tp); + curlx_free(s->alpn); curlx_free(s); } diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index 5fd9d3c05481..e6f359395b18 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -122,13 +122,13 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, /* All about an SSL session ticket */ struct Curl_ssl_session { - const void *sdata; /* session ticket data, plain bytes */ + uint8_t *sdata; /* session ticket data, plain bytes */ size_t sdata_len; /* number of bytes in sdata */ curl_off_t valid_until; /* seconds since EPOCH until ticket expires */ int ietf_tls_id; /* TLS protocol identifier negotiated */ char *alpn; /* APLN TLS negotiated protocol string */ size_t earlydata_max; /* max 0-RTT data supported by peer */ - const unsigned char *quic_tp; /* Optional QUIC transport param bytes */ + uint8_t *quic_tp; /* Optional QUIC transport param bytes */ size_t quic_tp_len; /* number of bytes in quic_tp */ struct Curl_llist_node list; /* internal storage handling */ BIT(sectrust_verified); /* session comes from sectrust verified TLS */ diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index e572e923f542..d8e51c4fd118 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -244,6 +244,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, uint16_t val16; uint32_t val32; uint64_t val64; + size_t dlen; CURLcode result; DEBUGASSERT(buf); @@ -271,6 +272,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, switch(val8) { case CURL_SPACK_ALPN: + curlx_free(s->alpn); result = spack_decstr16(&s->alpn, &buf, end); if(result) goto out; @@ -288,17 +290,21 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, s->ietf_tls_id = val16; break; case CURL_SPACK_QUICTP: { - result = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + result = spack_decdata16(&pval8, &dlen, &buf, end); if(result) goto out; + curlx_free(s->quic_tp); s->quic_tp = pval8; + s->quic_tp_len = dlen; break; } case CURL_SPACK_TICKET: { - result = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + result = spack_decdata16(&pval8, &dlen, &buf, end); if(result) goto out; + curlx_free(s->sdata); s->sdata = pval8; + s->sdata_len = dlen; break; } case CURL_SPACK_VALID_UNTIL: diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 45df54464959..83704d102072 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -220,7 +220,7 @@ test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 \ test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ -test1677 test1680 test1681 test1682 test1683 test1684 \ +test1677 test1678 test1680 test1681 test1682 test1683 test1684 \ test1685 test1686 \ \ test1700 test1702 test1703 test1704 test1705 test1706 test1707 \ diff --git a/tests/data/test1678 b/tests/data/test1678 new file mode 100644 index 000000000000..c0cada0ba79f --- /dev/null +++ b/tests/data/test1678 @@ -0,0 +1,21 @@ + + + + +ssls-import + + + +# Client-side + + +SSLS-EXPORT + + +lib%TESTNUMBER + + +SSL session import tests + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 140ec0bd5819..6fd8469dfa14 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -103,7 +103,7 @@ TESTS_C = \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ lib1647.c lib1648.c lib1649.c \ - lib1662.c \ + lib1662.c lib1678.c \ lib1686.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1678.c b/tests/libtest/lib1678.c new file mode 100644 index 000000000000..144cc9977a3f --- /dev/null +++ b/tests/libtest/lib1678.c @@ -0,0 +1,163 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +#if defined(USE_SSL) && defined(USE_SSLS_EXPORT) + +#define T1678_IMPORT_COUNT 64 +#define T1678_FIRST_TICKET_SIZE 4096 +#define T1678_FIRST_QUICTP_SIZE 127 +#define T1678_FIRST_ALPN_SIZE 10 + +static uint8_t *t1678_make_packet(size_t *packet_len) +{ + const size_t t1678_total = + 1 + /* format version */ + 1 + 2 + T1678_FIRST_TICKET_SIZE + /* first ticket */ + 1 + 2 + T1678_FIRST_QUICTP_SIZE + /* first QUIC traffic params */ + 1 + 2 + T1678_FIRST_ALPN_SIZE + /* first ALPN params */ + 1 + 2 + 1 + /* second ticket */ + 1 + 2 + 1 + /* second ALPN */ + 1 + 2 + 1; /* second QUIC traffic params */ + + uint8_t *packet = curlx_malloc(t1678_total); + uint8_t *p; + + if(!packet) + return NULL; + + p = packet; + + /* CURL_SPACK_VERSION */ + *p++ = 0x01; + + /* First CURL_SPACK_TICKET */ + *p++ = 0x04; + *p++ = (uint8_t)(T1678_FIRST_TICKET_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_TICKET_SIZE & 0x0ff); + memset(p, 'A', T1678_FIRST_TICKET_SIZE); + p += T1678_FIRST_TICKET_SIZE; + + /* First CURL_SPACK_QUICTP */ + *p++ = 0x07; + *p++ = (uint8_t)(T1678_FIRST_QUICTP_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_QUICTP_SIZE & 0x0ff); + memset(p, 'Q', T1678_FIRST_QUICTP_SIZE); + p += T1678_FIRST_QUICTP_SIZE; + + /* First CURL_SPACK_ALPN */ + *p++ = 0x05; + *p++ = (uint8_t)(T1678_FIRST_ALPN_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_ALPN_SIZE & 0x0ff); + memset(p, 'a', T1678_FIRST_ALPN_SIZE); + p += T1678_FIRST_ALPN_SIZE; + + /* Second CURL_SPACK_TICKET: one byte. */ + *p++ = 0x04; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'B'; + + /* Second CURL_SPACK_ALPN: one byte. */ + *p++ = 0x05; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'b'; + + /* Second CURL_SPACK_QUICTP: one byte. */ + *p++ = 0x07; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'R'; + + *packet_len = t1678_total; + return packet; +} + +static CURLcode test_lib1678(const char *URL) +{ + uint8_t *packet; + size_t packet_len; + CURLSH *share = NULL; + CURL *easy = NULL; + CURLSHcode shrc; + CURLcode result = CURLE_FAILED_INIT; + int i; + + (void)URL; + packet = t1678_make_packet(&packet_len); + if(!packet) + goto test_cleanup; + + result = curl_global_init(CURL_GLOBAL_DEFAULT); + if(result != CURLE_OK) + goto test_cleanup; + + share = curl_share_init(); + easy = curl_easy_init(); + + if(!share || !easy) + goto test_cleanup; + + shrc = curl_share_setopt(share, + CURLSHOPT_SHARE, + CURL_LOCK_DATA_SSL_SESSION); + if(shrc != CURLSHE_OK) + goto test_cleanup; + + result = curl_easy_setopt(easy, CURLOPT_SHARE, share); + if(result) + goto test_cleanup; + + for(i = 0; i < T1678_IMPORT_COUNT; ++i) { + result = curl_easy_ssls_import(easy, + "example.test:443", + NULL, + 0, + packet, + packet_len); + if(result) { + curl_mfprintf(stderr, + "import %d failed: %d (%s)\n", + i, (int)result, + curl_easy_strerror(result)); + break; + } + } + +test_cleanup: + curlx_free(packet); + curl_easy_cleanup(easy); + curl_share_cleanup(share); + curl_global_cleanup(); + + return result; +} +#else +static CURLcode test_lib1678(const char *URL) +{ + (void)URL; + return CURLE_OK; +} +#endif /* USE_SSL && USE_SSLS_EXPORT */ diff --git a/tests/runtests.pl b/tests/runtests.pl index b35d6b536897..449500119428 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -733,6 +733,7 @@ sub checksystemfeatures { # 'https-proxy' is used as "server" so consider it a protocol push @protocols, 'https-proxy'; } + $feature{"SSLS-EXPORT"} = $feat =~ /SSLS-EXPORT/; # Unicode support $feature{"Unicode"} = $feat =~ /Unicode/i; # Thread-safe init From d0545d223a96884cb157a90eab3313a232fb744c Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 14 Jul 2026 01:38:01 +0100 Subject: [PATCH 0675/1062] runtests: flush cached test parts when (re)loading a file Force test file reload on client connect in ftpserver. Closes #22319 --- tests/ftpserver.pl | 3 ++- tests/getpart.pm | 11 ++++++++--- tests/runner.pm | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index d0a3e22fa28e..0cb8cdc3442c 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -3142,7 +3142,8 @@ sub customize { $| = 1; &customize(); # read test control instructions - loadtest("$logdir/test$testno"); + # force a reload, a test retry may have regenerated the file + loadtest("$logdir/test$testno", 0, 1); my $welcome = $commandreply{"welcome"}; if(!$welcome) { diff --git a/tests/getpart.pm b/tests/getpart.pm index fc98da210c0b..10838b2675f0 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -44,7 +44,7 @@ BEGIN { ); } -use Memoize; +use Memoize qw(memoize flush_cache); my @xml; # test data file contents my $xmlfile; # test data filename @@ -199,9 +199,9 @@ sub partexists { # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result sub loadtest { - my ($file, $original) = @_; + my ($file, $original, $force) = @_; - if(defined $xmlfile && $file eq $xmlfile) { + if(!$force && defined $xmlfile && $file eq $xmlfile) { # This test is already loaded return } @@ -209,6 +209,11 @@ sub loadtest { undef @xml; $xmlfile = ""; + # flush results cached from an earlier load, the file may have been + # regenerated since with new variable substitutions + flush_cache('getpart'); + flush_cache('getpartattr'); + if(open(my $xmlh, "<", $file)) { if($original) { binmode $xmlh, ':crlf'; diff --git a/tests/runner.pm b/tests/runner.pm index 9e514f0e238d..594e383f7417 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -655,8 +655,8 @@ sub singletest_preprocess { } close($fulltesth) or die "Failure writing test file"; - # in case the process changed the file, reload it - loadtest("$LOGDIR/test${testnum}"); + # in case the process changed the file, force a reload + loadtest("$LOGDIR/test${testnum}", 0, 1); } ####################################################################### From f2fda908f922836134fec313d20b24f7cc2d8937 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 21:52:53 +0200 Subject: [PATCH 0676/1062] lib1560: test CURLU_DEFAULT_PORT more Closes #22314 --- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/data/test1560 b/tests/data/test1560 index 70de44d31e1e..f4cbaa8e0e58 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -36,7 +36,7 @@ lib%TESTNUMBER success -Allocations: 3450 +Allocations: 3550 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 9fb546bc05aa..5eda0bc6dc11 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -554,9 +554,70 @@ static const struct testcase get_parts_list[] = { {"file:///hello.html", "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]", 0, 0, CURLUE_OK}, + + /* verify that we get the right default ports */ {"https://127.0.0.1", "https | [11] | [12] | [13] | 127.0.0.1 | 443 | / | [16] | [17]", 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"http://127.0.0.1", + "http | [11] | [12] | [13] | 127.0.0.1 | 80 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"ftp://127.0.0.1", + "ftp | [11] | [12] | [13] | 127.0.0.1 | 21 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"ftps://127.0.0.1", + "ftps | [11] | [12] | [13] | 127.0.0.1 | 990 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"scp://127.0.0.1", + "scp | [11] | [12] | [13] | 127.0.0.1 | 22 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"sftp://127.0.0.1", + "sftp | [11] | [12] | [13] | 127.0.0.1 | 22 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"imap://127.0.0.1", + "imap | [11] | [12] | [13] | 127.0.0.1 | 143 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"imaps://127.0.0.1", + "imaps | [11] | [12] | [13] | 127.0.0.1 | 993 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"smtp://127.0.0.1", + "smtp | [11] | [12] | [13] | 127.0.0.1 | 25 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"smtps://127.0.0.1", + "smtps | [11] | [12] | [13] | 127.0.0.1 | 465 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"pop3://127.0.0.1", + "pop3 | [11] | [12] | [13] | 127.0.0.1 | 110 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"pop3s://127.0.0.1", + "pop3s | [11] | [12] | [13] | 127.0.0.1 | 995 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, +#ifndef CURL_DISABLE_WEBSOCKETS + {"ws://127.0.0.1", + "ws | [11] | [12] | [13] | 127.0.0.1 | 80 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"wss://127.0.0.1", + "wss | [11] | [12] | [13] | 127.0.0.1 | 443 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, +#endif + {"telnet://127.0.0.1", + "telnet | [11] | [12] | [13] | 127.0.0.1 | 23 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"gopher://127.0.0.1", + "gopher | [11] | [12] | [13] | 127.0.0.1 | 70 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"gophers://127.0.0.1", + "gophers | [11] | [12] | [13] | 127.0.0.1 | 70 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, +#ifndef CURL_DISABLE_LDAP + {"ldap://127.0.0.1", + "ldap | [11] | [12] | [13] | 127.0.0.1 | 389 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, + {"ldaps://127.0.0.1", + "ldaps | [11] | [12] | [13] | 127.0.0.1 | 636 | / | [16] | [17]", + 0, CURLU_DEFAULT_PORT, CURLUE_OK}, +#endif + {"https://127.0.0.1", "https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, From 4176aba5e4871a2f1c7c120dd76568f80f5d5ddc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 14 Jul 2026 22:49:38 +0200 Subject: [PATCH 0677/1062] tool_cb_prg: avoid integer overflows Verify in test 2093 Fixes #22316 Reported-by: xmoezzz on github Closes #22328 --- src/tool_cb_prg.c | 20 ++++++++++++----- tests/data/Makefile.am | 2 +- tests/data/test2093 | 50 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 tests/data/test2093 diff --git a/src/tool_cb_prg.c b/src/tool_cb_prg.c index 7d7f3a8d519e..b570923c4129 100644 --- a/src/tool_cb_prg.c +++ b/src/tool_cb_prg.c @@ -128,6 +128,14 @@ int tool_progress_cb(void *clientp, struct ProgressData *bar = &per->progressbar; curl_off_t total; curl_off_t point; + curl_off_t totalall; + curl_off_t nowall; + + totalall = ((CURL_OFF_T_MAX - dltotal) < ultotal) ? + CURL_OFF_T_MAX : dltotal + ultotal; + + nowall = ((CURL_OFF_T_MAX - dlnow) < ulnow) ? + CURL_OFF_T_MAX : dlnow + ulnow; if(!bar->calls) update_width(bar); @@ -136,27 +144,27 @@ int tool_progress_cb(void *clientp, indicating that we are expecting to get the filesize from the remote */ if(bar->initial_size < 0) { if(dltotal || ultotal) - total = dltotal + ultotal; + total = totalall; else total = CURL_OFF_T_MAX; } - else if((CURL_OFF_T_MAX - bar->initial_size) < (dltotal + ultotal)) + else if((CURL_OFF_T_MAX - bar->initial_size) < totalall) total = CURL_OFF_T_MAX; else - total = dltotal + ultotal + bar->initial_size; + total = totalall + bar->initial_size; /* Calculate the current progress. initial_size can be less than zero when indicating that we are expecting to get the filesize from the remote */ if(bar->initial_size < 0) { if(dltotal || ultotal) - point = dlnow + ulnow; + point = nowall; else point = CURL_OFF_T_MAX; } - else if((CURL_OFF_T_MAX - bar->initial_size) < (dlnow + ulnow)) + else if((CURL_OFF_T_MAX - bar->initial_size) < nowall) point = CURL_OFF_T_MAX; else - point = dlnow + ulnow + bar->initial_size; + point = nowall + bar->initial_size; if(bar->calls) { /* after first call... */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 83704d102072..40e7c7da5cd7 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -252,7 +252,7 @@ test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ -test2088 test2089 test2090 test2091 test2092 \ +test2088 test2089 test2090 test2091 test2092 test2093 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ test2108 test2109 test2110 test2113 test2114 test2115 test2116 \ \ diff --git a/tests/data/test2093 b/tests/data/test2093 new file mode 100644 index 000000000000..6c156140f5e8 --- /dev/null +++ b/tests/data/test2093 @@ -0,0 +1,50 @@ + + + + +HTTP +HTTP GET + + + +# Server-side + + +HTTP/1.1 200 OK swsclose +Content-Length: 9223372036854775807 +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +HTTP POST a few bytes with 2^63-1 bytes response + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -# -d "sending data" + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 12 +Content-Type: application/x-www-form-urlencoded + +sending data + + +18 + + + From 33dc64fd0e7bfa3b05f73496718a340e125a90f9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Jul 2026 19:13:45 +0200 Subject: [PATCH 0678/1062] urlapi: allow URLs to not have userauth (hostname) Verified in test 1560 Fixes #22279 Reported-by: Bill Mill Closes #22313 --- lib/urlapi.c | 30 +++++++++++++++++++----------- tests/libtest/lib1560.c | 10 ++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 1ec87859ddaf..1aab7f9418d0 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -965,22 +965,30 @@ static CURLUcode parse_scheme(const char *url, CURLU *u, char *schemebuf, const char *schemep = NULL; if(schemelen) { - int i = 0; + int num_slashes = 0; const char *p = &url[schemelen + 1]; - while((*p == '/') && (i < 4)) { - p++; - i++; - } - - schemep = schemebuf; - if(!Curl_get_scheme(schemep) && - !(flags & CURLU_NON_SUPPORT_SCHEME)) + if(!Curl_get_scheme(schemebuf) && !(flags & CURLU_NON_SUPPORT_SCHEME)) return CURLUE_UNSUPPORTED_SCHEME; - if((i < 1) || (i > 3)) - /* less than one or more than three slashes */ + if(!ISSLASH(*p)) + /* less than one */ return CURLUE_BAD_SLASHES; + if((flags & CURLU_NO_AUTHORITY)) { + while(ISSLASH(*p) && (num_slashes < 2)) { + p++; + num_slashes++; + } + } + else { + while(ISSLASH(*p) && (num_slashes < 4)) { + p++; + num_slashes++; + } + if(num_slashes > 3) + return CURLUE_BAD_SLASHES; + } + schemep = schemebuf; *hostpp = p; /* hostname starts here */ } else { diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 5eda0bc6dc11..9c8f0e2b358a 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -153,6 +153,16 @@ struct clearurlcase { }; static const struct testcase get_parts_list[] = { + /* non-supported URL without hostname */ + {"weird:///path", + "weird | [11] | [12] | [13] | | [15] | /path | [16] | [17]", + CURLU_NON_SUPPORT_SCHEME|CURLU_NO_AUTHORITY, 0, CURLUE_OK}, + /* non-supported URL without hostname, using path with multiple leading + slashes */ + {"weird:////path", + "weird | [11] | [12] | [13] | | [15] | //path | [16] | [17]", + CURLU_NON_SUPPORT_SCHEME|CURLU_NO_AUTHORITY, 0, CURLUE_OK}, + /* RFC 4291 IPv4-Mapped IPv6 Addresses */ {"https://[0:0:0:0:0:FFFF:129.144.52.38]:1234", "https | [11] | [12] | [13] | [::ffff:129.144.52.38] | 1234 " From 820c014578b56b9fa4d2c472e90b5060198dd404 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sun, 12 Jul 2026 17:52:16 +0100 Subject: [PATCH 0679/1062] cfilters: fix event-based connection shutdown - Adjust pollset during connection shutdown. - Separate the FIRSTSOCKET and SECONDSOCKET check so that one being in connect or shutdown no longer adds poll events for the other. - Fix shutdown state evaluation (as detailed below). - Add a unit test for Curl_conn_adjust_pollset. - Add a client for event testing. - Test that shutdown sockets stay with the socket callback until done. The pollset predicate now reads the connection's own shutdown state rather than going through data->conn, and the EXPIRE_SHUTDOWN arming in cshutdn_perform() works again: next_expire_ms started at 0 and could never be set, so a peer that never sends a close_notify would still park its connection forever, timeout or not. Reported-by: pszemus@users.noreply.github.com Fixes https://github.com/curl/curl/issues/22282 Closes https://github.com/curl/curl/pull/22304 --- lib/cfilters.c | 24 ++- lib/connect.c | 11 +- lib/connect.h | 2 +- lib/cshutdn.c | 4 +- lib/transfer.c | 2 +- tests/data/Makefile.am | 2 +- tests/data/test2606 | 18 ++ tests/http/test_19_shutdown.py | 34 ++++ tests/libtest/Makefile.inc | 1 + tests/libtest/cli_ev_download.c | 313 ++++++++++++++++++++++++++++++++ tests/unit/Makefile.inc | 1 + tests/unit/unit2606.c | 221 ++++++++++++++++++++++ 12 files changed, 612 insertions(+), 21 deletions(-) create mode 100644 tests/data/test2606 create mode 100644 tests/libtest/cli_ev_download.c create mode 100644 tests/unit/unit2606.c diff --git a/lib/cfilters.c b/lib/cfilters.c index fb1914d133e8..c5ee51a1b42f 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -206,7 +206,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) } *done = FALSE; - if(!Curl_shutdown_started(data, sockindex)) { + if(!Curl_shutdown_started(data->conn, sockindex)) { Curl_shutdown_start(data, sockindex, 0); } else { @@ -748,23 +748,29 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, struct easy_pollset *ps) { CURLcode result = CURLE_OK; + bool want_io = !!ps->n; int i; DEBUGASSERT(data); DEBUGASSERT(conn); /* During connect time, connection filters may add sockets to the pollset * even when the transfer neither wants to send nor receive. And those - * sockets, when having events, are served. - * Once connected however, a transfer that neither wants to send nor receive + * sockets, when having events, are served. The same applies to a + * filter chain whose shutdown has started. + * Once a filter chain is connected however and before its shutdown + * starts, a transfer that neither wants to send nor receive * will never call the connection filters. Any sockets added by the filters * will not change state and POLLIN/POLLOUT events will trigger forever, - * making us busy loop. See #21671 */ - if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) || - (conn->cfilter[SECONDARYSOCKET] && - !Curl_conn_is_connected(conn, SECONDARYSOCKET))) { - for(i = 0; (i < 2) && !result && conn; ++i) { + * making us busy loop. See #21671. + * Gate each filter chain on its own state, so that one chain being in + * connect or shutdown does not add poll events for the other. Check + * against the transfer's own interest, before any chain added sockets + * of its own. */ + for(i = 0; (i < 2) && !result; ++i) { + if(conn->cfilter[i] && + (want_io || !Curl_conn_is_connected(conn, i) || + Curl_shutdown_started(conn, i))) result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); - } } return result; } diff --git a/lib/connect.c b/lib/connect.c index 6d6ddd2e91a1..86e97b955a01 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -75,7 +75,7 @@ UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, timediff_t timeleft_ms = 0; timediff_t ctimeleft_ms = 0; - if(Curl_shutdown_started(data, FIRSTSOCKET)) + if(data->conn && Curl_shutdown_started(data->conn, FIRSTSOCKET)) return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET); else if(Curl_is_connecting(data)) { timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? @@ -164,13 +164,10 @@ void Curl_shutdown_clear(struct Curl_easy *data, int sockindex) memset(pt, 0, sizeof(*pt)); } -bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) +bool Curl_shutdown_started(struct connectdata *conn, int sockindex) { - if(data->conn) { - struct curltime *pt = &data->conn->shutdown.start[sockindex]; - return (pt->tv_sec > 0) || (pt->tv_usec > 0); - } - return FALSE; + const struct curltime *pt = &conn->shutdown.start[sockindex]; + return (pt->tv_sec > 0) || (pt->tv_usec > 0); } /* diff --git a/lib/connect.h b/lib/connect.h index a5b7042d6e59..20eedaf10e1d 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -58,7 +58,7 @@ timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, void Curl_shutdown_clear(struct Curl_easy *data, int sockindex); /* TRUE iff shutdown has been started */ -bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); +bool Curl_shutdown_started(struct connectdata *conn, int sockindex); /* * Used to extract socket and connectdata struct for the most recent diff --git a/lib/cshutdn.c b/lib/cshutdn.c index cc77e401fd32..30cbaa552acb 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -76,7 +76,7 @@ static void cshutdn_run_once(struct Curl_easy *data, /* We expect to be attached when called */ DEBUGASSERT(data->conn == conn); - if(!Curl_shutdown_started(data, FIRSTSOCKET)) { + if(!Curl_shutdown_started(conn, FIRSTSOCKET)) { Curl_shutdown_start(data, FIRSTSOCKET, 0); } @@ -253,7 +253,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn, /* idata has one timer list, but maybe more than one connection. * Set EXPIRE_SHUTDOWN to the smallest time left for all. */ ms = Curl_conn_shutdown_timeleft(data, conn); - if(ms && ms < next_expire_ms) + if(ms && (!next_expire_ms || (ms < next_expire_ms))) next_expire_ms = ms; } e = enext; diff --git a/lib/transfer.c b/lib/transfer.c index 2eab7cabad4c..b7902de9a172 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -155,7 +155,7 @@ static bool xfer_recv_shutdown_started(struct Curl_easy *data) { if(!data || !data->conn) return FALSE; - return Curl_shutdown_started(data, data->conn->recv_idx); + return Curl_shutdown_started(data->conn, data->conn->recv_idx); } CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 40e7c7da5cd7..e88472728b23 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -267,7 +267,7 @@ test2408 test2409 test2410 test2411 test2412 test2413 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ -test2600 test2601 test2602 test2603 test2604 test2605 \ +test2600 test2601 test2602 test2603 test2604 test2605 test2606 \ \ test2700 test2701 test2702 test2703 test2704 test2705 test2706 test2707 \ test2708 test2709 test2710 test2711 test2712 test2713 test2714 test2715 \ diff --git a/tests/data/test2606 b/tests/data/test2606 new file mode 100644 index 000000000000..1ce80013a717 --- /dev/null +++ b/tests/data/test2606 @@ -0,0 +1,18 @@ + + + + +unittest + + + +# Client-side + + +unittest + + +Curl_conn_adjust_pollset unit test + + + diff --git a/tests/http/test_19_shutdown.py b/tests/http/test_19_shutdown.py index f8a948acd661..4ac447d75837 100644 --- a/tests/http/test_19_shutdown.py +++ b/tests/http/test_19_shutdown.py @@ -211,3 +211,37 @@ def test_19_07_shutdown_by_curl(self, env: Env, httpd, proto, share_connect): if re.match(r'.*SHUTDOWN] shutdown, done=1', line)] # we see less clean shutdowns as total limit forces early closes assert len(shutdowns) < count, f'{shutdowns}' + + # Strictly event-based transfers, no connection reuse. Connections + # whose graceful shutdown cannot finish right away must have their + # socket stay registered with the application's socket callback, + # or they never make progress and leak until multi cleanup. + @pytest.mark.parametrize("proto", ['http/1.1']) + def test_19_08_event_shutdown_watched(self, env: Env, httpd, proto): + if not env.curl_is_debug(): + pytest.skip('only works for curl debug builds') + count = 5 + docname = 'data.json' + url = f'https://localhost:{env.https_port}/{docname}' + client = LocalClient(name='cli_ev_download', env=env, run_env={ + # make socket receives block often, so the TLS shutdown + # cannot finish on its first attempt + 'CURL_DBG_SOCK_RBLOCK': '90', + 'CURL_DEBUG': 'ssl,multi' + }) + if not client.exists(): + pytest.skip(f'example client not built: {client.name}') + r = client.run(args=[ + '-n', f'{count}', '-C', env.ca.cert_file, url + ]) + r.check_exit_code(0) + m = None + for line in r.stderr.splitlines(): + m = re.match(r'.*\[ev] final: watched=(\d+) socks_left=(\d+)', line) + if m: + break + assert m, f'no client event summary found: {r.stderr}' + # shutdown sockets were watched after transfers finished and + # all shutdowns finished within the event loop + assert int(m.group(1)) > 0, f'{r.stderr}' + assert int(m.group(2)) == 0, f'{r.stderr}' diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 6fd8469dfa14..07d78b51925f 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -54,6 +54,7 @@ TOOLX_C = \ # All libtest programs TESTS_C = \ + cli_ev_download.c \ cli_ftp_upload.c \ cli_h2_pausing.c \ cli_h2_serverpush.c \ diff --git a/tests/libtest/cli_ev_download.c b/tests/libtest/cli_ev_download.c new file mode 100644 index 000000000000..e3ca6ad406a7 --- /dev/null +++ b/tests/libtest/cli_ev_download.c @@ -0,0 +1,313 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +/* An event-based download client, driving transfers purely via + * curl_multi_socket_action() with its own socket/timer callbacks, the + * way an event-based application does. It runs its transfers serially + * with CURLOPT_FORBID_REUSE, so every transfer's connection enters + * shutdown when done, and reports whether the connection sockets + * remain under event supervision until their shutdown finished. */ + +#define EV_DL_MAX_SOCKS 8 + +struct ev_dl_ctx { + CURLM *multi; + struct curltime timer_started; + curl_socket_t fds[EV_DL_MAX_SOCKS]; + int actions[EV_DL_MAX_SOCKS]; + size_t nsocks; + long timer_ms; /* last timeout set, -1 for none */ +}; + +static int ev_dl_socket_cb(CURL *easy, curl_socket_t fd, int what, + void *clientp, void *socketp) +{ + struct ev_dl_ctx *ctx = clientp; + size_t i; + + (void)easy; + (void)socketp; + for(i = 0; i < ctx->nsocks; ++i) { + if(ctx->fds[i] == fd) + break; + } + if(what == CURL_POLL_REMOVE) { + if(i < ctx->nsocks) { + ctx->nsocks--; + ctx->fds[i] = ctx->fds[ctx->nsocks]; + ctx->actions[i] = ctx->actions[ctx->nsocks]; + } + } + else { + if(i == ctx->nsocks) { + if(ctx->nsocks >= EV_DL_MAX_SOCKS) { + curl_mfprintf(stderr, "[ev] too many sockets to track\n"); + return -1; + } + ctx->nsocks++; + } + ctx->fds[i] = fd; + ctx->actions[i] = what; + } + curl_mfprintf(stderr, "[ev] socket fd=%ld what=%d, tracking %zu\n", + (long)fd, what, ctx->nsocks); + return 0; +} + +static int ev_dl_timer_cb(CURLM *multi, long timeout_ms, void *clientp) +{ + struct ev_dl_ctx *ctx = clientp; + + (void)multi; + ctx->timer_ms = timeout_ms; + if(timeout_ms >= 0) + ctx->timer_started = curlx_now(); + return 0; +} + +static size_t ev_dl_discard_cb(char *ptr, size_t size, size_t nmemb, + void *userdata) +{ + (void)ptr; + (void)userdata; + return size * nmemb; +} + +/* wait for socket events or the timer, then feed libcurl. */ +static CURLMcode ev_dl_roundtrip(struct ev_dl_ctx *ctx) +{ + curl_socket_t fds[EV_DL_MAX_SOCKS]; + int actions[EV_DL_MAX_SOCKS]; + struct timeval tv; + fd_set rd, wr; + size_t i, nsocks; + long wait_ms = 100; + int maxfd = -1; + int running = 0; + CURLMcode mres = CURLM_OK; + + FD_ZERO(&rd); + FD_ZERO(&wr); + for(i = 0; i < ctx->nsocks; ++i) { + if(ctx->actions[i] & CURL_POLL_IN) + FD_SET(ctx->fds[i], &rd); + if(ctx->actions[i] & CURL_POLL_OUT) + FD_SET(ctx->fds[i], &wr); + if((int)ctx->fds[i] > maxfd) + maxfd = (int)ctx->fds[i]; + } + if(ctx->timer_ms >= 0) { + long left = ctx->timer_ms - + (long)curlx_timediff_ms(curlx_now(), ctx->timer_started); + if(left < 0) + left = 0; + if(left < wait_ms) + wait_ms = left; + } + tv.tv_sec = wait_ms / 1000; + tv.tv_usec = (int)(wait_ms % 1000) * 1000; + select_wrapper(maxfd + 1, &rd, &wr, NULL, &tv); + + /* the callbacks change our socket table while we dispatch */ + nsocks = ctx->nsocks; + memcpy(fds, ctx->fds, sizeof(fds)); + memcpy(actions, ctx->actions, sizeof(actions)); + for(i = 0; i < nsocks; ++i) { + int ev_bitmask = 0; + if((actions[i] & CURL_POLL_IN) && FD_ISSET(fds[i], &rd)) + ev_bitmask |= CURL_CSELECT_IN; + if((actions[i] & CURL_POLL_OUT) && FD_ISSET(fds[i], &wr)) + ev_bitmask |= CURL_CSELECT_OUT; + if(ev_bitmask) { + mres = curl_multi_socket_action(ctx->multi, fds[i], ev_bitmask, + &running); + if(mres) + return mres; + } + } + + if((ctx->timer_ms >= 0) && + (curlx_timediff_ms(curlx_now(), ctx->timer_started) >= ctx->timer_ms)) { + ctx->timer_ms = -1; + mres = curl_multi_socket_action(ctx->multi, CURL_SOCKET_TIMEOUT, 0, + &running); + } + return mres; +} + +static void usage_ev_download(const char *msg) +{ + if(msg) + curl_mfprintf(stderr, "%s\n", msg); + curl_mfprintf(stderr, + "usage: [options] url\n" + " event-based downloads with connection reuse forbidden\n" + " -n number of transfers to do serially (default: 5)\n" + " -C certfile for CA verification\n" + ); +} + +static CURLcode test_cli_ev_download(const char *URL) +{ + struct ev_dl_ctx ctx; + CURLM *multi = NULL; + char *cafile = NULL; + const char *url; + size_t transfer_count = 5; + size_t i; + int watched = 0; + struct curltime started; + CURLcode result = CURLE_OK; + int ch; + + (void)URL; + memset(&ctx, 0, sizeof(ctx)); + ctx.timer_ms = -1; + + while((ch = cgetopt(test_argc, test_argv, "hn:C:")) != -1) { + switch(ch) { + case 'h': + usage_ev_download(NULL); + result = (CURLcode)2; + goto optcleanup; + case 'n': { + const char *opt = coptarg; + curl_off_t num; + if(!curlx_str_number(&opt, &num, LONG_MAX)) + transfer_count = (size_t)num; + break; + } + case 'C': + curlx_free(cafile); + cafile = curlx_strdup(coptarg); + break; + default: + usage_ev_download("invalid option"); + result = (CURLcode)1; + goto optcleanup; + } + } + test_argc -= coptind; + test_argv += coptind; + + if(test_argc != 1) { + usage_ev_download("not enough arguments"); + result = (CURLcode)2; + goto optcleanup; + } + url = test_argv[0]; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + result = (CURLcode)3; + goto optcleanup; + } + curl_global_trace("ids,time"); + + multi = curl_multi_init(); + if(!multi) { + curl_mfprintf(stderr, "curl_multi_init() failed\n"); + result = (CURLcode)3; + goto cleanup; + } + ctx.multi = multi; + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, ev_dl_socket_cb); + curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, &ctx); + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, ev_dl_timer_cb); + curl_multi_setopt(multi, CURLMOPT_TIMERDATA, &ctx); + + for(i = 0; i < transfer_count && !result; ++i) { + CURL *easy = curl_easy_init(); + int done = 0; + int running = 0; + + if(!easy) { + curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i); + result = (CURLcode)1; + goto cleanup; + } + curl_easy_setopt(easy, CURLOPT_URL, url); + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, ev_dl_discard_cb); + curl_easy_setopt(easy, CURLOPT_FORBID_REUSE, 1L); + curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); + if(cafile) + curl_easy_setopt(easy, CURLOPT_CAINFO, cafile); + + curl_multi_add_handle(multi, easy); + curl_mfprintf(stderr, "[t-%zu] STARTED\n", i); + curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running); + + started = curlx_now(); + while(!done) { + struct CURLMsg *m; + int msgq = 0; + + if(ev_dl_roundtrip(&ctx)) { + curl_mfprintf(stderr, "[t-%zu] multi failure\n", i); + result = (CURLcode)1; + goto cleanup; + } + m = curl_multi_info_read(multi, &msgq); + if(m && (m->msg == CURLMSG_DONE)) { + done = 1; + result = m->data.result; + curl_mfprintf(stderr, "[t-%zu] FINISHED with result %d\n", + i, (int)result); + } + else if(curlx_timediff_ms(curlx_now(), started) > (timediff_t)30000) { + curl_mfprintf(stderr, "[t-%zu] transfer timed out\n", i); + result = (CURLcode)1; + goto cleanup; + } + } + curl_multi_remove_handle(multi, easy); + curl_easy_cleanup(easy); + + /* The transfer is over and its connection may not be reused. A + * graceful shutdown that could not finish right away needs its + * socket watched by us, or it can never make progress. */ + curl_mfprintf(stderr, "[t-%zu] sockets tracked after done: %zu\n", + i, ctx.nsocks); + if(ctx.nsocks) + watched++; + } + + /* drive the remaining shutdowns via socket events */ + started = curlx_now(); + while(ctx.nsocks && + (curlx_timediff_ms(curlx_now(), started) < (timediff_t)5000)) { + if(ev_dl_roundtrip(&ctx)) + break; + } + curl_mfprintf(stderr, "[ev] final: watched=%d socks_left=%zu\n", + watched, ctx.nsocks); + +cleanup: + curl_multi_cleanup(multi); + curl_global_cleanup(); +optcleanup: + curlx_free(cafile); + return result; +} diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index c292e7a1985c..4fe2dc645b50 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -46,6 +46,7 @@ TESTS_C = \ unit1961.c unit1979.c unit1980.c \ unit2413.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ + unit2606.c \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ unit3227.c \ diff --git a/tests/unit/unit2606.c b/tests/unit/unit2606.c new file mode 100644 index 000000000000..ebe8b275fd89 --- /dev/null +++ b/tests/unit/unit2606.c @@ -0,0 +1,221 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" + +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "select.h" + +/* Test the gating in Curl_conn_adjust_pollset(): each filter chain + * of a connection must only get to adjust the pollset when the transfer + * wants to send or receive, or when that chain itself is connecting or + * shutting down. One chain being in connect or shutdown must not add + * poll events for the other, see #21671. */ + +struct t2606_ctx { + int calls; + bool add_socket; + curl_socket_t sock; +}; + +static void t2606_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + /* ctx is owned by the test case, nothing to free */ + cf->ctx = NULL; +} + +static CURLcode t2606_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + (void)cf; + (void)data; + *done = TRUE; + return CURLE_OK; +} + +static CURLcode t2606_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct t2606_ctx *ctx = cf->ctx; + ctx->calls++; + if(ctx->add_socket) + return Curl_pollset_add_in(data, ps, ctx->sock); + return CURLE_OK; +} + +static const struct Curl_cftype t2606_cft = { + "TEST-PS", + CF_TYPE_IP_CONNECT, + CURL_LOG_LVL_NONE, + t2606_cf_destroy, + t2606_cf_connect, + Curl_cf_def_shutdown, + t2606_cf_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +struct t2606_case { + int id; + bool has_c1; /* a SECONDARYSOCKET chain exists */ + bool c0_connected; + bool c1_connected; + bool c0_shutdown; /* shutdown started on the chain */ + bool c1_shutdown; + bool xfer_io; /* the pollset holds a transfer socket already */ + bool c0_adds; /* chain 0 adds a socket when called */ + int exp_c0_calls; + int exp_c1_calls; +}; + +static CURLcode t2606_setup(CURL **easy) +{ + CURLcode result = CURLE_OK; + + global_init(CURL_GLOBAL_ALL); + *easy = curl_easy_init(); + if(!*easy) { + curl_global_cleanup(); + return CURLE_OUT_OF_MEMORY; + } + return result; +} + +static void t2606_stop(CURL *easy) +{ + curl_easy_cleanup(easy); + curl_global_cleanup(); +} + +static void t2606_run(struct Curl_easy *data, const struct t2606_case *tc) +{ + struct connectdata *conn; + struct Curl_cfilter *cf0 = NULL, *cf1 = NULL; + struct t2606_ctx ctx0, ctx1; + struct easy_pollset ps; + CURLcode result; + char msg[128]; + + conn = curlx_calloc(1, sizeof(*conn)); + abort_if(!conn, "could not allocate a connection"); + + memset(&ctx0, 0, sizeof(ctx0)); + memset(&ctx1, 0, sizeof(ctx1)); + ctx0.sock = 42; + ctx0.add_socket = tc->c0_adds; + ctx1.sock = 43; + + result = Curl_cf_create(&cf0, &t2606_cft, &ctx0); + fail_unless(!result, "creating filter chain 0 failed"); + if(!result) { + Curl_conn_cf_add(data, conn, FIRSTSOCKET, cf0); + cf0->connected = tc->c0_connected; + if(tc->c0_shutdown) + conn->shutdown.start[FIRSTSOCKET] = curlx_now(); + } + + if(tc->has_c1) { + result = Curl_cf_create(&cf1, &t2606_cft, &ctx1); + fail_unless(!result, "creating filter chain 1 failed"); + if(!result) { + Curl_conn_cf_add(data, conn, SECONDARYSOCKET, cf1); + cf1->connected = tc->c1_connected; + if(tc->c1_shutdown) + conn->shutdown.start[SECONDARYSOCKET] = curlx_now(); + } + } + + Curl_pollset_init(&ps); + if(tc->xfer_io) { + result = Curl_pollset_add_in(data, &ps, 7); + fail_unless(!result, "preloading the pollset failed"); + } + + result = Curl_conn_adjust_pollset(data, conn, &ps); + + curl_msprintf(msg, "case %d: adjust_pollset failed", tc->id); + fail_unless(!result, msg); + curl_msprintf(msg, "case %d: chain 0 called %d times, expected %d", + tc->id, ctx0.calls, tc->exp_c0_calls); + fail_unless(ctx0.calls == tc->exp_c0_calls, msg); + curl_msprintf(msg, "case %d: chain 1 called %d times, expected %d", + tc->id, ctx1.calls, tc->exp_c1_calls); + fail_unless(ctx1.calls == tc->exp_c1_calls, msg); + if(tc->c0_adds && tc->exp_c0_calls) { + curl_msprintf(msg, "case %d: chain 0 did not add its socket", tc->id); + fail_unless(ps.n > 0, msg); + } + + Curl_pollset_cleanup(&ps); + Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, conn, FIRSTSOCKET); +unit_test_abort: + curlx_free(conn); +} + +static CURLcode test_unit2606(const char *arg) +{ + CURL *easy; + + UNITTEST_BEGIN(t2606_setup(&easy)) + + static const struct t2606_case TEST_CASES[] = { + /* both chains connected and quiet, transfer idle: nothing runs */ + { 1, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 0, 0 }, + /* transfer wants IO: both chains run */ + { 2, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, 1, 1 }, + /* chain 0 connecting, chain 1 connected and quiet: only chain 0 */ + { 3, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, 1, 0 }, + /* chain 1 connecting, chain 0 connected and quiet: only chain 1 */ + { 4, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, 1 }, + /* chain 0 shutting down, chain 1 connected and quiet: only chain 0 */ + { 5, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, 1, 0 }, + /* chain 1 shutting down, chain 0 connected and quiet: only chain 1 */ + { 6, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, 0, 1 }, + /* single connected quiet chain, transfer idle: nothing runs */ + { 7, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, 0 }, + /* single chain shutting down: runs */ + { 8, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, 1, 0 }, + /* a socket added by connecting chain 0 must not run quiet chain 1 */ + { 9, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, 1, 0 }, + }; + + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(TEST_CASES); ++i) { + t2606_run(easy, &TEST_CASES[i]); + } + + UNITTEST_END(t2606_stop(easy)) +} From 1027d07704836c8d642abbed34ab98535de47433 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 18 Jul 2026 02:42:39 +0200 Subject: [PATCH 0680/1062] timeval: make `Curl_freq` variable static (Windows) Follow-up to 64d4f5c4b660be112a02be104e7314e8bf264caa #20852 Closes #22346 --- lib/curlx/timeval.c | 2 +- lib/system_win32.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/curlx/timeval.c b/lib/curlx/timeval.c index 2363e6083944..7e9f3f71e889 100644 --- a/lib/curlx/timeval.c +++ b/lib/curlx/timeval.c @@ -27,7 +27,7 @@ #include "system_win32.h" -LARGE_INTEGER Curl_freq; +static LARGE_INTEGER Curl_freq; /* For tool or tests, we must initialize before calling curlx_now(). Providing this function here is wrong. */ diff --git a/lib/system_win32.h b/lib/system_win32.h index 8a51f096702a..d504b563db4b 100644 --- a/lib/system_win32.h +++ b/lib/system_win32.h @@ -26,8 +26,6 @@ #include "curl_setup.h" #ifdef _WIN32 -extern LARGE_INTEGER Curl_freq; - CURLcode Curl_win32_init(long flags); void Curl_win32_cleanup(long init_flags); #else From 1128177a6f725d1f8c4cba4f7ff6532969109e7a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 18 Jul 2026 03:18:11 +0200 Subject: [PATCH 0681/1062] timeval: rename timer frequency variable It's no longer a global variable. Follow-up to 1027d07704836c8d642abbed34ab98535de47433 #22346 Closes #22347 --- lib/curlx/timeval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/curlx/timeval.c b/lib/curlx/timeval.c index 7e9f3f71e889..01b20b4da132 100644 --- a/lib/curlx/timeval.c +++ b/lib/curlx/timeval.c @@ -27,24 +27,24 @@ #include "system_win32.h" -static LARGE_INTEGER Curl_freq; +static LARGE_INTEGER s_time_freq; /* For tool or tests, we must initialize before calling curlx_now(). Providing this function here is wrong. */ void curlx_now_init(void) { - QueryPerformanceFrequency(&Curl_freq); + QueryPerformanceFrequency(&s_time_freq); } /* In case of bug fix this function has a counterpart in tool_util.c */ void curlx_pnow(struct curltime *pnow) { LARGE_INTEGER count; - DEBUGASSERT(Curl_freq.QuadPart); + DEBUGASSERT(s_time_freq.QuadPart); QueryPerformanceCounter(&count); - pnow->tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart); - pnow->tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 / - Curl_freq.QuadPart); + pnow->tv_sec = (time_t)(count.QuadPart / s_time_freq.QuadPart); + pnow->tv_usec = (int)((count.QuadPart % s_time_freq.QuadPart) * 1000000 / + s_time_freq.QuadPart); } #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ From 703a999602bcddfc297a75d9991a82c5e17182e9 Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Sun, 28 Jun 2026 17:43:01 -0300 Subject: [PATCH 0682/1062] tests: fix type promotion on 32-bit arches in http test code As reported in the bug: > On a 32-bit architecture (i386): > 1. `chunks * chunk_size` evaluates to `unsigned int` (32-bit > unsigned). > 2. Due to Usual Arithmetic Conversions in C, the signed `-1` (int) > operand of the ternary operator is promoted to `unsigned int`, > yielding `4294967295` (0xFFFFFFFF). > 3. The ternary operator returns `4294967295` as an `unsigned int`. > 4. This unsigned value is then assigned to `r->clength` (apr_off_t, > 64-bit signed). Since the source is unsigned, it is zero-extended, > resulting in `r->clength` becoming `+4294967295`. > 5. The subsequently executed check `if(r->clength >= 0)` evaluates to > true. > 6. Inside the block, `apr_ltoa(r->pool, (long)r->clength)` casts it to > a 32-bit signed `long` (on 32-bit platforms), which truncates it > back to `-1`, formatting it as "-1" and sending the > "Content-Length: -1" header. > > On a 64-bit architecture (amd64), `size_t` is 64-bit, and `-1` (32-bit > int) is promoted to `unsigned long` (64-bit), yielding > `18446744073709551615`. When assigned to `r->clength` (64-bit signed), > it wraps back to `-1`, which correctly skips the Content-Length > generation. > > This type promotion mismatch can be safely fixed by avoiding the > signed/unsigned mixture in the ternary operator. Origin: debian, https://bugs.debian.org/1140793 Bug-Debian: https://bugs.debian.org/1140793 These are standard Debian patches' trailers, I can get rid of then if you prefer. I've tested the fix using [debusine on i386 runner](https://debusine.debian.net/debian/developers/artifact/4083252/raw/log): > 782s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[http/1.0] PASSED [ 17%] > 782s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[http/1.1] PASSED [ 18%] > 782s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[h2] PASSED [ 18%] Versus the [i386 run in Debian's infrastructure](https://debusine.debian.net/debian/developers/artifact/4084220/raw/log): > 2690s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[http/1.0] FAILED [ 17%] > 2690s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[http/1.1] PASSED [ 18%] > 2690s tests/http/test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[h2] PASSED [ 18%] Closes #22210 --- tests/http/testenv/mod_curltest/mod_curltest.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 25f32c79046b..3eec35381d5f 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -404,7 +404,10 @@ static int curltest_tweak_handler(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing " "request, %s", r->args? r->args : "(no args)"); r->status = http_status; - r->clength = with_cl ? (chunks * chunk_size) : -1; + if(with_cl) + r->clength = (apr_off_t)chunks * chunk_size; + else + r->clength = -1; r->chunked = (r->proto_num >= HTTP_VERSION(1, 1)) && !with_cl; apr_table_setn(r->headers_out, "request-id", request_id); if(r->clength >= 0) { From bd58857201452aba99d78742990296f1a6b72c6a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 20 Jul 2026 17:34:20 +0200 Subject: [PATCH 0683/1062] vtls_scache: allocate absolute filename on heap (Windows) To allow any full path lengths, beyond the 260 characters allowed by `_MAX_PATH`. Follow-up to fa0ccd9f1fbbbd77bf50b26e3ba231ea6c729474 #15774 Closes #22357 --- lib/vtls/vtls_scache.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index c7e1cdb46c4d..7148bf3297e7 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -76,9 +76,13 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, * when used in another process with different CWD. When a path does not * exist, this does not work. Then, we add the path as is. */ #ifdef _WIN32 - char abspath[_MAX_PATH]; - if(_fullpath(abspath, path, _MAX_PATH)) - return curlx_dyn_addf(buf, ":%s-%s", name, abspath); + char *abspath = _fullpath(NULL, path, 0); + if(abspath) { + CURLcode result = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + /* !checksrc! disable BANNEDFUNC 1 */ + free(abspath); /* allocated by CRT, use system free() */ + return result; + } *is_local = TRUE; #elif defined(HAVE_REALPATH) if(path[0] != '/') { From 009fd378e8f01c97ebe67a14a41a06d56430f3df Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 8 Jul 2026 10:34:51 +0200 Subject: [PATCH 0684/1062] lib: add multi_wakeup_internal For threaded resolving, added an additional socket/eventfd pair to the multi handle for notifications from threads. The original "double use" of the standard wakeup pair did lead to regressions for apps. The API definition of curl_multi_poll/wait/wakeup is pretty tight regarding what effects what and adding notifications on top of that broke what apps perceived to be the contract. Fixes #22272 Reported-by: Sergei Zimmerman Closes #22274 --- lib/asyn-thrdd.c | 8 +++--- lib/multi.c | 36 ++++++++++++++++++++++---- lib/multihandle.h | 13 +++++++++- lib/multiif.h | 4 +++ tests/data/Makefile.am | 2 +- tests/data/test2414 | 30 +++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib2414.c | 53 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 tests/data/test2414 create mode 100644 tests/libtest/lib2414.c diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 1ee69f83bf03..29e9b138d3f4 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -437,7 +437,7 @@ static void async_thrdd_item_process(void *arg) #endif /* HAVE_GETADDRINFO */ -#ifdef ENABLE_WAKEUP +#ifdef ENABLE_INTERNAL_WAKEUP static void async_thrdd_event(const struct curl_thrdq *tqueue, Curl_thrdq_event ev, void *user_data) @@ -446,7 +446,7 @@ static void async_thrdd_event(const struct curl_thrdq *tqueue, (void)tqueue; switch(ev) { case CURL_THRDQ_EV_ITEM_DONE: - (void)curl_multi_wakeup(multi); + Curl_multi_wakeup_internal(multi); break; default: break; @@ -693,7 +693,7 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, #endif if(!async->done) { -#ifndef ENABLE_WAKEUP +#ifndef ENABLE_INTERNAL_WAKEUP timediff_t stutter_ms, elapsed_ms; elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); if(elapsed_ms < 3) @@ -736,7 +736,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(thrdd->rr.channel) (void)Curl_ares_perform(thrdd->rr.channel, 0); #endif -#ifndef ENABLE_WAKEUP +#ifndef ENABLE_INTERNAL_WAKEUP Curl_async_thrdd_multi_process(data->multi); #endif diff --git a/lib/multi.c b/lib/multi.c index 7d0bc0a125c6..265a2472276f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -268,6 +268,10 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, multi->wakeup_pair[0] = CURL_SOCKET_BAD; multi->wakeup_pair[1] = CURL_SOCKET_BAD; #endif +#ifdef ENABLE_INTERNAL_WAKEUP + multi->wakeup_internal[0] = CURL_SOCKET_BAD; + multi->wakeup_internal[1] = CURL_SOCKET_BAD; +#endif if(Curl_mntfy_resize(multi) || Curl_uint32_bset_resize(&multi->process, xfer_table_size) || @@ -315,6 +319,10 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) goto error; #endif +#ifdef ENABLE_INTERNAL_WAKEUP + if(Curl_wakeup_init(multi->wakeup_internal, TRUE) < 0) + goto error; +#endif if(Curl_probeipv6(multi)) goto error; @@ -360,6 +368,9 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, #ifdef ENABLE_WAKEUP Curl_wakeup_destroy(multi->wakeup_pair); #endif +#ifdef ENABLE_INTERNAL_WAKEUP + Curl_wakeup_destroy(multi->wakeup_internal); +#endif curlx_free(multi); return NULL; @@ -395,7 +406,7 @@ bool Curl_is_connecting(struct Curl_easy *data) static CURLMcode multi_assess_wakeup(struct Curl_multi *multi) { -#ifdef ENABLE_WAKEUP +#ifdef ENABLE_INTERNAL_WAKEUP if(multi->socket_cb) return Curl_multi_ev_assess_xfer(multi, multi->admin); #else @@ -1148,14 +1159,14 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, CURLcode result = CURLE_OK; Curl_pollset_reset(ps); -#ifdef ENABLE_WAKEUP +#ifdef ENABLE_INTERNAL_WAKEUP /* The admin handle always listens on the wakeup socket when there * are transfers alive. */ if(data->multi && (data == data->multi->admin) && data->multi->xfers_really_alive) { CURL_TRC_M(data, "adding wakeup, %u xfers really alive", data->multi->xfers_really_alive); - result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); + result = Curl_pollset_add_in(data, ps, data->multi->wakeup_internal[0]); } #endif /* If the transfer has no connection, this is fine. Happens when @@ -1713,6 +1724,18 @@ CURLMcode curl_multi_wakeup(CURLM *m) return mresult; } +#ifdef ENABLE_INTERNAL_WAKEUP +void Curl_multi_wakeup_internal(struct Curl_multi *multi) +{ + /* This is expected to be invocable from another thread which + * does NOT outlive the multi handle. Check for sanity. */ + if(GOOD_MULTI_HANDLE(multi)) + Curl_wakeup_signal(multi->wakeup_internal); + else + DEBUGASSERT(0); +} +#endif + /* * multi_ischanged() is called * @@ -2739,10 +2762,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { -#ifdef ENABLE_WAKEUP +#ifdef ENABLE_INTERNAL_WAKEUP /* Consume any pending wakeup signals before processing. * This is necessary for event based processing. See #21547 */ - (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); + (void)Curl_wakeup_consume(multi->wakeup_internal, TRUE); #endif #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); @@ -3050,6 +3073,9 @@ CURLMcode curl_multi_cleanup(CURLM *m) #ifdef ENABLE_WAKEUP Curl_wakeup_destroy(multi->wakeup_pair); #endif +#ifdef ENABLE_INTERNAL_WAKEUP + Curl_wakeup_destroy(multi->wakeup_internal); +#endif multi_xfer_bufs_free(multi); Curl_mntfy_cleanup(multi); diff --git a/lib/multihandle.h b/lib/multihandle.h index 5cf2c640e547..1af07fa02ba4 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -77,6 +77,11 @@ typedef enum { #if !defined(CURL_DISABLE_SOCKETPAIR) && !defined(USE_WINSOCK) #define ENABLE_WAKEUP #endif +#if !defined(CURL_DISABLE_SOCKETPAIR) && \ + defined(USE_RESOLV_THREADED) && \ + !defined(USE_WINSOCK) +#define ENABLE_INTERNAL_WAKEUP +#endif /* value for MAXIMUM CONCURRENT STREAMS upper limit */ #define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) @@ -172,7 +177,13 @@ struct Curl_multi { #ifdef ENABLE_WAKEUP curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for wakeup 0 is used for read, 1 is used - for write */ + for write. Used by curl_multi_wakeup() */ +#endif +#ifdef ENABLE_INTERNAL_WAKEUP + curl_socket_t wakeup_internal[2]; /* eventfd()/pipe()/socketpair() used for + wakeup 0 is used for read, 1 is used + for write. Used for internal wakeups, + e.g. threaded resolver. */ #endif unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of diff --git a/lib/multiif.h b/lib/multiif.h index 7fad9ca33e4f..023d3253aac1 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -164,4 +164,8 @@ void Curl_multi_clear_dirty(struct Curl_easy *data); void Curl_multi_set_now(struct Curl_multi *multi); +#ifdef ENABLE_INTERNAL_WAKEUP +void Curl_multi_wakeup_internal(struct Curl_multi *multi); +#endif + #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index e88472728b23..32ad4b1e522c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -263,7 +263,7 @@ test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 test2308 test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ -test2408 test2409 test2410 test2411 test2412 test2413 \ +test2408 test2409 test2410 test2411 test2412 test2413 test2414 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ diff --git a/tests/data/test2414 b/tests/data/test2414 new file mode 100644 index 000000000000..197beffe8f9c --- /dev/null +++ b/tests/data/test2414 @@ -0,0 +1,30 @@ + + + + +multi +wakeup + + + +# Client-side + + +wakeup + + + + +lib%TESTNUMBER + + +wakeup, perform and poll needs to exit + + + + + +# Verify data after the test has been "shot" + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 07d78b51925f..7b959e0df498 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -119,7 +119,7 @@ TESTS_C = \ lib2023.c lib2032.c lib2082.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ lib2402.c lib2404.c lib2405.c \ - lib2412.c \ + lib2412.c lib2414.c \ lib2502.c lib2504.c lib2505.c lib2506.c \ lib2700.c \ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ diff --git a/tests/libtest/lib2414.c b/tests/libtest/lib2414.c new file mode 100644 index 000000000000..e04dff3de7b5 --- /dev/null +++ b/tests/libtest/lib2414.c @@ -0,0 +1,53 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Dmitry Karpov + * + * 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 "first.h" +#include "testtrace.h" + +static CURLcode test_lib2414(const char *URL) +{ + CURLM *multi = NULL; + CURLcode result = CURLE_OK; + int running; + + (void)URL; + global_init(CURL_GLOBAL_ALL); + + multi = curl_multi_init(); + if(!multi) { + curl_mfprintf(stderr, "curl_multi_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + curl_multi_wakeup(multi); + curl_multi_perform(multi, &running); + curl_multi_poll(multi, NULL, 0, INT_MAX, NULL); + +test_cleanup: + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + return result; +} From f65253056a1b3d0bc6b27f450a6291ed253f6491 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 20 Jul 2026 23:44:38 +0200 Subject: [PATCH 0685/1062] cf-quiche.c: minor tidy-ups Pointed out by the GitHub AI thing Closes #22360 --- lib/vquic/cf-quiche.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index b38ab7d3e957..e0016d37d28d 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -254,7 +254,6 @@ static bool cf_quiche_do_expire(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, void *user_data) { - (void)stream; (void)user_data; CURL_TRC_CF(sdata, cf, "conn closed, mark as dirty"); stream->xfer_result = CURLE_SEND_ERROR; @@ -580,7 +579,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, } } -struct cf_quich_disp_ctx { +struct cf_quiche_disp_ctx { uint64_t stream_id; struct Curl_cfilter *cf; struct Curl_multi *multi; @@ -589,7 +588,7 @@ struct cf_quich_disp_ctx { static bool cf_quiche_disp_event(uint32_t mid, void *val, void *user_data) { - struct cf_quich_disp_ctx *dctx = user_data; + struct cf_quiche_disp_ctx *dctx = user_data; struct h3_stream_ctx *stream = val; if(stream->id == dctx->stream_id) { @@ -630,7 +629,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, else { /* another transfer, do not return errors, as they are not for * the calling transfer */ - struct cf_quich_disp_ctx dctx; + struct cf_quiche_disp_ctx dctx; dctx.stream_id = (uint64_t)rv; dctx.cf = cf; dctx.multi = data->multi; @@ -1141,7 +1140,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * sending the 30x response. * This is sort of a race: had the transfer loop called recv first, * it would see the response and stop/discard sending on its own- */ - CURL_TRC_CF(data, cf, "[%" PRIu64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" PRIu64 "] discarding data " "on closed stream with response", stream->id); result = CURLE_OK; *pnwritten = len; @@ -1502,8 +1501,8 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, } if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* sent everything, quiche does not seem to support a graceful - * shutdown waiting for a reply, so ware done. */ + /* sent everything, quiche does not seem to support a graceful shutdown + * waiting for a reply, so we are done. */ CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); *done = TRUE; } From b41c28e70af47e1a49606db9c28f622f2a0052d0 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 16 Jul 2026 11:26:33 +0200 Subject: [PATCH 0686/1062] lib: client writer tweaks - keep pause state at request - keep presense of content decoding writers at request - only add cw-pause writer when content decoding happens - define macro for passing data down the writer chain scorecard: add option to test with h2o server Closes #22335 --- lib/cw-out.c | 45 ++------- lib/cw-out.h | 10 -- lib/cw-pause.c | 6 +- lib/request.h | 10 +- lib/sendf.c | 175 ++++++++++++++------------------- lib/sendf.h | 24 ++--- tests/http/scorecard.py | 40 ++++++-- tests/http/testenv/__init__.py | 1 + 8 files changed, 138 insertions(+), 173 deletions(-) diff --git a/lib/cw-out.c b/lib/cw-out.c index b38fde455fd4..35ded4b475d5 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -97,7 +97,6 @@ static void cw_out_buf_free(struct cw_out_buf *cwbuf) struct cw_out_ctx { struct Curl_cwriter super; struct cw_out_buf *buf; - BIT(paused); BIT(errored); }; @@ -171,8 +170,7 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype, } } -static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, - struct Curl_easy *data, +static CURLcode cw_out_cb_write(struct Curl_easy *data, curl_write_callback wcb, void *wcb_data, cw_out_type otype, @@ -200,7 +198,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } - ctx->paused = TRUE; + data->req.writer.paused = TRUE; CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client"); result = Curl_xfer_pause_recv(data, TRUE); return result ? result : CURLE_AGAIN; @@ -245,18 +243,18 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, *pconsumed = 0; if(otype == CW_OUT_BODY_0LEN) { DEBUGASSERT(!blen); - return cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + return cw_out_cb_write(data, wcb, wcb_data, otype, buf, blen, &nwritten); } else { - while(blen && !ctx->paused) { + while(blen && !data->req.writer.paused) { if(!flush_all && blen < min_write) break; wlen = max_write ? CURLMIN(blen, max_write) : blen; if(otype == CW_OUT_BODY) result = Curl_pgrs_deliver_check(data, wlen); if(!result) - result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + result = cw_out_cb_write(data, wcb, wcb_data, otype, buf, wlen, &nwritten); if(result) return result; @@ -314,7 +312,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, if(!cwbuf) return CURLE_OK; - if(ctx->paused) + if(data->req.writer.paused) return CURLE_OK; /* write the end of the chain until it blocks or gets empty */ @@ -327,7 +325,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, return result; if(*plast) { /* could not write last, paused again? */ - DEBUGASSERT(ctx->paused); + DEBUGASSERT(data->req.writer.paused); return CURLE_OK; } } @@ -457,7 +455,7 @@ static CURLcode cw_out_do_flush(struct Curl_easy *data, if(ctx->errored) return CURLE_WRITE_ERROR; - if(ctx->paused) + if(data->req.writer.paused) return CURLE_OK; /* not doing it */ result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); @@ -486,33 +484,6 @@ const struct Curl_cwtype Curl_cwt_out = { sizeof(struct cw_out_ctx) }; -bool Curl_cw_out_is_paused(struct Curl_easy *data) -{ - struct Curl_cwriter *cw_out; - struct cw_out_ctx *ctx; - - cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); - if(!cw_out) - return FALSE; - - ctx = (struct cw_out_ctx *)cw_out; - return (bool)ctx->paused; -} - -CURLcode Curl_cw_out_unpause(struct Curl_easy *data) -{ - struct Curl_cwriter *cw_out; - CURLcode result = CURLE_OK; - - cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); - if(cw_out) { - struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; - CURL_TRC_WRITE(data, "[OUT] unpause"); - ctx->paused = FALSE; - } - return result; -} - CURLcode Curl_cw_out_done(struct Curl_easy *data) { struct Curl_cwriter *cw_out; diff --git a/lib/cw-out.h b/lib/cw-out.h index 7de6524bc580..ee2b4b6307bc 100644 --- a/lib/cw-out.h +++ b/lib/cw-out.h @@ -34,16 +34,6 @@ struct Curl_easy; */ extern const struct Curl_cwtype Curl_cwt_out; -/** - * Return TRUE iff 'cw-out' client write has paused data. - */ -bool Curl_cw_out_is_paused(struct Curl_easy *data); - -/** - * Flush any buffered date to the client, chunk collation still applies. - */ -CURLcode Curl_cw_out_unpause(struct Curl_easy *data); - /** * Mark EndOfStream reached and flush ALL data to the client. */ diff --git a/lib/cw-pause.c b/lib/cw-pause.c index a42f61f4baf9..ccdc7657b187 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -100,7 +100,6 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, struct Curl_cwriter *cw_pause) { struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)cw_pause; - bool decoding = Curl_cwriter_is_content_decoding(data); CURLcode result = CURLE_OK; /* write the end of the chain until it blocks or gets empty */ @@ -112,7 +111,7 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, while((*plast)->next) /* got to last in list */ plast = &(*plast)->next; if(Curl_bufq_peek(&(*plast)->b, &buf, &blen)) { - wlen = (decoding && ((*plast)->type & CLIENTWRITE_BODY)) ? + wlen = ((*plast)->type & CLIENTWRITE_BODY) ? CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen; result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, wlen); @@ -151,7 +150,6 @@ static CURLcode cw_pause_write(struct Curl_easy *data, struct cw_pause_ctx *ctx = writer->ctx; CURLcode result = CURLE_OK; size_t wlen = 0; - bool decoding = Curl_cwriter_is_content_decoding(data); if(ctx->buf && !Curl_cwriter_is_paused(data)) { result = cw_pause_flush(data, writer); @@ -164,7 +162,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, DEBUGASSERT(!ctx->buf); /* content decoding might blow up size considerably, write smaller * chunks to make pausing need buffer less. */ - wlen = (decoding && (type & CLIENTWRITE_BODY)) ? + wlen = (type & CLIENTWRITE_BODY) ? CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen; if(wlen < blen) wtype &= ~CLIENTWRITE_EOS; diff --git a/lib/request.h b/lib/request.h index e67865a98433..6662cf19e7e0 100644 --- a/lib/request.h +++ b/lib/request.h @@ -101,10 +101,16 @@ struct SingleRequest { /* Client Writer stack, handles transfer- and content-encodings, protocol * checks, pausing by client callbacks. */ - struct Curl_cwriter *writer_stack; + struct { + struct Curl_cwriter *stack; + BIT(paused); + } writer; /* Client Reader stack, handles transfer- and content-encodings, protocol * checks, pausing by client callbacks. */ - struct Curl_creader *reader_stack; + struct { + struct Curl_creader *stack; + BIT(paused); + } reader; struct bufq sendbuf; /* data which needs to be send to the server */ size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ time_t timeofdoc; diff --git a/lib/sendf.c b/lib/sendf.c index efaba08eb4ca..8f51a772a228 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -46,24 +46,24 @@ static void cl_reset_writer(struct Curl_easy *data) { - struct Curl_cwriter *writer = data->req.writer_stack; + struct Curl_cwriter *writer = data->req.writer.stack; while(writer) { - data->req.writer_stack = writer->next; + data->req.writer.stack = writer->next; writer->cwt->do_close(data, writer); curlx_free(writer); - writer = data->req.writer_stack; + writer = data->req.writer.stack; } } static void cl_reset_reader(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; data->req.reader_started = FALSE; while(reader) { - data->req.reader_stack = reader->next; + data->req.reader.stack = reader->next; reader->crt->do_close(data, reader); curlx_free(reader); - reader = data->req.reader_stack; + reader = data->req.reader.stack; } } @@ -95,7 +95,7 @@ void Curl_client_reset(struct Curl_easy *data) CURLcode Curl_client_start(struct Curl_easy *data) { if(data->req.rewind_read) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; CURLcode result = CURLE_OK; CURL_TRC_READ(data, "client start, rewind readers"); @@ -124,25 +124,6 @@ void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) data->req.rewind_read = !!enable; } -/* Write data using an unencoding writer stack. */ -CURLcode Curl_cwriter_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) -{ - if(!writer) - return CURLE_WRITE_ERROR; - return writer->cwt->do_write(data, writer, type, buf, nbytes); -} - -CURLcode Curl_cwriter_flush(struct Curl_easy *data, - struct Curl_cwriter *writer) -{ - if(!writer) { - return CURLE_WRITE_ERROR; - } - return writer->cwt->do_flush(data, writer); -} - CURLcode Curl_cwriter_def_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -340,47 +321,39 @@ static const struct Curl_cwtype cw_raw = { sizeof(struct Curl_cwriter) }; +static void cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct Curl_cwriter **anchor = &data->req.writer.stack; + + /* Insert the writer as first in its phase. + * Skip existing writers of lower phases. */ + while(*anchor && (*anchor)->phase < writer->phase) + anchor = &((*anchor)->next); + writer->next = *anchor; + *anchor = writer; +} + static CURLcode do_init_writer_stack(struct Curl_easy *data) { struct Curl_cwriter *writer; CURLcode result; - DEBUGASSERT(!data->req.writer_stack); - result = Curl_cwriter_create(&data->req.writer_stack, + DEBUGASSERT(!data->req.writer.stack); + result = Curl_cwriter_create(&data->req.writer.stack, data, &Curl_cwt_out, CURL_CW_CLIENT); if(result) return result; - /* This places the "pause" writer behind the "download" writer that - * is added below. Meaning the "download" can do checks on content length - * and other things *before* write outs are buffered for paused transfers. */ - result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, - CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } if(result) return result; + cwriter_add(data, writer); result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } if(result) return result; + cwriter_add(data, writer); return result; } @@ -405,14 +378,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, DEBUGASSERT(!(type & CLIENTWRITE_INFO) || ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); - if(!data->req.writer_stack) { + if(!data->req.writer.stack) { result = do_init_writer_stack(data); if(result) return result; - DEBUGASSERT(data->req.writer_stack); + DEBUGASSERT(data->req.writer.stack); } - result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); + result = Curl_cwriter_write(data, data->req.writer.stack, type, buf, len); CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", (unsigned int)type, len, (int)result); return result; @@ -422,14 +395,14 @@ CURLcode Curl_client_flush(struct Curl_easy *data) { CURLcode result; - if(!data->req.writer_stack) { + if(!data->req.writer.stack) { result = do_init_writer_stack(data); if(result) return result; - DEBUGASSERT(data->req.writer_stack); + DEBUGASSERT(data->req.writer.stack); } - result = Curl_cwriter_flush(data, data->req.writer_stack); + result = Curl_cwriter_flush(data, data->req.writer.stack); CURL_TRC_WRITE(data, "client_flush() -> %d", (int)result); return result; } @@ -476,18 +449,33 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) struct Curl_cwriter *w; size_t n = 0; - for(w = data->req.writer_stack; w; w = w->next) { + for(w = data->req.writer.stack; w; w = w->next) { if(w->phase == phase) ++n; } return n; } +static CURLcode cwriter_ensure_pause_writer(struct Curl_easy *data) +{ + struct Curl_cwriter *writer = + Curl_cwriter_get_by_type(data, &Curl_cwt_pause); + CURLcode result = CURLE_OK; + + if(!writer) { + result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, + CURL_CW_BEFORE_DECODE); + if(!result) + cwriter_add(data, writer); + } + return result; +} + CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer) { CURLcode result; - struct Curl_cwriter **anchor = &data->req.writer_stack; + struct Curl_cwriter **anchor = &data->req.writer.stack; if(!*anchor) { result = do_init_writer_stack(data); @@ -495,12 +483,16 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data, return result; } - /* Insert the writer as first in its phase. - * Skip existing writers of lower phases. */ - while(*anchor && (*anchor)->phase < writer->phase) - anchor = &((*anchor)->next); - writer->next = *anchor; - *anchor = writer; + if(writer->phase == CURL_CW_CONTENT_DECODE) { + /* On adding a content decoder, add the pause writer. Do this + * BEFORE the given writer as any failure will make the + * caller destroy the writer again. */ + result = cwriter_ensure_pause_writer(data); + if(result) + return result; + } + + cwriter_add(data, writer); return CURLE_OK; } @@ -508,7 +500,7 @@ struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, const char *name) { struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { + for(writer = data->req.writer.stack; writer; writer = writer->next) { if(!strcmp(name, writer->cwt->name)) return writer; } @@ -519,34 +511,17 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, const struct Curl_cwtype *cwt) { struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { + for(writer = data->req.writer.stack; writer; writer = writer->next) { if(writer->cwt == cwt) return writer; } return NULL; } -bool Curl_cwriter_is_content_decoding(struct Curl_easy *data) -{ - struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { - if(writer->phase == CURL_CW_CONTENT_DECODE) - return TRUE; - } - return FALSE; -} - -bool Curl_cwriter_is_paused(struct Curl_easy *data) -{ - return Curl_cw_out_is_paused(data); -} - CURLcode Curl_cwriter_unpause(struct Curl_easy *data) { - CURLcode result = Curl_cw_out_unpause(data); - if(!result) - result = Curl_cwriter_flush(data, data->req.writer_stack); - return result; + data->req.writer.paused = FALSE; + return Curl_cwriter_flush(data, data->req.writer.stack); } CURLcode Curl_creader_read(struct Curl_easy *data, @@ -1141,9 +1116,9 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data, DEBUGASSERT(r); DEBUGASSERT(r->crt); DEBUGASSERT(r->phase == CURL_CR_CLIENT); - DEBUGASSERT(!data->req.reader_stack); + DEBUGASSERT(!data->req.reader.stack); - data->req.reader_stack = r; + data->req.reader.stack = r; clen = r->crt->total_length(data, r); /* if we do not have 0 length init, and crlf conversion is wanted, * add the reader for it */ @@ -1184,7 +1159,7 @@ CURLcode Curl_creader_add(struct Curl_easy *data, struct Curl_creader *reader) { CURLcode result; - struct Curl_creader **anchor = &data->req.reader_stack; + struct Curl_creader **anchor = &data->req.reader.stack; if(!*anchor) { result = Curl_creader_set_fread(data, data->state.infilesize); @@ -1227,11 +1202,11 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, DEBUGASSERT(eos); *nread = 0; - if(!data->req.reader_stack) { + if(!data->req.reader.stack) { result = Curl_creader_set_fread(data, data->state.infilesize); if(result) return result; - DEBUGASSERT(data->req.reader_stack); + DEBUGASSERT(data->req.reader.stack); } if(!data->req.reader_started) { Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1); @@ -1248,7 +1223,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, if(ul_avail < (curl_off_t)blen) blen = (size_t)ul_avail; } - result = Curl_creader_read(data, data->req.reader_stack, buf, blen, + result = Curl_creader_read(data, data->req.reader.stack, buf, blen, nread, eos); out: @@ -1259,7 +1234,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, bool Curl_creader_needs_rewind(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { if(reader->crt->needs_rewind(data, reader)) { CURL_TRC_READ(data, "client reader needs rewind before next request"); @@ -1445,13 +1420,13 @@ CURLcode Curl_creader_set_buf(struct Curl_easy *data, curl_off_t Curl_creader_total_length(struct Curl_easy *data) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; return r ? r->crt->total_length(data, r) : -1; } curl_off_t Curl_creader_client_length(struct Curl_easy *data) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; while(r && r->phase != CURL_CR_CLIENT) r = r->next; return r ? r->crt->total_length(data, r) : -1; @@ -1459,7 +1434,7 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data) CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; while(r && r->phase != CURL_CR_CLIENT) r = r->next; return r ? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR; @@ -1467,7 +1442,7 @@ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) CURLcode Curl_creader_unpause(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; CURLcode result = CURLE_OK; while(reader) { @@ -1482,7 +1457,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) bool Curl_creader_is_paused(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { if(reader->crt->is_paused(data, reader)) @@ -1494,7 +1469,7 @@ bool Curl_creader_is_paused(struct Curl_easy *data) void Curl_creader_done(struct Curl_easy *data, int premature) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { reader->crt->done(data, reader, premature); reader = reader->next; @@ -1505,7 +1480,7 @@ struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, const struct Curl_crtype *crt) { struct Curl_creader *r; - for(r = data->req.reader_stack; r; r = r->next) { + for(r = data->req.reader.stack; r; r = r->next) { if(r->crt == crt) return r; } diff --git a/lib/sendf.h b/lib/sendf.h index 0cf6245ff673..7f31b246622b 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -24,6 +24,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "cw-out.h" /** * Type of data that is being written to the client (application) @@ -104,6 +105,7 @@ typedef enum { CURL_CW_RAW, /* raw data written, before any decoding */ CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ CURL_CW_PROTOCOL, /* after transfer, but before content decoding */ + CURL_CW_BEFORE_DECODE, /* after protocol, but before content decoding */ CURL_CW_CONTENT_DECODE, /* remove content-encodings */ CURL_CW_CLIENT /* data written to client */ } Curl_cwriter_phase; @@ -176,22 +178,16 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, const char *name); -/** - * Convenience method for calling `writer->do_write()` that - * checks for NULL writer. - */ -CURLcode Curl_cwriter_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -CURLcode Curl_cwriter_flush(struct Curl_easy *data, - struct Curl_cwriter *writer); +/* Convenience method for calling `writer->do_write()` that + * checks for NULL writer. */ +#define Curl_cwriter_write(d, w, t, b, n) \ + ((w) ? (w)->cwt->do_write((d), (w), (t), (b), (n)) : CURLE_WRITE_ERROR) -/** - * Return TRUE iff client writer is paused. - */ -bool Curl_cwriter_is_paused(struct Curl_easy *data); +#define Curl_cwriter_flush(d, w) \ + ((w) ? (w)->cwt->do_flush((d), (w)) : CURLE_WRITE_ERROR) -bool Curl_cwriter_is_content_decoding(struct Curl_easy *data); +/* TRUE if client writer is paused. */ +#define Curl_cwriter_is_paused(d) ((bool)(d)->req.writer.paused) /** * Unpause client writer and flush any buffered date to the client. diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 2a45450aa40d..60b1a35f4c2d 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -40,6 +40,7 @@ Dante, Env, ExecResult, + H2oServer, Httpd, NghttpxQuic, RunProfile, @@ -776,17 +777,19 @@ def run_score(args, protocol): uploads = None requests = args.requests - test_httpd = protocol != 'h3' - test_caddy = protocol == 'h3' - if args.caddy or args.httpd: - test_caddy = args.caddy - test_httpd = args.httpd - rv = 0 env = Env() env.setup() env.test_timeout = None + test_httpd = protocol != 'h3' + test_h2o = protocol == 'h3' and env.have_h2o() + test_caddy = protocol == 'h3' and not test_h2o + if args.caddy or args.httpd or args.h2o: + test_caddy = args.caddy + test_httpd = args.httpd + test_h2o = args.h2o + sockd = None socks_args = None if args.socks4 and args.socks5: @@ -803,6 +806,7 @@ def run_score(args, protocol): httpd = None nghttpx = None caddy = None + h2o = None try: cards = [] @@ -859,6 +863,26 @@ def run_score(args, protocol): card.setup_resources(server_docs, downloads) cards.append(card) + if test_h2o: + h2o = H2oServer(env=env) + h2o.clear_logs() + assert h2o.initial_start() + server_descr = f'H2o/{env.h2o_version()}' + server_port = h2o.port + server_docs = h2o.docs_dir + card = ScoreRunner(env=env, + protocol=protocol, + server_descr=server_descr, + server_port=server_port, + verbose=args.verbose, curl_verbose=args.curl_verbose, + download_parallel=args.download_parallel, + upload_parallel=args.upload_parallel, + with_flame=args.flame, + socks_args=socks_args, + limit_rate=args.limit_rate) + card.setup_resources(server_docs, downloads) + cards.append(card) + if test_caddy and env.caddy: backend = '' if uploads and httpd is None: @@ -921,6 +945,8 @@ def run_score(args, protocol): caddy.stop() if nghttpx: nghttpx.stop(wait_dead=False) + if h2o: + h2o.stop() if httpd: httpd.stop() if sockd: @@ -951,6 +977,8 @@ def main(): default=1, help="how many sample runs to make") parser.add_argument("--httpd", action='store_true', default=False, help="evaluate httpd server only") + parser.add_argument("--h2o", action='store_true', default=False, + help="evaluate h2o server only") parser.add_argument("--caddy", action='store_true', default=False, help="evaluate caddy server only") parser.add_argument("--curl-verbose", action='store_true', diff --git a/tests/http/testenv/__init__.py b/tests/http/testenv/__init__.py index 08ab2e231162..d2eea486faa7 100644 --- a/tests/http/testenv/__init__.py +++ b/tests/http/testenv/__init__.py @@ -38,6 +38,7 @@ from .dante import Dante from .dnsd import Dnsd from .env import Env +from .h2o import H2oServer, H2oProxy from .httpd import Httpd from .nghttpx import Nghttpx, NghttpxFwd, NghttpxQuic from .sshd import Sshd From f0f84d1251f84ee1d421c2faf419a64a76d28f6c Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Thu, 16 Jul 2026 16:30:19 +0100 Subject: [PATCH 0687/1062] http: avoid length underflow in Curl_compareheader Closes #22338 --- lib/http.c | 4 ++++ tests/data/test1625 | 2 +- tests/unit/unit1625.c | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/http.c b/lib/http.c index 9af3a7051470..6fdaa2e434d6 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1440,6 +1440,10 @@ bool Curl_compareheader(const char *headerline, /* line to check */ do curlx_str_passblanks(&p); while(!curlx_str_single(&p, ',')); + /* trailing blanks may move the parsing point past the value end, + then there is nothing left to match */ + if((size_t)(p - o) > len) + break; len -= (p - o); } } diff --git a/tests/data/test1625 b/tests/data/test1625 index 58d661c3850a..c717933b3086 100644 --- a/tests/data/test1625 +++ b/tests/data/test1625 @@ -19,7 +19,7 @@ Curl_compareheader unit test -31 invokes +33 invokes diff --git a/tests/unit/unit1625.c b/tests/unit/unit1625.c index 370b65a4fa73..491b553954a7 100644 --- a/tests/unit/unit1625.c +++ b/tests/unit/unit1625.c @@ -102,6 +102,11 @@ static CURLcode test_unit1625(const char *arg) { "Encoding: super-nice", "Encoding:", "super-nice", TRUE }, /* hyphenated second token */ { "Encoding: extra-good, super-nice", "Encoding:", "super-nice", TRUE }, + /* trailing blanks after the last comma */ + { "Encoding: gzipped, ", "Encoding:", "gzip", FALSE }, + /* the scan must not cross into a second line */ + { "Encoding: gzipped, \r\nEncoding: gzip, chunked", "Encoding:", + "chunked", FALSE }, }; for(i = 0; i < CURL_ARRAYSIZE(list); i++) { From 5bb7d7a4f27ca16eb12a02cdd43f40493a51272b Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Thu, 16 Jul 2026 13:29:44 +0100 Subject: [PATCH 0688/1062] http: stop dropping large custom headers Closes #22336 --- lib/http.c | 21 ++++++++------- tests/data/Makefile.am | 2 +- tests/data/test3228 | 58 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test3229 | 37 +++++++++++++++++++++++++++ tests/unit/unit1625.c | 18 +++++++++++++ 5 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 tests/data/test3228 create mode 100644 tests/data/test3229 diff --git a/lib/http.c b/lib/http.c index 6fdaa2e434d6..487973e06cd0 100644 --- a/lib/http.c +++ b/lib/http.c @@ -175,7 +175,7 @@ static bool header_has_value(const char **headerp, struct Curl_str *outp) (!curlx_str_single(headerp, ':') || !curlx_str_single(headerp, ';')); if(value) { - curlx_str_untilnl(headerp, outp, MAX_HTTP_RESP_HEADER_SIZE); + curlx_str_cspn(headerp, outp, "\r\n"); curlx_str_trimblanks(outp); } return value; @@ -1415,7 +1415,7 @@ bool Curl_compareheader(const char *headerline, /* line to check */ /* pass the header */ p = &headerline[hlen]; - if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE)) + if(curlx_str_cspn(&p, &val, "\r\n")) return FALSE; curlx_str_trimblanks(&val); @@ -1432,7 +1432,7 @@ bool Curl_compareheader(const char *headerline, /* line to check */ !p[clen])) return TRUE; /* match! */ /* advance to the next comma */ - if(curlx_str_until(&p, &next, MAX_HTTP_RESP_HEADER_SIZE, ',') || + if(curlx_str_until(&p, &next, len, ',') || curlx_str_single(&p, ',')) break; /* no comma, get out */ @@ -1784,21 +1784,22 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, struct Curl_str name; const char *p = headers->data; const char *origp = p; + size_t hlen = strlen(origp); /* explicitly asked to send header without content is done by a header that ends with a semicolon, but there must be no colon present in the name */ - if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ';') && + if(!curlx_str_until(&p, &name, hlen, ';') && !curlx_str_single(&p, ';') && !curlx_str_single(&p, '\0') && !memchr(curlx_str(&name), ':', curlx_strlen(&name))) blankheader = TRUE; else { p = origp; - if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ':') && + if(!curlx_str_until(&p, &name, hlen, ':') && !curlx_str_single(&p, ':')) { struct Curl_str val; - curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE); + curlx_str_untilnl(&p, &val, hlen); curlx_str_trimblanks(&val); if(!curlx_strlen(&val)) /* no content, do not send this */ @@ -1849,9 +1850,11 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, other hosts */ !Curl_auth_allowed_to_host(data)) ; - else if(blankheader) - result = curlx_dyn_addf(req, "%.*s:\r\n", (int)curlx_strlen(&name), - curlx_str(&name)); + else if(blankheader) { + result = curlx_dyn_addn(req, curlx_str(&name), curlx_strlen(&name)); + if(!result) + result = curlx_dyn_addn(req, STRCONST(":\r\n")); + } else result = curlx_dyn_addf(req, "%s\r\n", origp); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 32ad4b1e522c..760a37a05f2b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -284,7 +284,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ -test3223 test3224 test3225 test3226 test3227 \ +test3223 test3224 test3225 test3226 test3227 test3228 test3229 \ \ test3300 test3301 test3302 test3303 test3304 test3305 \ \ diff --git a/tests/data/test3228 b/tests/data/test3228 new file mode 100644 index 000000000000..c0ad8ab5ebad --- /dev/null +++ b/tests/data/test3228 @@ -0,0 +1,58 @@ + + + + +HTTP +HTTP GET +HTTP added headers + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close + +-foo- + + + +# Client-side + + +http + + +Send custom request headers larger than the response header limit + + +header "X-Large: %repeat[307200 x x]%" +header "Connection: %repeat[307200 x x]%" +header "%repeat[307201 x X]%;" + + +-K - http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +X-Large: %repeat[307200 x x]% +%repeat[307201 x X]%: +Connection: %repeat[307200 x x]% + + + +Maximum allocated: 4000000 + + + diff --git a/tests/data/test3229 b/tests/data/test3229 new file mode 100644 index 000000000000..02964053f353 --- /dev/null +++ b/tests/data/test3229 @@ -0,0 +1,37 @@ + + + + +HTTP +HTTP GET +HTTP added headers + + + +# Client-side + + +http + + +Reject a custom request header larger than the request buffer + + +header "X-Large: %repeat[1048576 x x]%" + + +-K - http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + +# 100 == CURLE_TOO_LARGE + +100 + + +Maximum allocated: 6000000 + + + diff --git a/tests/unit/unit1625.c b/tests/unit/unit1625.c index 491b553954a7..9ca59b940288 100644 --- a/tests/unit/unit1625.c +++ b/tests/unit/unit1625.c @@ -38,7 +38,11 @@ static CURLcode test_unit1625(const char *arg) { UNITTEST_BEGIN_SIMPLE + char *large; size_t i; + const size_t large_value_len = MAX_HTTP_RESP_HEADER_SIZE; + const size_t large_prefix_len = strlen("Encoding: "); + const size_t large_suffix_len = strlen(", chunked"); static const struct check1625 list[] = { /* basic case */ { "Encoding: gzip, chunked", "Encoding:", "chunked", TRUE }, @@ -129,6 +133,20 @@ static CURLcode test_unit1625(const char *arg) if(i != CURL_ARRAYSIZE(list)) return CURLE_FAILED_INIT; + large = curlx_malloc(large_prefix_len + large_value_len + + large_suffix_len + 1); + if(!large) + return CURLE_OUT_OF_MEMORY; + + memcpy(large, "Encoding: ", large_prefix_len + 1); + memset(&large[large_prefix_len], 'a', large_value_len); + memcpy(&large[large_prefix_len + large_value_len], ", chunked", + large_suffix_len + 1); + fail_unless(Curl_compareheader(large, STRCONST("Encoding:"), + STRCONST("chunked")), + "token after 300 KiB was not found"); + curlx_free(large); + UNITTEST_END_SIMPLE } #else /* CURL_DISABLE_HTTP */ From c5fd5eb55a58f0f370c69d56fd0dabfe76035474 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 17 Jul 2026 14:10:03 +0200 Subject: [PATCH 0689/1062] uint-spbset: reused empty chunks Fix chunk allocations by reusing existing empty chunks when a new offset is needed. Before this fix, spbsets would only ever grow with added numbers outside the range of existing chunks. Closes #22340 --- lib/uint-spbset.c | 148 +++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 48 deletions(-) diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index d4e6c8e70bf5..66b0fcb6708c 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -74,90 +74,142 @@ uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset) return n; } -static struct uint32_spbset_chunk *uint32_spbset_get_chunk( - struct uint32_spbset *bset, uint32_t i, bool grow) +static bool uint32_spbset_empty_chunk(struct uint32_spbset_chunk *chunk) { - struct uint32_spbset_chunk *chunk, **panchor = NULL; - uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK); - - if(!bset) - return NULL; + uint32_t i; + for(i = 0; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) { + if(chunk->slots[i]) + return FALSE; + } + return TRUE; +} +static struct uint32_spbset_chunk *uint32_spbset_unlink_empty( + struct uint32_spbset *bset, uint32_t for_offset) +{ + struct uint32_spbset_chunk *chunk, **panchor = NULL; for(chunk = &bset->head; chunk; panchor = &chunk->next, chunk = chunk->next) { - if(chunk->offset == i_offset) { - return chunk; - } - else if(chunk->offset > i_offset) { - /* need new chunk here */ - chunk = NULL; + if(uint32_spbset_empty_chunk(chunk)) break; + } + if(chunk) { + if(chunk == &bset->head) { /* head chunk is empty */ + if(!bset->head.next || (for_offset < bset->head.next->offset)) { + return &bset->head; + } + /* swap head and next, unlink */ + chunk = bset->head.next; + memcpy(&bset->head, chunk, sizeof(bset->head)); + memset(chunk, 0, sizeof(*chunk)); + } + else { + *panchor = chunk->next; /* unlink */ + memset(chunk, 0, sizeof(*chunk)); + } + } + return chunk; +} + +static struct uint32_spbset_chunk *uint32_spbset_insert_chunk( + struct uint32_spbset *bset, struct uint32_spbset_chunk *nchunk) +{ + struct uint32_spbset_chunk *chunk, **panchor; + + /* insert nchunk into set's ordered chunk list */ + if(nchunk->offset < bset->head.offset) { + /* swap chunk and head */ + uint32_t offset = nchunk->offset; + memcpy(nchunk, &bset->head, sizeof(*nchunk)); + memset(&bset->head, 0, sizeof(bset->head)); + bset->head.next = nchunk; + bset->head.offset = offset; + return &bset->head; + } + DEBUGASSERT(nchunk->offset > bset->head.offset); + panchor = &bset->head.next; + for(chunk = *panchor; chunk; + panchor = &chunk->next, chunk = chunk->next) { + if(chunk->offset > nchunk->offset) { /* insert before this chunk */ + nchunk->next = chunk; + *panchor = nchunk; + return nchunk; } } + /* no chunk with larger offset, append */ + *panchor = nchunk; + return nchunk; +} - if(!grow) +static struct uint32_spbset_chunk *uint32_spbset_get_chunk( + struct uint32_spbset *bset, uint32_t i, bool grow) +{ + struct uint32_spbset_chunk *chunk; + uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK); + + if(!bset) return NULL; - /* need a new one */ - chunk = curlx_calloc(1, sizeof(*chunk)); - if(!chunk) + for(chunk = &bset->head; chunk; chunk = chunk->next) { + if(chunk->offset == i_offset) + return chunk; + else if(chunk->offset > i_offset) + break; /* need new chunk here */ + } + if(!grow) /* just a check if the chunk exists */ return NULL; - if(panchor) { /* insert between panchor and *panchor */ - chunk->next = *panchor; - *panchor = chunk; + /* Is there an empty chunk to reuse? */ + chunk = uint32_spbset_unlink_empty(bset, i_offset); + if(chunk) { + chunk->offset = i_offset; + if(chunk == &bset->head) /* head chunk is empty, stayed linked */ + return &bset->head; + /* was really unlinked, need to insert below */ } - else { /* prepend to head, switching places */ - memcpy(chunk, &bset->head, sizeof(*chunk)); - memset(&bset->head, 0, sizeof(bset->head)); - bset->head.next = chunk; + else { + /* need a new one */ + chunk = curlx_calloc(1, sizeof(*chunk)); + if(!chunk) + return NULL; + chunk->offset = i_offset; } - chunk->offset = i_offset; - return chunk; + + return uint32_spbset_insert_chunk(bset, chunk); } bool Curl_uint32_spbset_add(struct uint32_spbset *bset, uint32_t i) { - struct uint32_spbset_chunk *chunk; - uint32_t i_chunk; - - chunk = uint32_spbset_get_chunk(bset, i, TRUE); + struct uint32_spbset_chunk *chunk = uint32_spbset_get_chunk(bset, i, TRUE); if(!chunk) return FALSE; DEBUGASSERT(i >= chunk->offset); - i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); - chunk->slots[(i_chunk / 64)] |= ((uint64_t)1 << (i_chunk % 64)); + i -= chunk->offset; + DEBUGASSERT(i < (CURL_UINT32_SPBSET_CH_SLOTS * 64)); + chunk->slots[(i / 64)] |= ((uint64_t)1 << (i % 64)); return TRUE; } void Curl_uint32_spbset_remove(struct uint32_spbset *bset, uint32_t i) { - struct uint32_spbset_chunk *chunk; - uint32_t i_chunk; - - chunk = uint32_spbset_get_chunk(bset, i, FALSE); + struct uint32_spbset_chunk *chunk = uint32_spbset_get_chunk(bset, i, FALSE); if(chunk) { DEBUGASSERT(i >= chunk->offset); - i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); - chunk->slots[(i_chunk / 64)] &= ~((uint64_t)1 << (i_chunk % 64)); + i -= chunk->offset; + DEBUGASSERT(i < (CURL_UINT32_SPBSET_CH_SLOTS * 64)); + chunk->slots[(i / 64)] &= ~((uint64_t)1 << (i % 64)); } } bool Curl_uint32_spbset_contains(struct uint32_spbset *bset, uint32_t i) { - struct uint32_spbset_chunk *chunk; - uint32_t i_chunk; - - chunk = uint32_spbset_get_chunk(bset, i, FALSE); + struct uint32_spbset_chunk *chunk = uint32_spbset_get_chunk(bset, i, FALSE); if(chunk) { DEBUGASSERT(i >= chunk->offset); - i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); - return (chunk->slots[i_chunk / 64] & - ((uint64_t)1 << (i_chunk % 64))) != 0; + i -= chunk->offset; + DEBUGASSERT(i < (CURL_UINT32_SPBSET_CH_SLOTS * 64)); + return (chunk->slots[i / 64] & ((uint64_t)1 << (i % 64))) != 0; } return FALSE; } From 7ad33fe56f25424fb51a10405b849de81ff74b98 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 15 Jul 2026 17:21:04 +0200 Subject: [PATCH 0690/1062] cf-dns: resolve on filter demand Use separate dns cache entries for addresses (A+AAAA) and HTTPS-RR results. That makes also "negative" results independent of each other. Dns cache entries, once in use, can no longer be modified safely, as concurrent use would require each access then to be done under lock. By using separate entries, we can update a HTTPS entry without needing to duplicate an existing address entry for the same host+port. Connection filters can now ask for DNS resolves. This works at any time during connection setup and while connect is going on. `Curl_conn_dns_add_addr_resolve()` and `Curl_conn_dns_add_https_resolve()` are used for that. They check if the resolve is already ongoing, can be added to a resolve not started yet or, as last resort, create a new filter instance and add it to the connection (it's easier to add more filters than making the same filter handle multiple resolves. Since DNS filters are removed once the connection is established, there is no later penalty). HTTPS-RR queries are added by the `HTTPS-CONNECT`, `SSL` and `QUIC` filters. The latter will only do that when ECH is configured and supported. That means we trigger HTTPS-RR queries only when the results matter. Add test_22_06 for ALPN influenced via HTTPS-RR when tunneling through a proxy. This did not work before. Adjust test2100 to use https: as the previous http: URL no longer triggers HTTPS-RR resolves. Closes #22216 --- lib/asyn-ares.c | 104 ++++++------ lib/asyn-base.c | 7 +- lib/asyn-thrdd.c | 34 ++-- lib/asyn.h | 6 +- lib/cf-dns.c | 285 +++++++++++++++++++++------------ lib/cf-dns.h | 36 ++--- lib/cf-https-connect.c | 38 +++-- lib/cf-https-connect.h | 2 + lib/cf-ip-happy.c | 16 +- lib/cf-setup.c | 2 +- lib/cfilters.c | 33 ++-- lib/cfilters.h | 8 +- lib/connect.c | 22 +-- lib/dnscache.c | 246 ++++++++++++++++++++-------- lib/dnscache.h | 15 +- lib/doh.c | 125 ++++++--------- lib/hostip.c | 105 +++++++----- lib/hostip.h | 20 ++- lib/httpsrr.c | 49 +++--- lib/httpsrr.h | 9 +- lib/multi.c | 5 +- lib/socks.c | 86 ++++------ lib/vquic/vquic.c | 24 ++- lib/vquic/vquic.h | 1 + lib/vtls/vtls.c | 15 +- tests/data/test2100 | 25 +-- tests/data/test716 | 2 +- tests/http/test_06_eyeballs.py | 2 +- tests/http/test_22_httpsrr.py | 19 +++ tests/unit/unit1607.c | 6 +- tests/unit/unit1658.c | 3 +- 31 files changed, 793 insertions(+), 557 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index ef4bbcbcc602..106c7333f5b5 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -188,25 +188,6 @@ static CURLcode async_ares_init(struct Curl_easy *data, return result; } -/* - * async_ares_cleanup() cleans up async resolver data. - */ -static void async_ares_cleanup(struct Curl_resolv_async *async) -{ - struct async_ares_ctx *ares = &async->ares; - if(ares->res_A) { - Curl_freeaddrinfo(ares->res_A); - ares->res_A = NULL; - } - if(ares->res_AAAA) { - Curl_freeaddrinfo(ares->res_AAAA); - ares->res_AAAA = NULL; - } -#ifdef USE_HTTPSRR - Curl_httpsrr_cleanup(&ares->hinfo); -#endif -} - void Curl_async_ares_shutdown(struct Curl_easy *data, struct Curl_resolv_async *async) { @@ -227,7 +208,19 @@ void Curl_async_ares_destroy(struct Curl_easy *data, ares_destroy(ares->channel); ares->channel = NULL; } - async_ares_cleanup(async); + if(ares->res_A) { + Curl_freeaddrinfo(ares->res_A); + ares->res_A = NULL; + } + if(ares->res_AAAA) { + Curl_freeaddrinfo(ares->res_AAAA); + ares->res_AAAA = NULL; + } +#ifdef USE_HTTPSRR + curlx_safefree(ares->https_name); + Curl_httpsrr_destroy(ares->hinfo); + ares->hinfo = NULL; +#endif } CURLcode Curl_async_pollset(struct Curl_easy *data, @@ -259,6 +252,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, struct Curl_dns_entry **pdns) { struct async_ares_ctx *ares = &async->ares; + struct Curl_dns_entry *dns = NULL; CURLcode result = CURLE_OK; DEBUGASSERT(pdns); @@ -279,34 +273,31 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, /* all c-ares operations done, what is the result to report? */ result = ares->result; if(ares->ares_status == ARES_SUCCESS && !result) { - struct Curl_dns_entry *dns = - Curl_dnscache_mk_entry2(data, async->dns_queries, - &ares->res_AAAA, &ares->res_A, - async->hostname, async->port); - if(!dns) { - result = CURLE_OUT_OF_MEMORY; - goto out; + + if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { + dns = Curl_dnsc_mk_addr2(data, async->dns_queries, + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } + #ifdef HTTPSRR_WORKS - if(async->dns_queries & CURL_DNSQ_HTTPS) { - if(ares->hinfo.complete) { - struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); - if(!lhrr) - result = CURLE_OUT_OF_MEMORY; - else - Curl_dns_entry_set_https_rr(dns, lhrr); + if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { + dns = Curl_dnsc_mk_https(data, &ares->hinfo, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - else - Curl_dns_entry_set_https_rr(dns, NULL); } #endif - if(!result) { - *pdns = dns; - } } /* if we have not found anything, report the proper * CURLE_COULDNT_RESOLVE_* code */ - if(!result && !*pdns) { + if(!result && !dns) { const char *msg = NULL; /* only an authoritative "does not exist" answer from every query may be cached as a negative entry, not transient failures like @@ -319,13 +310,16 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, result = Curl_async_failed(data, async, msg); } - CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", - (int)result, *pdns ? "" : "not "); - async_ares_cleanup(async); + CURL_TRC_DNS(data, "[%s] ares_take_result, result=%d, ares_result=%d, " + "ares_status=%d, dns=%sfound", + Curl_resolv_query_str(async->dns_queries), + (int)result, (int)ares->result, ares->ares_status, + dns ? "" : "not "); out: if(result != CURLE_AGAIN) ares->result = result; + *pdns = result ? NULL : dns; return result; } @@ -400,7 +394,7 @@ const struct Curl_https_rrinfo *Curl_async_get_https( struct Curl_resolv_async *async) { if(Curl_async_knows_https(data, async)) - return &async->ares.hinfo; + return async->ares.hinfo; return NULL; } @@ -626,6 +620,9 @@ static void async_ares_rr_done(void *user_data, ares_status_t status, async->dns_responses |= CURL_DNSQ_HTTPS; async->queries_ongoing--; async->done = !async->queries_ongoing; + ares->ares_status = status; + if((status != ARES_ENOTFOUND) && (status != ARES_ENODATA)) + ares->transient_err = TRUE; if((ARES_SUCCESS != status) || !dnsrec) return; ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); @@ -677,7 +674,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname); + CURL_TRC_DNS(data, "[AAAA] ares: query records for %s", async->hostname); hints.ai_family = PF_INET6; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; @@ -691,7 +688,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname); + CURL_TRC_DNS(data, "[A] ares: query records for %s", async->hostname); hints.ai_family = PF_INET; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; @@ -701,20 +698,19 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, } #ifdef USE_HTTPSRR - memset(&ares->hinfo, 0, sizeof(ares->hinfo)); if(async->dns_queries & CURL_DNSQ_HTTPS) { - char *rrname = NULL; + ares->https_name = NULL; if(async->port != 443) { - rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); - if(!rrname) + ares->https_name = curl_maprintf("_%d._https.%s", + async->port, async->hostname); + if(!ares->https_name) return CURLE_OUT_OF_MEMORY; } - CURL_TRC_DNS(data, "ares: query HTTPS records for %s", - rrname ? rrname : async->hostname); - ares->hinfo.rrname = rrname; + CURL_TRC_DNS(data, "[HTTPS] ares: query records for %s", + ares->https_name ? ares->https_name : async->hostname); async->queries_ongoing++; ares_query_dnsrec(ares->channel, - rrname ? rrname : async->hostname, + ares->https_name ? ares->https_name : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, async_ares_rr_done, async, NULL); } diff --git a/lib/asyn-base.c b/lib/asyn-base.c index 62cb0effe491..b140b0f5b986 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -256,9 +256,10 @@ CURLcode Curl_async_failed(struct Curl_easy *data, } #endif - failf(data, "Could not resolve %s: %s%s%s%s", - host_or_proxy, async->hostname, - detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); + if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve %s: %s%s%s%s", + host_or_proxy, async->hostname, + detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); return result; } diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 29e9b138d3f4..a3166f52f86a 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -242,8 +242,7 @@ static CURLcode async_rr_start(struct Curl_easy *data, } #endif - memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); - thrdd->rr.hinfo.rrname = rrname; + thrdd->rr.https_name = rrname; async->queries_ongoing++; ares_query_dnsrec(thrdd->rr.channel, rrname ? rrname : async->hostname, ARES_CLASS_IN, @@ -291,7 +290,8 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, ares_destroy(async->thrdd.rr.channel); async->thrdd.rr.channel = NULL; } - Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); + curlx_safefree(async->thrdd.rr.https_name); + Curl_httpsrr_destroy(async->thrdd.rr.hinfo); #endif async_thrdd_item_destroy(async->thrdd.res_A); async->thrdd.res_A = NULL; @@ -668,6 +668,9 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, #endif out: + if(!async->queries_ongoing) + async->done = TRUE; + if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", async->hostname, async->port, (int)result); @@ -769,7 +772,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if((thrdd->res_A && thrdd->res_A->res) || (thrdd->res_AAAA && thrdd->res_AAAA->res)) { - dns = Curl_dnscache_mk_entry2( + dns = Curl_dnsc_mk_addr2( data, async->dns_queries, thrdd->res_A ? &thrdd->res_A->res : NULL, thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, @@ -778,22 +781,19 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto out; } + } #ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - struct Curl_https_rrinfo *lhrr = NULL; - if(thrdd->rr.hinfo.complete) { - lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); - if(!lhrr) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - Curl_httpsrr_trace(data, lhrr); - Curl_dns_entry_set_https_rr(dns, lhrr); + if(!dns && thrdd->rr.channel) { + Curl_httpsrr_trace(data, thrdd->rr.hinfo); + dns = Curl_dnsc_mk_https(data, &thrdd->rr.hinfo, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } -#endif } +#endif if(dns) { *pdns = dns; @@ -864,7 +864,7 @@ const struct Curl_https_rrinfo *Curl_async_get_https( { #ifdef USE_HTTPSRR_ARES if(Curl_async_knows_https(data, async)) - return &async->thrdd.rr.hinfo; + return async->thrdd.rr.hinfo; #else (void)data; (void)async; diff --git a/lib/asyn.h b/lib/asyn.h index e4bc05e0ddec..76996f888aa0 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -120,7 +120,8 @@ struct async_ares_ctx { CURLcode result; /* CURLE_OK or error handling response */ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ #ifdef USE_HTTPSRR - struct Curl_https_rrinfo hinfo; + char *https_name; + struct Curl_https_rrinfo *hinfo; #endif BIT(transient_err); /* an A/AAAA query failed without the resolver answering that the name does not exist */ @@ -144,7 +145,8 @@ struct async_thrdd_ctx { #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; - struct Curl_https_rrinfo hinfo; + char *https_name; + struct Curl_https_rrinfo *hinfo; } rr; #endif }; diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 6388d65a9e31..4152a00c1788 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -30,6 +30,7 @@ #include "dnscache.h" #include "httpsrr.h" #include "curl_trc.h" +#include "multiif.h" #include "progress.h" #include "url.h" #include "cf-dns.h" @@ -44,7 +45,6 @@ struct cf_dns_ctx { uint8_t transport; BIT(started); BIT(announced); - BIT(complete_resolve); BIT(for_proxy); }; @@ -52,8 +52,7 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, - bool for_proxy, - bool complete_resolve) + bool for_proxy) { struct cf_dns_ctx *ctx; @@ -65,10 +64,10 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, ctx->dns_queries = dns_queries; ctx->transport = transport; ctx->for_proxy = for_proxy; - ctx->complete_resolve = complete_resolve; - CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", - peer->hostname, peer->port, ctx->transport, ctx->dns_queries); + CURL_TRC_DNS(data, "[%s] created DNS filter for %s:%u, transport=%x", + Curl_resolv_query_str(ctx->dns_queries), + peer->hostname, peer->port, ctx->transport); return ctx; } @@ -131,22 +130,28 @@ static void cf_dns_report(struct Curl_cfilter *cf, } else { curlx_dyn_init(&tmp, 1024); - infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); + if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); #ifdef CURLRES_IPV6 - cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); + cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); #endif - cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); + cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); + } #ifdef USE_HTTPSRR - if(!dns->hinfo) - infof(data, "HTTPS-RR: -"); - else if(!Curl_httpsrr_applicable(data, dns->hinfo)) - infof(data, "HTTPS-RR: not applicable"); - else { - CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); - if(!result) - infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); - else - infof(data, "Error printing HTTPS-RR information"); + else if(ctx->dns_queries & CURL_DNSQ_HTTPS) { + if(!dns->hinfo) + infof(data, "HTTPS-RR %s:%u: -", dns->hostname, dns->port); + else if(!Curl_httpsrr_applicable(data, dns->hinfo)) + infof(data, "HTTPS-RR %s:%u: not applicable", + dns->hostname, dns->port); + else { + CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); + if(!result) + infof(data, "HTTPS-RR %s:%u: %s", + dns->hostname, dns->port, curlx_dyn_ptr(&tmp)); + else + infof(data, "Error printing HTTPS-RR information"); + } } #endif curlx_dyn_free(&tmp); @@ -165,25 +170,40 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, { struct cf_dns_ctx *ctx = cf->ctx; timediff_t timeout_ms = Curl_timeleft_ms(data); - CURLcode result; + CURLcode result = CURLE_OK; *pdns = NULL; - CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", + CURL_TRC_CF(data, cf, "[%s] cf_dns_start %s %s:%u", + Curl_resolv_query_str(ctx->dns_queries), ctx->peer->unix_socket ? "unix-domain-socket" : "host", ctx->peer->hostname, ctx->peer->port); if(ctx->peer->unix_socket) - ctx->dns_queries = 0; - else if(Curl_is_ipv4addr(ctx->peer->hostname)) - ctx->dns_queries |= CURL_DNSQ_A; + ctx->dns_queries = CURL_DNSQ_A; /* treat it like an A resolve */ + + if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + if(Curl_is_ipv4addr(ctx->peer->hostname)) + ctx->dns_queries |= CURL_DNSQ_A; #ifdef USE_IPV6 - else if(ctx->peer->ipv6) - ctx->dns_queries |= CURL_DNSQ_AAAA; + else if(ctx->peer->ipv6) + ctx->dns_queries |= CURL_DNSQ_AAAA; #endif - result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, - (bool)ctx->for_proxy, timeout_ms, - &ctx->resolv_id, pdns); + result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, + (bool)ctx->for_proxy, timeout_ms, + &ctx->resolv_id, pdns); + } +#ifdef USE_HTTPSRR + else if(ctx->dns_queries == CURL_DNSQ_HTTPS) { + result = Curl_resolv_https(data, ctx->peer, (bool)ctx->for_proxy, + timeout_ms, &ctx->resolv_id, pdns); + } +#endif + else { + failf(data, "unsupported DNS queries %x", ctx->dns_queries); + return CURLE_FAILED_INIT; + } + DEBUGASSERT(!result || !*pdns); if(!result) { /* resolved right away, either sync or from dnscache */ DEBUGASSERT(*pdns); @@ -201,7 +221,8 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else { DEBUGASSERT(result); - failf(data, "Could not resolve: %s", ctx->peer->hostname); + if(ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } } @@ -222,8 +243,6 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, /* We want AAAA answer as we prefer IPv6. If a sub-filter desires * HTTPS-RR, we check for that query as well. */ uint8_t wanted_answers = CURL_DNSQ_AAAA; - if(Curl_conn_cf_wants_httpsrr(cf, data)) - wanted_answers |= CURL_DNSQ_HTTPS; /* Note: if a query was never started, it is considered to have * an answer (e.g. a negative one). */ @@ -246,6 +265,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, bool *done) { struct cf_dns_ctx *ctx = cf->ctx; + bool ip_query = (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); if(cf->connected) { *done = TRUE; @@ -263,7 +283,8 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); } - if(ctx->resolv_result) { + if(ctx->resolv_result && ip_query) { + /* failing A|AAAA resolves is a hard failure. */ CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); return ctx->resolv_result; } @@ -289,13 +310,9 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, DEBUGASSERT(sub_done); } - /* sub filter chain is connected */ + /* sub filter chain is connected, this means the filter has done + * its work and is connected as well, if it has results or not. */ CURL_TRC_CF(data, cf, "connected filter chain below"); - if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { - /* This filter only connects when it has resolved everything. */ - CURL_TRC_CF(data, cf, "delay connect until resolve complete"); - return CURLE_OK; - } *done = TRUE; cf->connected = TRUE; Curl_resolv_destroy(data, ctx->resolv_id); @@ -315,8 +332,24 @@ static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { #ifdef USE_CURL_ASYNC - if(!cf->connected) + if(!cf->connected) { + /* The pollset only works when we have started the resolving. + * Otherwise we might wait on the WAKEUP socketpair forever. + * Since DNS filters can be added in the middle of a connect + * attempt, they are not always started that way. */ + struct cf_dns_ctx *ctx = cf->ctx; + if(!ctx->started) { + CURL_TRC_CF(data, cf, "adjust_pollset, starting %s:%u queries=%s", + ctx->peer->hostname, ctx->peer->port, + Curl_resolv_query_str(ctx->dns_queries)); + ctx->started = TRUE; + ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns); + if(ctx->resolv_result || ctx->dns) { + Curl_multi_mark_dirty(data); + } + } return Curl_resolv_pollset(data, ps); + } #else (void)cf; (void)data; @@ -351,7 +384,7 @@ static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_dns = { "DNS", - CF_TYPE_SETUP, + CF_TYPE_SETUP | CF_TYPE_DNS, CURL_LOG_LVL_NONE, cf_dns_destroy, cf_dns_connect, @@ -371,8 +404,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, - bool for_proxy, - bool complete_resolve) + bool for_proxy) { struct Curl_cfilter *cf = NULL; struct cf_dns_ctx *ctx; @@ -380,7 +412,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, (void)data; ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, - for_proxy, complete_resolve); + for_proxy); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -397,12 +429,12 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, /* Adds a "resolv" filter at the top of the connection's filter chain. * The filter will resolve the peer on the first connect attempt. */ -CURLcode Curl_cf_dns_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - struct Curl_peer *peer, - uint8_t dns_queries, - uint8_t transport) +static CURLcode cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { struct Curl_cfilter *cf = NULL; bool for_proxy = FALSE; @@ -415,8 +447,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, (peer == conn->http_proxy.peer); #endif - result = cf_dns_create(&cf, data, peer, dns_queries, transport, - for_proxy, FALSE); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, for_proxy); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -430,18 +461,16 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, * 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, - uint8_t dns_queries, - struct Curl_peer *peer, - uint8_t transport, - bool complete_resolve) +static CURLcode cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { struct Curl_cfilter *cf; CURLcode result; - result = cf_dns_create(&cf, data, peer, dns_queries, transport, - FALSE, complete_resolve); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, FALSE); if(result) return result; @@ -449,38 +478,93 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, return CURLE_OK; } -/* Return the resolv result from the first "resolv" filter, starting - * the given filter `cf` downwards. - */ -static CURLcode cf_dns_result(struct Curl_cfilter *cf, - struct Curl_peer *peer) +static CURLcode cf_dns_add_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { - for(; cf; cf = cf->next) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + struct Curl_cfilter *cf_dns = NULL; + bool is_addr = CURL_DNSQ_IS_ADDR(dns_queries); + + if((dns_queries & CURL_DNSQ_HTTPS) && + Curl_is_ipaddr(peer->hostname)) { + dns_queries = (uint8_t)(dns_queries & ~CURL_DNSQ_HTTPS); + } + + for(; cf && dns_queries; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; + cf_dns = cf; if(Curl_peer_same_destination(ctx->peer, peer)) { - if(ctx->dns || ctx->resolv_result) - return ctx->resolv_result; - return CURLE_AGAIN; + /* subtract queries already being scheduled/ongoing */ + dns_queries = (uint8_t)(~ctx->dns_queries & dns_queries); + if(!dns_queries) { /* already there */ + return CURLE_OK; + } + else if(is_addr && !ctx->started && + CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + ctx->dns_queries |= dns_queries; + CURL_TRC_DNS(data, "[%s] added queries=%s for %s:%u", + Curl_resolv_query_str(ctx->dns_queries), + Curl_resolv_query_str(dns_queries), + peer->hostname, peer->port); + return CURLE_OK; + } } - return CURLE_OK; /* ok, but no results */ } } - return CURLE_FAILED_INIT; + + if(dns_queries) { + /* No existing filter is handling these, add a new DNS filter + * (after the last one if there was one already. FCFS. */ + if(cf_dns) + return cf_dns_insert_after(cf_dns, data, peer, dns_queries, transport); + else + return cf_dns_add(data, conn, sockindex, peer, dns_queries, transport); + } + return CURLE_OK; +} + +CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) +{ + /* should only have address query bits */ + DEBUGASSERT(!(dns_queries & ~CURL_DNSQ_ADDR)); + return cf_dns_add_resolve(data, conn, sockindex, peer, + (dns_queries & CURL_DNSQ_ADDR), transport); } -/* Return the result of the DNS resolution for peer. Searches for a "resolv" - * filter from the top of the filter chain down. Returns +/* Return the result of the DNS address resolution for peer. + * Searches for a DNS 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, - struct Curl_peer *peer) +CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - return cf_dns_result(conn->cfilter[sockindex], peer); + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + } + } + return CURLE_FAILED_INIT; /* no one is resolving */ } static const struct Curl_addrinfo *cf_dns_get_nth_ai( @@ -508,20 +592,25 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( } /* Return the addrinfo at `index` for the given `family` from the - * first "resolve" filter underneath `cf`. If the DNS resolving is + * 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_cf_dns_get_ai(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *peer, - int ai_family, - unsigned int index) +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + struct Curl_peer *peer, + int sockindex, + int ai_family, + unsigned int index) { - (void)data; + struct connectdata *conn = data->conn; + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + CURL_TRC_CF(data, cf, "get %uth result for %s:%u, family=%d, dns=%d", + index, peer->hostname, peer->port, ai_family, !!ctx->dns); if(ctx->resolv_result) return NULL; else if(ctx->dns) @@ -534,22 +623,16 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, 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, - struct Curl_peer *peer, - int sockindex, - int ai_family, - unsigned int index) +#ifdef USE_HTTPSRR +CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - struct connectdata *conn = data->conn; - return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer, - ai_family, index); + return cf_dns_add_resolve(data, conn, sockindex, peer, + CURL_DNSQ_HTTPS, conn->transport_wanted); } -#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. @@ -563,7 +646,8 @@ Curl_conn_dns_get_https(struct Curl_easy *data, for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & CURL_DNSQ_HTTPS)) { if(ctx->dns) return ctx->dns->hinfo; else @@ -582,7 +666,8 @@ bool Curl_conn_dns_resolved_https(struct Curl_easy *data, for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & CURL_DNSQ_HTTPS)) { if(ctx->dns) return TRUE; else diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 891b1efea606..ef90d711e2f7 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -31,23 +31,16 @@ struct Curl_dns_entry; struct Curl_addrinfo; struct Curl_peer; -CURLcode Curl_cf_dns_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - struct Curl_peer *peer, - uint8_t dns_queries, - uint8_t transport); +CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport); -CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_peer *peer, - uint8_t transport, - bool complete_resolve); - -CURLcode Curl_conn_dns_result(struct connectdata *conn, - int sockindex, - struct Curl_peer *peer); +CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, struct Curl_peer *peer, @@ -55,13 +48,12 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, int ai_family, unsigned int index); -const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *peer, - int ai_family, - unsigned int index); - #ifdef USE_HTTPSRR +CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); + const struct Curl_https_rrinfo * Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 1a2e966ef66a..c39f59f2ee3a 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -110,6 +110,7 @@ static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b, struct cf_hc_ctx { cf_hc_state state; + struct Curl_peer *destination; /* who we ultimately want to talk to */ struct curltime started; /* when connect started */ CURLcode result; /* overall result */ CURLcode check_h3_result; @@ -123,21 +124,14 @@ struct cf_hc_ctx { BIT(ballers_complete); }; -static void cf_hc_ctx_close(struct Curl_easy *data, - struct cf_hc_ctx *ctx) +static void cf_hc_ctx_destroy(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_discard(&ctx->ballers[i], data); - } -} - -static void cf_hc_ctx_destroy(struct Curl_easy *data, - struct cf_hc_ctx *ctx) -{ - if(ctx) { - cf_hc_ctx_close(data, ctx); + Curl_peer_unlink(&ctx->destination); curlx_free(ctx); } } @@ -222,7 +216,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, ctx->state = CF_HC_SUCCESS; cf->connected = TRUE; - cf_hc_ctx_close(data, ctx); /* ballers may have failf()'d, the winner resets it, so our * errorbuf is clean again. */ Curl_reset_fail(data); @@ -296,6 +289,7 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, enum alpnid not_this_one) { #ifdef USE_HTTPSRR + struct cf_hc_ctx *ctx = cf->ctx; /* 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 @@ -304,9 +298,9 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, size_t i; /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https( - data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); + rr = Curl_conn_dns_get_https(data, cf->sockindex, ctx->destination); + CURL_TRC_CF(data, cf, "HTTPS-RR %savailable", rr ? "" : "not "); /* We do not support `rr->no_def_alpn`. */ if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { @@ -495,7 +489,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, if(!ctx->httpsrr_resolved) { ctx->httpsrr_resolved = Curl_conn_dns_resolved_https( - data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); + data, cf->sockindex, ctx->destination); #ifdef DEBUGBUILD if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); @@ -744,7 +738,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", - CF_TYPE_SETUP | CF_TYPE_HTTPSRR, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, @@ -761,6 +755,7 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *destination, uint8_t def_transport) { struct Curl_cfilter *cf = NULL; @@ -772,6 +767,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + Curl_peer_link(&ctx->destination, destination); ctx->def_transport = def_transport; ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; @@ -788,6 +784,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, } static CURLcode cf_hc_add(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex, uint8_t def_transport) @@ -796,15 +793,21 @@ static CURLcode cf_hc_add(struct Curl_easy *data, CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, def_transport); + result = cf_hc_create(&cf, data, destination, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); + +#ifdef USE_HTTPSRR + result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex, + destination); +#endif out: return result; } CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex) { @@ -821,7 +824,8 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, !conn->bits.tls_enable_alpn) goto out; - result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); + result = cf_hc_add(data, destination, conn, sockindex, + conn->transport_wanted); out: return result; diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index 3160c0382a4b..caa2b17e7f34 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -31,10 +31,12 @@ struct Curl_cfilter; struct Curl_easy; struct connectdata; struct Curl_cftype; +struct Curl_peer; extern struct Curl_cftype Curl_cft_http_connect; CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex); diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 91ff029f6f68..794d5e4cb9ab 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -857,7 +857,8 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->dns_resolved) { - result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, + ctx->ballers.peer); if(!result) ctx->dns_resolved = TRUE; else if(result == CURLE_AGAIN) { @@ -888,10 +889,6 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, cf->connected = TRUE; cf->next = ctx->ballers.winner->cf; ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(ctx, data); - Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - /* whatever errors were reported by ballers, clear our errorbuf */ - Curl_reset_fail(data); if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ @@ -900,13 +897,16 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, struct ip_quadruple ipquad; bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { - const char *host; - Curl_conn_get_current_host(data, cf->sockindex, &host, NULL); CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", - host, ipquad.remote_ip, ipquad.remote_port); + ctx->ballers.peer->hostname, + ipquad.remote_ip, ipquad.remote_port); } } #endif + cf_ip_happy_ctx_clear(ctx, data); + Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); + /* whatever errors were reported by ballers, clear our errorbuf */ + Curl_reset_fail(data); data->info.numconnects++; /* to track the # of connections made */ } break; diff --git a/lib/cf-setup.c b/lib/cf-setup.c index b073989a4e34..7699ef5dfea3 100644 --- a/lib/cf-setup.c +++ b/lib/cf-setup.c @@ -251,7 +251,7 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, (int)result); return result; } - result = Curl_cf_quic_insert_after(cf, origin, peer); + result = Curl_cf_quic_insert_after(cf, data, origin, peer); if(result) { CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); return result; diff --git a/lib/cfilters.c b/lib/cfilters.c index c5ee51a1b42f..238698d3320e 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -644,17 +644,6 @@ int Curl_protocol_for_transport(uint8_t transport) } } -bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_HTTPSRR) - return TRUE; - } - return FALSE; -} - const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn) { @@ -728,17 +717,17 @@ CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { CURLcode result = CURLE_OK; - /* Get the lowest not-connected filter, if there are any */ - while(cf && !cf->connected && cf->next && !cf->next->connected) - cf = cf->next; - /* Skip all filters that have already shut down */ - while(cf && cf->shutdown) - cf = cf->next; - /* From there on, give all filters a chance to adjust the pollset. - * Lower filters are called later, so they may override */ - while(cf && !result) { - result = cf->cft->adjust_pollset(cf, data, ps); - cf = cf->next; + /* Go through all filters, top to bottom, and let them manage the pollset + * - connected filters can do so + * - CF_TYPE_DNS filters can + * - unconnected filters without next or connect next can + */ + for(; cf && !result; cf = cf->next) { + if(cf->shutdown) + continue; + if(cf->connected || (cf->cft->flags & CF_TYPE_DNS) || + !cf->next || cf->next->connected) + result = cf->cft->adjust_pollset(cf, data, ps); } return result; } diff --git a/lib/cfilters.h b/lib/cfilters.h index b1aad029e070..f55a572e2e93 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -197,7 +197,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * CF_TYPE_HTTP implement a version of the HTTP protocol * CF_TYPE_SETUP filter is only needed for connection setup and * can be removed once connected - * CF_TYPE_HTTPSRR filter that wants HTTPS-RR information + * CF_TYPE_DNS filter managing DNS resolve operations */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) @@ -205,7 +205,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, #define CF_TYPE_PROXY (1 << 3) #define CF_TYPE_HTTP (1 << 4) #define CF_TYPE_SETUP (1 << 5) -#define CF_TYPE_HTTPSRR (1 << 6) +#define CF_TYPE_DNS (1 << 6) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -352,10 +352,6 @@ int Curl_protocol_for_transport(uint8_t transport); const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data); -/* The filter (or one of its sub-filters) wants HTTPS-RR information. */ -bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, - struct Curl_easy *data); - #define CURL_CF_SSL_DEFAULT (-1) #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 diff --git a/lib/connect.c b/lib/connect.c index 86e97b955a01..ca81849ce786 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -237,22 +237,23 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, int sockindex, int ssl_mode) { + struct Curl_peer *first_peer = Curl_conn_get_first_peer(conn, sockindex); CURLcode result = CURLE_OK; - struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); DEBUGASSERT(!conn->cfilter[sockindex]); - if(!peer) + if(!first_peer) return CURLE_FAILED_INIT; #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && conn->scheme->protocol == CURLPROTO_HTTPS) { DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); - result = Curl_cf_https_setup(data, conn, sockindex); + result = Curl_cf_https_setup( + data, Curl_conn_get_destination(conn, sockindex), conn, sockindex); if(result) goto out; } @@ -266,13 +267,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, goto out; } + /* Whatever the filter chain will be in the end, it will need the + * resolving of `first_peer`. Add that now so the resolve is started + * right away. */ dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); -#ifdef USE_HTTPSRR - if(sockindex == FIRSTSOCKET) - dns_queries |= CURL_DNSQ_HTTPS; -#endif - result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, - conn->transport_wanted); + result = Curl_conn_dns_add_addr_resolve(data, conn, sockindex, + first_peer, dns_queries, + conn->transport_wanted); + if(result) + goto out; + DEBUGASSERT(conn->cfilter[sockindex]); out: return result; diff --git a/lib/dnscache.c b/lib/dnscache.c index 9c8341e56fb4..9f435cc0cd7a 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -64,10 +64,7 @@ static void dnscache_entry_free(struct Curl_dns_entry *dns) { Curl_freeaddrinfo(dns->addr); #ifdef USE_HTTPSRR - if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); - } + Curl_httpsrr_destroy(dns->hinfo); #endif curlx_free(dns); } @@ -76,17 +73,19 @@ static void dnscache_entry_free(struct Curl_dns_entry *dns) * Create a hostcache id string for the provided host + port, to be used by * the DNS caching. Without alloc. Return length of the id string. */ -static size_t create_dnscache_id(const char *name, +static size_t create_dnscache_id(char type, const char *name, size_t nlen, /* 0 or actual name length */ - uint16_t port, char *ptr, size_t buflen) + uint16_t port, + char *buf, size_t buflen) { size_t len = nlen ? nlen : strlen(name); DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 7)) - len = buflen - 7; + if(len > (buflen - 8)) + len = buflen - 8; /* store and lower case the name */ - Curl_strntolower(ptr, name, len); - return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; + buf[0] = type; + Curl_strntolower(buf + 1, name, len); + return curl_msnprintf(&buf[len + 1], 7, ":%u", port) + len + 1; } struct dnscache_prune_data { @@ -219,6 +218,8 @@ static CURLcode fetch_addr(struct Curl_easy *data, struct Curl_dns_entry *dns = NULL; char entry_id[MAX_HOSTCACHE_LEN]; size_t entry_len; + char entry_type = CURL_DNSQ_IS_ADDR(dns_queries) ? + CURL_DNST_ADDR : CURL_DNST_HTTPS; CURLcode result = CURLE_OK; *pdns = NULL; @@ -226,15 +227,16 @@ static CURLcode fetch_addr(struct Curl_easy *data, return CURLE_OK; /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, 0, port, + entry_len = create_dnscache_id(entry_type, hostname, 0, port, entry_id, sizeof(entry_id)); /* See if it is already in our dns cache */ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); + if(!dns && data->state.wildcard_resolve && CURL_DNSQ_IS_ADDR(dns_queries)) { + entry_len = create_dnscache_id(CURL_DNST_ADDR, "*", 1, port, + entry_id, sizeof(entry_id)); /* See if it is already in our dns cache */ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); @@ -255,9 +257,11 @@ static CURLcode fetch_addr(struct Curl_easy *data, } } - if(dns) { - if((dns->dns_queries & dns_queries) != dns_queries) { - /* The entry does not cover all wanted DNS queries, a miss. */ + /* We need to cache address information and HTTPS-RR separately. */ + if(dns && CURL_DNSQ_IS_ADDR(dns_queries)) { + if((uint8_t)(dns->dns_queries & dns_queries) != + (uint8_t)(dns_queries & CURL_DNSQ_ADDR)) { + /* The entry does not cover all wanted address queries, a miss. */ dns = NULL; } else if(!(dns->dns_responses & dns_queries)) { @@ -267,12 +271,12 @@ static CURLcode fetch_addr(struct Curl_easy *data, dns = NULL; result = CURLE_COULDNT_RESOLVE_HOST; } + else if(dns && !dns->addr) { /* negative entry */ + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } } - if(dns && !dns->addr) { /* negative entry */ - dns = NULL; - result = CURLE_COULDNT_RESOLVE_HOST; - } *pdns = dns; return result; } @@ -311,6 +315,9 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data, } dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cache lookup %s:%u queries=%s -> %d %sfound", + hostname, port, Curl_resolv_query_str(dns_queries), + (int)result, dns ? "" : "not "); *pentry = dns; return result; } @@ -407,11 +414,8 @@ static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, return FALSE; } -static struct Curl_dns_entry *dnscache_entry_create( +static struct Curl_dns_entry *dnsc_entry_create( struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr1, - struct Curl_addrinfo **paddr2, const char *hostname, size_t hostlen, uint16_t port, @@ -425,7 +429,6 @@ static struct Curl_dns_entry *dnscache_entry_create( goto out; dns->refcount = 1; /* the cache has the first reference */ - dns->dns_queries = dns_queries; dns->port = port; if(hostlen) memcpy(dns->hostname, hostname, hostlen); @@ -438,6 +441,29 @@ static struct Curl_dns_entry *dnscache_entry_create( dns->timestamp = *Curl_pgrs_now(data); } +out: + return dns; +} + +static struct Curl_dns_entry * +dnsc_entry_assign_addr( + struct Curl_easy *data, + struct Curl_dns_entry *dns, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2) +{ + if(!dns) + goto out; + /* only do this when this is the only reference */ + DEBUGASSERT(dns->refcount == 1); + DEBUGASSERT(dns->type == CURL_DNST_INIT); + + dns->type = CURL_DNST_ADDR; + /* queries should only be about addresses */ + DEBUGASSERT(!(dns_queries & ~CURL_DNSQ_ADDR)); + dns->dns_queries = (dns_queries & CURL_DNSQ_ADDR); + /* Take the given address lists into the entry */ if(paddr1 && *paddr1) { dns->addr = *paddr1; @@ -488,67 +514,129 @@ static struct Curl_dns_entry *dnscache_entry_create( return dns; } -struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr, const char *hostname, uint16_t port) { - return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, - hostname ? strlen(hostname) : 0, - port, FALSE); + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); + return dns; } -struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr1, struct Curl_addrinfo **paddr2, const char *hostname, uint16_t port) { - return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, - hostname ? strlen(hostname) : 0, - port, FALSE); + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr1, paddr2); + return dns; } #ifdef USE_HTTPSRR -void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, - struct Curl_https_rrinfo *hinfo) +static struct Curl_dns_entry * +dnsc_entry_assign_https(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo **phinfo) { + if(!dns) + goto out; /* only do this when this is the only reference */ DEBUGASSERT(dns->refcount == 1); - /* it should have been in the queries */ - DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS); + DEBUGASSERT(dns->type == CURL_DNST_INIT); + if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); + Curl_httpsrr_destroy(dns->hinfo); + dns->hinfo = NULL; + } + dns->type = CURL_DNST_HTTPS; + dns->dns_responses = dns->dns_queries = CURL_DNSQ_HTTPS; + if(phinfo) { + dns->hinfo = *phinfo; + *phinfo = NULL; } - dns->hinfo = hinfo; - dns->dns_responses |= CURL_DNSQ_HTTPS; +out: + if(phinfo && *phinfo) { + Curl_httpsrr_destroy(*phinfo); + *phinfo = NULL; + } + return dns; } + +struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + uint16_t port) +{ + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_https(dns, phinfo); + return dns; +} + +static struct Curl_dns_entry *dnsc_add_https(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns, *dns2; + + dns = dnsc_entry_create(data, hostname, hostname ? strlen(hostname) : 0, + port, permanent); + dns = dnsc_entry_assign_https(dns, phinfo); + if(!dns) + return NULL; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(CURL_DNST_HTTPS, hostname, hlen, port, + entry_id, sizeof(entry_id)); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + #endif /* USE_HTTPSRR */ -static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - uint8_t dns_queries, - struct Curl_addrinfo **paddr, - const char *hostname, - size_t hlen, - uint16_t port, - bool permanent) +static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) { char entry_id[MAX_HOSTCACHE_LEN]; size_t entry_len; struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; - dns = dnscache_entry_create(data, dns_queries, paddr, NULL, - hostname, hlen, port, permanent); + dns = dnsc_entry_create(data, hostname, hlen, port, permanent); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); if(!dns) return NULL; /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, hlen, port, + entry_len = create_dnscache_id(CURL_DNST_ADDR, hostname, hlen, port, entry_id, sizeof(entry_id)); /* Store the resolved data in our DNS cache. */ @@ -573,8 +661,12 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, if(!dnscache) return CURLE_FAILED_INIT; + if(!entry || (entry->type == CURL_DNST_INIT)) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id)); + idlen = create_dnscache_id(entry->type, entry->hostname, 0, entry->port, + id, sizeof(id)); /* Store the resolved data in our DNS cache and up ref count */ dnscache_lock(data, dnscache); @@ -584,6 +676,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, } entry->refcount++; dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cached entry for %s:%u queries=%s", + entry->hostname, entry->port, + Curl_resolv_query_str(entry->dns_queries)); return CURLE_OK; } @@ -593,16 +688,35 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, uint16_t port) { struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + DEBUGASSERT(dnscache); if(!dnscache) return CURLE_FAILED_INIT; dnscache_lock(data, dnscache); - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, - host, strlen(host), port, FALSE); + if(dns_queries & CURL_DNSQ_ADDR) { + /* put this new host in the cache */ + dns = dnsc_add_addr(data, dnscache, dns_queries, NULL, + host, strlen(host), port, FALSE); + if(!dns) + result = CURLE_OUT_OF_MEMORY; + } +#ifdef USE_HTTPSRR + else if(dns_queries == CURL_DNSQ_HTTPS) { + dns = dnsc_add_https(data, dnscache, NULL, + host, strlen(host), port, FALSE); + if(!dns) + result = CURLE_OUT_OF_MEMORY; + } +#endif + else { + /* a query we do not know, just cache nothing */ + DEBUGASSERT(0); + } + if(dns) { /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -610,10 +724,9 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, dnscache_unlock(data, dnscache); CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", host, port, Curl_resolv_query_str(dns_queries)); - return CURLE_OK; } dnscache_unlock(data, dnscache); - return CURLE_OUT_OF_MEMORY; + return result; } /* @@ -697,7 +810,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!curlx_str_number(&host, &num, 0xffff)) { /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), + entry_len = create_dnscache_id(CURL_DNST_ADDR, curlx_str(&source), curlx_strlen(&source), (uint16_t)num, entry_id, sizeof(entry_id)); dnscache_lock(data, dnscache); @@ -802,7 +915,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), + entry_len = create_dnscache_id(CURL_DNST_ADDR, + curlx_str(&source), curlx_strlen(&source), port, entry_id, sizeof(entry_id)); dnscache_lock(data, dnscache); @@ -828,10 +942,10 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); } - /* put this new host in the cache, an override for ALL dns queries */ - dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, - &head, curlx_str(&source), - curlx_strlen(&source), port, permanent); + /* put this new host in the cache, override all address queries */ + dns = dnsc_add_addr(data, dnscache, CURL_DNSQ_ADDR, + &head, curlx_str(&source), + curlx_strlen(&source), port, permanent); if(dns) /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -842,7 +956,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!dns) return CURLE_OUT_OF_MEMORY; - infof(data, "Added %.*s:%u:%s to DNS cache%s", + infof(data, "[DNS] added %.*s:%u:%s to cache%s", (int)curlx_strlen(&source), curlx_str(&source), port, addresses, permanent ? "" : " (non-permanent)"); diff --git a/lib/dnscache.h b/lib/dnscache.h index 9239977cf8c9..c029eb9d3041 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -36,6 +36,10 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; +#define CURL_DNST_INIT '\0' +#define CURL_DNST_ADDR 'A' +#define CURL_DNST_HTTPS 'H' + struct Curl_dns_entry { struct Curl_addrinfo *addr; #ifdef USE_HTTPSRR @@ -47,6 +51,7 @@ struct Curl_dns_entry { uint32_t refcount; /* hostname port number that resolved to addr. */ uint16_t port; + char type; /* CURL_DNST_ADDR or CURL_DNST_HTTPS */ uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */ uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */ /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ @@ -62,13 +67,13 @@ struct Curl_dns_entry { * * Returns entry or NULL on OOM. */ -struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr, const char *hostname, uint16_t port); -struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr1, struct Curl_addrinfo **paddr2, @@ -76,8 +81,10 @@ struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, uint16_t port); #ifdef USE_HTTPSRR -void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, - struct Curl_https_rrinfo *hinfo); +struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + uint16_t port); #endif /* USE_HTTPSRR */ /* unlink a dns entry, frees all resources if it was the last reference. diff --git a/lib/doh.c b/lib/doh.c index b1f138f32c32..f336eb50034b 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -1189,47 +1189,11 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, *hrr = lhrr; return CURLE_OK; err: - Curl_httpsrr_cleanup(lhrr); - curlx_safefree(lhrr); + Curl_httpsrr_destroy(lhrr); return result; } -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) -static void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr) -{ - DEBUGASSERT(hrr); - infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); - if(hrr->alpns[0] != ALPN_none) - infof(data, "HTTPS RR: alpns %u %u %u %u", - hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]); - else - infof(data, "HTTPS RR: no alpns"); - if(hrr->no_def_alpn) - infof(data, "HTTPS RR: no_def_alpn set"); - else - infof(data, "HTTPS RR: no_def_alpn not set"); - if(hrr->ipv4hints) { - doh_print_buf(data, "HTTPS RR: ipv4hints", - hrr->ipv4hints, hrr->ipv4hints_len); - } - else - infof(data, "HTTPS RR: no ipv4hints"); - if(hrr->echconfiglist) { - doh_print_buf(data, "HTTPS RR: ECHConfigList", - hrr->echconfiglist, hrr->echconfiglist_len); - } - else - infof(data, "HTTPS RR: no ECHConfigList"); - if(hrr->ipv6hints) { - doh_print_buf(data, "HTTPS RR: ipv6hint", - hrr->ipv6hints, hrr->ipv6hints_len); - } - else - infof(data, "HTTPS RR: no ipv6hints"); -} -# endif -#endif +#endif /* USE_HTTPSRR */ CURLcode Curl_doh_take_result(struct Curl_easy *data, struct Curl_resolv_async *async, @@ -1250,6 +1214,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { + struct Curl_dns_entry *dns = NULL; DOHcode rc[DOH_SLOT_COUNT]; bool negative = TRUE; int slot; @@ -1277,37 +1242,48 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, } } /* next slot */ - if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { - /* we have an address, of one kind or other */ - struct Curl_dns_entry *dns; - struct Curl_addrinfo *ai; + if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { + if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { + /* we have an address, of one kind or other */ + struct Curl_addrinfo *ai; - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { - CURL_TRC_DNS(data, "hostname: %s", dohp->host); - doh_show(data, &de); - } + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { + CURL_TRC_DNS(data, "hostname: %s", dohp->host); + doh_show(data, &de); + } - result = doh2ai(&de, dohp->host, dohp->port, &ai); - if(result) { - /* a decoded response without any usable address, e.g. only - CNAME records, is an authoritative "no data" answer */ - if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) - async->negative_answer = TRUE; - goto error; - } + result = doh2ai(&de, dohp->host, dohp->port, &ai); + if(result) { + /* a decoded response without any usable address, e.g. only + CNAME records, is an authoritative "no data" answer */ + if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) + async->negative_answer = TRUE; + goto error; + } - /* we got a response, create a dns entry. */ - dns = Curl_dnscache_mk_entry(data, async->dns_queries, - &ai, dohp->host, dohp->port); - if(!dns) { - result = CURLE_OUT_OF_MEMORY; - goto error; + /* we got a response, create a dns entry. */ + dns = Curl_dnsc_mk_addr(data, async->dns_queries, + &ai, dohp->host, dohp->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } /* address processing done */ + else { + /* every query failed. Only NXDOMAIN answers for all of them + make this a negative answer, eligible for caching. */ + async->negative_answer = negative; + result = async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } + } - /* Now add and HTTPSRR information if we have */ #ifdef USE_HTTPSRR + if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { + /* Now add and HTTPSRR information if we have */ + struct Curl_https_rrinfo *hrr = NULL; + if(de.numhttps_rrs > 0 && result == CURLE_OK) { - struct Curl_https_rrinfo *hrr = NULL; result = doh_resp_decode_httpsrr(data, de.https_rrs->val, de.https_rrs->len, &hrr); if(result) { @@ -1316,25 +1292,20 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, goto error; } infof(data, "Some HTTPS RR to process"); -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - doh_print_httpsrr(data, hrr); -#endif - Curl_dns_entry_set_https_rr(dns, hrr); } + Curl_httpsrr_trace(data, hrr); + dns = Curl_dnsc_mk_https(data, &hrr, async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } #endif /* USE_HTTPSRR */ - /* and add the entry to the cache */ + /* and add the entry to the cache */ + if(dns) result = Curl_dnscache_add(data, dns); - *pdns = dns; - } /* address processing done */ - else { - /* every query failed. Only NXDOMAIN answers for all of them - make this a negative answer, eligible for caching. */ - async->negative_answer = negative; - result = async->for_proxy ? - CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; - } - + *pdns = dns; } /* !dohp->pending */ else /* wait for pending DoH transactions to complete */ diff --git a/lib/hostip.c b/lib/hostip.c index 6afe63ff4555..dc39f581b287 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -354,7 +354,7 @@ static bool can_resolve_dns_queries(struct Curl_easy *data, uint8_t dns_queries) { (void)data; - if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) + if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) return FALSE; return TRUE; } @@ -445,7 +445,7 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, result = Curl_async_take_result(data, async, pdns); if(result == CURLE_AGAIN) { - CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, " + CURL_TRC_DNS(data, "[%s] resolve incomplete, responses=%s, " "ongoing=%d for %s:%d", Curl_resolv_query_str(async->dns_queries), Curl_resolv_query_str(async->dns_responses), @@ -458,11 +458,13 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, else if(result) { /* a local failure, not a resolve answer. Keep the error as it is so it does not get treated as one. */ - CURL_TRC_DNS(data, "resolve error %d for %s:%u", + CURL_TRC_DNS(data, "[%s] resolve error %d for %s:%u", + Curl_resolv_query_str(async->dns_queries), (int)result, async->hostname, async->port); } else { - CURL_TRC_DNS(data, "resolve complete for %s:%u", + CURL_TRC_DNS(data, "[%s] resolve complete for %s:%u", + Curl_resolv_query_str(async->dns_queries), async->hostname, async->port); DEBUGASSERT(*pdns); } @@ -514,6 +516,18 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, } #ifdef USE_HTTPSRR + +CURLcode Curl_resolv_https(struct Curl_easy *data, + struct Curl_peer *peer, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ + return Curl_resolv(data, peer, CURL_DNSQ_HTTPS, TRNSPRT_TCP, + for_proxy, timeout_ms, presolv_id, pdns); +} + const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) { @@ -555,6 +569,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, #endif struct Curl_addrinfo *addr = NULL; size_t hostname_len; + bool addr_queries = (dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); CURLcode result = CURLE_OK; *pnegative = FALSE; @@ -564,33 +579,34 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, *pdns = NULL; /* Check for "known" things to resolve ourselves. */ + if(addr_queries) { #ifndef USE_RESOLVE_ON_IPS - if(Curl_is_ipaddr(hostname)) { - /* test655 verifies that the announce is done, even though there - * is no real resolving. So, keep doing this. */ - result = Curl_resolv_announce_start(data, NULL); - if(result) + if(Curl_is_ipaddr(hostname)) { + /* test655 verifies that the announce is done, even though there + * is no real resolving. So, keep doing this. */ + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + /* shortcut literal IP addresses, if we are not told to resolve them. */ + result = Curl_str2addr(hostname, port, &addr); goto out; - /* shortcut literal IP addresses, if we are not told to resolve them. */ - result = Curl_str2addr(hostname, port, &addr); - goto out; - } + } #endif - hostname_len = strlen(hostname); - if(curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { - result = Curl_resolv_announce_start(data, NULL); - if(result) + 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."))) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = get_localhost(port, hostname); + if(!addr) + result = CURLE_OUT_OF_MEMORY; goto out; - addr = get_localhost(port, hostname); - if(!addr) - result = CURLE_OUT_OF_MEMORY; - goto out; + } } - #ifndef CURL_DISABLE_DOH if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { result = Curl_resolv_announce_start(data, NULL); @@ -654,7 +670,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, if(addr) { /* we got a response, create a dns entry, add to cache, return */ DEBUGASSERT(!*pdns); - *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); + *pdns = Curl_dnsc_mk_addr(data, dns_queries, &addr, hostname, port); if(!*pdns) result = CURLE_OUT_OF_MEMORY; } @@ -717,9 +733,9 @@ static CURLcode hostip_resolv(struct Curl_easy *data, } #ifdef DEBUGBUILD - CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)", - hostname, port, Curl_resolv_query_str(dns_queries)); - if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && + CURL_TRC_DNS(data, "[%s] hostip_resolv(%s:%u)", + Curl_resolv_query_str(dns_queries), hostname, port); + if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { infof(data, "DEBUG fail ipv6 resolve"); result = hostip_resolv_failed(data, hostname, for_proxy); @@ -742,6 +758,8 @@ static CURLcode hostip_resolv(struct Curl_easy *data, result = hostip_resolv_start(data, dns_queries, hostname, port, transport, for_proxy, timeout_ms, allowDOH, presolv_id, pdns, &negative); + CURL_TRC_DNS(data, "[%s] hostip_resolv started -> %d", + Curl_resolv_query_str(dns_queries), (int)result); } out: @@ -750,7 +768,8 @@ static CURLcode hostip_resolv(struct Curl_easy *data, if(IS_RESOLV_FAIL(result)) { if(cache_dns && negative) Curl_dnscache_add_negative(data, dns_queries, hostname, port); - failf(data, "Could not resolve: %s:%u", hostname, port); + if(dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s:%u", hostname, port); } else { failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); @@ -967,7 +986,7 @@ static CURLcode resolv_unix(struct Curl_easy *data, return result; } - *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + *pdns = Curl_dnsc_mk_addr(data, 0, &addr, NULL, 0); return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; } #endif /* USE_UNIX_SOCKETS */ @@ -1011,7 +1030,7 @@ CURLcode Curl_resolv(struct Curl_easy *data, timeout_ms = CURL_TIMEOUT_RESOLVE_MS; #ifdef USE_UNIX_SOCKETS - if(peer->unix_socket) + if((dns_queries & CURL_DNSQ_ADDR) && peer->unix_socket) return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); #else if(peer->unix_socket) @@ -1019,14 +1038,17 @@ CURLcode Curl_resolv(struct Curl_easy *data, #endif #ifdef USE_ALARM_TIMEOUT - if(timeout_ms && data->set.no_signal) { - /* Cannot use ALARM when signals are disabled */ - timeout_ms = 0; - } - if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, - transport, for_proxy, timeout_ms, presolv_id, - pdns); + if(dns_queries & CURL_DNSQ_ADDR) { + if(timeout_ms && data->set.no_signal) { + /* Cannot use ALARM when signals are disabled */ + timeout_ms = 0; + } + if(timeout_ms && !Curl_doh_wanted(data)) { + return resolv_alarm_timeout(data, dns_queries, + peer->hostname, peer->port, + transport, for_proxy, timeout_ms, + presolv_id, pdns); + } } #endif /* !USE_ALARM_TIMEOUT */ @@ -1094,7 +1116,8 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, if(async->negative_answer) Curl_dnscache_add_negative(data, async->dns_queries, async->hostname, async->port); - failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s:%u", async->hostname, async->port); } else if(result) { failf(data, "Error %d resolving %s:%u", diff --git a/lib/hostip.h b/lib/hostip.h index 45370ec48e0c..eef799184794 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -52,8 +52,8 @@ struct Curl_peer; #define CURL_DNSQ_AAAA (1U << 1) #define CURL_DNSQ_HTTPS (1U << 2) -#define CURL_DNSQ_ALL (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS) -#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA)) +#define CURL_DNSQ_ADDR (uint8_t)(CURL_DNSQ_A | CURL_DNSQ_AAAA) +#define CURL_DNSQ_IS_ADDR(x) (uint8_t)((x)&(CURL_DNSQ_ADDR)) #ifdef CURLVERBOSE const char *Curl_resolv_query_str(uint8_t dns_queries); @@ -153,6 +153,22 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, int ai_family, unsigned int index); #ifdef USE_HTTPSRR + +/* Start DNS resolving for HTTPS records. Returns + * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked). + * `*presolv_id` is 0. + * - CURLE_AGAIN: resolve is asynchronous and not finished yet. + * `presolv_id` is the identifier for querying results later. + * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. + */ +CURLcode Curl_resolv_https(struct Curl_easy *data, + struct Curl_peer *peer, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns); + + const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id); bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); diff --git a/lib/httpsrr.c b/lib/httpsrr.c index 53647b81ed25..a05a5cb5c15c 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -97,7 +97,7 @@ void Curl_httpsrr_trace(struct Curl_easy *data, struct dynbuf tmp; CURLcode result; - if(!rr || !rr->complete) { + if(!rr) { CURL_TRC_DNS(data, "[HTTPS-RR] not available"); return; } @@ -225,23 +225,15 @@ CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, return result; } -struct Curl_https_rrinfo *Curl_httpsrr_dup_move( - struct Curl_https_rrinfo *rrinfo) +void Curl_httpsrr_destroy(struct Curl_https_rrinfo *rrinfo) { - struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo)); - if(dup) - memset(rrinfo, 0, sizeof(*rrinfo)); - return dup; -} - -void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) -{ - curlx_safefree(rrinfo->target); - curlx_safefree(rrinfo->echconfiglist); - curlx_safefree(rrinfo->ipv4hints); - curlx_safefree(rrinfo->ipv6hints); - curlx_safefree(rrinfo->rrname); - rrinfo->complete = FALSE; + if(rrinfo) { + curlx_free(rrinfo->target); + curlx_free(rrinfo->echconfiglist); + curlx_free(rrinfo->ipv4hints); + curlx_free(rrinfo->ipv6hints); + curlx_free(rrinfo); + } } bool Curl_httpsrr_applicable(struct Curl_easy *data, @@ -269,11 +261,16 @@ static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, } CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, - struct Curl_https_rrinfo *hinfo) + struct Curl_https_rrinfo **phinfo) { - CURLcode result = CURLE_OK; + struct Curl_https_rrinfo *hinfo = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; size_t i; + hinfo = curlx_calloc(1, sizeof(*hinfo)); + if(!hinfo) + goto out; + for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) { const char *target; size_t opt; @@ -297,12 +294,20 @@ CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, opt++) { result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); if(result) - break; + goto out; } } + result = CURLE_OK; + out: - hinfo->complete = !result; - curlx_safefree(hinfo->rrname); + if(result) { + *phinfo = NULL; + Curl_httpsrr_destroy(hinfo); + } + else { + DEBUGASSERT(hinfo); + *phinfo = hinfo; + } return result; } diff --git a/lib/httpsrr.h b/lib/httpsrr.h index 2ee1beab3e76..8d6eed1c936c 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -38,7 +38,6 @@ struct Curl_easy; struct dynbuf; struct Curl_https_rrinfo { - char *rrname; /* if NULL, the same as the URL hostname */ /* * Fields from HTTPS RR. The only mandatory fields are priority and target. * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 @@ -57,16 +56,12 @@ struct Curl_https_rrinfo { BIT(no_def_alpn); /* keytag = 2 */ BIT(mandatory); /* keytag = 0 */ BIT(port_set); /* port value has been assigned */ - BIT(complete); /* values have been successfully assigned */ }; CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen); -struct Curl_https_rrinfo *Curl_httpsrr_dup_move( - struct Curl_https_rrinfo *rrinfo); - -void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); +void Curl_httpsrr_destroy(struct Curl_https_rrinfo *rrinfo); /* TRUE if the record is applicable to the transfer and its connection. */ bool Curl_httpsrr_applicable(struct Curl_easy *data, @@ -85,7 +80,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, #ifdef USE_ARES CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, - struct Curl_https_rrinfo *hinfo); + struct Curl_https_rrinfo **phinfo); #endif /* USE_ARES */ #ifdef CURLVERBOSE diff --git a/lib/multi.c b/lib/multi.c index 265a2472276f..9e3b092e487d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1182,10 +1182,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, break; case MSTATE_CONNECTING: - if(data->conn && !data->conn->bits.dns_resolved) - result = Curl_resolv_pollset(data, ps); - if(!result) - result = mstate_connecting_pollset(data, ps); + result = mstate_connecting_pollset(data, ps); break; case MSTATE_PROTOCONNECT: diff --git a/lib/socks.c b/lib/socks.c index ab3663e88628..71c7d1bb5647 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -105,7 +105,6 @@ struct socks_ctx { uint8_t proxy_type; unsigned char version; BIT(resolve_local); - BIT(start_resolving); BIT(socks4a); }; @@ -318,32 +317,19 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, const struct Curl_addrinfo *ai = NULL; CURLcode result; size_t nwritten; - bool dns_done; *done = FALSE; - if(sx->start_resolving) { - /* need to resolve hostname to add destination address */ - sx->start_resolving = FALSE; - result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->dest, TRNSPRT_TCP, TRUE); - if(result) { - failf(data, "unable to create DNS filter for socks"); - return CURLPX_UNKNOWN_FAIL; - } - } - - /* resolve the hostname by connecting the DNS filter */ - result = Curl_conn_cf_connect(cf->next, data, &dns_done); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, sx->dest); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->dest->hostname); - return CURLPX_RESOLVE_HOST; - } - else if(!dns_done) + if(result != CURLE_AGAIN) { + failf(data, "error %d resolving SOCKS destination %s:%u", + (int)result, sx->dest->hostname, sx->dest->port); + return CURLPX_RESOLVE_HOST; + } return CURLPX_OK; + } - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET, 0); if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; @@ -476,9 +462,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, case SOCKS4_ST_START: Curl_bufq_reset(&sx->iobuf); - sx->start_resolving = FALSE; sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A); - sx->resolve_local = !sx->socks4a; sx->presult = CURLPX_OK; /* SOCKS4 can only do IPv4, insist! */ @@ -526,7 +510,6 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, sxstate(sx, cf, data, SOCKS4_ST_SEND); goto process_state; } - sx->start_resolving = TRUE; sxstate(sx, cf, data, SOCKS4_ST_RESOLVING); FALLTHROUGH(); @@ -835,37 +818,24 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, CURLcode result; CURLproxycode presult = CURLPX_OK; size_t nwritten; - bool dns_done; *done = FALSE; - if(sx->start_resolving) { - /* need to resolve hostname to add destination address */ - sx->start_resolving = FALSE; - result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->dest, TRNSPRT_TCP, TRUE); - if(result) { - failf(data, "unable to create DNS filter for socks"); - return CURLPX_UNKNOWN_FAIL; - } - } - - /* resolve the hostname by connecting the DNS filter */ - result = Curl_conn_cf_connect(cf->next, data, &dns_done); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, sx->dest); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - sx->dest->hostname); - return CURLPX_RESOLVE_HOST; - } - else if(!dns_done) + if(result != CURLE_AGAIN) { + failf(data, "error %d resolving SOCKS destination %s:%u", + (int)result, sx->dest->hostname, sx->dest->port); + return CURLPX_RESOLVE_HOST; + } return CURLPX_OK; + } #ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_V4) - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET6, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET6, 0); #endif if(!ai) - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET, 0); if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", @@ -1037,7 +1007,6 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, switch(sx->state) { case SOCKS_ST_INIT: sx->version = 5; - sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5); sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); @@ -1126,7 +1095,6 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND); goto process_state; } - sx->start_resolving = TRUE; sxstate(sx, cf, data, SOCKS5_ST_RESOLVING); FALLTHROUGH(); @@ -1351,6 +1319,8 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, { struct Curl_cfilter *cf; struct socks_ctx *ctx; + bool resolve_local = FALSE; + uint8_t dns_queries = Curl_resolv_dns_queries(data, ip_version); CURLcode result; if(!dest) @@ -1358,10 +1328,16 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, switch(proxy_type) { case CURLPROXY_SOCKS5: + resolve_local = TRUE; + break; case CURLPROXY_SOCKS5_HOSTNAME: + break; case CURLPROXY_SOCKS4: + resolve_local = TRUE; + dns_queries = (uint8_t)(dns_queries & ~CURL_DNSQ_AAAA); + break; case CURLPROXY_SOCKS4A: - break; /* all supported */ + break; default: failf(data, "unknown proxytype %d option given", proxy_type); return CURLE_COULDNT_CONNECT; @@ -1376,13 +1352,21 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; + ctx->resolve_local = resolve_local; Curl_creds_link(&ctx->creds, creds); Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, BUFQ_OPT_SOFT_LIMIT); result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx); - if(!result) + if(!result) { Curl_conn_cf_insert_after(cf_at, cf); + if(ctx->resolve_local) { + result = Curl_conn_dns_add_addr_resolve(data, cf_at->conn, + cf_at->sockindex, + ctx->dest, dns_queries, + TRNSPRT_TCP); + } + } else socks_proxy_ctx_free(ctx); return result; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 4cfd3fbac427..802ffb7d3055 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -41,6 +41,7 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" +#include "cf-dns.h" #include "vquic/cf-ngtcp2.h" #include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2-proxy.h" @@ -763,19 +764,36 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, } CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, struct Curl_peer *origin, struct Curl_peer *peer) { + CURLcode result; + + (void)data; /* not used in all cases and compilers are stupid */ #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); + result = Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); #elif defined(USE_QUICHE) - return Curl_cf_quiche_insert_after(cf_at, origin, peer); + result = Curl_cf_quiche_insert_after(cf_at, origin, peer); #else (void)cf_at; (void)origin; (void)peer; - return CURLE_NOT_BUILT_IN; + result = CURLE_NOT_BUILT_IN; #endif + +#if defined(USE_HTTPSRR) && defined(USE_ECH) + /* When using ECH, kick off the HTTPS-RR resolve */ + if(!result && (origin->scheme->family == CURLPROTO_HTTP) && + CURLECH_ENABLED(data) && + Curl_ssl_supports(data, SSLSUPP_ECH) && + (data->set.tls_ech != CURLECH_GREASE) && + !data->set.str[STRING_ECH_CONFIG]) { + result = Curl_conn_dns_add_https_resolve(data, cf_at->conn, + cf_at->sockindex, origin); + } +#endif /* USE_HTTPSRR && USE_ECH */ + return result; } CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 5211a9b33a66..8674a3001de9 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -40,6 +40,7 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, int *qlogfdp); CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, struct Curl_peer *origin, struct Curl_peer *peer); diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 02dbb9f7c7e9..816912a13534 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -46,6 +46,7 @@ #include "urldata.h" #include "cfilters.h" +#include "cf-dns.h" #include "vtls/vtls.h" /* generic SSL protos etc */ #include "vtls/vtls_int.h" @@ -1421,8 +1422,20 @@ CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, result = cf_ssl_create(&cf, data, cf_at->conn); if(!result) result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config); - if(!result) + if(!result) { Curl_conn_cf_insert_after(cf_at, cf); +#if defined(USE_HTTPSRR) && defined(USE_ECH) + /* When using ECH, kick off the HTTPS-RR resolve */ + if((origin->scheme->family == CURLPROTO_HTTP) && + CURLECH_ENABLED(data) && + Curl_ssl_supports(data, SSLSUPP_ECH) && + (data->set.tls_ech != CURLECH_GREASE) && + !data->set.str[STRING_ECH_CONFIG]) { + result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex, + origin); + } +#endif /* USE_HTTPSRR && USE_ECH */ + } else if(cf) Curl_conn_cf_discard_chain(&cf, data); return result; diff --git a/tests/data/test2100 b/tests/data/test2100 index 8793c5786583..1bb5361e15f7 100644 --- a/tests/data/test2100 +++ b/tests/data/test2100 @@ -41,6 +41,7 @@ Funny-head: yesyes http +https # requires Debug so that it can use the DoH server without https @@ -55,7 +56,7 @@ IPv6 HTTP GET using DoH (with HTTPS RR) -http://foo.example.com:%HTTPPORT/%TESTNUMBER --doh-url http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001 +https://foo.example.com:%HTTPSPORT/%TESTNUMBER --insecure --doh-insecure --doh-url https://%HOSTIP:%HTTPSPORT/%TESTNUMBER0001 @@ -71,43 +72,43 @@ s/com\x00\x00(\x1c|\x01)/com-00-00!/g; %if HTTPSRR POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message -Content-Length: 33 +Content-Length: 47 -%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPSPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 33 %hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message -Content-Length: 47 +Content-Length: 33 -%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%GET /%TESTNUMBER HTTP/1.1 -Host: foo.example.com:%HTTPPORT +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%GET /%TESTNUMBER HTTP/1.1 +Host: foo.example.com:%HTTPSPORT User-Agent: curl/%VERSION Accept: */* %else POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 33 %hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 33 %hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%GET /%TESTNUMBER HTTP/1.1 -Host: foo.example.com:%HTTPPORT +Host: foo.example.com:%HTTPSPORT User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test716 b/tests/data/test716 index 6f692fbd0918..266826c8d5b1 100644 --- a/tests/data/test716 +++ b/tests/data/test716 @@ -30,7 +30,7 @@ SOCKS5 proxy with too long username # it should never connect to the target server -http://hohoho.example.com:99/%TESTNUMBER -x socks5://%repeat[256 x A]%:b@%HOSTIP:%SOCKSPORT +http://localhost:99/%TESTNUMBER -x socks5://%repeat[256 x A]%:b@%HOSTIP:%SOCKSPORT diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index d883822d54a0..8e2104df19e6 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -133,7 +133,7 @@ def test_06_13_timers(self, env: Env): # no immediately failed attempts, as should be he_timers_set = [line for line in r.trace_lines if re.match(r'.*\[TIMER] \[HAPPY_EYEBALLS] set for', line)] - assert len(he_timers_set) == 2, f'found: {"".join(he_timers_set)}\n{r.dump_logs()}' + assert len(he_timers_set) >= 2, f'found: {"".join(he_timers_set)}\n{r.dump_logs()}' # download using HTTP/3 on missing server with alt-svc pointing there @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 51ed3bbd9dec..0b95472aa725 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -129,3 +129,22 @@ def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd): r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '2', f'{r}' + + # download https: via https: proxytunnel + @pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'), + reason='curl lacks HTTPS-proxy support') + @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available") + def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd): + dnsd.set_answers(addr_a=['127.0.0.1'], + https=['10 . alpn=http/1.1']) + run_env = os.environ.copy() + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' + run_env['CURL_QUICK_EXIT'] = '1' + curl = CurlClient(env=env, run_env=run_env) + url = f'https://localhost:{env.https_port}/data.json' + xargs = curl.get_proxy_args(tunnel=True) + r = curl.http_download(urls=[url], alpn_proto='h2', with_stats=True, + extra_args=xargs) + r.check_response(count=1, http_status=200) + assert r.stats[0]['http_version'] == '1.1', f'{r}' diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index d4052b762fb0..2b556213405d 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -51,7 +51,7 @@ static CURLcode test_unit1607(const char *arg) /* lowercase host and port to retrieve the addresses from hostcache */ const char *host; - int port; + uint16_t port; /* whether we expect a permanent or non-permanent cache entry */ bool permanent; @@ -113,6 +113,7 @@ static CURLcode test_unit1607(const char *arg) easy = curl_easy_init(); if(!easy) goto error; + curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); /* create a multi handle and add the easy handle to it so that the hostcache is setup */ @@ -126,7 +127,8 @@ static CURLcode test_unit1607(const char *arg) Curl_loadhostpairs(easy); - entry_id = (void *)curl_maprintf("%s:%d", tests[i].host, tests[i].port); + entry_id = (void *)curl_maprintf("%c%s:%u", CURL_DNST_ADDR, + tests[i].host, tests[i].port); if(!entry_id) goto error; dns = Curl_hash_pick(&multi->dnscache.entries, diff --git a/tests/unit/unit1658.c b/tests/unit/unit1658.c index 49859c69c8d6..041ced7f293d 100644 --- a/tests/unit/unit1658.c +++ b/tests/unit/unit1658.c @@ -544,8 +544,7 @@ static CURLcode test_unit1658(const char *arg) /* free the generated struct */ if(hrr) { - Curl_httpsrr_cleanup(hrr); - curl_free(hrr); + Curl_httpsrr_destroy(hrr); } } curl_easy_cleanup(easy); From ab8d771d31f015327b555981992ade034603acf0 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 21 Jul 2026 10:30:42 +0200 Subject: [PATCH 0691/1062] lib: reader/writer fixes - clear writer paused bit when destroying writer stack - remove the reader paused bit that turned out not to be a good idea follow-up to b41c28e70af47e1 Closes #22361 --- lib/request.h | 1 - lib/sendf.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request.h b/lib/request.h index 6662cf19e7e0..ec127cb82e1b 100644 --- a/lib/request.h +++ b/lib/request.h @@ -109,7 +109,6 @@ struct SingleRequest { * checks, pausing by client callbacks. */ struct { struct Curl_creader *stack; - BIT(paused); } reader; struct bufq sendbuf; /* data which needs to be send to the server */ size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ diff --git a/lib/sendf.c b/lib/sendf.c index 8f51a772a228..114b550578e7 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -53,6 +53,7 @@ static void cl_reset_writer(struct Curl_easy *data) curlx_free(writer); writer = data->req.writer.stack; } + data->req.writer.paused = FALSE; } static void cl_reset_reader(struct Curl_easy *data) From dfc01ea2a3e01f04a310456af2132e2fc001f933 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 6 Jul 2026 12:59:45 +0200 Subject: [PATCH 0692/1062] hardening: add API guards Add call stacks to easy and multi instances. Record ongoing API calls and callback invocations there to detect recursion and not allowed invocations. Define enums for easy, multi and callbacks in `api.h`. In `api.c` define properties for these functions: - can they recurse - is the easy/multi handle destroyed during the call or should it be good afterwards - is the call allowed when a multi event callback is ongoing - is the call allowed when a notification callback is ongoing Entering a guard - checks that passed CURL*/CURLM* are GOOD on entering - checks that easy handle's `mid` is correct and it is known for it in the multi. - checks that call properties are obeyed (recursion, callback checks) - checks that passed CURL*/CURLM* are GOOD on leaving, unless call is known to kill it Checks for ongoing callbacks inspect the whole call stack and catches nested invocations (which our current flags can not). Call stacks in easy/multi handle are fixed size and will deny recursion when the limit is reached. The current limits are 7 for easy and 15 for multi now. Removes: - multi->in_callback, check is done via call stack - multi->in_ntfy_cb, check is done via call stack The overhead in my tests seems minimal, if noticeable at all. Closes #22237 --- lib/Makefile.inc | 2 + lib/api.c | 398 +++++++++++++++++++++ lib/api.h | 207 +++++++++++ lib/cf-socket.c | 20 +- lib/curl_trc.c | 17 +- lib/cw-out.c | 9 +- lib/doh.c | 4 +- lib/easy.c | 468 +++++++++++++----------- lib/ftp.c | 20 +- lib/ftplistparser.c | 21 +- lib/hostip.c | 5 +- lib/http2.c | 13 +- lib/http_chunks.c | 9 +- lib/multi.c | 813 ++++++++++++++++++++++-------------------- lib/multi_ev.c | 22 +- lib/multi_ntfy.c | 12 +- lib/multihandle.h | 25 +- lib/multiif.h | 12 +- lib/progress.c | 10 +- lib/rtsp.c | 5 +- lib/sendf.c | 25 +- lib/setopt.c | 21 +- lib/transfer.h | 4 + lib/url.c | 2 +- lib/urldata.h | 45 +-- lib/vssh/libssh.c | 11 +- lib/vssh/libssh2.c | 20 +- lib/vtls/openssl.c | 5 +- lib/ws.c | 361 ++++++++++--------- tests/unit/unit3214.c | 2 +- 30 files changed, 1656 insertions(+), 932 deletions(-) create mode 100644 lib/api.c create mode 100644 lib/api.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 266ba52af377..aff75e28a54d 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -154,6 +154,7 @@ LIB_VSSH_HFILES = \ LIB_CFILES = \ altsvc.c \ amigaos.c \ + api.c \ asyn-ares.c \ asyn-base.c \ asyn-thrdd.c \ @@ -289,6 +290,7 @@ LIB_CFILES = \ LIB_HFILES = \ altsvc.h \ amigaos.h \ + api.h \ arpa_telnet.h \ asyn.h \ bufq.h \ diff --git a/lib/api.c b/lib/api.c new file mode 100644 index 000000000000..adcc51b3f893 --- /dev/null +++ b/lib/api.c @@ -0,0 +1,398 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "api.h" +#include "multiif.h" + +struct Curl_eapi_fn_props { + Curl_eapi_fn fn; + uint8_t data_is_killed; /* easy handle is killed in call */ + uint8_t recurse; /* may be called when another call is in progress */ + uint8_t no_event_cb; /* may not be called during a multi event callback */ +}; + +static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { + /* function kill rec !ev */ + { CURL_EAPI_FN_easy_cleanup, 1, 0, 0 }, + { CURL_EAPI_FN_easy_duphandle, 0, 1, 0 }, + { CURL_EAPI_FN_easy_getinfo, 0, 1, 0 }, + { CURL_EAPI_FN_easy_pause, 0, 1, 1 }, + { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0 }, + { CURL_EAPI_FN_easy_perform, 0, 0, 0 }, + { CURL_EAPI_FN_easy_recv, 0, 0, 0 }, + { CURL_EAPI_FN_easy_reset, 0, 0, 0 }, + { CURL_EAPI_FN_easy_send, 0, 0, 0 }, + { CURL_EAPI_FN_easy_setopt, 0, 1, 0 }, + { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0 }, + { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0 }, + { CURL_EAPI_FN_easy_upkeep, 0, 0, 0 }, + { CURL_EAPI_FN_ws_recv, 0, 1, 0 }, + { CURL_EAPI_FN_ws_send, 0, 1, 0 }, + { CURL_EAPI_FN_ws_start_frame, 0, 1, 0 }, +}; + +struct Curl_mapi_fn_props { + Curl_mapi_fn fn; + uint8_t multi_is_killed; /* multi handle is killed during call */ + uint8_t recurse; /* may be called when another call is in progress */ + uint8_t allow_ntfy_cb; /* may be called during a notify callback */ +}; + +static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = { + /* function kill rec ntfy */ + { CURL_MAPI_FN_multi_add_handle, 0, 0, 1 }, + { CURL_MAPI_FN_multi_assign, 0, 1, 1 }, + { CURL_MAPI_FN_multi_cleanup, 1, 0, 0 }, + { CURL_MAPI_FN_multi_fdset, 0, 0, 1 }, + { CURL_MAPI_FN_multi_get_handles, 0, 1, 1 }, + { CURL_MAPI_FN_multi_get_offt, 0, 1, 1 }, + { CURL_MAPI_FN_multi_info_read, 0, 1, 1 }, + { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1 }, + { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1 }, + { CURL_MAPI_FN_multi_perform, 0, 0, 0 }, + { CURL_MAPI_FN_multi_poll, 0, 0, 1 }, + { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1 }, + { CURL_MAPI_FN_multi_setopt, 0, 0, 1 }, + { CURL_MAPI_FN_multi_socket_action, 0, 0, 0 }, + { CURL_MAPI_FN_multi_socket_all, 0, 0, 0 }, + { CURL_MAPI_FN_multi_socket, 0, 0, 0 }, + { CURL_MAPI_FN_multi_timeout, 0, 0, 1 }, + { CURL_MAPI_FN_multi_wait, 0, 0, 1 }, + { CURL_MAPI_FN_multi_waitfds, 0, 0, 1 }, +}; + +struct Curl_cbapi_fn_props { + Curl_cbapi_fn fn; + uint8_t is_event_cb; /* is a multi event processing callback */ +}; + +static const struct Curl_cbapi_fn_props +cbapi_fn_props[CURL_CBAPI_FN_LAST - CURL_CBAPI_FN_START] = { + { CURL_CBAPI_FN_easy_chunk_bgn, 0 }, + { CURL_CBAPI_FN_easy_chunk_end, 0 }, + { CURL_CBAPI_FN_easy_closesocket, 0 }, + { CURL_CBAPI_FN_easy_cr_in_read, 0 }, + { CURL_CBAPI_FN_easy_cr_in_resume_from, 0 }, + { CURL_CBAPI_FN_easy_cw_out_cb, 0 }, + { CURL_CBAPI_FN_easy_fdebug, 0 }, + { CURL_CBAPI_FN_easy_fnmatch_data, 0 }, + { CURL_CBAPI_FN_easy_fopensocket, 0 }, + { CURL_CBAPI_FN_easy_fprereq, 0 }, + { CURL_CBAPI_FN_easy_fprogress, 0 }, + { CURL_CBAPI_FN_easy_fread_func, 0 }, + { CURL_CBAPI_FN_easy_fsockopt, 0 }, + { CURL_CBAPI_FN_easy_fsslctx, 0 }, + { CURL_CBAPI_FN_easy_fwrite_rtp, 0 }, + { CURL_CBAPI_FN_easy_fxferinfo, 0 }, + { CURL_CBAPI_FN_easy_ioctl_func, 0 }, + { CURL_CBAPI_FN_easy_resolver_start, 0 }, + { CURL_CBAPI_FN_easy_seek_func, 0 }, + { CURL_CBAPI_FN_easy_ssh_hostkeyfunc, 0 }, + { CURL_CBAPI_FN_easy_ssh_keyfunc, 0 }, + { CURL_CBAPI_FN_easy_trailer_callback, 0 }, + + { CURL_CBAPI_FN_multi_ntfy_cb, 0 }, + { CURL_CBAPI_FN_multi_push_cb, 0 }, + { CURL_CBAPI_FN_multi_socket_cb, 1 }, + { CURL_CBAPI_FN_multi_timer_cb, 1 }, +}; + +static bool eapi_in_event_cb(struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) { + uint16_t fn = multi->callstack.calls[i]; + if((fn < CURL_CBAPI_FN_LAST) && + cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb) + return TRUE; + } + } + } + return FALSE; +} + +static bool mapi_in_ntfy_cb(struct Curl_multi *multi) +{ + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] == CURL_CBAPI_FN_multi_ntfy_cb) + return TRUE; + } + } + return FALSE; +} + +bool Curl_api_multi_is_in_callback(struct Curl_multi *multi) +{ + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) + return TRUE; + } + } + return FALSE; +} + +bool Curl_api_is_in_callback(struct Curl_easy *data) +{ + if(data && data->multi) { + return Curl_api_multi_is_in_callback(data->multi); + } + return FALSE; +} + +bool Curl_eapi_enter(struct Curl_eapi_guard *guard, + CURL *curl, + Curl_eapi_fn fn, + CURLcode *presult) +{ + struct Curl_easy *data = curl; + const struct Curl_eapi_fn_props *fn_props; + CURLcode result = CURLE_OK; + + guard->depth = 0; + + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_EASY_HANDLE(data)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + /* verify that is either not added to a multi handle OR has a + * GOOD multi handle that knows `data` for `data->mid`. */ + if(data->mid != UINT32_MAX) { + if(GOOD_MULTI_HANDLE(data->multi)) { + if(!Curl_multi_knows_easy(data->multi, data)) { + /* But multi does not know it, something is fishy, better deny call */ + DEBUGASSERT(0); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + } + else { + DEBUGASSERT(0); /* data needs to have a GOOD multi handle */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + } + else if(data->multi) { + DEBUGASSERT(0); /* data should not have a multi handle */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + /* verify that the call `fn` we're about to enter is known + * and check call properties to be admitting. */ + if(fn >= CURL_EAPI_FN_LAST) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + fn_props = &eapi_fn_props[fn]; + DEBUGASSERT(fn_props->fn == fn); + if(!fn_props->recurse) { + if(data->callstack.count) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + data->callstack.calls[data->callstack.count-1])); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + if(data->multi && data->multi->callstack.count) { +#ifdef CURLVERBOSE + + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u with multi call to %u ongoing\n", (uint16_t)fn, + data->multi->callstack.calls[data->multi->callstack.count-1])); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + } + + if(fn_props->no_event_cb && eapi_in_event_cb(data)) { + /* Not allowed to be invoked while an event cb is ongoing */ +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u while event callback ongoing\n", + (uint16_t)fn)); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + + /* all fine, add to data's callstack */ + if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) { + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + data->callstack.calls[data->callstack.count] = (uint16_t)fn; + ++data->callstack.count; + guard->depth = data->callstack.count; + guard->data = fn_props->data_is_killed ? NULL : data; + +out: + if(presult) + *presult = result; + return guard->depth > 0; +} + +void Curl_eapi_leave(struct Curl_eapi_guard *guard) +{ + if(guard->depth) { + /* guard->data is set when handle is supposed to stay alive during call */ + if(guard->data && GOOD_EASY_HANDLE(guard->data)) { + if(guard->depth > guard->data->callstack.count) { + DEBUGASSERT(0); /* something very wrong */ + } + else { + if(guard->depth < guard->data->callstack.count) { + DEBUGASSERT(0); /* someone forgot to clean up */ + } + /* reset to depth the guard was entered in */ + guard->data->callstack.count = (uint16_t)(guard->depth - 1); + } + } + } +} + +bool Curl_mapi_enter(struct Curl_mapi_guard *guard, + CURLM *m, + Curl_mapi_fn fn, + CURLMcode *pmresult) +{ + struct Curl_multi *multi = m; + const struct Curl_mapi_fn_props *fn_props; + CURLMcode mresult = CURLM_OK; + + guard->depth = 0; + + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_MULTI_HANDLE(multi)) { + mresult = CURLM_BAD_HANDLE; + goto out; + } + if(fn >= CURL_MAPI_FN_LAST) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } + fn_props = &mapi_fn_props[fn]; + DEBUGASSERT(fn_props->fn == fn); + if(fn_props->allow_ntfy_cb && mapi_in_ntfy_cb(multi)) { + /* explicitly allowed, even though normal recursion may not */ + } + else if(!fn_props->recurse && multi->callstack.count) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "MAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + multi->callstack.calls[multi->callstack.count-1])); +#endif + mresult = CURLM_RECURSIVE_API_CALL; + goto out; + } + + /* all fine, add to data's callstack */ + if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) { + mresult = CURLM_RECURSIVE_API_CALL; + goto out; + } + multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; + ++multi->callstack.count; + guard->depth = multi->callstack.count; + guard->multi = fn_props->multi_is_killed ? NULL : multi; + +out: + if(pmresult) + *pmresult = mresult; + return guard->depth > 0; +} + +void Curl_mapi_leave(struct Curl_mapi_guard *guard) +{ + if(guard->depth) { + /* guard->data is set when handle is supposed to stay alive during call */ + if(guard->multi && GOOD_MULTI_HANDLE(guard->multi)) { + if(guard->depth > guard->multi->callstack.count) { + DEBUGASSERT(0); /* something very wrong */ + } + else { + if(guard->depth < guard->multi->callstack.count) { + DEBUGASSERT(0); /* someone forgot to clean up */ + } + /* reset to depth the guard was entered in */ + guard->multi->callstack.count = (uint16_t)(guard->depth - 1); + } + } + } +} + +void Curl_cbapi_enter(struct Curl_mapi_guard *guard, + struct Curl_easy *data, + struct Curl_multi *multi, + Curl_cbapi_fn fn) +{ + guard->depth = 0; + + if(!multi) + multi = data ? data->multi : NULL; + /* if not multi is involved here, just leave */ + if(!multi) + return; + /* invalid callback specifier? */ + if((fn >= CURL_CBAPI_FN_LAST) || (fn < CURL_CBAPI_FN_START)) { + DEBUGASSERT(0); + return; + } + DEBUGASSERT(cbapi_fn_props[fn - CURL_CBAPI_FN_START].fn == fn); + if(multi->callstack.count) { + size_t i; + for(i = multi->callstack.count; i; --i) { + if(multi->callstack.calls[i - 1] == fn) { + /* recursive invocation of the same callback */ + DEBUGASSERT(0); + return; + } + } + } + + /* all fine, add to data's callstack */ + /* if multi callstack already at max depth, leave */ + if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) + return; + multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; + ++multi->callstack.count; + guard->depth = multi->callstack.count; + guard->multi = multi; +} + +void Curl_cbapi_leave(struct Curl_mapi_guard *guard) +{ + Curl_mapi_leave(guard); +} diff --git a/lib/api.h b/lib/api.h new file mode 100644 index 000000000000..c6810a392ebb --- /dev/null +++ b/lib/api.h @@ -0,0 +1,207 @@ +#ifndef HEADER_CURL_API_H +#define HEADER_CURL_API_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on easy handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_EASY_HANDLE(x) \ + (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_EASY_HANDLE(x) \ + ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) +#endif + +#define CURLMULTI_MAGIC_NUMBER 0x000bab1e + +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on multi handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER) ? TRUE : \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_MULTI_HANDLE(x) \ + ((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER) +#endif + +/* the API functions called on a CURL* */ +typedef enum { + CURL_EAPI_FN_easy_cleanup, + CURL_EAPI_FN_easy_duphandle, + CURL_EAPI_FN_easy_getinfo, + CURL_EAPI_FN_easy_pause, + CURL_EAPI_FN_easy_perform_ev, + CURL_EAPI_FN_easy_perform, + CURL_EAPI_FN_easy_recv, + CURL_EAPI_FN_easy_reset, + CURL_EAPI_FN_easy_send, + CURL_EAPI_FN_easy_setopt, + CURL_EAPI_FN_easy_ssls_export, + CURL_EAPI_FN_easy_ssls_import, + CURL_EAPI_FN_easy_upkeep, + CURL_EAPI_FN_ws_recv, + CURL_EAPI_FN_ws_send, + CURL_EAPI_FN_ws_start_frame, + CURL_EAPI_FN_LAST +} Curl_eapi_fn; + +/* the API functions called on a CURLM* */ +typedef enum { + CURL_MAPI_FN_multi_add_handle, + CURL_MAPI_FN_multi_assign, + CURL_MAPI_FN_multi_cleanup, + CURL_MAPI_FN_multi_fdset, + CURL_MAPI_FN_multi_get_handles, + CURL_MAPI_FN_multi_get_offt, + CURL_MAPI_FN_multi_info_read, + CURL_MAPI_FN_multi_notify_disable, + CURL_MAPI_FN_multi_notify_enable, + CURL_MAPI_FN_multi_perform, + CURL_MAPI_FN_multi_poll, + CURL_MAPI_FN_multi_remove_handle, + CURL_MAPI_FN_multi_setopt, + CURL_MAPI_FN_multi_socket_action, + CURL_MAPI_FN_multi_socket_all, + CURL_MAPI_FN_multi_socket, + CURL_MAPI_FN_multi_timeout, + CURL_MAPI_FN_multi_wait, + CURL_MAPI_FN_multi_waitfds, + CURL_MAPI_FN_LAST +} Curl_mapi_fn; + +#define CURL_CBAPI_FN_START (16 * 1024) + +/* the callback functions */ +typedef enum { + CURL_CBAPI_FN_easy_chunk_bgn = CURL_CBAPI_FN_START, + CURL_CBAPI_FN_easy_chunk_end, + CURL_CBAPI_FN_easy_closesocket, + CURL_CBAPI_FN_easy_cr_in_read, + CURL_CBAPI_FN_easy_cr_in_resume_from, + CURL_CBAPI_FN_easy_cw_out_cb, + CURL_CBAPI_FN_easy_fdebug, + CURL_CBAPI_FN_easy_fnmatch_data, + CURL_CBAPI_FN_easy_fopensocket, + CURL_CBAPI_FN_easy_fprereq, + CURL_CBAPI_FN_easy_fprogress, + CURL_CBAPI_FN_easy_fread_func, + CURL_CBAPI_FN_easy_fsockopt, + CURL_CBAPI_FN_easy_fsslctx, + CURL_CBAPI_FN_easy_fwrite_rtp, + CURL_CBAPI_FN_easy_fxferinfo, + CURL_CBAPI_FN_easy_ioctl_func, + CURL_CBAPI_FN_easy_resolver_start, + CURL_CBAPI_FN_easy_seek_func, + CURL_CBAPI_FN_easy_ssh_hostkeyfunc, + CURL_CBAPI_FN_easy_ssh_keyfunc, + CURL_CBAPI_FN_easy_trailer_callback, + + CURL_CBAPI_FN_multi_ntfy_cb, + CURL_CBAPI_FN_multi_push_cb, + CURL_CBAPI_FN_multi_socket_cb, + CURL_CBAPI_FN_multi_timer_cb, + + CURL_CBAPI_FN_LAST +} Curl_cbapi_fn; + + +#define CURL_EAPI_MAX_RECURSION 7 + +struct Curl_eapi_stack { + uint16_t count; + uint16_t calls[CURL_EAPI_MAX_RECURSION]; +}; + +struct Curl_eapi_guard { + struct Curl_easy *data; /* != NULL if handle stays */ + uint16_t depth; /* > 0 if this guard was entered */ +}; + +bool Curl_eapi_enter(struct Curl_eapi_guard *guard, + CURL *curl, + Curl_eapi_fn fn, + CURLcode *presult); +void Curl_eapi_leave(struct Curl_eapi_guard *guard); + +/* Curl_eapi_enter() checks for curl being NULL, but windows compiler + * analyzers do not realize this. *sigh* */ +#define CURL_EAPI_ENTER(g, curl, fn, r) \ + Curl_eapi_enter((g), (curl), CURL_EAPI_FN_##fn, (r)) && (curl) +#define CURL_EAPI_LEAVE(g) \ + Curl_eapi_leave(g) + + +#define CURL_MAPI_MAX_RECURSION 15 + +struct Curl_mapi_stack { + uint16_t count; + uint16_t calls[CURL_MAPI_MAX_RECURSION]; +}; + +struct Curl_mapi_guard { + struct Curl_multi *multi; /* != NULL if handle stays */ + uint16_t depth; /* > 0 if this guard was entered */ +}; + +bool Curl_mapi_enter(struct Curl_mapi_guard *guard, + CURLM *m, + Curl_mapi_fn fn, + CURLMcode *pmresult); +void Curl_mapi_leave(struct Curl_mapi_guard *guard); + +/* Curl_mapi_enter() checks for m being NULL, but windows compiler + * analyzers do not realize this. *sigh* */ +#define CURL_MAPI_ENTER(g, m, fn, r) \ + Curl_mapi_enter((g), (m), CURL_MAPI_FN_##fn, (r)) && (m) +#define CURL_MAPI_LEAVE(g) \ + Curl_mapi_leave(g) + + +void Curl_cbapi_enter(struct Curl_mapi_guard *guard, + struct Curl_easy *data, + struct Curl_multi *multi, + Curl_cbapi_fn fn); +void Curl_cbapi_leave(struct Curl_mapi_guard *guard); + +#define CURL_CBAPI_START(g, d, fn) \ + Curl_cbapi_enter((g), (d), NULL, CURL_CBAPI_FN_##fn) +#define CURL_CBAPI_MULTI_START(g, m, fn) \ + Curl_cbapi_enter((g), NULL, (m), CURL_CBAPI_FN_##fn) +#define CURL_CBAPI_END(g) \ + Curl_cbapi_leave(g) +#define CURL_CBAPI_MULTI_END(g) \ + Curl_cbapi_leave(g) + + +bool Curl_api_is_in_callback(struct Curl_easy *data); +bool Curl_api_multi_is_in_callback(struct Curl_multi *multi); + +#endif /* HEADER_CURL_API_H */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 0741f2ba415c..9c65963ce3d4 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -430,11 +430,12 @@ static CURLcode socket_open(struct Curl_easy *data, * might have been changed and this 'new' address will actually be used * here to connect. */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fopensocket); *sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, (struct curl_sockaddr *)addr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } else { /* opensocket callback not set, so create the socket now */ @@ -521,11 +522,12 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn, return 0; if(use_callback && conn && conn->fclosesocket) { + struct Curl_mapi_guard guard; int rc; Curl_multi_will_close(data, sock); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_closesocket); rc = conn->fclosesocket(conn->closesocket_client, sock); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); return rc; } @@ -1220,11 +1222,12 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(data->set.fsockopt) { /* activate callback for setting socket options */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fsockopt); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_IPCXN); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; @@ -2231,13 +2234,14 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); if(data->set.fsockopt) { + struct Curl_mapi_guard guard; int error = 0; /* activate callback for setting socket options */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fsockopt); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(error) return CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 91b180f908a9..1400707a7ad8 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -51,11 +51,11 @@ static void trc_write(struct Curl_easy *data, curl_infotype type, { if(data->set.verbose) { if(data->set.fdebug) { - uint8_t inCallback = Curl_is_in_callback(data); - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } else { static const char s_infotype[CURLINFO_END][3] = { @@ -133,22 +133,21 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, char buf[TRC_LINE_MAX]; size_t len; if(data->set.fdebug) { - uint8_t inCallback = Curl_is_in_callback(data); - + struct Curl_mapi_guard guard; if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) { len = trc_print_ids(data, buf, TRC_LINE_MAX); len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s", (int)size, ptr); len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } else { - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } } else { diff --git a/lib/cw-out.c b/lib/cw-out.c index 35ded4b475d5..5ff4a695a1c1 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -184,9 +184,12 @@ static CURLcode cw_out_cb_write(struct Curl_easy *data, DEBUGASSERT(data->conn); *pnwritten = 0; - Curl_set_in_callback(data, TRUE); - nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_cw_out_cb); + nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data); + CURL_CBAPI_END(&guard); + } CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu", blen, (otype == CW_OUT_HDS) ? "header" : "body", nwritten); diff --git a/lib/doh.c b/lib/doh.c index f336eb50034b..776a96fea886 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -435,7 +435,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, private_data via CURLOPT_PRIVATE if they so choose. */ DEBUGASSERT(!doh->set.private_data); - if(curl_multi_add_handle(multi, doh)) + if(Curl_multi_add_handle(multi, doh)) goto error; *pmid = doh->mid; @@ -1339,7 +1339,7 @@ static void doh_close(struct Curl_easy *data, continue; } /* data->multi might already be reset at this time */ - curl_multi_remove_handle(data->multi, probe_data); + Curl_multi_remove_handle(data->multi, probe_data); Curl_close(&probe_data); } data->sub_xfer_done = NULL; diff --git a/lib/easy.c b/lib/easy.c index 2e2c4d0e252c..fa67c906567a 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -44,6 +44,7 @@ #endif #include "urldata.h" +#include "api.h" #include "transfer.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" @@ -776,7 +777,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) return CURLE_OUT_OF_MEMORY; } - if(multi->in_callback) + if(Curl_api_multi_is_in_callback(multi)) return CURLE_RECURSIVE_API_CALL; /* Copy relevant easy options to the multi handle */ @@ -784,7 +785,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit); data->multi_easy = NULL; /* pretend it does not exist */ - mresult = curl_multi_add_handle(multi, data); + mresult = Curl_multi_add_handle(multi, data); if(mresult) { curl_multi_cleanup(multi); if(mresult == CURLM_OUT_OF_MEMORY) @@ -803,7 +804,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* ignoring the return code is not nice, but atm we cannot really handle a failure here, room for future improvement! */ - (void)curl_multi_remove_handle(multi, data); + (void)Curl_multi_remove_handle(multi, data); sigpipe_restore(&sigpipe_ctx); @@ -817,7 +818,14 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) */ CURLcode curl_easy_perform(CURL *curl) { - return easy_perform(curl, FALSE); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_perform, &result)) { + result = easy_perform(curl, FALSE); + } + CURL_EAPI_LEAVE(&guard); + return result; } #ifdef DEBUGBUILD @@ -827,7 +835,14 @@ CURLcode curl_easy_perform(CURL *curl) */ CURLcode curl_easy_perform_ev(struct Curl_easy *easy) { - return easy_perform(easy, TRUE); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, easy, easy_perform_ev, &result)) { + result = easy_perform(easy, TRUE); + } + CURL_EAPI_LEAVE(&guard); + return result; } #endif @@ -837,13 +852,16 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *easy) */ void curl_easy_cleanup(CURL *curl) { - struct Curl_easy *data = curl; - if(GOOD_EASY_HANDLE(data)) { + struct Curl_eapi_guard guard; + + if(CURL_EAPI_ENTER(&guard, curl, easy_cleanup, NULL)) { + struct Curl_easy *data = curl; struct Curl_sigpipe_ctx sigpipe_ctx; sigpipe_ignore(data, &sigpipe_ctx); Curl_close(&data); sigpipe_restore(&sigpipe_ctx); } + CURL_EAPI_LEAVE(&guard); } /* @@ -853,20 +871,22 @@ void curl_easy_cleanup(CURL *curl) #undef curl_easy_getinfo CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { - struct Curl_easy *data = curl; - va_list arg; - void *paramp; + struct Curl_eapi_guard guard; CURLcode result; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) { + struct Curl_easy *data = curl; + va_list arg; + void *paramp; - va_start(arg, info); - paramp = va_arg(arg, void *); + va_start(arg, info); + paramp = va_arg(arg, void *); - result = Curl_getinfo(data, info, paramp); + result = Curl_getinfo(data, info, paramp); - va_end(arg); + va_end(arg); + } + CURL_EAPI_LEAVE(&guard); return result; } @@ -952,122 +972,123 @@ static void dupeasy_meta_freeentry(void *p) */ CURL *curl_easy_duphandle(CURL *curl) { - struct Curl_easy *data = curl; + struct Curl_eapi_guard guard; struct Curl_easy *outcurl = NULL; - if(!GOOD_EASY_HANDLE(data)) - goto fail; - outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); - if(!outcurl) - goto fail; + if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) { + struct Curl_easy *data = curl; + + outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); + if(!outcurl) + goto fail; - /* - * We setup a few buffers we need. We should probably make them - * get setup on-demand in the code, as that would probably decrease - * the likeliness of us forgetting to init a buffer here in the future. - */ - outcurl->set.buffer_size = data->set.buffer_size; - - Curl_hash_init(&outcurl->meta_hash, 23, - Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry); - curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); - Curl_bufref_init(&outcurl->state.url); - Curl_bufref_init(&outcurl->state.referer); - Curl_netrc_init(&outcurl->state.netrc); - - /* the connection pool is setup on demand */ - outcurl->state.lastconnect_id = -1; - outcurl->state.recent_conn_id = -1; - outcurl->id = -1; - outcurl->mid = UINT32_MAX; - outcurl->master_mid = UINT32_MAX; + /* + * We setup a few buffers we need. We should probably make them + * get setup on-demand in the code, as that would probably decrease + * the likeliness of us forgetting to init a buffer here in the future. + */ + outcurl->set.buffer_size = data->set.buffer_size; + + Curl_hash_init(&outcurl->meta_hash, 23, + Curl_hash_str, curlx_str_key_compare, + dupeasy_meta_freeentry); + curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_bufref_init(&outcurl->state.url); + Curl_bufref_init(&outcurl->state.referer); + Curl_netrc_init(&outcurl->state.netrc); + + /* the connection pool is setup on demand */ + outcurl->state.lastconnect_id = -1; + outcurl->state.recent_conn_id = -1; + outcurl->id = -1; + outcurl->mid = UINT32_MAX; + outcurl->master_mid = UINT32_MAX; #ifndef CURL_DISABLE_HTTP - Curl_llist_init(&outcurl->state.httphdrs, NULL); + Curl_llist_init(&outcurl->state.httphdrs, NULL); #endif - Curl_initinfo(outcurl); + Curl_initinfo(outcurl); - /* copy all userdefined values */ - if(dupset(outcurl, data)) - goto fail; + /* copy all userdefined values */ + if(dupset(outcurl, data)) + goto fail; - outcurl->progress.hide = data->progress.hide; - outcurl->progress.callback = data->progress.callback; + outcurl->progress.hide = data->progress.hide; + outcurl->progress.callback = data->progress.callback; #ifndef CURL_DISABLE_COOKIES - outcurl->state.cookielist = NULL; - if(data->cookies && data->state.cookie_engine) { - /* If cookies are enabled in the parent handle, we enable them - in the clone as well! */ - outcurl->cookies = Curl_cookie_init(); - if(!outcurl->cookies) - goto fail; - outcurl->state.cookie_engine = TRUE; - } + outcurl->state.cookielist = NULL; + if(data->cookies && data->state.cookie_engine) { + /* If cookies are enabled in the parent handle, we enable them + in the clone as well! */ + outcurl->cookies = Curl_cookie_init(); + if(!outcurl->cookies) + goto fail; + outcurl->state.cookie_engine = TRUE; + } - if(data->state.cookielist) { - outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); - if(!outcurl->state.cookielist) - goto fail; - } + if(data->state.cookielist) { + outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) + goto fail; + } #endif - if(Curl_bufref_ptr(&data->state.url)) { - Curl_bufref_set(&outcurl->state.url, - Curl_bufref_dup(&data->state.url), 0, - curl_free); - if(!Curl_bufref_ptr(&outcurl->state.url)) - goto fail; - } - if(Curl_bufref_ptr(&data->state.referer)) { - Curl_bufref_set(&outcurl->state.referer, - Curl_bufref_dup(&data->state.referer), 0, - curl_free); - if(!Curl_bufref_ptr(&outcurl->state.referer)) - goto fail; - } + if(Curl_bufref_ptr(&data->state.url)) { + Curl_bufref_set(&outcurl->state.url, + Curl_bufref_dup(&data->state.url), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.url)) + goto fail; + } + if(Curl_bufref_ptr(&data->state.referer)) { + Curl_bufref_set(&outcurl->state.referer, + Curl_bufref_dup(&data->state.referer), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.referer)) + goto fail; + } - /* Reinitialize an SSL engine for the new handle - * note: the engine name has already been copied by dupset */ - if(outcurl->set.str[STRING_SSL_ENGINE]) { - if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) - goto fail; - } + /* Reinitialize an SSL engine for the new handle + * note: the engine name has already been copied by dupset */ + if(outcurl->set.str[STRING_SSL_ENGINE]) { + if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) + goto fail; + } #ifndef CURL_DISABLE_ALTSVC - if(data->asi) { - outcurl->asi = Curl_altsvc_init(); - if(!outcurl->asi) - goto fail; - if(outcurl->set.str[STRING_ALTSVC]) - (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); - } + if(data->asi) { + outcurl->asi = Curl_altsvc_init(); + if(!outcurl->asi) + goto fail; + if(outcurl->set.str[STRING_ALTSVC]) + (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); + } #endif #ifndef CURL_DISABLE_HSTS - if(data->hsts) { - outcurl->hsts = Curl_hsts_init(); - if(!outcurl->hsts) - goto fail; - if(outcurl->set.str[STRING_HSTS]) - (void)Curl_hsts_loadfile(outcurl, - outcurl->hsts, outcurl->set.str[STRING_HSTS]); - (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); - - /* Copy entries learned at runtime. (E.g. Strict-Transport-Security - headers.) */ - if(Curl_hsts_copy(outcurl->hsts, data->hsts)) - goto fail; - } + if(data->hsts) { + outcurl->hsts = Curl_hsts_init(); + if(!outcurl->hsts) + goto fail; + if(outcurl->set.str[STRING_HSTS]) + (void)Curl_hsts_loadfile(outcurl, + outcurl->hsts, outcurl->set.str[STRING_HSTS]); + (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); + + /* Copy entries learned at runtime. (E.g. Strict-Transport-Security + headers.) */ + if(Curl_hsts_copy(outcurl->hsts, data->hsts)) + goto fail; + } #endif - outcurl->magic = CURLEASY_MAGIC_NUMBER; - - /* we reach this point and thus we are OK */ - + /* we reach this point and thus we are OK */ + outcurl->magic = CURLEASY_MAGIC_NUMBER; + } + CURL_EAPI_LEAVE(&guard); return outcurl; fail: - if(outcurl) { #ifndef CURL_DISABLE_COOKIES curlx_free(outcurl->cookies); @@ -1079,6 +1100,7 @@ CURL *curl_easy_duphandle(CURL *curl) curlx_free(outcurl); } + CURL_EAPI_LEAVE(&guard); return NULL; } @@ -1088,38 +1110,41 @@ CURL *curl_easy_duphandle(CURL *curl) */ void curl_easy_reset(CURL *curl) { - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return; + struct Curl_eapi_guard guard; - Curl_req_hard_reset(&data->req, data); - Curl_hash_clean(&data->meta_hash); + if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) { + struct Curl_easy *data = curl; - /* clear all meta data */ - Curl_meta_reset(data); - /* zero out UserDefined data: */ - Curl_freeset(data); - memset(&data->set, 0, sizeof(struct UserDefined)); - Curl_init_userdefined(data); + Curl_req_hard_reset(&data->req, data); + Curl_hash_clean(&data->meta_hash); - /* zero out Progress data: */ - memset(&data->progress, 0, sizeof(struct Progress)); + /* clear all meta data */ + Curl_meta_reset(data); + /* zero out UserDefined data: */ + Curl_freeset(data); + memset(&data->set, 0, sizeof(struct UserDefined)); + Curl_init_userdefined(data); - /* zero out PureInfo data: */ - Curl_initinfo(data); + /* zero out Progress data: */ + memset(&data->progress, 0, sizeof(struct Progress)); - data->progress.hide = TRUE; - data->state.current_speed = -1; /* init to negative == impossible */ - data->state.recent_conn_id = -1; /* clear remembered connection id */ + /* zero out PureInfo data: */ + Curl_initinfo(data); - /* zero out authentication data: */ - memset(&data->state.authhost, 0, sizeof(struct auth)); - memset(&data->state.authproxy, 0, sizeof(struct auth)); + data->progress.hide = TRUE; + data->state.current_speed = -1; /* init to negative == impossible */ + data->state.recent_conn_id = -1; /* clear remembered connection id */ + + /* zero out authentication data: */ + memset(&data->state.authhost, 0, sizeof(struct auth)); + memset(&data->state.authproxy, 0, sizeof(struct auth)); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) - Curl_http_auth_cleanup_digest(data); + Curl_http_auth_cleanup_digest(data); #endif - data->master_mid = UINT32_MAX; + data->master_mid = UINT32_MAX; + } + CURL_EAPI_LEAVE(&guard); } /* @@ -1137,63 +1162,60 @@ void curl_easy_reset(CURL *curl) */ CURLcode curl_easy_pause(CURL *curl, int action) { + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - bool changed = FALSE; - struct Curl_easy *data = curl; - bool recv_paused, recv_paused_new; - bool send_paused, send_paused_new; - uint8_t in_c; - - if(!GOOD_EASY_HANDLE(data) || !data->conn) - /* crazy input, do not continue */ - return CURLE_BAD_FUNCTION_ARGUMENT; - - in_c = Curl_is_in_callback(data); - if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE) - return CURLE_RECURSIVE_API_CALL; - recv_paused = Curl_xfer_recv_is_paused(data); - recv_paused_new = (action & CURLPAUSE_RECV); - send_paused = Curl_xfer_send_is_paused(data); - send_paused_new = (action & CURLPAUSE_SEND); + if(CURL_EAPI_ENTER(&guard, curl, easy_pause, &result)) { + bool changed = FALSE; + struct Curl_easy *data = curl; + bool recv_paused, recv_paused_new; + bool send_paused, send_paused_new; - if((send_paused != send_paused_new) || - (send_paused_new != Curl_creader_is_paused(data))) { - changed = TRUE; - result = Curl_1st_fatal( - result, Curl_xfer_pause_send(data, send_paused_new)); - } + if(!data->conn) { + /* crazy input, do not continue */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } - if(recv_paused != recv_paused_new) { - changed = TRUE; - result = Curl_1st_fatal( - result, Curl_xfer_pause_recv(data, recv_paused_new)); - } + recv_paused = Curl_xfer_recv_is_paused(data); + recv_paused_new = (action & CURLPAUSE_RECV); + send_paused = Curl_xfer_send_is_paused(data); + send_paused_new = (action & CURLPAUSE_SEND); - /* If not completely pausing both directions now, run again in any case. */ - if(!Curl_xfer_is_blocked(data)) { - /* reset the too-slow time keeper */ - data->state.keeps_speed.tv_sec = 0; - if(data->multi) { - Curl_multi_mark_dirty(data); /* make it run */ - /* On changes, tell application to update its timers. */ - if(changed) { - if(Curl_update_timer(data->multi) && !result) - result = CURLE_ABORTED_BY_CALLBACK; - } + if((send_paused != send_paused_new) || + (send_paused_new != Curl_creader_is_paused(data))) { + changed = TRUE; + result = Curl_1st_fatal( + result, Curl_xfer_pause_send(data, send_paused_new)); } - } - if(!result && changed && !data->state.done && data->multi) - /* pause/unpausing may result in multi event changes */ - if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) - result = CURLE_ABORTED_BY_CALLBACK; + if(recv_paused != recv_paused_new) { + changed = TRUE; + result = Curl_1st_fatal( + result, Curl_xfer_pause_recv(data, recv_paused_new)); + } - if(in_c) - /* this might have called a callback recursively which might have set this - to false again on exit */ - Curl_set_in_callback(data, in_c); + /* If not completely pausing both directions, run again in any case. */ + if(!Curl_xfer_is_blocked(data)) { + /* reset the too-slow time keeper */ + data->state.keeps_speed.tv_sec = 0; + if(data->multi) { + Curl_multi_mark_dirty(data); /* make it run */ + /* On changes, tell application to update its timers. */ + if(changed) { + if(Curl_update_timer(data->multi) && !result) + result = CURLE_ABORTED_BY_CALLBACK; + } + } + } + if(!result && changed && !data->state.done && data->multi) + /* pause/unpausing may result in multi event changes */ + if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) + result = CURLE_ABORTED_BY_CALLBACK; + } +out: + CURL_EAPI_LEAVE(&guard); return result; } @@ -1226,16 +1248,11 @@ static CURLcode easy_connection(struct Curl_easy *data, * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) +CURLcode Curl_easy_recv(struct Curl_easy *data, + void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c; - struct Curl_easy *data = curl; - - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; result = easy_connection(data, &c); if(result) @@ -1250,6 +1267,18 @@ CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n); } +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) +{ + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_recv, &result)) { + result = Curl_easy_recv(curl, buffer, buflen, n); + } + CURL_EAPI_LEAVE(&guard); + return result; +} + #ifndef CURL_DISABLE_WEBSOCKETS CURLcode Curl_connect_only_attach(struct Curl_easy *data) { @@ -1307,16 +1336,17 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n) { - size_t written = 0; + struct Curl_eapi_guard guard; CURLcode result; - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; - result = Curl_senddata(data, buffer, buflen, &written); - *n = written; + if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) { + struct Curl_easy *data = curl; + size_t written = 0; + + result = Curl_senddata(data, buffer, buflen, &written); + *n = written; + } + CURL_EAPI_LEAVE(&guard); return result; } @@ -1325,16 +1355,15 @@ CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, */ CURLcode curl_easy_upkeep(CURL *curl) { - struct Curl_easy *data = curl; - /* Verify that we got an easy handle we can work with. */ - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; + struct Curl_eapi_guard guard; + CURLcode result; - /* Use the common function to keep connections alive. */ - return Curl_cpool_upkeep(data); + if(CURL_EAPI_ENTER(&guard, curl, easy_upkeep, &result)) { + /* Use the common function to keep connections alive. */ + result = Curl_cpool_upkeep((struct Curl_easy *)curl); + } + CURL_EAPI_LEAVE(&guard); + return result; } CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, @@ -1342,13 +1371,15 @@ CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *sdata, size_t sdata_len) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) - return CURLE_RECURSIVE_API_CALL; - return Curl_ssl_session_import(data, session_key, - shmac, shmac_len, sdata, sdata_len); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_import, &result)) { + result = Curl_ssl_session_import((struct Curl_easy *)curl, session_key, + shmac, shmac_len, sdata, sdata_len); + } + CURL_EAPI_LEAVE(&guard); + return result; #else (void)curl; (void)session_key; @@ -1365,12 +1396,15 @@ CURLcode curl_easy_ssls_export(CURL *curl, void *userptr) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) - return CURLE_RECURSIVE_API_CALL; - return Curl_ssl_session_export(data, export_fn, userptr); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_export, &result)) { + result = Curl_ssl_session_export((struct Curl_easy *)curl, + export_fn, userptr); + } + CURL_EAPI_LEAVE(&guard); + return result; #else (void)curl; (void)export_fn; diff --git a/lib/ftp.c b/lib/ftp.c index f3c07d672296..338a7280b037 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1714,10 +1714,11 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -3658,9 +3659,10 @@ static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc) { if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_end); data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); freedirs(ftpc); } ftpc->known_filesize = -1; @@ -4101,11 +4103,12 @@ static CURLcode wc_statemach(struct Curl_easy *data, infof(data, "Wildcard - START of \"%s\"", finfo->filename); if(data->set.chunk_bgn) { long userresponse; - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_bgn); userresponse = data->set.chunk_bgn( finfo, data->set.wildcardptr, (int)Curl_llist_count(&wildcard->filelist)); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename); @@ -4143,9 +4146,10 @@ static CURLcode wc_statemach(struct Curl_easy *data, case CURLWC_SKIP: { if(data->set.chunk_end) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_end); data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } Curl_node_remove(Curl_llist_head(&wildcard->filelist)); wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ? diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 23fbd1f07c40..2fbff9ea38f7 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -323,18 +323,21 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, compare = Curl_fnmatch; /* filter pattern-corresponding filenames */ - Curl_set_in_callback(data, TRUE); - if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { - /* discard symlink which is containing multiple " -> " */ - if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && - (strstr(finfo->strings.target, " -> "))) { + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fnmatch_data); + if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { + /* discard symlink which is containing multiple " -> " */ + if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && + (strstr(finfo->strings.target, " -> "))) { + add = FALSE; + } + } + else { add = FALSE; } + CURL_CBAPI_END(&guard); } - else { - add = FALSE; - } - Curl_set_in_callback(data, FALSE); if(add) { Curl_llist_append(llist, finfo, &infop->list); diff --git a/lib/hostip.c b/lib/hostip.c index dc39f581b287..7427f8fdd9a6 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -363,13 +363,14 @@ CURLcode Curl_resolv_announce_start(struct Curl_easy *data, void *resolver) { if(data->set.resolver_start) { + struct Curl_mapi_guard guard; int rc; CURL_TRC_DNS(data, "announcing resolve to application"); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_resolver_start); rc = data->set.resolver_start(resolver, NULL, data->set.resolver_start_client); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc) { CURL_TRC_DNS(data, "application aborted resolve"); return CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/http2.c b/lib/http2.c index ff3d3d2741fb..8612efc30298 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -817,11 +817,14 @@ static int push_promise(struct Curl_cfilter *cf, goto fail; } - Curl_set_in_callback(data, TRUE); - rv = data->multi->push_cb(data, newhandle, - stream->push_headers_used, &heads, - data->multi->push_userp); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, multi_push_cb); + rv = data->multi->push_cb(data, newhandle, + stream->push_headers_used, &heads, + data->multi->push_userp); + CURL_CBAPI_END(&guard); + } /* free the headers again */ free_push_headers(stream); diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 93f5bb6c4222..fe8e98bd17a3 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -519,9 +519,12 @@ static CURLcode add_last_chunk(struct Curl_easy *data, if(result) goto out; - Curl_set_in_callback(data, TRUE); - rc = data->set.trailer_callback(&trailers, data->set.trailer_data); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_trailer_callback); + rc = data->set.trailer_callback(&trailers, data->set.trailer_data); + CURL_CBAPI_END(&guard); + } if(rc != CURL_TRAILERFUNC_OK) { failf(data, "operation aborted by trailing headers callback"); diff --git a/lib/multi.c b/lib/multi.c index 9e3b092e487d..c18d30be1931 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -72,20 +72,6 @@ #define CURL_TLS_SESSION_SIZE 25 #endif -#define CURL_MULTI_HANDLE 0x000bab1e - -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on multi handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) -#else -#define GOOD_MULTI_HANDLE(x) \ - ((x) && (x)->magic == CURL_MULTI_HANDLE) -#endif - static void move_pending_to_connect(struct Curl_multi *multi, struct Curl_easy *data); static CURLMcode add_next_timeout(const struct curltime *pnow, @@ -247,7 +233,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(!multi) return NULL; - multi->magic = CURL_MULTI_HANDLE; + multi->magic = CURLMULTI_MAGIC_NUMBER; Curl_dnscache_init(&multi->dnscache, dnssize); Curl_mntfy_init(multi); @@ -474,28 +460,16 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, return CURLM_OK; } -CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) +CURLMcode Curl_multi_add_handle(struct Curl_multi *multi, + struct Curl_easy *data) { CURLMcode mresult; - struct Curl_multi *multi = m; - struct Curl_easy *data = curl; - - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; /* Prevent users from adding same easy handle more than once and prevent adding to more than one multi stack */ if(data->multi) return CURLM_ADDED_ALREADY; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->dead) { /* a "dead" handle cannot get added transfers while any existing easy handles are still alive - but if there are none alive anymore, it is @@ -526,18 +500,18 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) Curl_llist_init(&data->state.timeoutlist, NULL); /* - * No failure allowed in this function beyond this point. No modification of - * easy nor multi handle allowed before this except for potential multi's - * connection pool growing which will not be undone in this function no - * matter what. + * No failure allowed in this function beyond this point. No modification + * of easy nor multi handle allowed before this except for potential + * multi's connection pool growing which will not be undone in this + * function no matter what. */ if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; data->state.os_errno = 0; - /* make the Curl_easy refer back to this multi handle - before Curl_expire() - is called. */ + /* make the Curl_easy refer back to this multi handle - before + Curl_expire() is called. */ data->multi = multi; /* set the easy handle */ @@ -589,6 +563,23 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) return CURLM_OK; } +CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) +{ + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_add_handle, &mresult)) { + struct Curl_easy *data = curl; + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(data)) + mresult = CURLM_BAD_EASY_HANDLE; + else + mresult = Curl_multi_add_handle(m, data); + } + CURL_MAPI_LEAVE(&guard); + return mresult; +} + #if 0 /* Debug-function, used like this: * @@ -792,23 +783,14 @@ static void close_connect_only(struct connectdata *conn, connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) +CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, + struct Curl_easy *data) { - struct Curl_multi *multi = m; - struct Curl_easy *data = curl; + CURLMcode mresult; bool premature; struct Curl_llist_node *e; - CURLMcode mresult; uint32_t mid; - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - /* Prevent users from trying to remove same easy handle more than once */ if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ @@ -826,9 +808,6 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) return CURLM_INTERNAL_ERROR; } - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - premature = (data->mstate < MSTATE_COMPLETED); /* If the 'state' is not INIT or COMPLETED, we might need to do something @@ -948,6 +927,22 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) return CURLM_OK; } +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) +{ + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) { + struct Curl_easy *data = curl; + if(!GOOD_EASY_HANDLE(data)) + mresult = CURLM_BAD_EASY_HANDLE; + else + mresult = Curl_multi_remove_handle(m, data); + } + CURL_MAPI_LEAVE(&guard); + return mresult; +} + /* Return TRUE if the application asked for multiplexing */ bool Curl_multiplex_wanted(const struct Curl_multi *multi) { @@ -1272,54 +1267,55 @@ CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd) { - /* Scan through all the easy handles to get the file descriptors set. - Some easy handles may not have connected to the remote host yet, - and then we must make sure that is done. */ - int this_max_fd = -1; - struct Curl_multi *multi = m; - struct easy_pollset ps; - unsigned int i; - uint32_t mid; - (void)exc_fd_set; - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + struct Curl_mapi_guard guard; + CURLMcode mresult; - Curl_pollset_init(&ps); - if(Curl_uint32_bset_first(&multi->process, &mid)) { - do { - struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(CURL_MAPI_ENTER(&guard, m, multi_fdset, &mresult)) { + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_multi *multi = m; + struct easy_pollset ps; + int this_max_fd = -1; + unsigned int i; + uint32_t mid; + (void)exc_fd_set; - if(!data) { - DEBUGASSERT(0); - continue; - } + Curl_pollset_init(&ps); + if(Curl_uint32_bset_first(&multi->process, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - Curl_multi_pollset(data, &ps); - for(i = 0; i < ps.n; i++) { - if(!FDSET_SOCK(ps.sockets[i])) - /* pretend it does not exist */ + if(!data) { + DEBUGASSERT(0); continue; - if(ps.actions[i] & CURL_POLL_IN) - FD_SET(ps.sockets[i], read_fd_set); - if(ps.actions[i] & CURL_POLL_OUT) - FD_SET(ps.sockets[i], write_fd_set); - if((int)ps.sockets[i] > this_max_fd) - this_max_fd = (int)ps.sockets[i]; - } - } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); - } + } - Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, - read_fd_set, write_fd_set, &this_max_fd); + Curl_multi_pollset(data, &ps); + for(i = 0; i < ps.n; i++) { + if(!FDSET_SOCK(ps.sockets[i])) + /* pretend it does not exist */ + continue; + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); + if((int)ps.sockets[i] > this_max_fd) + this_max_fd = (int)ps.sockets[i]; + } + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + } - *max_fd = this_max_fd; - Curl_pollset_cleanup(&ps); + Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, + read_fd_set, write_fd_set, &this_max_fd); - return CURLM_OK; + *max_fd = this_max_fd; + Curl_pollset_cleanup(&ps); + + mresult = CURLM_OK; + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_waitfds(CURLM *m, @@ -1327,46 +1323,49 @@ CURLMcode curl_multi_waitfds(CURLM *m, unsigned int size, unsigned int *fd_count) { - struct Curl_waitfds cwfds; - CURLMcode mresult = CURLM_OK; - struct Curl_multi *multi = m; - struct easy_pollset ps; - unsigned int need = 0; - uint32_t mid; - - if(!ufds && (size || !fd_count)) - return CURLM_BAD_FUNCTION_ARGUMENT; + struct Curl_mapi_guard guard; + CURLMcode mresult; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(CURL_MAPI_ENTER(&guard, m, multi_waitfds, &mresult)) { + struct Curl_waitfds cwfds; + struct Curl_multi *multi = m; + struct easy_pollset ps; + unsigned int need = 0; + uint32_t mid; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + if(!ufds && (size || !fd_count)) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } - Curl_pollset_init(&ps); - Curl_waitfds_init(&cwfds, ufds, size); - if(Curl_uint32_bset_first(&multi->process, &mid)) { - do { - struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - if(!data) { - DEBUGASSERT(0); - Curl_uint32_bset_remove(&multi->process, mid); - Curl_uint32_bset_remove(&multi->dirty, mid); - continue; - } - Curl_multi_pollset(data, &ps); - need += Curl_waitfds_add_ps(&cwfds, &ps); - } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); - } + Curl_pollset_init(&ps); + Curl_waitfds_init(&cwfds, ufds, size); + mresult = CURLM_OK; + if(Curl_uint32_bset_first(&multi->process, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(!data) { + DEBUGASSERT(0); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); + continue; + } + Curl_multi_pollset(data, &ps); + need += Curl_waitfds_add_ps(&cwfds, &ps); + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + } - need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); + need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); - if(need != cwfds.n && ufds) - mresult = CURLM_OUT_OF_MEMORY; + if(need != cwfds.n && ufds) + mresult = CURLM_OUT_OF_MEMORY; - if(fd_count) - *fd_count = need; - Curl_pollset_cleanup(&ps); + if(fd_count) + *fd_count = need; + Curl_pollset_cleanup(&ps); + } +out: + CURL_MAPI_LEAVE(&guard); return mresult; } @@ -1570,12 +1569,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, int wakeup_idx = -1; #endif - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; @@ -1683,7 +1676,14 @@ CURLMcode curl_multi_wait(CURLM *m, int timeout_ms, int *ret) { - return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_wait, &mresult)) { + mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_poll(CURLM *m, @@ -1692,7 +1692,14 @@ CURLMcode curl_multi_poll(CURLM *m, int timeout_ms, int *ret) { - return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_poll, &mresult)) { + mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_wakeup(CURLM *m) @@ -1771,10 +1778,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, { CURLMcode mresult; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - mresult = curl_multi_add_handle(multi, data); + mresult = Curl_multi_add_handle(multi, data); if(!mresult) { CURLcode result; @@ -1782,7 +1786,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, connection, only this transfer */ result = Curl_init_do(data, NULL); if(result) { - curl_multi_remove_handle(multi, data); + Curl_multi_remove_handle(multi, data); return CURLM_INTERNAL_ERROR; } @@ -1955,11 +1959,6 @@ static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done) return CURLE_OK; } -static void set_in_callback(struct Curl_multi *multi, bool value) -{ - multi->in_callback = value; -} - /* * posttransfer() is called immediately after a transfer ends */ @@ -2186,16 +2185,17 @@ static CURLMcode multistate_do(struct Curl_easy *data, CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; if(data->set.fprereq) { + struct Curl_mapi_guard guard; int prereq_rc; /* call the prerequest callback function */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fprereq); prereq_rc = data->set.fprereq(data->set.prereq_userp, data->info.primary.remote_ip, data->info.primary.local_ip, data->info.primary.remote_port, data->info.primary.local_port); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(prereq_rc != CURL_PREREQFUNC_OK) { failf(data, "operation aborted by pre-request callback"); /* failure in pre-request callback - do not do any other processing */ @@ -2740,9 +2740,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLMcode mresult; CURLcode result = CURLE_OK; - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - if(multi->dead) { /* a multi-level callback returned error before, meaning every individual transfer now has failed */ @@ -2911,12 +2908,6 @@ static CURLMcode multi_perform(struct Curl_multi *multi, uint32_t mid; struct Curl_sigpipe_ctx sigpipe_ctx; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - sigpipe_init(&sigpipe_ctx); if(Curl_uint32_bset_first(&multi->process, &mid)) { @@ -2987,32 +2978,35 @@ static CURLMcode multi_perform(struct Curl_multi *multi, CURLMcode curl_multi_perform(CURLM *m, int *running_handles) { - struct Curl_multi *multi = m; - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + struct Curl_mapi_guard guard; + CURLMcode mresult; - return multi_perform(multi, running_handles); + if(CURL_MAPI_ENTER(&guard, m, multi_perform, &mresult)) { + mresult = multi_perform(m, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_cleanup(CURLM *m) { - struct Curl_multi *multi = m; - if(GOOD_MULTI_HANDLE(multi)) { + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_cleanup, &mresult)) { + struct Curl_multi *multi = m; void *entry; uint32_t mid; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; /* First remove all remaining easy handles, * close internal ones. admin handle is special */ if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { do { struct Curl_easy *data = entry; - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_HANDLE; + if(!GOOD_EASY_HANDLE(data)) { + mresult = CURLM_BAD_HANDLE; + goto out; + } #ifdef DEBUGBUILD if(mid != data->mid) { @@ -3089,9 +3083,11 @@ CURLMcode curl_multi_cleanup(CURLM *m) Curl_uint32_tbl_destroy(&multi->xfers); curlx_free(multi); - return CURLM_OK; + mresult = CURLM_OK; } - return CURLM_BAD_HANDLE; +out: + CURL_MAPI_LEAVE(&guard); + return mresult; } /* @@ -3106,30 +3102,32 @@ CURLMcode curl_multi_cleanup(CURLM *m) CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue) { - struct Curl_message *msg; - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMsg *msg_result = NULL; *msgs_in_queue = 0; /* default to none */ + if(CURL_MAPI_ENTER(&guard, m, multi_info_read, NULL)) { + struct Curl_multi *multi = m; + if(Curl_llist_count(&multi->msglist)) { + /* there is one or more messages in the list */ + struct Curl_llist_node *e; + struct Curl_message *msg; - if(GOOD_MULTI_HANDLE(multi) && - !multi->in_callback && - Curl_llist_count(&multi->msglist)) { - /* there is one or more messages in the list */ - struct Curl_llist_node *e; - - /* extract the head of the list to return */ - e = Curl_llist_head(&multi->msglist); + /* extract the head of the list to return */ + e = Curl_llist_head(&multi->msglist); - msg = Curl_node_elem(e); + msg = Curl_node_elem(e); - /* remove the extracted entry */ - Curl_node_remove(e); + /* remove the extracted entry */ + Curl_node_remove(e); - *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); + *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); - return &msg->extmsg; + msg_result = &msg->extmsg; + } } - return NULL; + CURL_MAPI_LEAVE(&guard); + return msg_result; } void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) @@ -3357,126 +3355,125 @@ static CURLMcode multi_socket(struct Curl_multi *multi, #undef curl_multi_setopt CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) { + struct Curl_mapi_guard guard; CURLMcode mresult = CURLM_OK; - va_list param; - unsigned long uarg; - struct Curl_multi *multi = m; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(CURL_MAPI_ENTER(&guard, m, multi_setopt, &mresult)) { + struct Curl_multi *multi = m; + va_list param; + unsigned long uarg; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + va_start(param, option); - va_start(param, option); - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - multi->socket_cb = va_arg(param, curl_socket_callback); - break; - case CURLMOPT_SOCKETDATA: - multi->socket_userp = va_arg(param, void *); - break; - case CURLMOPT_PUSHFUNCTION: - multi->push_cb = va_arg(param, curl_push_callback); - break; - case CURLMOPT_PUSHDATA: - multi->push_userp = va_arg(param, void *); - break; - case CURLMOPT_PIPELINING: - multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; - break; - case CURLMOPT_TIMERFUNCTION: - multi->timer_cb = va_arg(param, curl_multi_timer_callback); - break; - case CURLMOPT_TIMERDATA: - multi->timer_userp = va_arg(param, void *); - break; - case CURLMOPT_MAXCONNECTS: - uarg = va_arg(param, unsigned long); - if(uarg <= UINT_MAX) - multi->maxconnects = (unsigned int)uarg; - break; - case CURLMOPT_MAX_HOST_CONNECTIONS: - if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - /* options formerly used for pipelining */ - case CURLMOPT_MAX_PIPELINE_LENGTH: - break; - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_PIPELINING_SITE_BL: - break; - case CURLMOPT_PIPELINING_SERVER_BL: - break; - case CURLMOPT_MAX_CONCURRENT_STREAMS: { - long streams = va_arg(param, long); - if((streams < 1) || (streams > INT_MAX)) - streams = 100; - multi->max_concurrent_streams = (unsigned int)streams; - break; - } - case CURLMOPT_NETWORK_CHANGED: { - long val = va_arg(param, long); - if(val & CURLMNWC_CLEAR_ALL) - /* In the beginning, all values available to set were 1 by mistake. We - converted this to mean "all", thus setting all the bits - automatically */ - val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; - if(val & CURLMNWC_CLEAR_DNS) { - Curl_dnscache_clear(multi->admin); + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + multi->socket_cb = va_arg(param, curl_socket_callback); + break; + case CURLMOPT_SOCKETDATA: + multi->socket_userp = va_arg(param, void *); + break; + case CURLMOPT_PUSHFUNCTION: + multi->push_cb = va_arg(param, curl_push_callback); + break; + case CURLMOPT_PUSHDATA: + multi->push_userp = va_arg(param, void *); + break; + case CURLMOPT_PIPELINING: + multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; + break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; + case CURLMOPT_MAXCONNECTS: + uarg = va_arg(param, unsigned long); + if(uarg <= UINT_MAX) + multi->maxconnects = (unsigned int)uarg; + break; + case CURLMOPT_MAX_HOST_CONNECTIONS: + if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + /* options formerly used for pipelining */ + case CURLMOPT_MAX_PIPELINE_LENGTH: + break; + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_PIPELINING_SITE_BL: + break; + case CURLMOPT_PIPELINING_SERVER_BL: + break; + case CURLMOPT_MAX_CONCURRENT_STREAMS: { + long streams = va_arg(param, long); + if((streams < 1) || (streams > INT_MAX)) + streams = 100; + multi->max_concurrent_streams = (unsigned int)streams; + break; } - if(val & CURLMNWC_CLEAR_CONNS) { - Curl_cpool_nw_changed(multi->admin); + case CURLMOPT_NETWORK_CHANGED: { + long val = va_arg(param, long); + if(val & CURLMNWC_CLEAR_ALL) + /* In the beginning, all values available to set were 1 by mistake. We + converted this to mean "all", thus setting all the bits + automatically */ + val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; + if(val & CURLMNWC_CLEAR_DNS) { + Curl_dnscache_clear(multi->admin); + } + if(val & CURLMNWC_CLEAR_CONNS) { + Curl_cpool_nw_changed(multi->admin); + } + break; } - break; - } - case CURLMOPT_NOTIFYFUNCTION: - multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback); - break; - case CURLMOPT_NOTIFYDATA: - multi->ntfy.ntfy_cb_data = va_arg(param, void *); - break; - case CURLMOPT_RESOLVE_THREADS_MAX: + case CURLMOPT_NOTIFYFUNCTION: + multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback); + break; + case CURLMOPT_NOTIFYDATA: + multi->ntfy.ntfy_cb_data = va_arg(param, void *); + break; + case CURLMOPT_RESOLVE_THREADS_MAX: #ifdef USE_RESOLV_THREADED - uarg = va_arg(param, long); - if((uarg <= 0) || (uarg > UINT32_MAX)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - else { - CURLcode result = Curl_async_thrdd_multi_set_props( - multi, 0, (uint32_t)uarg, 2000); - switch(result) { - case CURLE_OK: - mresult = CURLM_OK; - break; - case CURLE_BAD_FUNCTION_ARGUMENT: + uarg = va_arg(param, long); + if((uarg <= 0) || (uarg > UINT32_MAX)) mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - case CURLE_OUT_OF_MEMORY: - mresult = CURLM_OUT_OF_MEMORY; - break; - default: - mresult = CURLM_INTERNAL_ERROR; - break; + else { + CURLcode result = Curl_async_thrdd_multi_set_props( + multi, 0, (uint32_t)uarg, 2000); + switch(result) { + case CURLE_OK: + mresult = CURLM_OK; + break; + case CURLE_BAD_FUNCTION_ARGUMENT: + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLE_OUT_OF_MEMORY: + mresult = CURLM_OUT_OF_MEMORY; + break; + default: + mresult = CURLM_INTERNAL_ERROR; + break; + } } - } #endif - break; - case CURLMOPT_QUICK_EXIT: - multi->quick_exit = va_arg(param, long) ? 1 : 0; - break; - default: - mresult = CURLM_UNKNOWN_OPTION; - break; + break; + case CURLMOPT_QUICK_EXIT: + multi->quick_exit = va_arg(param, long) ? 1 : 0; + break; + default: + mresult = CURLM_UNKNOWN_OPTION; + break; + } + va_end(param); } - va_end(param); + CURL_MAPI_LEAVE(&guard); return mresult; } @@ -3485,33 +3482,39 @@ CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, FALSE, s, 0, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket, &mresult)) { + mresult = multi_socket(m, FALSE, s, 0, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, FALSE, s, ev_bitmask, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket_action, &mresult)) { + mresult = multi_socket(m, FALSE, s, ev_bitmask, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket_all, &mresult)) { + mresult = multi_socket(m, TRUE, CURL_SOCKET_BAD, 0, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } static bool multi_has_dirties(struct Curl_multi *multi) @@ -3543,6 +3546,7 @@ static void multi_timeout(struct Curl_multi *multi, VERBOSE(struct Curl_easy *data = NULL); if(multi->dead) { + *expire_time = tv_zero; *timeout_ms = 0; return; } @@ -3599,18 +3603,17 @@ static void multi_timeout(struct Curl_multi *multi, CURLMcode curl_multi_timeout(CURLM *m, long *timeout_ms) { - struct curltime expire_time; - struct Curl_multi *multi = m; - - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + struct Curl_mapi_guard guard; + CURLMcode mresult; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) { + struct curltime expire_time; - multi_timeout(multi, &expire_time, timeout_ms); - return CURLM_OK; + multi_timeout(m, &expire_time, timeout_ms); + mresult = CURLM_OK; + } + CURL_MAPI_LEAVE(&guard); + return mresult; } /* @@ -3619,7 +3622,7 @@ CURLMcode curl_multi_timeout(CURLM *m, */ CURLMcode Curl_update_timer(struct Curl_multi *multi) { - struct curltime expire_ts; + struct curltime expire_ts = { 0, 0 }; long timeout_ms; int rc; bool set_value = FALSE; @@ -3656,11 +3659,13 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) } if(set_value) { + struct Curl_mapi_guard guard; + multi->last_expire_ts = expire_ts; multi->last_timeout_ms = timeout_ms; - set_in_callback(multi, TRUE); + CURL_CBAPI_MULTI_START(&guard, multi, multi_timer_cb); rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); - set_in_callback(multi, FALSE); + CURL_CBAPI_MULTI_END(&guard); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -3858,11 +3863,14 @@ void Curl_expire_clear(struct Curl_easy *data) CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp) { - struct Curl_multi *multi = m; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + struct Curl_mapi_guard guard; + CURLMcode mresult; - return Curl_multi_ev_assign(multi, sockfd, sockp); + if(CURL_MAPI_ENTER(&guard, m, multi_assign, &mresult)) { + mresult = Curl_multi_ev_assign(m, sockfd, sockp); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } static void move_pending_to_connect(struct Curl_multi *multi, @@ -3928,18 +3936,6 @@ static void process_pending_handles(struct Curl_multi *multi) } } -/* 'value' used to be a boolean but can now also contain more info */ -void Curl_set_in_callback(struct Curl_easy *data, uint8_t value) -{ - if(data && data->multi) - data->multi->in_callback = value; -} - -uint8_t Curl_is_in_callback(struct Curl_easy *data) -{ - return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO; -} - unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) { DEBUGASSERT(multi); @@ -3948,24 +3944,31 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) CURL **curl_multi_get_handles(CURLM *m) { - struct Curl_multi *multi = m; - void *entry; - size_t count = Curl_uint32_tbl_count(&multi->xfers); - CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); - if(a) { - unsigned int i = 0; - uint32_t mid; + struct Curl_mapi_guard guard; + CURL **a = NULL; - if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { - do { - struct Curl_easy *data = entry; - DEBUGASSERT(i < count); - if(!data->state.internal) - a[i++] = data; - } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); + if(CURL_MAPI_ENTER(&guard, m, multi_get_handles, NULL)) { + struct Curl_multi *multi = m; + void *entry; + size_t count = Curl_uint32_tbl_count(&multi->xfers); + + a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); + if(a) { + unsigned int i = 0; + uint32_t mid; + + if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { + do { + struct Curl_easy *data = entry; + DEBUGASSERT(i < count); + if(!data->state.internal) + a[i++] = data; + } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); + } + a[i] = NULL; /* last entry is a NULL */ } - a[i] = NULL; /* last entry is a NULL */ } + CURL_MAPI_LEAVE(&guard); return a; } @@ -3973,40 +3976,49 @@ CURLMcode curl_multi_get_offt(CURLM *m, CURLMinfo_offt info, curl_off_t *pvalue) { - struct Curl_multi *multi = m; - uint32_t n; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - if(!pvalue) - return CURLM_BAD_FUNCTION_ARGUMENT; + if(CURL_MAPI_ENTER(&guard, m, multi_get_offt, &mresult)) { + struct Curl_multi *multi = m; + uint32_t n; - switch(info) { - case CURLMINFO_XFERS_CURRENT: - n = Curl_uint32_tbl_count(&multi->xfers); - if(n && multi->admin) - --n; - *pvalue = (curl_off_t)n; - return CURLM_OK; - case CURLMINFO_XFERS_RUNNING: - n = Curl_uint32_bset_count(&multi->process); - if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid)) - --n; - *pvalue = (curl_off_t)n; - return CURLM_OK; - case CURLMINFO_XFERS_PENDING: - *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending); - return CURLM_OK; - case CURLMINFO_XFERS_DONE: - *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent); - return CURLM_OK; - case CURLMINFO_XFERS_ADDED: - *pvalue = multi->xfers_total_ever; - return CURLM_OK; - default: - *pvalue = -1; - return CURLM_UNKNOWN_OPTION; + if(!pvalue) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } + + switch(info) { + case CURLMINFO_XFERS_CURRENT: + n = Curl_uint32_tbl_count(&multi->xfers); + if(n && multi->admin) + --n; + *pvalue = (curl_off_t)n; + break; + case CURLMINFO_XFERS_RUNNING: + n = Curl_uint32_bset_count(&multi->process); + if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid)) + --n; + *pvalue = (curl_off_t)n; + break; + case CURLMINFO_XFERS_PENDING: + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending); + break; + case CURLMINFO_XFERS_DONE: + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent); + break; + case CURLMINFO_XFERS_ADDED: + *pvalue = multi->xfers_total_ever; + break; + default: + *pvalue = -1; + mresult = CURLM_UNKNOWN_OPTION; + break; + } } +out: + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, @@ -4190,6 +4202,11 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, return NULL; } +bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data) +{ + return Curl_uint32_tbl_get(&multi->xfers, data->mid) == data; +} + unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) { DEBUGASSERT(multi); @@ -4212,20 +4229,26 @@ void Curl_multi_clear_dirty(struct Curl_easy *data) CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification) { - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - return Curl_mntfy_enable(multi, notification); + if(CURL_MAPI_ENTER(&guard, m, multi_notify_enable, &mresult)) { + mresult = Curl_mntfy_enable(m, notification); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification) { - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - return Curl_mntfy_disable(multi, notification); + if(CURL_MAPI_ENTER(&guard, m, multi_notify_disable, &mresult)) { + mresult = Curl_mntfy_disable(m, notification); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } #ifdef DEBUGBUILD diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 12d1ca2abba0..3b7eeb0dc3a6 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -34,11 +34,6 @@ #include "uint-spbset.h" #include "multihandle.h" -static void mev_in_callback(struct Curl_multi *multi, uint8_t value) -{ - multi->in_callback = value; -} - #ifdef DEBUGBUILD #define SH_ENTRY_MAGIC 0x570091d #endif @@ -206,12 +201,14 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, /* We managed this socket before, tell the socket callback to forget it. */ if(entry->announced && multi->socket_cb) { + struct Curl_mapi_guard guard; + NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); - mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); + CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->user_data); - mev_in_callback(multi, IN_CALLBACK_NO); + CURL_CBAPI_END(&guard); entry = mev_sh_entry_get(&multi->ev.sh_entries, s); if(entry) entry->announced = FALSE; @@ -282,10 +279,13 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)", s, (comboaction & CURL_POLL_IN) ? "IN" : "", (comboaction & CURL_POLL_OUT) ? "OUT" : ""); - mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); - rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, - entry->user_data); - mev_in_callback(multi, IN_CALLBACK_NO); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb); + rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, + entry->user_data); + CURL_CBAPI_MULTI_END(&guard); + } if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; diff --git a/lib/multi_ntfy.c b/lib/multi_ntfy.c index 1319aaec0745..48c37239519a 100644 --- a/lib/multi_ntfy.c +++ b/lib/multi_ntfy.c @@ -179,8 +179,13 @@ void Curl_mntfy_add(struct Curl_easy *data, unsigned int type) CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi) { - DEBUGASSERT(!multi->in_ntfy_callback); - multi->in_ntfy_callback = TRUE; + struct Curl_mapi_guard guard; + + if(!multi) + return CURLM_BAD_FUNCTION_ARGUMENT; + + CURL_CBAPI_MULTI_START(&guard, multi, multi_ntfy_cb); + while(multi->ntfy.head && !multi->ntfy.failure) { struct mntfy_chunk *chunk = multi->ntfy.head; /* this may cause new notifications to be added! */ @@ -194,7 +199,8 @@ CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi) multi->ntfy.head = chunk->next; mnfty_chunk_destroy(chunk); } - multi->in_ntfy_callback = FALSE; + + CURL_CBAPI_MULTI_END(&guard); if(multi->ntfy.failure) { CURLMcode mresult = multi->ntfy.failure; diff --git a/lib/multihandle.h b/lib/multihandle.h index 1af07fa02ba4..b0c10e92c181 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -23,6 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "api.h" #include "llist.h" #include "hash.h" #include "conncache.h" @@ -45,10 +46,6 @@ struct Curl_message { struct CURLMsg extmsg; }; -#define IN_CALLBACK_NO 0 -#define IN_CALLBACK_YES 1 -#define IN_CALLBACK_FORBID_EASY_PAUSE 2 - /* NOTE: if you add a state here, add the name to the statenames[] array * in curl_trc.c as well! */ @@ -89,14 +86,14 @@ typedef enum { /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up - this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ - unsigned int magic; + this multi handle with an easy handle. + Set this to CURLMULTI_MAGIC_NUMBER. */ + uint32_t magic; + uint32_t xfers_alive; /* amount of added transfers that have + not yet reached COMPLETE state */ + uint32_t xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ - unsigned int xfers_alive; /* amount of added transfers that have - not yet reached COMPLETE state */ - unsigned int xfers_really_alive; /* amount of added transfers that have - passed INIT state but are not COMPLETE yet */ - curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ struct uint32_bset process; /* transfer being processed */ @@ -104,8 +101,12 @@ struct Curl_multi { struct uint32_bset pending; /* transfers in waiting (conn limit etc.) */ struct uint32_bset msgsent; /* transfers done with message for application */ + struct Curl_mapi_stack callstack; /* multi api calls ongoing */ + struct Curl_llist msglist; /* a list of messages from completed transfers */ + curl_off_t xfers_total_ever; /* total of added transfers, ever. */ + struct Curl_easy *admin; /* internal easy handle for admin operations. gets assigned `mid` 0 on multi init */ @@ -194,11 +195,9 @@ struct Curl_multi { #endif uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ uint32_t last_resolv_id; /* id of the last DNS resolve operation */ - uint8_t in_callback; /* conditions for executing in callbacks */ BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ - BIT(in_ntfy_callback); /* true while dispatching notifications */ #ifdef USE_OPENSSL BIT(ssl_seeded); #endif diff --git a/lib/multiif.h b/lib/multiif.h index 023d3253aac1..aaac33e94267 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -37,13 +37,16 @@ void Curl_attach_connection(struct Curl_easy *data, struct connectdata *conn); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); -void Curl_set_in_callback(struct Curl_easy *data, uint8_t value); -uint8_t Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); bool Curl_is_connecting(struct Curl_easy *data); void Curl_multi_connchanged(struct Curl_multi *multi); +CURLMcode Curl_multi_add_handle(struct Curl_multi *multi, + struct Curl_easy *data); +CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, + struct Curl_easy *data); + /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, @@ -148,11 +151,14 @@ void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf); /** * Get the easy handle for the given mid. - * Returns NULL if not found. + * Returns NULL if not found or not a GOOD_EASY_HANDLE() */ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, uint32_t mid); +/* TRUE if multi knows about data via its `mid` */ +bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data); + /* Get the # of transfers current in process/pending. */ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi); diff --git a/lib/progress.c b/lib/progress.c index 72d03edf2de8..45e3db14f5a8 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -659,13 +659,14 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) int rc; if(data->set.fxferinfo) { /* There is a callback set, call that */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fxferinfo); rc = data->set.fxferinfo(data->set.progress_client, data->progress.dl.total_size, data->progress.dl.cur_size, data->progress.ul.total_size, data->progress.ul.cur_size); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURL_PROGRESSFUNC_CONTINUE) { if(rc) { failf(data, "Callback aborted"); @@ -676,13 +677,14 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) } else if(data->set.fprogress) { /* The older deprecated callback is set, call that */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fprogress); rc = data->set.fprogress(data->set.progress_client, (double)data->progress.dl.total_size, (double)data->progress.dl.cur_size, (double)data->progress.ul.total_size, (double)data->progress.ul.cur_size); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURL_PROGRESSFUNC_CONTINUE) { if(rc) { failf(data, "Callback aborted"); diff --git a/lib/rtsp.c b/lib/rtsp.c index 8c5cdd564388..bc8d32622a95 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -598,6 +598,7 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data, static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) { + struct Curl_mapi_guard guard; size_t wrote; curl_write_callback writeit; void *user_ptr; @@ -620,9 +621,9 @@ static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, user_ptr = data->set.out; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fwrite_rtp); wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(wrote == CURL_WRITEFUNC_PAUSE) { failf(data, "Cannot pause RTP"); diff --git a/lib/sendf.c b/lib/sendf.c index 114b550578e7..449a4bc44739 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -678,9 +678,10 @@ static CURLcode cr_in_read(struct Curl_easy *data, } nread = 0; if(ctx->read_cb && blen) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_cr_in_read); nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); ctx->has_used_cb = TRUE; } @@ -778,9 +779,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, return CURLE_READ_ERROR; if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -792,6 +794,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, } /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { + struct Curl_mapi_guard guard; char scratch[4 * 1024]; size_t readthisamountnow = (offset - passed > (curl_off_t)sizeof(scratch)) ? @@ -799,10 +802,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, curlx_sotouz(offset - passed); size_t actuallyread; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_cr_in_resume_from); actuallyread = ctx->read_cb(scratch, 1, readthisamountnow, ctx->cb_user_data); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { @@ -838,11 +841,12 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, return CURLE_OK; if(data->set.seek_func) { + struct Curl_mapi_guard guard; int err; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_seek_func); err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); if(err) { failf(data, "seek callback returned error %d", err); @@ -850,12 +854,13 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, } } else if(data->set.ioctl_func) { + struct Curl_mapi_guard guard; curlioerr err; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ioctl_func); err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, data->set.ioctl_client); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err); if(err) { failf(data, "ioctl callback returned error %d", (int)err); diff --git a/lib/setopt.c b/lib/setopt.c index 9a87e748d2a6..e6a067c5e6fb 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2916,23 +2916,24 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ - #undef curl_easy_setopt CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) { - va_list arg; + struct Curl_eapi_guard guard; CURLcode result; - struct Curl_easy *data = curl; - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, easy_setopt, &result)) { + struct Curl_easy *data = curl; + va_list arg; - va_start(arg, option); + va_start(arg, option); - result = Curl_vsetopt(data, option, arg); + result = Curl_vsetopt(data, option, arg); - va_end(arg); - if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", (unsigned int)option); + va_end(arg); + if(result == CURLE_BAD_FUNCTION_ARGUMENT) + failf(data, "setopt 0x%x got bad argument", (unsigned int)option); + } + CURL_EAPI_LEAVE(&guard); return result; } diff --git a/lib/transfer.h b/lib/transfer.h index 7507ce27bdb9..9bdc93f3ed42 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -147,4 +147,8 @@ CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); * use a forward proxy. */ bool Curl_xfer_is_secure(struct Curl_easy *data); +/* Internal variant of the API function */ +CURLcode Curl_easy_recv(struct Curl_easy *data, + void *buffer, size_t buflen, size_t *n); + #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/lib/url.c b/lib/url.c index dd4e33c9d17c..4ec31a3e5b95 100644 --- a/lib/url.c +++ b/lib/url.c @@ -206,7 +206,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* This handle is still part of a multi handle, take care of this first and detach this handle from there. This detaches the connection. */ - curl_multi_remove_handle(data->multi, data); + Curl_multi_remove_handle(data->multi, data); } else { /* Detach connection if any is left. This should not be normal, but can be diff --git a/lib/urldata.h b/lib/urldata.h index 508ab729c144..16179318c063 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -53,6 +53,7 @@ #include "curlx/timeval.h" +#include "api.h" #include "asyn.h" #include "cookie.h" #include "creds.h" @@ -132,19 +133,6 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #define UPLOADBUFFER_MAX (2 * 1024 * 1024) #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE -#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on easy handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_EASY_HANDLE(x) \ - (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) -#else -#define GOOD_EASY_HANDLE(x) \ - ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) -#endif - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif @@ -1224,34 +1212,35 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ uint32_t magic; - /* once an easy handle is tied to a connection pool a non-negative number to - distinguish this transfer from other using the same pool. For easier - tracking in log output. This may wrap around after LONG_MAX to 0 again, - so it has no uniqueness guarantee for large processings. Note: it has no - uniqueness either IFF more than one connection pool is used by the - libcurl application. */ - curl_off_t id; /* once an easy handle is added to a multi, either explicitly by the * libcurl application or implicitly during `curl_easy_perform()`, * a unique identifier inside this one multi instance. */ uint32_t mid; - uint32_t master_mid; /* if set, this transfer belongs to a master */ - multi_sub_xfer_done_cb *sub_xfer_done; - - struct connectdata *conn; - CURLMstate mstate; /* the handle's state */ CURLcode result; /* previous result */ - struct Curl_message msg; /* A single posted message. */ - + struct connectdata *conn; struct Curl_multi *multi; /* if non-NULL, points to the multi handle struct to which this "belongs" when used by the multi interface */ + struct Curl_eapi_stack callstack; /* easy api calls ongoing */ + + struct Curl_share *share; /* Share, handles global variable mutexing */ + struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle struct to which this "belongs" when used by the easy interface */ - struct Curl_share *share; /* Share, handles global variable mutexing */ + struct Curl_message msg; /* A single posted message. */ + + /* once an easy handle is tied to a connection pool a non-negative number to + distinguish this transfer from other using the same pool. For easier + tracking in log output. This may wrap around after LONG_MAX to 0 again, + so it has no uniqueness guarantee for large processings. Note: it has no + uniqueness either IFF more than one connection pool is used by the + libcurl application. */ + curl_off_t id; + uint32_t master_mid; /* if set, this transfer belongs to a master */ + multi_sub_xfer_done_cb *sub_xfer_done; /* `meta_hash` is a general key-value store for implementations * with the lifetime of the easy handle. diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 3d1203d3b655..89d2f33bcc3c 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -290,6 +290,8 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) } if(func) { /* use callback to determine action */ + struct Curl_mapi_guard guard; + rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); if(rc != SSH_OK) goto cleanup; @@ -321,11 +323,11 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) goto cleanup; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc); rc = func(data, knownkeyp, /* from the knownhosts file */ &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); switch(rc) { case CURLKHSTAT_FINE_ADD_TO_FILE: @@ -1097,10 +1099,11 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 5f1353303359..672e9437c0f0 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -330,6 +330,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, * What hostname does OpenSSH store in its file if an IDN name is * used? */ + struct Curl_mapi_guard guard; enum curl_khmatch keymatch; curl_sshkeycallback func = data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; @@ -402,11 +403,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, keymatch = (enum curl_khmatch)keycheck; /* Ask the callback how to behave */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc); rc = func(data, knownkeyp, /* from the knownhosts file */ &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } } else { @@ -595,11 +596,12 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, &keylen, &sshkeytype); if(remotekey) { + struct Curl_mapi_guard guard; enum curl_khtype keytype = convert_ssh2_keytype(sshkeytype); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_hostkeyfunc); rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, (int)keytype, remotekey, keylen); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURLKHMATCH_OK) { myssh_to(data, sshc, SSH_SESSION_FREE); failf(data, "SSH: callback failed host public key verification"); @@ -1028,10 +1030,11 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -1043,6 +1046,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { + struct Curl_mapi_guard guard; char scratch[4 * 1024]; size_t readthisamountnow = (data->state.resume_from - passed > @@ -1050,11 +1054,11 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); size_t actuallyread; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fread_func); actuallyread = data->state.fread_func(scratch, 1, readthisamountnow, data->state.in); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 094fc3a374f1..23114937ae72 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3961,6 +3961,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + struct Curl_mapi_guard guard; /* When a user callback is installed to modify the SSL_CTX, * we need to do the full initialization before calling it. * See: #11800 */ @@ -3970,10 +3971,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; octx->x509_store_setup = TRUE; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fsslctx); result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, data->set.ssl.fsslctxp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(result) { failf(data, "error signaled by SSL ctx callback"); return result; diff --git a/lib/ws.c b/lib/ws.c index 3082860389bb..335986c06af5 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -762,7 +762,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, out: if(!result) { - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(result == CURLE_AGAIN) result = CURLE_OK; } @@ -1110,7 +1110,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, } } else { - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(result) return result; @@ -1143,7 +1143,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, } /* flush, blocking when in callback */ - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(!result && ws->sendbuf_payload > 0) { *pnsent += ws->sendbuf_payload; buffer += ws->sendbuf_payload; @@ -1581,104 +1581,116 @@ static CURLcode nw_in_recv(void *reader_ctx, size_t *pnread) { struct Curl_easy *data = reader_ctx; - return curl_easy_recv(data, buf, buflen, pnread); + return Curl_easy_recv(data, buf, buflen, pnread); } CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *recv, const struct curl_ws_frame **metap) { - struct Curl_easy *data = curl; - struct connectdata *conn; - struct websocket *ws; - struct ws_collect ctx; + struct Curl_eapi_guard guard; + CURLcode result = CURLE_OK; *recv = 0; *metap = NULL; - if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer)) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, ws_recv, &result)) { + struct Curl_easy *data = curl; + struct connectdata *conn; + struct websocket *ws; + struct ws_collect ctx; - conn = data->conn; - if(!conn) { - /* Unhappy hack with lifetimes of transfers and connection */ - if(!data->set.connect_only) { - failf(data, "[WS] CONNECT_ONLY is required"); - return CURLE_UNSUPPORTED_PROTOCOL; + if(buflen && !buffer) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - Curl_getconnectinfo(data, &conn); + conn = data->conn; if(!conn) { - failf(data, "[WS] connection not found"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] connection is not setup for websocket"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - memset(&ctx, 0, sizeof(ctx)); - ctx.data = data; - ctx.ws = ws; - ctx.buffer = buffer; - ctx.buflen = buflen; - - while(1) { - CURLcode result; - - /* receive more when our buffer is empty */ - if(Curl_bufq_is_empty(&ws->recvbuf)) { - size_t n; - result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); - if(result) - return result; - else if(n == 0) { - /* connection closed */ - infof(data, "[WS] connection expectedly closed?"); - return CURLE_GOT_NOTHING; + /* Unhappy hack with lifetimes of transfers and connection */ + if(!data->set.connect_only) { + failf(data, "[WS] CONNECT_ONLY is required"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; } - CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", - Curl_bufq_len(&ws->recvbuf)); - } - result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, - ws_client_collect, &ctx); - if(result == CURLE_AGAIN) { - if(!ctx.written) { - ws_dec_info(&ws->dec, data, "need more input"); - continue; /* nothing written, try more input */ + Curl_getconnectinfo(data, &conn); + if(!conn) { + failf(data, "[WS] connection not found"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - break; } - else if(result) { - return result; + ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] connection is not setup for websocket"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - else if(ctx.written) { - /* The decoded frame is passed back to our caller. - * There are frames like PING were we auto-respond to and - * that we do not return. For these `ctx.written` is not set. */ - break; + + memset(&ctx, 0, sizeof(ctx)); + ctx.data = data; + ctx.ws = ws; + ctx.buffer = buffer; + ctx.buflen = buflen; + + while(1) { + /* receive more when our buffer is empty */ + if(Curl_bufq_is_empty(&ws->recvbuf)) { + size_t n; + result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); + if(result) + goto out; + else if(n == 0) { + /* connection closed */ + infof(data, "[WS] connection expectedly closed?"); + result = CURLE_GOT_NOTHING; + goto out; + } + CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", + Curl_bufq_len(&ws->recvbuf)); + } + + result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, + ws_client_collect, &ctx); + if(result == CURLE_AGAIN) { + if(!ctx.written) { + ws_dec_info(&ws->dec, data, "need more input"); + continue; /* nothing written, try more input */ + } + break; + } + else if(result) { + goto out; + } + else if(ctx.written) { + /* The decoded frame is passed back to our caller. + * There are frames like PING were we auto-respond to and + * that we do not return. For these `ctx.written` is not set. */ + break; + } } - } - /* update frame information to be passed back */ - update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, - ctx.payload_len, ctx.bufidx); - *metap = &ws->recvframe; - *recv = ws->recvframe.len; - CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - FMT_OFF_T ", %" FMT_OFF_T " left)", - buflen, *recv, ws->recvframe.offset, - ws->recvframe.bytesleft); - /* all's well, try to send any pending control. we do not know - * when the application will call `curl_ws_send()` again. */ - if(!data->set.ws_raw_mode && ws->pending.type) { - CURLcode r2 = ws_enc_add_pending(data, ws); - if(!r2) - (void)ws_flush(data, ws, Curl_is_in_callback(data)); + /* update frame information to be passed back */ + update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, + ctx.payload_len, ctx.bufidx); + *metap = &ws->recvframe; + *recv = ws->recvframe.len; + CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" + FMT_OFF_T ", %" FMT_OFF_T " left)", + buflen, *recv, ws->recvframe.offset, + ws->recvframe.bytesleft); + /* all's well, try to send any pending control. we do not know + * when the application will call `curl_ws_send()` again. */ + if(!data->set.ws_raw_mode && ws->pending.type) { + CURLcode r2 = ws_enc_add_pending(data, ws); + if(!r2) + (void)ws_flush(data, ws, Curl_api_is_in_callback(data)); + } + result = CURLE_OK; } - return CURLE_OK; +out: + CURL_EAPI_LEAVE(&guard); + return result; } static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, @@ -1721,7 +1733,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, result = ws_send_raw_blocking(data, ws, (const char *)out, outlen); n = result ? 0 : outlen; } - else if(data->set.connect_only || Curl_is_in_callback(data)) + else if(data->set.connect_only || Curl_api_is_in_callback(data)) result = Curl_senddata(data, out, outlen, &n); else { result = Curl_xfer_send(data, out, outlen, FALSE, &n); @@ -1805,7 +1817,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, if(!buflen) return CURLE_OK; - if(Curl_is_in_callback(data)) { + if(Curl_api_is_in_callback(data)) { /* When invoked from inside callbacks, we do a blocking send as the * callback will probably not implement partial writes that may then * mess up the ws framing subsequently. @@ -1835,75 +1847,80 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, curl_off_t fragsize, unsigned int flags) { - struct websocket *ws; - const uint8_t *buffer = buffer_arg; + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - struct Curl_easy *data = curl; - size_t ndummy; - size_t *pnsent = sent ? sent : &ndummy; - - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T - ", flags=%x), raw=%d", - buflen, fragsize, flags, data->set.ws_raw_mode); - *pnsent = 0; + if(CURL_EAPI_ENTER(&guard, curl, ws_send, &result)) { + struct websocket *ws; + const uint8_t *buffer = buffer_arg; + struct Curl_easy *data = curl; + size_t ndummy; + size_t *pnsent = sent ? sent : &ndummy; - if(!buffer && buflen) { - failf(data, "[WS] buffer is NULL when buflen is not"); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto out; - } + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x), raw=%d", + buflen, fragsize, flags, data->set.ws_raw_mode); - if(!data->conn && data->set.connect_only) { - result = Curl_connect_only_attach(data); - if(result) - goto out; - } - if(!data->conn) { - failf(data, "[WS] No associated connection"); - result = CURLE_SEND_ERROR; - goto out; - } - ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] Not a websocket transfer"); - result = CURLE_SEND_ERROR; - goto out; - } + *pnsent = 0; - if(data->set.ws_raw_mode) { - /* In raw mode, we write directly to the connection */ - /* try flushing any content still waiting to be sent. */ - result = ws_flush(data, ws, FALSE); - if(result) + if(!buffer && buflen) { + failf(data, "[WS] buffer is NULL when buflen is not"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; + } - if(!buffer) { - failf(data, "[WS] buffer is NULL in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; + if(!data->conn && data->set.connect_only) { + result = Curl_connect_only_attach(data); + if(result) + goto out; } - if(!sent) { - failf(data, "[WS] sent is NULL in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; + if(!data->conn) { + failf(data, "[WS] No associated connection"); + result = CURLE_SEND_ERROR; + goto out; } - if(fragsize || flags) { - failf(data, "[WS] fragsize and flags must be zero in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; + ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] Not a websocket transfer"); + result = CURLE_SEND_ERROR; + goto out; } - result = ws_send_raw(data, buffer, buflen, pnsent); - goto out; - } - /* Not RAW mode, we do the frame encoding */ - result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent); + if(data->set.ws_raw_mode) { + /* In raw mode, we write directly to the connection */ + /* try flushing any content still waiting to be sent. */ + result = ws_flush(data, ws, FALSE); + if(result) + goto out; + + if(!buffer) { + failf(data, "[WS] buffer is NULL in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(!sent) { + failf(data, "[WS] sent is NULL in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(fragsize || flags) { + failf(data, "[WS] fragsize and flags must be zero in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + result = ws_send_raw(data, buffer, buflen, pnsent); + goto out; + } + /* Not RAW mode, we do the frame encoding */ + result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent); + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x, raw=%d) -> %d, %zu", + buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, + *pnsent); + } out: - CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T - ", flags=%x, raw=%d) -> %d, %zu", - buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, - *pnsent); + CURL_EAPI_LEAVE(&guard); return result; } @@ -1923,7 +1940,7 @@ const struct curl_ws_frame *curl_ws_meta(CURL *curl) /* we only return something for websocket, called from within the callback when not using raw mode */ struct Curl_easy *data = curl; - if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && + if(GOOD_EASY_HANDLE(data) && Curl_api_is_in_callback(data) && data->conn && !data->set.ws_raw_mode) { struct websocket *ws; ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); @@ -1937,46 +1954,48 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, unsigned int flags, curl_off_t frame_len) { - struct websocket *ws; + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - struct Curl_easy *data = curl; - - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(data->set.ws_raw_mode) { - failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); - return CURLE_FAILED_INIT; - } + if(CURL_EAPI_ENTER(&guard, curl, ws_start_frame, &result)) { + struct Curl_easy *data = curl; + struct websocket *ws; - CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, - flags, frame_len); + if(data->set.ws_raw_mode) { + failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); + result = CURLE_FAILED_INIT; + goto out; + } - if(!data->conn) { - failf(data, "[WS] No associated connection"); - result = CURLE_SEND_ERROR; - goto out; - } - ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] Not a websocket transfer"); - result = CURLE_SEND_ERROR; - goto out; - } + CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, + flags, frame_len); - if(ws->enc.payload_remain) { - failf(data, "[WS] previous frame not finished"); - result = CURLE_SEND_ERROR; - goto out; - } + if(!data->conn) { + failf(data, "[WS] No associated connection"); + result = CURLE_SEND_ERROR; + goto out; + } + ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] Not a websocket transfer"); + result = CURLE_SEND_ERROR; + goto out; + } - result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len, - &ws->sendbuf); - if(result) - CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", - (int)result); + if(ws->enc.payload_remain) { + failf(data, "[WS] previous frame not finished"); + result = CURLE_SEND_ERROR; + goto out; + } + result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len, + &ws->sendbuf); + if(result) + CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", + (int)result); + } out: + CURL_EAPI_LEAVE(&guard); return result; } diff --git a/tests/unit/unit3214.c b/tests/unit/unit3214.c index 191ef2f3f695..2697886931dd 100644 --- a/tests/unit/unit3214.c +++ b/tests/unit/unit3214.c @@ -48,7 +48,7 @@ static void checksize(const char *name, size_t size, size_t allowed) These sizes were chosen with platforms with 64-bit pointers in mind. */ #define MAX_CURL_EASY 5370 #define MAX_CONNECTDATA 1300 -#define MAX_CURL_MULTI 850 +#define MAX_CURL_MULTI 920 #define MAX_CURL_HTTPPOST 112 #define MAX_CURL_SLIST 16 #define MAX_CURL_KHKEY 24 From 4dc236a1093f407d975ee1329f5a0a97b0736eed Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sun, 12 Jul 2026 03:58:19 +0100 Subject: [PATCH 0693/1062] thrdpool: retry failed thread starts while items wait Verified in test 3306 Closes #22303 --- docs/libcurl/libcurl-env-dbg.md | 5 ++ lib/asyn-thrdd.c | 9 +++ lib/thrdpool.c | 24 ++++++- lib/thrdqueue.c | 21 ++++++ lib/thrdqueue.h | 13 +++- tests/data/Makefile.am | 2 +- tests/data/test3306 | 23 ++++++ tests/http/test_21_resolve.py | 18 +++++ tests/unit/Makefile.inc | 3 +- tests/unit/unit3306.c | 120 ++++++++++++++++++++++++++++++++ 10 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 tests/data/test3306 create mode 100644 tests/unit/unit3306.c diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index 5004b8873d46..1387df4badbf 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -197,6 +197,11 @@ eligible for negative DNS caching, instead of a transient failure. Make libcurl fail a resolve for IPv6 only. +## `CURL_DBG_THRDPOOL_FAIL_STARTS` + +Fail this many thread starts in a thread pool, as if the system +refused to spawn more threads. Read when the pool is created. + ## `CURL_QUICK_EXIT` Make `curl` use the quick exit option, even when built in debug mode. diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index a3166f52f86a..fa06550a5836 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -708,6 +708,15 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, else stutter_ms = 200; timeout_ms = CURLMIN(stutter_ms, timeout_ms); +#else + if(async->queries_ongoing && + !Curl_thrdq_check_started(data->multi->resolv_thrdq)) { + /* The queue has items but starting a worker thread to process + them just failed again; expire soon to check once more, + instead of sleeping on the full resolve timeout. */ + CURL_TRC_DNS(data, "resolver thread start failed again, retrying"); + timeout_ms = CURLMIN(100, timeout_ms); + } #endif Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); } diff --git a/lib/thrdpool.c b/lib/thrdpool.c index e8c7d3643551..51fabf04a665 100644 --- a/lib/thrdpool.c +++ b/lib/thrdpool.c @@ -28,6 +28,7 @@ #include "llist.h" #include "curl_threads.h" #include "curlx/timeval.h" +#include "curlx/strparse.h" #include "thrdpool.h" #ifdef CURLVERBOSE #include "curl_trc.h" @@ -64,6 +65,9 @@ struct curl_thrdpool { uint32_t max_threads; uint32_t idle_time_ms; uint32_t next_id; +#ifdef DEBUGBUILD + int dbg_fail_starts; /* fail this many thread starts */ +#endif BIT(aborted); BIT(detached); }; @@ -166,7 +170,14 @@ static CURLcode thrdslot_start(struct curl_thrdpool *tpool) tpool->refcount++; tslot->running = TRUE; - tslot->thread = Curl_thread_create(thrdslot_run, tslot); +#ifdef DEBUGBUILD + if(tpool->dbg_fail_starts > 0) { + --tpool->dbg_fail_starts; + tslot->thread = curl_thread_t_null; + } + else +#endif + tslot->thread = Curl_thread_create(thrdslot_run, tslot); if(tslot->thread == curl_thread_t_null) { /* never started */ tslot->running = FALSE; thrdpool_unlink(tpool, TRUE); @@ -320,6 +331,17 @@ CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, if(!tpool->name) goto out; +#ifdef DEBUGBUILD + { + const char *p = getenv("CURL_DBG_THRDPOOL_FAIL_STARTS"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, INT_MAX)) + tpool->dbg_fail_starts = (int)l; + } + } +#endif + result = Curl_thrdpool_set_props(tpool, min_threads, max_threads, idle_time_ms); diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c index f68f8e17973c..f7da23f3854d 100644 --- a/lib/thrdqueue.c +++ b/lib/thrdqueue.c @@ -295,10 +295,22 @@ CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, return result; } +bool Curl_thrdq_check_started(struct curl_thrdq *tqueue) +{ + size_t unprocessed; + + Curl_mutex_acquire(&tqueue->lock); + unprocessed = tqueue->aborted ? 0 : Curl_llist_count(&tqueue->sendq); + Curl_mutex_release(&tqueue->lock); + return !unprocessed || + !Curl_thrdpool_signal(tqueue->tpool, (uint32_t)unprocessed); +} + CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) { CURLcode result = CURLE_AGAIN; struct Curl_llist_node *e; + size_t signals = 0; *pitem = NULL; Curl_mutex_acquire(&tqueue->lock); @@ -316,8 +328,17 @@ CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) thrdq_item_destroy(qitem); result = CURLE_OK; } + else + signals = Curl_llist_count(&tqueue->sendq); out: Curl_mutex_release(&tqueue->lock); + /* Signal thread pool unlocked to avoid deadlocks. If items await + * processing while nothing was ready, make sure the pool has a + * thread to work on them. An earlier thread start may have failed, + * which `Curl_thrdq_send()` cannot report to its caller. Without + * this, such items would sit unprocessed until the next send. */ + if(signals) + (void)Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); return result; } diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h index 1144bb4185e8..b6dd92a25d9f 100644 --- a/lib/thrdqueue.h +++ b/lib/thrdqueue.h @@ -86,10 +86,21 @@ CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, * The caller takes ownership of the item received, e.g. the queue * relinquishes all references to item. * Returns CURLE_AGAIN when there is no processed item, setting `pitem` - * to NULL. + * to NULL. When nothing has been processed while items await sending, + * the pool is signalled to make sure a worker thread exists: an + * earlier thread start may have failed. */ CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem); +/* Check that items awaiting processing have a worker thread to run + * them, signalling the pool to start one when needed -- an earlier + * thread start may have failed. Returns TRUE when everything is ok: + * no items are waiting or the pool took the signal. FALSE when a + * thread start just failed again; callers may want to check again + * soon rather than wait indefinitely. + */ +bool Curl_thrdq_check_started(struct curl_thrdq *tqueue); + /* Return TRUE if the passed "item" matches. */ typedef bool Curl_thrdq_item_match_cb(void *item, void *match_data); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 760a37a05f2b..fc15cbc399c6 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -286,7 +286,7 @@ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ test3223 test3224 test3225 test3226 test3227 test3228 test3229 \ \ -test3300 test3301 test3302 test3303 test3304 test3305 \ +test3300 test3301 test3302 test3303 test3304 test3305 test3306 \ \ test3400 test3401 \ \ diff --git a/tests/data/test3306 b/tests/data/test3306 new file mode 100644 index 000000000000..fe2082f6cf5a --- /dev/null +++ b/tests/data/test3306 @@ -0,0 +1,23 @@ + + + + +unittest +threads + + + +# Client-side + + +unittest +Debug + + +CURL_DBG_THRDPOOL_FAIL_STARTS=5 + + +thrdqueue retries failed thread starts on receive + + + diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index e3f0358d741d..eaaf5c6006e2 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -265,6 +265,24 @@ def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd): if env.curl_is_verbose(): assert not [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + # a resolve gets processed even when the first resolver thread + # starts fail, e.g. when the system temporarily refuses to spawn + # threads. The transfer must fail on the (debug-forced) lookup + # failure well before the resolve timeout. + @pytest.mark.skipif(condition=not Env.curl_resolv_threaded(), reason="no threaded resolver") + def test_21_15_resolv_thread_start_fails(self, env: Env, httpd, nghttpx): + run_env = os.environ.copy() + run_env['CURL_DBG_THRDPOOL_FAIL_STARTS'] = '3' + run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = '10' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + url = 'https://test-1.http.curl.invalid/' + r = curl.http_download(urls=[url], with_stats=True, extra_args=[ + '--connect-timeout', '20' + ]) + r.check_exit_code(6) + r.check_stats(count=1, http_status=0, exitcode=6) + assert r.duration < timedelta(seconds=20), f'{r}' + def _clean_files(self, files): for file in files: if os.path.exists(file): diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index 4fe2dc645b50..fd05c070e42c 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -50,4 +50,5 @@ TESTS_C = \ unit3200.c unit3205.c \ unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ unit3227.c \ - unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c unit3400.c + unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c unit3306.c \ + unit3400.c diff --git a/tests/unit/unit3306.c b/tests/unit/unit3306.c new file mode 100644 index 000000000000..5b26ba8244c8 --- /dev/null +++ b/tests/unit/unit3306.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" + +#include "curlx/wait.h" +#include "thrdqueue.h" +#include "curl_threads.h" + +#if defined(USE_THREADS) && defined(DEBUGBUILD) + +struct unit3306_item { + int id; + BIT(processed); +}; + +static struct unit3306_item *unit3306_item_create(int id) +{ + struct unit3306_item *uitem; + uitem = curlx_calloc(1, sizeof(*uitem)); + if(uitem) { + uitem->id = id; + curl_mfprintf(stderr, "created item %d\n", uitem->id); + } + return uitem; +} + +static void unit3306_item_free(void *item) +{ + struct unit3306_item *uitem = item; + curl_mfprintf(stderr, "free item %d\n", uitem->id); + curlx_free(uitem); +} + +static void unit3306_process(void *item) +{ + struct unit3306_item *uitem = item; + curlx_wait_ms(1); + uitem->processed = TRUE; +} + +/* The test runs with CURL_DBG_THRDPOOL_FAIL_STARTS=5 set, making the + * pool's first 5 thread starts fail, as if the system temporarily ran + * against a thread limit. Each of the 3 sends attempts one thread + * start (3 failures) and no thread exists to process the queue. Only + * the receive side signalling the pool again - consuming the + * remaining 2 armed failures, one per empty receive, and then + * starting workers - lets the items get processed. */ +static CURLcode test_unit3306(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + struct curl_thrdq *tqueue; + int i, count = 3, nrecvd; + CURLcode result; + + fail_unless(getenv("CURL_DBG_THRDPOOL_FAIL_STARTS"), + "CURL_DBG_THRDPOOL_FAIL_STARTS must be set for this test"); + + result = Curl_thrdq_create(&tqueue, "unit3306", 0, 0, 2, 1, + unit3306_item_free, unit3306_process, + NULL, NULL); + fail_unless(!result, "queue create"); + for(i = 0; i < count; ++i) { + struct unit3306_item *uitem = unit3306_item_create(i); + fail_unless(uitem, "item create"); + result = Curl_thrdq_send(tqueue, uitem, NULL, 0); + fail_unless(!result, "send"); + } + + /* no send was able to start a thread. Receive until all items come + back processed, which needs the recv side to signal the pool. */ + nrecvd = 0; + for(i = 0; (nrecvd < count) && (i < 10000); ++i) { + void *item; + result = Curl_thrdq_recv(tqueue, &item); + fail_unless(!result || (result == CURLE_AGAIN), "recv"); + if(item) { + struct unit3306_item *uitem = item; + curl_mfprintf(stderr, "received item %d\n", uitem->id); + ++nrecvd; + fail_unless(uitem->processed, "recv unprocessed item"); + unit3306_item_free(item); + } + else + curlx_wait_ms(1); + } + Curl_thrdq_destroy(tqueue, TRUE); + tqueue = NULL; + fail_unless(nrecvd == count, "items not processed"); + + UNITTEST_END_SIMPLE +} + +#else +static CURLcode test_unit3306(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + UNITTEST_END_SIMPLE +} +#endif /* USE_THREADS && DEBUGBUILD */ From c221469032d4f89790b9d3a8cf3cebe4d4794b1d Mon Sep 17 00:00:00 2001 From: Alb3e3 <74142887+Alb3e3@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:22:11 +0200 Subject: [PATCH 0694/1062] urlapi: preserve empty markers in relative URLs Verified in test 1560 Closes #22298 --- lib/urlapi.c | 11 +++++++---- tests/libtest/lib1560.c | 23 +++++++++++++++-------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 1aab7f9418d0..d546b18397cd 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1279,16 +1279,16 @@ static CURLUcode redirect_url(const char *base, const char *relurl, case '#': /* fragment-only change */ - if(u->fragment) + if(u->fragment_present) cutoff = strchr(protsep, '#'); break; default: /* path or query-only change */ - if(u->query && u->query[0]) + if(u->query_present) /* remove existing query */ cutoff = strchr(protsep, '?'); - else if(u->fragment && u->fragment[0]) + else if(u->fragment_present) /* Remove existing fragment */ cutoff = strchr(protsep, '#'); @@ -1772,7 +1772,10 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, /* if the old URL is incomplete (we cannot get an absolute URL in 'oldurl'), replace the existing with the new. Always include "scheme://" to make the URL "complete" */ - uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags & ~CURLU_NO_GUESS_SCHEME); + /* Preserve empty query/fragment separators: they affect where relative + references splice into the base URL. */ + uc = curl_url_get(u, CURLUPART_URL, &oldurl, + (flags & ~CURLU_NO_GUESS_SCHEME) | CURLU_GET_EMPTY); if(uc == CURLUE_OUT_OF_MEMORY) return uc; else if(uc) diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 9c8f0e2b358a..fd8073c87608 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -1629,19 +1629,26 @@ static const struct redircase set_url_list[] = { 0, 0, CURLUE_OK}, {"http://example.org/foo/bar", "#", - "http://example.org/foo/bar", - /* This happens because the parser removes empty fragments */ + "http://example.org/foo/bar#", 0, 0, CURLUE_OK}, {"http://example.org/foo/bar", "?", - "http://example.org/foo/bar", - /* This happens because the parser removes empty queries */ + "http://example.org/foo/bar?", 0, 0, CURLUE_OK}, {"http://example.org/foo/bar", "?#", - "http://example.org/foo/bar", - /* This happens because the parser removes empty queries and fragments */ - 0, 0, CURLUE_OK}, + "http://example.org/foo/bar?#", + 0, 0, CURLUE_OK}, + {"http://host/path?", "#new", + "http://host/path?#new", 0, 0, CURLUE_OK}, + {"http://host/path?#", "#new", + "http://host/path?#new", 0, 0, CURLUE_OK}, + {"http://host/path#", "?new", + "http://host/path?new", 0, 0, CURLUE_OK}, + {"http://host/path?#", "?new", + "http://host/path?new", 0, 0, CURLUE_OK}, + {"http://host/path?#", "sub", + "http://host/sub", 0, 0, CURLUE_OK}, {"http://example.com/please/../gimme/%TESTNUMBER?foobar#hello", "http://example.net/there/it/is/../../tes t case=/%TESTNUMBER0002? yes no", "http://example.net/there/tes%20t%20case=/%TESTNUMBER0002?+yes+no", @@ -1716,7 +1723,7 @@ static int set_url(void) } else { char *url = NULL; - rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); + rc = curl_url_get(urlp, CURLUPART_URL, &url, CURLU_GET_EMPTY); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); From 0c4eebb3fa9d18f04b03c2bb2a53ef670bf6e389 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 20 Jul 2026 13:50:34 +0200 Subject: [PATCH 0695/1062] test: adjust test_06_13 for 0100::/64 being blackholed Fixes #22344 Reported-by: Carlos Henrique Lima Melara Closes #22355 --- tests/http/test_06_eyeballs.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index 8e2104df19e6..1dc3726f4887 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -109,7 +109,16 @@ def test_06_12_stats_fail_tcp(self, env: Env, httpd, nghttpx): @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") def test_06_13_timers(self, env: Env): curl = CurlClient(env=env) - # IPv6 0100::/64 is supposed to go into the void (rfc6666) + # IPv6 0100::/64 is supposed to go into the void (rfc6666), but in + # some implementations, this is broken. Try to detect this + r = curl.http_download(urls=['https://xxx.invalid/'], extra_args=[ + '--resolve', 'xxx.invalid:443:0100::1', + '--connect-timeout', '0.5', + ]) + # this should error with CURLE_OPERATION_TIMEDOUT + if r.exit_code != 28: + pytest.skip('system does not blackhole 0100::/64') + r = curl.http_download(urls=['https://xxx.invalid/'], extra_args=[ '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', '--connect-timeout', '1', From a53bae792280e2cb947712fb3a9bd38c76db42eb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 21 Jul 2026 23:55:11 +0200 Subject: [PATCH 0696/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 66 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ba6f25d390a8..2c0f27e4e09c 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,12 +4,13 @@ curl and libcurl 8.22.0 Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1494 - Contributors: 3745 + Authors: 1497 + Contributors: 3749 This release includes the following changes: o gssapi: add support for Apple GSS Framework [72] + o hardening: add API guards [64] o TLS: drop support for TLS-SRP [71] This release includes the following bugfixes: @@ -20,6 +21,7 @@ This release includes the following bugfixes: o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] + o cfilters: fix event-based connection shutdown [91] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] o cmake: flatten build tree, tidy up base dir variables [12] @@ -44,24 +46,33 @@ This release includes the following bugfixes: o ftp: reject control bytes in ACCT and alternative-to-user [26] o gopher: reject CR and LF in the selector [1] o hostip: only cache negative resolves for authoritative answers [16] + o http: avoid length underflow in Curl_compareheader [78] + o http: stop dropping large custom headers [69] o http: trim custom header name before the Authorization drop [17] + o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] o INSTALL.md: add building-from-source overview section [29] + o INTERNALS.md: require quiche 0.20.0+ [101] o ldap: support insecure mode for Windows native LDAP [3] o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] o lib: add "Curl_" prefix to two global functions [84] + o lib: add multi_wakeup_internal [86] o lib: fix 'ns' -> 'us' in trace messages [57] o lib: ratelimit timestamps [14] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] + o mime.c: avoid integer overflow in base64 size calculation [105] o mime: reject CR and LF in mail part name and filename [30] o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] + o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] o openssl+sectrust: fix session reuse [4] o openssl+sectrust: move session verified set into result check [82] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] + o quiche: set the max field section size [100] + o runtests: flush cached test parts when (re)loading a file [95] o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] @@ -69,18 +80,31 @@ This release includes the following bugfixes: o setopt: error for CURLOPT_SHARE when easy handle is used [68] o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] + o ssls: fix potential memory leak on import [96] o sws: allow connection-monitor to log all disconnects [2] o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] + o test1560: allow to build and run without LDAP support [109] + o test798: force IPv4 to avoid cross-runner port aliasing [97] + o test: adjust test_06_13 for 0100::/64 being blackholed [13] + o tests: fix type promotion on 32-bit arches in http test code [88] o tests: remove test1701 [58] o tests: skip test 311 for wolfSSL 5.9.2 [63] + o thrdpool: retry failed thread starts while items wait [62] + o tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) [102] o tidy-up: typos, comment nits [60] + o timeval: make `Curl_freq` variable static (Windows) [90] o tool: do not flush on out-null [38] o tool: fix memory use in parallel mode [59] o tool: init progress bar on demand [39] o tool_cb_hdr: de-duplicate filename setter [24] + o tool_cb_prg: avoid integer overflows [93] o tool_operate: remove call to abort() [23] + o uint-spbset: reused empty chunks [67] + o unit3214: fix to pass on systems with >=128-bit pointers [107] o url: reject control codes in credentials set via CURLOPT [70] + o urlapi: allow URLs to not have userauth (hostname) [92] o urlapi: do not keep an internal port string [31] + o urlapi: preserve empty markers in relative URLs [61] o vquic: add Curl_ prefix to some global functions [76] o vssh: keyfile use cleanups [83] o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] @@ -106,14 +130,16 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Alhuda Khan, Bigtang on hackerone, Bryan Henderson, Christian Ullrich, - Christoph Reiter, Dan Fandrich, Daniel Stenberg, dependabot[bot], + Alb3e3, Alhuda Khan, Bigtang on hackerone, Bill Mill, Bryan Henderson, + Carlos Henrique Lima Melara, Christian Ullrich, Christoph Reiter, + Collin Funk, Dan Fandrich, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, - Laurent Sabourin, Memduh Çelik, Patrick Monnerat, Ray Satiro, renovate[bot], - Roger Leigh, Sam James, Samuel Dainard, smaeljaish on hackerone, - Stefan Eissing, stze on hackerone, Viktor Szakats - (29 contributors) + Keng-Yu Lin, Laurent Sabourin, Memduh Çelik, Patrick Monnerat, + pszemus on github, Ray Satiro, renovate[bot], Roger Leigh, Sam James, + Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, + stze on hackerone, Viktor Szakats, xmoezzz on github + (37 contributors) References to bug reports and discussions on issues: @@ -129,6 +155,7 @@ References to bug reports and discussions on issues: [10] = https://curl.se/bug/?i=22191 [11] = https://curl.se/bug/?i=22193 [12] = https://curl.se/bug/?i=22192 + [13] = https://curl.se/bug/?i=22344 [14] = https://curl.se/bug/?i=22292 [15] = https://curl.se/bug/?i=22187 [16] = https://curl.se/bug/?i=22302 @@ -176,8 +203,13 @@ References to bug reports and discussions on issues: [58] = https://curl.se/bug/?i=22280 [59] = https://curl.se/bug/?i=22277 [60] = https://curl.se/bug/?i=22294 + [61] = https://curl.se/bug/?i=22298 + [62] = https://curl.se/bug/?i=22303 [63] = https://curl.se/bug/?i=22286 + [64] = https://curl.se/bug/?i=22237 + [67] = https://curl.se/bug/?i=22340 [68] = https://curl.se/bug/?i=22253 + [69] = https://curl.se/bug/?i=22336 [70] = https://curl.se/bug/?i=22236 [71] = https://curl.se/bug/?i=21965 [72] = https://curl.se/bug/?i=22052 @@ -186,8 +218,26 @@ References to bug reports and discussions on issues: [75] = https://curl.se/bug/?i=22255 [76] = https://curl.se/bug/?i=22254 [77] = https://curl.se/bug/?i=22250 + [78] = https://curl.se/bug/?i=22338 [79] = https://curl.se/bug/?i=22251 [80] = https://curl.se/bug/?i=22225 [82] = https://curl.se/bug/?i=22248 [83] = https://curl.se/bug/?i=22243 [84] = https://curl.se/bug/?i=22245 + [86] = https://curl.se/bug/?i=22272 + [88] = https://curl.se/bug/?i=22210 + [90] = https://curl.se/bug/?i=22346 + [91] = https://curl.se/bug/?i=22282 + [92] = https://curl.se/bug/?i=22279 + [93] = https://curl.se/bug/?i=22316 + [95] = https://curl.se/bug/?i=22319 + [96] = https://curl.se/bug/?i=22323 + [97] = https://curl.se/bug/?i=22318 + [98] = https://curl.se/bug/?i=22317 + [100] = https://curl.se/bug/?i=22331 + [101] = https://curl.se/bug/?i=22333 + [102] = https://curl.se/bug/?i=22334 + [103] = https://curl.se/bug/?i=22326 + [105] = https://curl.se/bug/?i=22320 + [107] = https://curl.se/bug/?i=22299 + [109] = https://curl.se/bug/?i=22312 From 057b25135848575d6f62eed97874a8d71e72af1d Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:13:15 +0200 Subject: [PATCH 0697/1062] tool_doswin: add stdin relay auth Verify the peer with a random number in the background thread. Closes #21467 --- docs/KNOWN_BUGS.md | 15 ------ lib/curlx/winapi.c | 20 ++++++++ lib/curlx/winapi.h | 1 + lib/rand.c | 20 +------- lib/rand.h | 6 --- lib/vtls/schannel.c | 4 +- projects/Windows/tmpl/curl.vcxproj | 48 +++++++++---------- src/tool_doswin.c | 75 ++++++++++++++++++++++++------ 8 files changed, 111 insertions(+), 78 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index 359c2fab9955..59f11de0b5cb 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -105,21 +105,6 @@ would do if you used `-T` file. See [curl issue 12171](https://github.com/curl/curl/issues/12171) -## Windows stdin relay accepts unauthenticated local connections - -curl features a Windows-only stdin relay in `src/tool_doswin.c` that creates a -loopback TCP listener and spawns a thread to accept the first incoming -connection, then forwards stdin to it. There is no authentication or peer -validation on the accepted socket. A local attacker can race to connect to the -ephemeral loopback port (discoverable via local port enumeration/scan) before -curl connects, causing the thread to send stdin/upload data to the attacker or -to disrupt the transfer. - -The function should verify the client-side with a random number similar to the -socketpair emulation function in libcurl. It cannot verify the source address -and port since there is this widespread habit on Windows to run tools that -MITM even local TCP connections for security. - # Build and portability issues ## OS400 port requires deprecated IBM library diff --git a/lib/curlx/winapi.c b/lib/curlx/winapi.c index f025ca48c941..1d6ef87abe82 100644 --- a/lib/curlx/winapi.c +++ b/lib/curlx/winapi.c @@ -103,4 +103,24 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen) return buf; } + +#ifndef WITHOUT_LIBCURL + +#include +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +CURLcode curlx_win32_random(unsigned char *entropy, size_t length) +{ + memset(entropy, 0, length); + + if(BCryptGenRandom(NULL, entropy, (ULONG)length, + BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) + return CURLE_FAILED_INIT; + + return CURLE_OK; +} +#endif /* WITHOUT_LIBCURL */ + #endif /* _WIN32 */ diff --git a/lib/curlx/winapi.h b/lib/curlx/winapi.h index d30f5efa13c7..21e864c028d6 100644 --- a/lib/curlx/winapi.h +++ b/lib/curlx/winapi.h @@ -28,6 +28,7 @@ #define WINAPI_ERROR_LEN 100 const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen); const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen); +CURLcode curlx_win32_random(unsigned char *entropy, size_t length); #endif #endif /* HEADER_CURLX_WINAPI_H */ diff --git a/lib/rand.c b/lib/rand.c index 3260fe3345bf..71f629e72349 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -33,23 +33,7 @@ #include "rand.h" #include "escape.h" -#ifdef _WIN32 -#include -#ifndef STATUS_SUCCESS -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -#endif - -CURLcode Curl_win32_random(unsigned char *entropy, size_t length) -{ - memset(entropy, 0, length); - - if(BCryptGenRandom(NULL, entropy, (ULONG)length, - BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) - return CURLE_FAILED_INIT; - - return CURLE_OK; -} -#endif +#include "curlx/winapi.h" #ifndef USE_SSL /* ---- possibly non-cryptographic version following ---- */ @@ -64,7 +48,7 @@ static CURLcode weak_random(struct Curl_easy *data, #ifdef _WIN32 (void)data; { - CURLcode result = Curl_win32_random(entropy, length); + CURLcode result = curlx_win32_random(entropy, length); if(result != CURLE_NOT_BUILT_IN) return result; } diff --git a/lib/rand.h b/lib/rand.h index a02717074fdb..452c1b67ef90 100644 --- a/lib/rand.h +++ b/lib/rand.h @@ -49,10 +49,4 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num); CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); -#ifdef _WIN32 -/* Random generator shared between the Schannel vtls and Curl_rand*() - functions */ -CURLcode Curl_win32_random(unsigned char *entropy, size_t length); -#endif - #endif /* HEADER_CURL_RAND_H */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 6a69237f4e64..accda3bbccca 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -50,7 +50,7 @@ #include "vtls/x509asn1.h" #include "system_win32.h" #include "curlx/version_win32.h" -#include "rand.h" +#include "curlx/winapi.h" #include "curlx/strparse.h" #include "progress.h" #include "curl_sha256.h" @@ -2637,7 +2637,7 @@ static CURLcode schannel_random(struct Curl_easy *data, { (void)data; - return Curl_win32_random(entropy, length); + return curlx_win32_random(entropy, length); } static CURLcode schannel_checksum(const unsigned char *input, diff --git a/projects/Windows/tmpl/curl.vcxproj b/projects/Windows/tmpl/curl.vcxproj index 6d63601b6bba..a6a74e7aad70 100644 --- a/projects/Windows/tmpl/curl.vcxproj +++ b/projects/Windows/tmpl/curl.vcxproj @@ -852,7 +852,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -882,7 +882,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -911,7 +911,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -941,7 +941,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -970,7 +970,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1002,7 +1002,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1033,7 +1033,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1065,7 +1065,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1096,7 +1096,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1128,7 +1128,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1159,7 +1159,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1190,7 +1190,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1222,7 +1222,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1254,7 +1254,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1285,7 +1285,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1315,7 +1315,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1344,7 +1344,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1373,7 +1373,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1403,7 +1403,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1433,7 +1433,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1462,7 +1462,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1494,7 +1494,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1525,7 +1525,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1555,7 +1555,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + bcrypt.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console diff --git a/src/tool_doswin.c b/src/tool_doswin.c index f836af95063c..a64665bcd95e 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -29,6 +29,7 @@ #include "curlx/version_win32.h" /* for curlx_verify_windows_version() */ #ifdef _WIN32 +# include "curlx/winapi.h" /* for curlx_win32_random() */ # include #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ # define CURL_USE_LFN(f) 0 /* long filenames never available */ @@ -709,6 +710,9 @@ struct win_thread_data { /* This is the listen socket for the thread. It is closed after the first connection. */ curl_socket_t socket_l; + /* This is the random number which the background thread will use to verify + * the peer. */ + uint64_t expected_auth_val; }; static DWORD WINAPI win_stdin_thread_func(void *thread_data) @@ -716,6 +720,8 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) struct win_thread_data *tdata = (struct win_thread_data *)thread_data; struct sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); + size_t nread = 0; + uint64_t auth_val = 0; curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, (struct sockaddr *)&clientAddr, @@ -728,6 +734,28 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) sclose(tdata->socket_l); tdata->socket_l = CURL_SOCKET_BAD; + + do { + ssize_t ret = sread(socket_w, ((unsigned char *)&auth_val) + nread, + sizeof(auth_val) - nread); + if(ret <= 0) { + if(!ret) { + errorf("relay peer disconnected"); + } + else { + errorf("read error: %d", SOCKERRNO); + } + + goto ThreadCleanup; + } + nread += ret; + } while(nread < sizeof(auth_val)); + + if(auth_val != tdata->expected_auth_val) { + errorf("relay peer auth failed"); + goto ThreadCleanup; + } + if(shutdown(socket_w, SHUT_RD)) { errorf("shutdown error: %d", SOCKERRNO); goto ThreadCleanup; @@ -748,8 +776,6 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) break; } ThreadCleanup: - CloseHandle(tdata->stdin_handle); - tdata->stdin_handle = NULL; if(tdata->socket_l != CURL_SOCKET_BAD) { sclose(tdata->socket_l); tdata->socket_l = CURL_SOCKET_BAD; @@ -766,6 +792,9 @@ curl_socket_t win32_stdin_read_thread(void) { int rc = 0; struct win_thread_data *tdata = NULL; + HANDLE stdin_handle = NULL; + uint64_t auth_rnd; + size_t nwritten = 0; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; @@ -815,13 +844,20 @@ curl_socket_t win32_stdin_read_thread(void) break; } + if(curlx_win32_random((unsigned char *)&auth_rnd, sizeof(auth_rnd))) { + errorf("curlx_win32_random() error"); + break; + } + tdata->expected_auth_val = auth_rnd; + /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), - GetCurrentProcess(), &tdata->stdin_handle, + GetCurrentProcess(), &stdin_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { errorf("DuplicateHandle error: 0x%08lx", GetLastError()); break; } + tdata->stdin_handle = stdin_handle; /* Start up the thread. We do not bother keeping a reference to it because it runs until program termination. From here on out all reads @@ -850,6 +886,18 @@ curl_socket_t win32_stdin_read_thread(void) break; } + do { + ssize_t ret = swrite(socket_r, ((unsigned char *)&auth_rnd) + nwritten, + sizeof(auth_rnd) - nwritten); + + if(ret <= 0) { + errorf("socket write error: %d", SOCKERRNO); + goto err; + } + + nwritten += ret; + } while(nwritten < sizeof(auth_rnd)); + if(shutdown(socket_r, SHUT_WR)) { errorf("shutdown error: %d", SOCKERRNO); break; @@ -864,25 +912,26 @@ curl_socket_t win32_stdin_read_thread(void) rc = 1; } while(0); +err: if(rc != 1) { - if(socket_r != CURL_SOCKET_BAD && tdata) { + if(stdin_thread) { + TerminateThread(stdin_thread, 1); + CloseHandle(stdin_thread); + stdin_thread = NULL; + } + + if(socket_r != CURL_SOCKET_BAD) { if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && - tdata->stdin_handle) { + stdin_handle) { /* restore STDIN */ - SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle); - tdata->stdin_handle = NULL; + SetStdHandle(STD_INPUT_HANDLE, stdin_handle); + stdin_handle = NULL; } sclose(socket_r); socket_r = CURL_SOCKET_BAD; } - if(stdin_thread) { - TerminateThread(stdin_thread, 1); - CloseHandle(stdin_thread); - stdin_thread = NULL; - } - if(tdata) { if(tdata->stdin_handle) CloseHandle(tdata->stdin_handle); From 6bff85be68785e8dce7131279775688af75b0339 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 20 Jul 2026 10:29:41 +0200 Subject: [PATCH 0698/1062] tool_xattr: add support for Windows alternate data stream Requires a supported filesystem, i.e. NTFS. Example: ```console > curl.exe https://curl.se/index.html --output test.txt --xattr --referer https://curl.se/ > cat < test.txt:Zone.Identifier [ZoneTransfer] HostUrl=https://curl.se/index.html ReferrerUrl=https://curl.se/ ``` Where newlines are CLRF, `ReferrerUrl` is set when using `--referer` option. Also: - test688: make it test `--referer`. - document `user.xdg.referrer.url` in `--xattr` man page. Refs: https://en.wikipedia.org/wiki/Mark_of_the_Web https://en.wikipedia.org/wiki/NTFS#Alternate_data_stream_(ADS) https://learn.microsoft.com/en-us/windows/win32/fileio/file-streams https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c54dec26-1551-4d3a-a0ea-4fa40f848eb3 Ref: #22345 Closes #22354 --- .github/scripts/pyspelling.words | 1 + docs/cmdline-opts/xattr.md | 11 ++-- src/tool_operate.c | 2 +- src/tool_xattr.c | 89 ++++++++++++++++++++++++++------ src/tool_xattr.h | 6 ++- tests/data/test644 | 5 ++ tests/data/test687 | 5 ++ tests/data/test688 | 10 +++- 8 files changed, 107 insertions(+), 22 deletions(-) diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 1c0fb96d9b05..7563fcd6c726 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -392,6 +392,7 @@ IMAPS imaps impacket implementers +INI init initializer inlined diff --git a/docs/cmdline-opts/xattr.md b/docs/cmdline-opts/xattr.md index 380cdbdfd35d..893394c9d540 100644 --- a/docs/cmdline-opts/xattr.md +++ b/docs/cmdline-opts/xattr.md @@ -20,6 +20,11 @@ Store metadata in the extended file attributes. When saving output to a file, tell curl to store file metadata in extended file attributes. Currently, `curl` is stored in the `creator` attribute, -the URL is stored in the `xdg.origin.url` attribute and, for HTTP, the content -type is stored in the `mime_type` attribute. If the file system does not -support extended attributes, a warning is issued. +the URL is stored in the `xdg.origin.url` attribute, for HTTP, the content +type is stored in the `mime_type` attribute, and if set, the referrer URL in +`user.xdg.referrer.url`. If the file system does not support extended +attributes, a warning is issued. + +Since curl 8.22.0 this option is also supported on Windows, where it creates +an Alternate Data Stream named `Zone.Identifier`. It contains an INI formatted +`ZoneTransfer` section, with values: `HostUrl`, `ReferrerUrl` (if set). diff --git a/src/tool_operate.c b/src/tool_operate.c index 56e3544ca2bb..213f20b88ff9 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -650,7 +650,7 @@ static CURLcode post_output_handling(struct per_transfer *per, /* Set file extended attributes */ if(!result && config->xattr && outs->fopened && outs->stream) { - rc = fwrite_xattr(curl, per->url, fileno(outs->stream)); + rc = fwrite_xattr(curl, per->url, fileno(outs->stream), outs->filename); if(rc) { char errbuf[STRERROR_LEN]; warnf("Error setting extended attributes on '%s': %s", outs->filename, diff --git a/src/tool_xattr.c b/src/tool_xattr.c index cdf8296307a5..7bf487a07783 100644 --- a/src/tool_xattr.c +++ b/src/tool_xattr.c @@ -27,19 +27,6 @@ #ifdef USE_XATTR -/* mapping table of curl metadata to extended attribute names */ -static const struct xattr_mapping { - const char *attr; /* name of the xattr */ - CURLINFO info; -} mappings[] = { - /* mappings proposed by - * https://freedesktop.org/wiki/CommonExtendedAttributes/ - */ - { "user.xdg.referrer.url", CURLINFO_REFERER }, - { "user.mime_type", CURLINFO_CONTENT_TYPE }, - { NULL, CURLINFO_NONE } /* last element, abort here */ -}; - /* returns a new URL that needs to be freed */ /* @unittest: 1621 */ UNITTEST char *stripcredentials(const char *url) @@ -74,6 +61,20 @@ UNITTEST char *stripcredentials(const char *url) return NULL; } +#ifndef _WIN32 +/* mapping table of curl metadata to extended attribute names */ +static const struct xattr_mapping { + const char *attr; /* name of the xattr */ + CURLINFO info; +} mappings[] = { + /* mappings proposed by + * https://freedesktop.org/wiki/CommonExtendedAttributes/ + */ + { "user.xdg.referrer.url", CURLINFO_REFERER }, + { "user.mime_type", CURLINFO_CONTENT_TYPE }, + { NULL, CURLINFO_NONE } /* last element, abort here */ +}; + static int xattr(int fd, const char *attr, /* name of the xattr */ const char *value) @@ -102,13 +103,70 @@ static int xattr(int fd, } return err; } +#else +static int win32_file_stream(CURL *curl, FILE *fs, const char *url) +{ + int err = 1; + char *value = NULL; + char *nurl = stripcredentials(url); + CURLcode result = curl_easy_getinfo(curl, CURLINFO_REFERER, &value); + + if(nurl && !result) { + err = 0; + err |= (fputs("[ZoneTransfer]\n", fs) == EOF); + if(value) { + err |= (fputs("ReferrerUrl=", fs) == EOF); + err |= (fputs(value, fs) == EOF); + err |= (fputs("\n", fs) == EOF); + } + err |= (fputs("HostUrl=", fs) == EOF); + err |= (fputs(nurl, fs) == EOF); + err |= (fputs("\n", fs) == EOF); + } + curl_free(nurl); + return err; +} +#endif /* !_WIN32 */ + /* store metadata from the curl request alongside the downloaded * file using extended attributes */ -int fwrite_xattr(CURL *curl, const char *url, int fd) +int fwrite_xattr(CURL *curl, const char *url, int fd, const char *filename) { + int err; +#ifdef _WIN32 + char *fn_abs, *fn_stream; + FILE *fs; + (void)fd; + + /* convert to absolute path to prevent Windows interpreting a 'X:' + filename as 'drive-letter:'. */ + fn_abs = _fullpath(NULL, filename, 0); + if(!fn_abs) + return 1; + + fn_stream = curl_maprintf("%s:%s", fn_abs, "Zone.Identifier"); + /* !checksrc! disable BANNEDFUNC 1 */ + free(fn_abs); /* allocated by CRT, use system free() */ + if(!fn_stream) + return 1; + + fs = curlx_fopen(fn_stream, FOPEN_WRITETEXT); + curl_free(fn_stream); + if(!fs) + return 1; + +#ifdef DEBUGBUILD + if(getenv("CURL_FAKE_XATTR")) + win32_file_stream(curl, stdout, url); +#endif + err = win32_file_stream(curl, fs, url); + curlx_fclose(fs); +#else int i = 0; - int err = xattr(fd, "user.creator", "curl"); + (void)filename; + + err = xattr(fd, "user.creator", "curl"); /* loop through all xattr-curlinfo pairs and abort on a set error */ while(!err && mappings[i].attr) { @@ -125,6 +183,7 @@ int fwrite_xattr(CURL *curl, const char *url, int fd) err = xattr(fd, "user.xdg.origin.url", nurl); curl_free(nurl); } +#endif return err; } #endif diff --git a/src/tool_xattr.h b/src/tool_xattr.h index 6720ccb2bd9a..f2ccfe209a37 100644 --- a/src/tool_xattr.h +++ b/src/tool_xattr.h @@ -33,17 +33,19 @@ # include # include # define USE_XATTR +#elif defined(_WIN32) +# define USE_XATTR #endif #ifdef USE_XATTR -int fwrite_xattr(CURL *curl, const char *url, int fd); +int fwrite_xattr(CURL *curl, const char *url, int fd, const char *filename); #ifdef UNITTESTS UNITTEST char *stripcredentials(const char *url); #endif #else -#define fwrite_xattr(a, b, c) 0 +#define fwrite_xattr(a, b, c, d) 0 #endif #endif /* HEADER_CURL_TOOL_XATTR_H */ diff --git a/tests/data/test644 b/tests/data/test644 index e2199510be7a..acf5e08aab2d 100644 --- a/tests/data/test644 +++ b/tests/data/test644 @@ -76,9 +76,14 @@ Accept: */* +%if win32 +[ZoneTransfer] +HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%else user.creator => curl user.mime_type => text/html user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%endif diff --git a/tests/data/test687 b/tests/data/test687 index fd3052da60c9..045348b301f3 100644 --- a/tests/data/test687 +++ b/tests/data/test687 @@ -53,9 +53,14 @@ Accept: */* +%if win32 +[ZoneTransfer] +HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%else user.creator => curl user.mime_type => fake/data user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%endif diff --git a/tests/data/test688 b/tests/data/test688 index 44f621e33c49..32ed0e9f30ce 100644 --- a/tests/data/test688 +++ b/tests/data/test688 @@ -39,7 +39,7 @@ CURL_FAKE_XATTR=1 basic --xattr with (uppercase) -O ---xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER +--xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER --referer https://referrer.invalid/ @@ -50,12 +50,20 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* +Referer: https://referrer.invalid/ +%if win32 +[ZoneTransfer] +ReferrerUrl=https://referrer.invalid/ +HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%else user.creator => curl +user.xdg.referrer.url => https://referrer.invalid/ user.mime_type => fake/data user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER +%endif From 41b4a911234207b7c5271f8147053769570b06e7 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 22 Jul 2026 12:22:18 +0200 Subject: [PATCH 0699/1062] scripts: replace/extend `--` with `--end-of-options` in git commands It makes these scripts require Git 2.24.0+ (2019-11-04). Refs: https://git-scm.com/docs/gitcli https://nesbitt.io/2026/07/21/end-of-options.html https://github.com/git/git/commit/19e8789b236dfe33667747d5523d6689bb59b5ef Closes #22369 --- .github/scripts/cleancmd.pl | 2 +- scripts/badwords | 2 +- scripts/checksrc.pl | 4 ++-- scripts/delta | 4 ++-- scripts/singleuse.pl | 2 +- tests/test1275.pl | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/scripts/cleancmd.pl b/.github/scripts/cleancmd.pl index d6ddeb7274f4..722015b08fdc 100755 --- a/.github/scripts/cleancmd.pl +++ b/.github/scripts/cleancmd.pl @@ -122,7 +122,7 @@ sub process { } my @filemasks = @ARGV; -open(my $git_ls_files, '-|', 'git', 'ls-files', '--', @filemasks) or die "Failed running git ls-files: $!"; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--end-of-options', @filemasks) or die "Failed running git ls-files: $!"; while(my $f = <$git_ls_files>) { chomp $f; process($f); diff --git a/scripts/badwords b/scripts/badwords index a6cc6cd0a29c..53c93f40318a 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -318,7 +318,7 @@ sub file { } my @filemasks = @ARGV; -open(my $git_ls_files, '-|', 'git', 'ls-files', '--', ":!:$file", @filemasks) or die "Failed running git ls-files: $!"; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--end-of-options', ":!:$file", @filemasks) or die "Failed running git ls-files: $!"; my @files; while(my $each = <$git_ls_files>) { chomp $each; diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index b1062308a473..ea71ee7ecb88 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -1236,12 +1236,12 @@ sub scanfile { @copyright = sort {$$b{year} cmp $$a{year}} @copyright; # if the file is modified, assume commit year this year - if(qx(git status -s -- "$file") =~ /^ [MARCU]/) { + if(qx(git status -s --end-of-options "$file") =~ /^ [MARCU]/) { $commityear = (localtime(time))[5] + 1900; } else { # min-parents=1 to ignore wrong initial commit in truncated repos - my $grl = qx(git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- "$file"); + my $grl = qx(git rev-list --max-count=1 --min-parents=1 --timestamp --end-of-options HEAD -- "$file"); if($grl) { chomp $grl; $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900; diff --git a/scripts/delta b/scripts/delta index 7a1793dbb36a..d5b174044f51 100755 --- a/scripts/delta +++ b/scripts/delta @@ -131,8 +131,8 @@ my $total = $now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; my $totalhttpget = $now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; # Number of public functions in libcurl -my $apublic = cmd('git', 'grep', '^CURL_EXTERN', '--', 'include/curl'); -my $bpublic = cmd('git', 'grep', '^CURL_EXTERN', $start, '--', 'include/curl'); +my $apublic = cmd('git', 'grep', '--end-of-options', '^CURL_EXTERN', '--', 'include/curl'); +my $bpublic = cmd('git', 'grep', '--end-of-options', '^CURL_EXTERN', $start, '--', 'include/curl'); my $public = $apublic - $bpublic; # diffstat diff --git a/scripts/singleuse.pl b/scripts/singleuse.pl index fc6cc7f1116c..6332a9bfec8b 100755 --- a/scripts/singleuse.pl +++ b/scripts/singleuse.pl @@ -167,7 +167,7 @@ sub doublecheck { my ($f, $used) = @_; - open(F, '-|', 'git', 'grep', '-Fwle', $f, '--', 'lib', @unittests, 'projects'); + open(F, '-|', 'git', 'grep', '-Fwl', '--end-of-options', $f, '--', 'lib', @unittests, 'projects'); my @also; while() { my $e = $_; diff --git a/tests/test1275.pl b/tests/test1275.pl index 3b33bb3cf0e7..d46fd6248941 100755 --- a/tests/test1275.pl +++ b/tests/test1275.pl @@ -29,7 +29,7 @@ my $root = $ARGV[0] || ".."; my @m; -if(open(O, '-|', 'git', 'ls-files', '--', $root)) { +if(open(O, '-|', 'git', 'ls-files', '--end-of-options', $root)) { push @m, ; close(O); } From 2c3200f5785fed310d0541d9c428d822248869ed Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 22 Jul 2026 00:05:27 +0200 Subject: [PATCH 0700/1062] dnscache: avoid double unlock Follow-up to 7ad33fe56f25424fb51a1040 Pointed out by Zeropath Closes #22364 --- lib/dnscache.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/dnscache.c b/lib/dnscache.c index 9f435cc0cd7a..73de3cacb318 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -721,7 +721,6 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, /* release the returned reference; the cache itself will keep the * entry alive: */ dns->refcount--; - dnscache_unlock(data, dnscache); CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", host, port, Curl_resolv_query_str(dns_queries)); } From 381c3d56e332b7decb5392a84ccac0e972b4c56d Mon Sep 17 00:00:00 2001 From: Pavel Sobolev Date: Tue, 21 Jul 2026 21:47:33 +0300 Subject: [PATCH 0701/1062] ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` Frees the memory allocated by `ngtcp2_crypto_ossl_init`, thus making Valgrind happy. Works for versions of ngtcp2 >= 1.24.0. Ref: https://github.com/ngtcp2/ngtcp2/issues/2207 Closes #22363 --- lib/easy.c | 1 + lib/vquic/vquic.c | 8 ++++++++ lib/vquic/vquic.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/lib/easy.c b/lib/easy.c index fa67c906567a..7321e738782e 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -268,6 +268,7 @@ void curl_global_cleanup(void) } Curl_ssl_cleanup(); + Curl_vquic_cleanup(); Curl_async_global_cleanup(); #ifdef _WIN32 diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 802ffb7d3055..1f5e5bc6b8b0 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -67,6 +67,14 @@ int Curl_vquic_init(void) return 1; } +void Curl_vquic_cleanup(void) +{ +#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2) && \ + (NGTCP2_VERSION_NUM >= 0x011800) + ngtcp2_crypto_ossl_free(); +#endif +} + void Curl_quic_ver(char *p, size_t len) { #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 8674a3001de9..f1363e2c0a76 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -33,6 +33,7 @@ struct Curl_addrinfo; void Curl_quic_ver(char *p, size_t len); int Curl_vquic_init(void); +void Curl_vquic_cleanup(void); CURLcode Curl_qlogdir(struct Curl_easy *data, unsigned char *scid, @@ -81,6 +82,7 @@ extern struct Curl_cftype Curl_cft_h3_proxy; #else #define Curl_vquic_init() 1 +#define Curl_vquic_cleanup() #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, From 3a0c600a1e327dfcfe3b5a4730196b531f2669c4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 28 Jun 2026 22:38:51 +0200 Subject: [PATCH 0702/1062] tidy-up: fix comment indentation Closes #22373 --- docs/examples/cookie_interface.c | 3 +- docs/examples/htmltitle.cpp | 16 +++--- docs/examples/post-callback.c | 12 ++-- docs/examples/sendrecv.c | 3 +- docs/examples/simplessl.c | 2 +- docs/examples/threaded.c | 6 +- docs/libcurl/opts/CURLOPT_COOKIELIST.md | 8 +-- docs/libcurl/opts/CURLOPT_ERRORBUFFER.md | 5 +- include/curl/curl.h | 12 ++-- include/curl/curlver.h | 3 +- include/curl/multi.h | 28 +++++----- lib/asyn-ares.c | 2 +- lib/cookie.c | 19 +++---- lib/cookie.h | 2 +- lib/curl_config-cmake.h.in | 16 +++--- lib/curl_fopen.c | 15 +++-- lib/curl_ntlm_core.c | 2 +- lib/curl_setup.h | 12 ++-- lib/curlx/basename.c | 36 ++++++------ lib/curlx/strparse.h | 3 +- lib/fake_addrinfo.c | 12 ++-- lib/file.c | 6 +- lib/ftp.c | 47 +++++++--------- lib/functypes.h | 2 +- lib/gopher.c | 3 +- lib/hash.h | 4 +- lib/http.c | 8 +-- lib/http.h | 3 +- lib/http_aws_sigv4.c | 6 +- lib/http_chunks.c | 3 +- lib/http_digest.c | 8 +-- lib/llist.c | 6 +- lib/multi.c | 14 ++--- lib/parsedate.c | 71 ++++++++++++------------ lib/progress.c | 11 ++-- lib/protocol.h | 3 +- lib/proxy.c | 2 +- lib/select.c | 6 +- lib/select.h | 6 +- lib/setopt.c | 3 +- lib/sha256.c | 10 ++-- lib/socks.c | 6 +- lib/url.c | 3 +- lib/urlapi.c | 5 +- lib/urldata.h | 2 +- lib/vauth/digest.c | 30 +++++----- lib/vauth/ntlm.c | 2 +- lib/vssh/libssh2.c | 18 +++--- lib/vtls/apple.c | 2 +- lib/vtls/openssl.c | 13 ++--- lib/vtls/schannel.c | 66 ++++++++++------------ lib/vtls/schannel_int.h | 4 +- lib/vtls/vtls.c | 2 +- lib/vtls/vtls_config.c | 2 +- lib/ws.c | 67 +++++++++++----------- projects/vms/curl_crtl_init.c | 10 ++-- src/tool_cb_dbg.c | 4 +- src/tool_cb_dbg.h | 5 +- src/tool_cb_hdr.c | 8 +-- src/tool_cb_hdr.h | 5 +- src/tool_cb_prg.c | 8 +-- src/tool_cb_prg.h | 5 +- src/tool_cb_rea.c | 10 ++-- src/tool_cb_rea.h | 10 ++-- src/tool_cb_see.c | 11 ++-- src/tool_cb_see.h | 4 +- src/tool_cb_soc.c | 9 ++- src/tool_cb_soc.h | 5 +- src/tool_cb_wrt.c | 5 +- src/tool_cb_wrt.h | 5 +- src/tool_formparse.c | 5 +- src/tool_getparam.c | 3 +- src/tool_helpers.c | 4 +- src/tool_main.c | 4 +- src/tool_paramhlp.c | 3 +- src/tool_progress.c | 12 ++-- src/tool_urlglob.c | 3 +- src/tool_writeout.c | 2 +- src/tool_writeout_json.c | 3 +- tests/libtest/lib1156.c | 18 +++--- tests/libtest/lib1560.c | 32 +++++------ tests/libtest/lib1978.c | 2 +- tests/libtest/lib514.c | 10 ++-- tests/libtest/lib590.c | 21 ++++--- tests/server/dnsd.c | 32 +++++------ tests/server/mqttd.c | 21 +++---- tests/server/sockfilt.c | 10 ++-- tests/server/socksd.c | 45 +++++++-------- tests/unit/unit1609.c | 60 ++++++++++---------- tests/unit/unit1667.c | 23 ++++---- 90 files changed, 474 insertions(+), 604 deletions(-) diff --git a/docs/examples/cookie_interface.c b/docs/examples/cookie_interface.c index aa3ea1d013ff..698044deaf5b 100644 --- a/docs/examples/cookie_interface.c +++ b/docs/examples/cookie_interface.c @@ -117,8 +117,7 @@ int main(void) /* HTTP-header style cookie. If you use the Set-Cookie format and do not specify a domain then the cookie is sent for any domain and is not modified, likely not what you intended. For more information refer to - the CURLOPT_COOKIELIST documentation. - */ + the CURLOPT_COOKIELIST documentation. */ snprintf(nline, sizeof(nline), "Set-Cookie: OLD_PREF=3d141414bf4209321; " "expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.example.com"); diff --git a/docs/examples/htmltitle.cpp b/docs/examples/htmltitle.cpp index cce6d16e11a1..c1130ce522e6 100644 --- a/docs/examples/htmltitle.cpp +++ b/docs/examples/htmltitle.cpp @@ -24,14 +24,14 @@ /* * Get a webpage, extract the title with libxml. * - - Written by Lars Nilsson - - GNU C++ compile command line suggestion (edit paths accordingly): - - g++ -Wall -I/opt/curl/include -I/opt/libxml/include/libxml2 htmltitle.cpp \ - -o htmltitle -L/opt/curl/lib -L/opt/libxml/lib -lcurl -lxml2 -*/ + * + * Written by Lars Nilsson + * + * GNU C++ compile command line suggestion (edit paths accordingly): + * + * g++ -Wall -I/opt/curl/include -I/opt/libxml/include/libxml2 htmltitle.cpp \ + * -o htmltitle -L/opt/curl/lib -L/opt/libxml/lib -lcurl -lxml2 + */ #include #include #include diff --git a/docs/examples/post-callback.c b/docs/examples/post-callback.c index 729642cbce6a..e5b70366d1e1 100644 --- a/docs/examples/post-callback.c +++ b/docs/examples/post-callback.c @@ -102,13 +102,11 @@ int main(void) /* get verbose debug output please */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* - If you use POST to an HTTP 1.1 server, you can send data without knowing - the size before starting the POST if you use chunked encoding. You - enable this by adding a header like "Transfer-Encoding: chunked" with - CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must - specify the size in the request. - */ + /* If you use POST to an HTTP 1.1 server, you can send data without knowing + the size before starting the POST if you use chunked encoding. You + enable this by adding a header like "Transfer-Encoding: chunked" with + CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must + specify the size in the request. */ #ifdef USE_CHUNKED { struct curl_slist *chunk = NULL; diff --git a/docs/examples/sendrecv.c b/docs/examples/sendrecv.c index a8d06970288d..2777ca0a6dda 100644 --- a/docs/examples/sendrecv.c +++ b/docs/examples/sendrecv.c @@ -89,8 +89,7 @@ int main(void) supports "natively", you are doing it wrong and you should stop. This example uses HTTP only to show how to use this API, it does not - suggest that writing an application doing this is sensible. - */ + suggest that writing an application doing this is sensible. */ curl = curl_easy_init(); if(curl) { diff --git a/docs/examples/simplessl.c b/docs/examples/simplessl.c index 43fe34cf3fbf..24e08c4c7144 100644 --- a/docs/examples/simplessl.c +++ b/docs/examples/simplessl.c @@ -46,7 +46,7 @@ 4. if you do not use a crypto engine: 4.1. set pKeyName to the filename of your client key 4.2. if the format of the key file is DER, set pKeyType to "DER" -*/ + */ int main(void) { diff --git a/docs/examples/threaded.c b/docs/examples/threaded.c index 54b315bc57b7..b858c1a3f828 100644 --- a/docs/examples/threaded.c +++ b/docs/examples/threaded.c @@ -73,11 +73,9 @@ static void *pull_one_url(void *p) return NULL; } -/* - int pthread_create(pthread_t *new_thread_ID, +/* int pthread_create(pthread_t *new_thread_ID, const pthread_attr_t *attr, - void * (*start_func)(void *), void *arg); -*/ + void * (*start_func)(void *), void *arg); */ int main(void) { diff --git a/docs/libcurl/opts/CURLOPT_COOKIELIST.md b/docs/libcurl/opts/CURLOPT_COOKIELIST.md index ae7d650db37b..9b9456ec1233 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIELIST.md +++ b/docs/libcurl/opts/CURLOPT_COOKIELIST.md @@ -100,17 +100,15 @@ int main(void) before a transfer is performed. Cookies in the list that have the same hostname, path and name as in my_cookie are skipped. That is because libcurl has already imported my_cookie and it is considered a "live" - cookie. A live cookie is not replaced by one read from a file. - */ + cookie. A live cookie is not replaced by one read from a file. */ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt"); /* import */ /* Cookies are exported after curl_easy_cleanup is called. The server may have added, deleted or modified cookies by then. The cookies that - were skipped on import are not exported. - */ + were skipped on import are not exported. */ curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookies.txt"); /* export */ - result = curl_easy_perform(curl); /* cookies imported from cookies.txt */ + result = curl_easy_perform(curl); /* cookies imported from cookies.txt */ curl_easy_cleanup(curl); /* cookies exported to cookies.txt */ } diff --git a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md index 6ba194aabb92..fa3a7c7994ec 100644 --- a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md +++ b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md @@ -82,9 +82,8 @@ int main(void) result = curl_easy_perform(curl); /* if the request did not complete correctly, show the error - information. if no detailed error information was written to errbuf - show the more generic information from curl_easy_strerror instead. - */ + information. if no detailed error information was written to errbuf + show the more generic information from curl_easy_strerror instead. */ if(result != CURLE_OK) { size_t len = strlen(errbuf); fprintf(stderr, "\nlibcurl: (%d) ", result); diff --git a/include/curl/curl.h b/include/curl/curl.h index adc0c90e7259..326804eb1851 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -632,7 +632,7 @@ typedef enum { match */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer - */ + */ CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from inside a callback */ CURLE_AUTH_ERROR, /* 94 - an authentication function returned an @@ -1537,8 +1537,7 @@ typedef enum { /* FTP Option that causes missing dirs to be created on the remote server. In 7.19.4 we introduced the convenience enums for this option using the - CURLFTP_CREATE_DIR prefix. - */ + CURLFTP_CREATE_DIR prefix. */ CURLOPT(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110), /* Set this to a bitmask value to enable the particular authentications @@ -1589,7 +1588,7 @@ typedef enum { CURLUSESSL_TRY - try using SSL, proceed anyway otherwise CURLUSESSL_CONTROL - SSL for the control connection or fail CURLUSESSL_ALL - SSL for all communication or fail - */ + */ CURLOPT(CURLOPT_USE_SSL, CURLOPTTYPE_VALUES, 119), /* The _LARGE version of the standard POSTFIELDSIZE option */ @@ -1615,7 +1614,7 @@ typedef enum { CURLFTPAUTH_DEFAULT - let libcurl decide CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL - */ + */ CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_VALUES, 129), CURLOPTDEPRECATED(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130, @@ -1650,8 +1649,7 @@ typedef enum { CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 makes no additional attempts but the first. - */ + Thus, setting it to 1 makes no additional attempts but the first. */ CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), /* no transfer, set up connection and let application use the socket by diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 4a673da4668c..98d10c4fc2b5 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -56,8 +56,7 @@ Note: This define is the full hex number and _does not_ use the CURL_VERSION_BITS() macro since curl's own configure script greps for it - and needs it to contain the full number. -*/ + and needs it to contain the full number. */ #define LIBCURL_VERSION_NUM 0x081600 /* diff --git a/include/curl/multi.h b/include/curl/multi.h index 060b73eeec7f..8cef6e774e46 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -24,20 +24,20 @@ * ***************************************************************************/ /* - This is an "external" header file. Do not give away any internals here! - - GOALS - - o Enable a "pull" interface. The application that uses libcurl decides where - and when to ask libcurl to get/send data. - - o Enable multiple simultaneous transfers in the same thread without making it - complicated for the application. - - o Enable the application to select() on its own file descriptors and curl's - file descriptors simultaneous easily. - -*/ + * This is an "external" header file. Do not give away any internals here! + * + * GOALS + * + * o Enable a "pull" interface. The application that uses libcurl decides where + * and when to ask libcurl to get/send data. + * + * o Enable multiple simultaneous transfers in the same thread without making + * it complicated for the application. + * + * o Enable the application to select() on its own file descriptors and curl's + * file descriptors simultaneous easily. + * + */ /* * This header file should not really need to include "curl.h" since curl.h diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 106c7333f5b5..72bfda510a0b 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -148,7 +148,7 @@ static CURLcode async_ares_init(struct Curl_easy *data, if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to overwrite c-ares' timeout. - */ + */ DEBUGASSERT(ares_ver); if(ares_ver < 0x011400) { options.timeout = CARES_TIMEOUT_PER_ATTEMPT; diff --git a/lib/cookie.c b/lib/cookie.c index 91dc8d5fd0d2..83baf435f118 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -48,8 +48,7 @@ future. (from RFC6265bis draft-19) For the sake of easier testing, align the capped time to an even 60 second - boundary. -*/ + boundary. */ static void cap_expires(time_t now, struct Cookie *co) { if(co->expires && (TIME_T_MAX - COOKIES_MAXAGE - 30) > now) { @@ -335,15 +334,14 @@ static bool bad_domain(const char *domain, size_t len) } #endif -/* - RFC 6265 section 4.1.1 says a server should accept this range: +/* RFC 6265 section 4.1.1 says a server should accept this range: - cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - Yet, Firefox and Chrome as of June 2022 accept space, comma and double-quotes - fine. The prime reason for filtering out control bytes is that some HTTP - servers return 400 for requests that contain such. -*/ + Yet, Firefox and Chrome as of June 2022 accept space, comma and + double-quotes fine. The prime reason for filtering out control bytes is that + some HTTP servers return 400 for requests that contain such. + */ static bool invalid_octets(const char *ptr, size_t len) { const unsigned char *p = (const unsigned char *)ptr; @@ -359,8 +357,7 @@ static bool invalid_octets(const char *ptr, size_t len) /* The maximum length we accept a date string for the 'expire' keyword. The standard date formats are within the 30 bytes range. This adds an extra - margin to make sure it realistically works with what is used out there. -*/ + margin to make sure it realistically works with what is used out there. */ #define MAX_DATE_LENGTH 80 #define COOKIE_NAME 0 diff --git a/lib/cookie.h b/lib/cookie.h index b8ef8b8ea152..286c47fd3afa 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -72,7 +72,7 @@ struct CookieInfo { In the 6265bis draft document section 5.4 it is phrased even stronger: "If the sum of the lengths of the name string and the value string is more than 4096 octets, abort these steps and ignore the set-cookie-string entirely." -*/ + */ /** Limits for INCOMING cookies **/ diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index fbafbc088a65..97d8f54f5ac7 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -349,7 +349,7 @@ #cmakedefine HAVE_IOCTLSOCKET_CAMEL 1 /* Define to 1 if you have a working IoctlSocket camel case FIONBIO function. - */ + */ #cmakedefine HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1 /* Define to 1 if you have a working ioctlsocket FIONBIO function. */ @@ -607,14 +607,12 @@ /* cpu-machine-OS */ #cmakedefine CURL_OS ${CURL_OS} -/* - Note: SIZEOF_* variables are fetched with CMake through check_type_size(). - As per CMake documentation on CheckTypeSize, C preprocessor code is - generated by CMake into SIZEOF_*_CODE. This is what we use in the - following statements. - - Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html -*/ +/* Note: SIZEOF_* variables are fetched with CMake through check_type_size(). + As per CMake documentation on CheckTypeSize, C preprocessor code is + generated by CMake into SIZEOF_*_CODE. This is what we use in the + following statements. + Ref: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html + */ /* The size of `int', as computed by sizeof. */ ${SIZEOF_INT_CODE} diff --git a/lib/curl_fopen.c b/lib/curl_fopen.c index abb17eab4b87..75054ffb77bf 100644 --- a/lib/curl_fopen.c +++ b/lib/curl_fopen.c @@ -30,16 +30,15 @@ #include "rand.h" #include "curl_fopen.h" -/* - The dirslash() function breaks a null-terminated pathname string into - directory and filename components then returns the directory component up - to, *AND INCLUDING*, a final '/'. If there is no directory in the path, - this instead returns a "" string. +/* The dirslash() function breaks a null-terminated pathname string into + directory and filename components then returns the directory component up + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + this instead returns a "" string. - This function returns a pointer to malloc'ed memory. + This function returns a pointer to malloc'ed memory. - The input path to this function is expected to have a filename part. -*/ + The input path to this function is expected to have a filename part. + */ #ifdef _WIN32 #define PATHSEP "\\" diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 58a446c95db0..6978d42d93dd 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -551,7 +551,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, unsigned char **ntresp, unsigned int *ntresp_len) { - /* NTLMv2 response structure : + /* NTLMv2 response structure: ----------------------------------------------------------------------------- 0 HMAC MD5 16 bytes ------BLOB------------------------------------------------------------------- diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 5a5d8dbf1f35..487a3c5e391b 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1250,19 +1250,17 @@ typedef unsigned int curl_bit; #endif /* In Windows the default file mode is text but an application can override it. -Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 -*/ + Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ #if defined(_WIN32) || defined(MSDOS) #define FOPEN_READTEXT "rt" #define FOPEN_WRITETEXT "wt" #define FOPEN_APPENDTEXT "at" #elif defined(__CYGWIN__) /* Cygwin has specific behavior we need to address when _WIN32 is not defined. -https://cygwin.com/cygwin-ug-net/using-textbinary.html -For write we want our output to have line endings of LF and be compatible with -other Cygwin utilities. For read we want to handle input that may have line -endings either CRLF or LF so 't' is appropriate. -*/ + https://cygwin.com/cygwin-ug-net/using-textbinary.html + For write we want our output to have line endings of LF and be compatible + with other Cygwin utilities. For read we want to handle input that may have + line endings either CRLF or LF so 't' is appropriate. */ #define FOPEN_READTEXT "rt" #define FOPEN_WRITETEXT "w" #define FOPEN_APPENDTEXT "a" diff --git a/lib/curlx/basename.c b/lib/curlx/basename.c index d2fd160ff224..eda3d1195ac7 100644 --- a/lib/curlx/basename.c +++ b/lib/curlx/basename.c @@ -27,30 +27,28 @@ #include "curlx/basename.h" -/* - (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 - Edition) +/* (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + Edition) - The basename() function shall take the pathname pointed to by path and - return a pointer to the final component of the pathname, deleting any - trailing '/' characters. + The basename() function shall take the pathname pointed to by path and + return a pointer to the final component of the pathname, deleting any + trailing '/' characters. - If the string pointed to by path consists entirely of the '/' character, - basename() shall return a pointer to the string "/". If the string pointed - to by path is exactly "//", it is implementation-defined whether '/' or "//" - is returned. + If the string pointed to by path consists entirely of the '/' character, + basename() shall return a pointer to the string "/". If the string pointed + to by path is exactly "//", it is implementation-defined whether '/' or "//" + is returned. - If path is a null pointer or points to an empty string, basename() shall - return a pointer to the string ".". + If path is a null pointer or points to an empty string, basename() shall + return a pointer to the string ".". - The basename() function may modify the string pointed to by path, and may - return a pointer to static storage that may then be overwritten by a - subsequent call to basename(). + The basename() function may modify the string pointed to by path, and may + return a pointer to static storage that may then be overwritten by a + subsequent call to basename(). - The basename() function need not be reentrant. A function that is not - required to be reentrant is not required to be thread-safe. - -*/ + The basename() function need not be reentrant. A function that is not + required to be reentrant is not required to be thread-safe. + */ char *curlx_basename(char *path) { /* Ignore all the details above for now and make a quick and simple diff --git a/lib/curlx/strparse.h b/lib/curlx/strparse.h index c7801b2cb346..7963dd62128a 100644 --- a/lib/curlx/strparse.h +++ b/lib/curlx/strparse.h @@ -106,8 +106,7 @@ void curlx_str_passblanks(const char **linep); /* given a hexadecimal letter, return the binary value. '0' returns 0, 'a' returns 10. THIS ONLY WORKS ON VALID HEXADECIMAL LETTER INPUT. Verify - before calling this! -*/ + before calling this. */ extern const unsigned char curlx_hexasciitable[]; #define curlx_hexval(x) (unsigned char)(curlx_hexasciitable[(x) - '0'] & 0x0f) diff --git a/lib/fake_addrinfo.c b/lib/fake_addrinfo.c index 030dc35283e6..6857d6348518 100644 --- a/lib/fake_addrinfo.c +++ b/lib/fake_addrinfo.c @@ -129,14 +129,10 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) return cafirst; } -/* - RETURN VALUE - - getaddrinfo() returns 0 if it succeeds, or one of the following nonzero - error codes: - - ... -*/ +/* RETURN VALUE + getaddrinfo() returns 0 if it succeeds, or one of the following nonzero + error codes: + ... */ int r_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, diff --git a/lib/file.c b/lib/file.c index fff8feeb92a5..9c151643f98e 100644 --- a/lib/file.c +++ b/lib/file.c @@ -177,8 +177,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) On other platforms, we need the slash to indicate an absolute pathname. On Windows, absolute paths start - with a drive letter. - */ + with a drive letter. */ actual_path = real_path; if((actual_path[0] == '/') && actual_path[1] && @@ -388,8 +387,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) /* This implementation ignores the hostname in conformance with RFC 1738. Only local files (reachable via the standard file system) are supported. This means that files on remotely mounted directories - (via NFS, Samba, NT sharing) can be accessed through a file:// URL - */ + (via NFS, Samba, NT sharing) can be accessed through a file:// URL */ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); CURLcode result = CURLE_OK; curlx_struct_stat statbuf; diff --git a/lib/ftp.c b/lib/ftp.c index 338a7280b037..c5725c48ec51 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -232,12 +232,10 @@ static CURLcode ftp_parse_url_path(struct Curl_easy *data, if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) fileName = rawPath; /* this is a full file path */ - /* - else: ftpc->file is not used anywhere other than for operations on - a file. In other words, never for directory operations, - so we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - */ + /* else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations, + so we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. */ break; case FTPFILE_SINGLECWD: @@ -820,8 +818,7 @@ static const char *pathpiece(struct ftp_conn *ftpc, int num) ftp_state_cwd() sends the range of CWD commands to the server to change to the correct directory. It may also need to send MKD commands to create - missing ones, if that option is enabled. -*/ + missing ones, if that option is enabled. */ static CURLcode ftp_state_cwd(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp) @@ -1415,19 +1412,17 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = CURLE_OK; - /* - Here's the executive summary on what to do: + /* Here's the executive summary on what to do: - PASV is RFC959, expect: - 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) + PASV is RFC959, expect: + 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) - LPSV is RFC1639, expect: - 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) + LPSV is RFC1639, expect: + 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) - EPSV is RFC2428, expect: - 229 Entering Extended Passive Mode (|||port|) - - */ + EPSV is RFC2428, expect: + 229 Entering Extended Passive Mode (|||port|) + */ static const char mode[][5] = { "EPSV", "PASV" }; int modeoff; @@ -1550,14 +1545,12 @@ static CURLcode ftp_state_list(struct Curl_easy *data, way. It has turned out that the NLST list output is not the same on all servers either... */ - /* - if FTPFILE_NOCWD was specified, we should add the path + /* if FTPFILE_NOCWD was specified, we should add the path as argument for the LIST / NLST / or custom command. Whether the server will support this, is uncertain. The other ftp_filemethods will CWD into dir/dir/ first and - then do LIST (in that case: nothing to do here) - */ + then do LIST (in that case: nothing to do here) */ const char *lstArg = NULL; int lstArglen = 0; char *cmd; @@ -1948,8 +1941,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, In addition: asking for the size for 'TYPE A' transfers is not constructive since servers do not report the converted size. - Thus, skip it. - */ + Thus, skip it. */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) ftp_state(data, ftpc, FTP_RETR); @@ -3045,7 +3037,7 @@ static CURLcode ftp_pwd_resp(struct Curl_easy *data, The directory name can contain any character; embedded double-quotes should be escaped by double-quotes (the "quote-doubling" convention). - */ + */ /* scan for the first double-quote for non-standard responses */ while(*ptr != '\n' && *ptr != '\0' && *ptr != '"') @@ -3321,7 +3313,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* Reply format is like 215 - */ + */ while(*ptr == ' ') ptr++; for(start = ptr; *ptr && *ptr != ' '; ptr++) @@ -4340,8 +4332,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, disconnect wait in vain and cause more problems than we need to. ftp_quit() will check the state of ftp->ctl_valid. If it is ok it - will try to send the QUIT command, otherwise it will return. - */ + will try to send the QUIT command, otherwise it will return. */ ftpc->shutdown = TRUE; if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) ftpc->ctl_valid = FALSE; diff --git a/lib/functypes.h b/lib/functypes.h index 3938295dd1f7..7fcee834ff25 100644 --- a/lib/functypes.h +++ b/lib/functypes.h @@ -33,7 +33,7 @@ 1. For systems that run configure or cmake, the alternatives are provided here. 2. For systems with config-*.h files, define them there. -*/ + */ #ifdef USE_WINSOCK /* int recv(SOCKET, char *, int, int) */ diff --git a/lib/gopher.c b/lib/gopher.c index 39f4bc0efc62..9781ffff631c 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -147,8 +147,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that are not possible to send now will be sent in the doing function repeatedly - until the entire request is sent. - */ + until the entire request is sent. */ what = SOCKET_WRITABLE(sockfd, timeout_ms); if(what < 0) { result = CURLE_SEND_ERROR; diff --git a/lib/hash.h b/lib/hash.h index 1d7d3de8cecd..d4c73b778647 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -31,8 +31,8 @@ typedef size_t (*hash_function)(void *key, size_t slots_num); /* - Comparator function prototype. Compares two keys. -*/ + * Comparator function prototype. Compares two keys. + */ typedef size_t (*comp_function)(void *key1, size_t key1_len, void *key2, diff --git a/lib/http.c b/lib/http.c index 487973e06cd0..bc3212036d60 100644 --- a/lib/http.c +++ b/lib/http.c @@ -167,8 +167,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, #endif /* If the header has a value, this function returns TRUE and the value is in - 'outp' with blanks trimmed off. -*/ + 'outp' with blanks trimmed off. */ static bool header_has_value(const char **headerp, struct Curl_str *outp) { bool value = !curlx_str_cspn(headerp, outp, ";:") && @@ -3359,7 +3358,7 @@ static CURLcode http_header_c(struct Curl_easy *data, JavaWebServer/1.1.1 obviously sends the header this way! The third added since some servers use that! The fourth means the requested range was unsatisfied. - */ + */ const char *ptr = v; @@ -4344,8 +4343,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, if(!fine_statusline) { /* If user has set option HTTP200ALIASES, - compare header line against list of aliases - */ + compare header line against list of aliases */ statusline check = checkhttpprefix(data, hd, hdlen); if(check == STATUS_DONE) { fine_statusline = TRUE; diff --git a/lib/http.h b/lib/http.h index 5050215743f2..83b4cc6c6414 100644 --- a/lib/http.h +++ b/lib/http.h @@ -137,8 +137,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, will always be unconditionally sent and thus it may not be larger than can always be afforded to send twice. - It must not be greater than 64K to work on VMS. -*/ + It must not be greater than 64K to work on VMS. */ #ifndef MAX_INITIAL_POST_SIZE #define MAX_INITIAL_POST_SIZE (64 * 1024) #endif diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index a1b1120964f6..d4ad7cc5f0c7 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -223,8 +223,7 @@ static CURLcode uri_encode_path(struct Curl_str *original_path, } /* Normalize the query part. Make sure %2B is left percent encoded, and not - decoded to plus, then encoded to space. -*/ + decoded to plus, then encoded to space. */ static CURLcode normalize_query(const char *string, size_t len, struct dynbuf *db) { @@ -827,8 +826,7 @@ static CURLcode parse_sigv4_params(struct Curl_easy *data, /* provider0[:provider1[:region[:service]]] - No string can be longer than N bytes of non-whitespace - */ + No string can be longer than N bytes of non-whitespace */ if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) { failf(data, "first aws-sigv4 provider cannot be empty"); return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/lib/http_chunks.c b/lib/http_chunks.c index fe8e98bd17a3..85f453eb59fc 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -194,8 +194,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, case CHUNK_DATA: /* We expect 'datasize' of data. We have 'blen' right now, it can be - more or less than 'datasize'. Get the smallest piece. - */ + more or less than 'datasize'. Get the smallest piece. */ piece = blen; if(ch->datasize < (curl_off_t)blen) piece = curlx_sotouz(ch->datasize); diff --git a/lib/http_digest.c b/lib/http_digest.c index 25783d096972..31a9db35a827 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -33,11 +33,9 @@ /* Test example headers: -WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" -Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598" - -*/ - + WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" + Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598" + */ CURLcode Curl_input_digest(struct Curl_easy *data, bool proxy, const char *header) /* rest of the *-authenticate: diff --git a/lib/llist.c b/lib/llist.c index ce0c59cc50ed..6528ebaef2c3 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -211,8 +211,7 @@ struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list) /* llist_tail() returns the last 'struct Curl_llist_node *', which might be NULL - @unittest 1300 -*/ + @unittest 1300 */ UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list); UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list) { @@ -251,8 +250,7 @@ struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n) /* llist_node_prev() returns the previous element in a list from a given Curl_llist_node - @unittest 1300 -*/ + @unittest 1300 */ UNITTEST struct Curl_llist_node *llist_node_prev( const struct Curl_llist_node *n); UNITTEST struct Curl_llist_node *llist_node_prev( diff --git a/lib/multi.c b/lib/multi.c index c18d30be1931..0732c7bfbae1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -51,11 +51,9 @@ /* initial multi->xfers table size for a full multi */ #define CURL_XFER_TABLE_SIZE 512 -/* - CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 - to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every - curl handle takes 6K memory, therefore this 3K are not significant. -*/ +/* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 + to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every + curl handle takes 6K memory, therefore this 3K are not significant. */ #ifndef CURL_SOCKET_HASH_TABLE_SIZE #define CURL_SOCKET_HASH_TABLE_SIZE 911 #endif @@ -861,8 +859,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, forbidden from reuse. This easy handle cannot find the connection anymore once removed from the multi handle - Better close the connection here, at once. - */ + Better close the connection here, at once. */ struct connectdata *c; curl_socket_t s; s = Curl_getconnectinfo(data, &c); @@ -3898,8 +3895,7 @@ static void move_pending_to_connect(struct Curl_multi *multi, it can potentially allow hundreds of new transfers. We could consider an improvement where we store the queue reason and allow - more pipewait rechecks than others. -*/ + more pipewait rechecks than others. */ static void process_pending_handles(struct Curl_multi *multi) { uint32_t mid = multi->last_pending_mid; diff --git a/lib/parsedate.c b/lib/parsedate.c index b19423169af9..ec9f21296b5e 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -27,59 +27,57 @@ #include "curlx/strparse.h" #include "curlx/strcopy.h" -/* - A brief summary of the date string formats this parser groks: - - RFC 2616 3.3.1 +/* A brief summary of the date string formats this parser groks: - Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 - Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + RFC 2616 3.3.1 - we support dates without week day name: + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - 06 Nov 1994 08:49:37 GMT - 06-Nov-94 08:49:37 GMT - Nov 6 08:49:37 1994 + we support dates without week day name: - without the time zone: + 06 Nov 1994 08:49:37 GMT + 06-Nov-94 08:49:37 GMT + Nov 6 08:49:37 1994 - 06 Nov 1994 08:49:37 - 06-Nov-94 08:49:37 + without the time zone: - weird order: + 06 Nov 1994 08:49:37 + 06-Nov-94 08:49:37 - 1994 Nov 6 08:49:37 (GNU date fails) - GMT 08:49:37 06-Nov-94 Sunday - 94 6 Nov 08:49:37 (GNU date fails) + weird order: - time left out: + 1994 Nov 6 08:49:37 (GNU date fails) + GMT 08:49:37 06-Nov-94 Sunday + 94 6 Nov 08:49:37 (GNU date fails) - 1994 Nov 6 - 06-Nov-94 - Sun Nov 6 94 + time left out: - unusual separators: + 1994 Nov 6 + 06-Nov-94 + Sun Nov 6 94 - 1994.Nov.6 - Sun/Nov/6/94/GMT + unusual separators: - commonly used time zone names: + 1994.Nov.6 + Sun/Nov/6/94/GMT - Sun, 06 Nov 1994 08:49:37 CET - 06 Nov 1994 08:49:37 EST + commonly used time zone names: - time zones specified using RFC822 style: + Sun, 06 Nov 1994 08:49:37 CET + 06 Nov 1994 08:49:37 EST - Sun, 12 Sep 2004 15:05:58 -0700 - Sat, 11 Sep 2004 21:32:11 +0200 + time zones specified using RFC822 style: - compact numerical date strings: + Sun, 12 Sep 2004 15:05:58 -0700 + Sat, 11 Sep 2004 21:32:11 +0200 - 20040912 15:05:58 -0700 - 20040911 +0200 + compact numerical date strings: -*/ + 20040912 15:05:58 -0700 + 20040911 +0200 + */ #if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS) @@ -193,8 +191,7 @@ static const struct tzinfo tz[] = { /* returns: -1 no day 0 monday - 6 sunday -*/ - + */ static int checkday(const char *check, size_t len) { int i; diff --git a/lib/progress.c b/lib/progress.c index 45e3db14f5a8..3c0f3d57d7c0 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -33,8 +33,7 @@ #ifndef CURL_DISABLE_PROGRESS_METER /* Provide a string that is 7 letters long (plus the zero byte). - @unittest 1636 -*/ + @unittest 1636 */ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds); UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) { @@ -83,8 +82,7 @@ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) but never longer than 6 columns (+ one zero byte). Add suffix k, M, G when suitable... - @unittest 1636 -*/ + @unittest 1636 */ UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen); UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen) { @@ -176,8 +174,7 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) return &data->progress.now; } -/* - New proposed interface, 9th of February 2000: +/* New proposed interface, 9th of February 2000: pgrsStartNow() - sets start time pgrsSetDownloadSize(x) - known expected download size @@ -186,7 +183,7 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) pgrsSetUploadCounter() - amount of data currently uploaded pgrsUpdate() - show progress pgrsDone() - transfer complete -*/ + */ int Curl_pgrsDone(struct Curl_easy *data) { diff --git a/lib/protocol.h b/lib/protocol.h index 50e320d0f66c..031bb834e417 100644 --- a/lib/protocol.h +++ b/lib/protocol.h @@ -209,8 +209,7 @@ struct Curl_protocol { #define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ /* some protocols will have to call the underlying functions without regard to what exact state the socket signals. IE even if the socket says "readable", - the send function might need to be called while uploading, or vice versa. -*/ + the send function might need to be called while uploading, or vice versa. */ #define PROTOPT_DIRLOCK (1 << 3) #define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ #define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it diff --git a/lib/proxy.c b/lib/proxy.c index 28738a15d6a8..b69d89a2ce0a 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -146,7 +146,7 @@ static bool match_host(const char *token, size_t tokenlen, /* A: example.com matches 'example.com' B: www.example.com matches 'example.com' C: nonexample.com DOES NOT match 'example.com' - */ + */ if(tokenlen == namelen) /* case A, exact match */ match = curl_strnequal(token, name, namelen); diff --git a/lib/select.c b/lib/select.c index 888490f2c9ac..281917bd70c4 100644 --- a/lib/select.c +++ b/lib/select.c @@ -287,12 +287,10 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) } } - /* - Note also that Winsock ignores the first argument, so we do not worry + /* Note also that Winsock ignores the first argument, so we do not worry about the fact that maxfd is computed incorrectly with Winsock (since curl_socket_t is unsigned in such cases and thus -1 is the largest - value). - */ + value). */ r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms); if(r <= 0) { if((r == -1) && (SOCKERRNO == SOCKEINTR)) diff --git a/lib/select.h b/lib/select.h index dbbace4527e7..8fe519aee661 100644 --- a/lib/select.h +++ b/lib/select.h @@ -80,10 +80,8 @@ int Curl_socket_check(curl_socket_t readfd0, int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); -/* - With Winsock the valid range is [0..INVALID_SOCKET-1] according to - https://learn.microsoft.com/windows/win32/winsock/socket-data-type-2 -*/ +/* With Winsock the valid range is [0..INVALID_SOCKET-1] according to + https://learn.microsoft.com/windows/win32/winsock/socket-data-type-2 */ #ifdef USE_WINSOCK #define VALID_SOCK(s) ((s) < INVALID_SOCKET) #define FDSET_SOCK(x) 1 diff --git a/lib/setopt.c b/lib/setopt.c index e6a067c5e6fb..51cd58a7f9f8 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1823,8 +1823,7 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) else { /* Allocate even when size == 0. This satisfies the need of possible later address compare to detect the COPYPOSTFIELDS mode, and to mark - that postfields is used rather than read function or form data. - */ + that postfields is used rather than read function or form data. */ char *p = curlx_memdup0(ptr, pflen); if(!p) return CURLE_OUT_OF_MEMORY; diff --git a/lib/sha256.c b/lib/sha256.c index 670532a32558..e2278d366413 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -382,12 +382,11 @@ static CURLcode my_sha256_init(void *in) return CURLE_OK; } -/* - Process a block of memory though the hash +/* Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) -*/ + */ static void my_sha256_update(void *ctx, const unsigned char *in, unsigned int len) @@ -422,12 +421,11 @@ static void my_sha256_update(void *ctx, } } -/* - Terminate the hash to get the digest +/* Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (32 bytes) @return 0 if successful -*/ + */ static void my_sha256_final(unsigned char *out, void *ctx) { struct sha256_state *md = ctx; diff --git a/lib/socks.c b/lib/socks.c index 71c7d1bb5647..67810d3fca9f 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -932,7 +932,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, o IPv4 address: 0x01, BND.ADDR = 4-byte o domain name: 0x03, BND.ADDR = [ 1-byte length, string ] o IPv6 address: 0x04, BND.ADDR = 16-byte - */ + */ if(resp[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLPX_BAD_VERSION; @@ -1159,9 +1159,7 @@ static void socks_proxy_ctx_free(struct socks_ctx *ctx) the next magic steps. If 'done' is not set TRUE, it is not done yet and must be called again. - Note: this function's sub-functions call failf() - -*/ + Note: this function's sub-functions call failf() */ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) diff --git a/lib/url.c b/lib/url.c index 4ec31a3e5b95..1c837c586e8a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -730,8 +730,7 @@ static bool url_match_connect_config(struct connectdata *conn, it would take a lot of processing to make it really accurate. Instead, this matching will assume that reuses of bound connections will most likely also reuse the exact same binding parameters and missing out a - few edge cases should not hurt anyone much. - */ + few edge cases should not hurt anyone much. */ if((conn->localport != m->needle->localport) || (conn->localportrange != m->needle->localportrange) || (m->needle->localdev && diff --git a/lib/urlapi.c b/lib/urlapi.c index d546b18397cd..cbb16a43c427 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -209,7 +209,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.'))) { /* RFC 3986 3.1 explains: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ + */ } else { break; @@ -388,8 +388,7 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, default port. Firefox, Chrome and Safari all do that. Do not do it if the URL has no scheme, to make something that looks like - a scheme not work! - */ + a scheme not work! */ curlx_dyn_setlen(host, keep); portptr++; if(!*portptr) diff --git a/lib/urldata.h b/lib/urldata.h index 16179318c063..b0c7af234415 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1062,7 +1062,7 @@ struct UserDefined { /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP 1 - create directories that do not exist 2 - the same but also allow MKD to fail once - */ + */ uint8_t ftp_create_missing_dirs; #endif uint8_t use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 1818fc96c7bd..683933a953b6 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -751,16 +751,15 @@ static CURLcode auth_create_digest_http_message( convert_to_ascii(hashbuf, (unsigned char *)userh); } - /* - If the algorithm is "MD5" or unspecified (which then defaults to MD5): + /* If the algorithm is "MD5" or unspecified (which then defaults to MD5): - A1 = unq(username-value) ":" unq(realm-value) ":" passwd + A1 = unq(username-value) ":" unq(realm-value) ":" passwd - If the algorithm is "MD5-sess" then: + If the algorithm is "MD5-sess" then: - A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":" - unq(nonce-value) ":" unq(cnonce-value) - */ + A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":" + unq(nonce-value) ":" unq(cnonce-value) + */ hashthis = curl_maprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "", passwdp); @@ -790,18 +789,17 @@ static CURLcode auth_create_digest_http_message( convert_to_ascii(hashbuf, ha1); } - /* - If the "qop" directive's value is "auth" or is unspecified, then A2 is: + /* If the "qop" directive's value is "auth" or is unspecified, then A2 is: - A2 = Method ":" digest-uri-value + A2 = Method ":" digest-uri-value - If the "qop" value is "auth-int", then A2 is: + If the "qop" value is "auth-int", then A2 is: - A2 = Method ":" digest-uri-value ":" H(entity-body) + A2 = Method ":" digest-uri-value ":" H(entity-body) - (The "Method" value is the HTTP request method as specified in section - 5.1.1 of RFC 2616) - */ + (The "Method" value is the HTTP request method as specified in section + 5.1.1 of RFC 2616) + */ uri_quoted = auth_digest_string_quoted((const char *)uripath); if(!uri_quoted) { @@ -872,7 +870,7 @@ static CURLcode auth_create_digest_http_message( web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe characters. - */ + */ userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); if(!userp_quoted) { result = CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 2803c05d9f0e..0be26b3cac27 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -301,7 +301,7 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, 2. A 'short' containing the allocated space for the buffer in bytes. 3. A 'long' containing the offset to the start of the buffer in bytes, from the beginning of the NTLM message. -*/ + */ /* * Curl_auth_is_ntlm_supported() diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 672e9437c0f0..a17aa9400462 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3403,18 +3403,16 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) #ifndef CURL_DISABLE_PROXY if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { - /* - Setup libssh2 callbacks to make it read/write TLS from the socket. - - ssize_t - recvcb(libssh2_socket_t sock, void *buffer, size_t length, - int flags, void **abstract); + /* Setup libssh2 callbacks to make it read/write TLS from the socket. - ssize_t - sendcb(libssh2_socket_t sock, const void *buffer, size_t length, - int flags, void **abstract); + ssize_t + recvcb(libssh2_socket_t sock, void *buffer, size_t length, + int flags, void **abstract); - */ + ssize_t + sendcb(libssh2_socket_t sock, const void *buffer, size_t length, + int flags, void **abstract); + */ #if LIBSSH2_VERSION_NUM >= 0x010b01 infof(data, "SSH: using HTTPS proxy"); #if defined(__clang__) && __clang_major__ >= 16 diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index 2e132f29772b..0535ca00c600 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -36,7 +36,7 @@ "SSL/TLS Strong Encryption: An Introduction" https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html -*/ + */ #include "curl_setup.h" diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 23114937ae72..9f576a9d521f 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2024,7 +2024,7 @@ static void ossl_close_all(struct Curl_easy *data) in the certificate and must exactly match the IP in the URI. This function is now used from ngtcp2 (QUIC) as well. -*/ + */ static CURLcode ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, struct ssl_peer *peer, @@ -2110,8 +2110,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, type itself: for example for an IA5String the data is ASCII" It has been however verified that in 0.9.6 and 0.9.7, IA5String - is always null-terminated. - */ + is always null-terminated. */ if((altlen == strlen(altptr)) && /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ @@ -3149,7 +3148,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, OpenSSL do alternate chain checking by default but we do not know how to determine that in a reliable manner. https://web.archive.org/web/20190422050538/rt.openssl.org/Ticket/Display.html?id=3621 - */ + */ x509flags |= X509_V_FLAG_TRUSTED_FIRST; if(!ssl_config->no_partialchain && !ssl_crlfile) { @@ -3159,8 +3158,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, instead of needing the whole chain. Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we - cannot do partial chains with a CRL check. - */ + cannot do partial chains with a CRL check. */ x509flags |= X509_V_FLAG_PARTIAL_CHAIN; } (void)X509_STORE_set_flags(store, x509flags); @@ -3803,8 +3801,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that workaround despite the fact that SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to keep the secure workaround, the - SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. - */ + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. */ ctx_options = SSL_OP_ALL | SSL_OP_NO_TICKET | SSL_OP_NO_COMPRESSION; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index accda3bbccca..6bbeb0487bbf 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -122,8 +122,7 @@ /* ALPN requires version 8.1 of the Windows SDK, which was shipped with Visual Studio 2013, aka _MSC_VER 1800: https://learn.microsoft.com/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831771 - Or mingw-w64 9.0 or upper. -*/ + Or mingw-w64 9.0 or upper. */ #if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \ (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)) #define HAS_ALPN_SCHANNEL @@ -161,8 +160,7 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3 built-in. Previous builds of Windows 10 had broken TLS 1.3 - implementations that could be enabled via registry. - */ + implementations that could be enabled via registry. */ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; @@ -432,7 +430,7 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, if(fInCert || blob) { /* Reading a .p12 or .pfx file, like the example at bottom of https://learn.microsoft.com/archive/msdn-technet-forums/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 - */ + */ CRYPT_DATA_BLOB datablob; WCHAR *pszPassword; size_t pwd_len = 0; @@ -990,7 +988,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, At the moment we do not pass inbuf unless we are using ALPN since we only use it for that, and WINE (for which we currently disable ALPN) is giving us problems with inbuf regardless. https://github.com/curl/curl/issues/983 - */ + */ sspi_status = Curl_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, NULL, backend->cred->sni_hostname, backend->req_flags, 0, 0, @@ -1419,16 +1417,15 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { SCH_DEV(infof(data, "schannel: encrypted data length: %lu", inbuf[1].cbBuffer)); - /* - There are two cases where we could be getting extra data here: - 1. If we are renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2. (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. - */ + /* There are two cases where we could be getting extra data here: + 1. If we are renegotiating a connection and the handshake is already + complete (from the server perspective), it can encrypted app data + (not handshake data) in an extra buffer at this point. + 2. (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a + connection and this extra data is part of the handshake. + We should process the data immediately; waiting for the socket to + be ready may fail since the server is done sending handshake data. + */ /* check if the remaining data is less than the total amount and therefore begins after the already processed data */ if(backend->encdata.offset > inbuf[1].cbBuffer) { @@ -2030,21 +2027,19 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* send the encrypted message including header, data and trailer */ len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; - /* - it is important to send the full message which includes the header, - encrypted payload, and trailer. Until the client receives all the - data a coherent message has not been delivered and the client - cannot read any of it. - - If we wanted to buffer the unwritten encrypted bytes, we would - tell the client that all data it has requested to be sent has been - sent. The unwritten encrypted bytes would be the first bytes to - send on the next invocation. - Here's the catch with this - if we tell the client that all the - bytes have been sent, does the client call this method again to - send the buffered data? Looking at who calls this function, it - seems the answer is NO. - */ + /* it is important to send the full message which includes the header, + encrypted payload, and trailer. Until the client receives all the + data a coherent message has not been delivered and the client + cannot read any of it. + + If we wanted to buffer the unwritten encrypted bytes, we would + tell the client that all data it has requested to be sent has been + sent. The unwritten encrypted bytes would be the first bytes to + send on the next invocation. + Here's the catch with this - if we tell the client that all the + bytes have been sent, does the client call this method again to + send the buffered data? Looking at who calls this function, it + seems the answer is NO. */ /* send entire message or fail */ while(len > *pnwritten) { @@ -2162,8 +2157,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } /* it is debatable what to return when !len. Regardless we cannot return immediately because there may be data to decrypt (in the case we want to - decrypt all encrypted cached data) so handle !len later in cleanup. - */ + decrypt all encrypted cached data) so handle !len later in cleanup. */ else if(len && !backend->recv_connection_closed) { /* the encrypted buffer must be large enough to hold all the bytes requested and some TLS record overhead. 'len' is a buffer size, so this @@ -2347,8 +2341,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, The behavior here is a matter of debate. We do not want to be vulnerable to a truncation attack however there is some browser precedent for - ignoring the close_notify for compatibility reasons. - */ + ignoring the close_notify for compatibility reasons. */ if(len && !backend->decdata.offset && backend->recv_connection_closed && !backend->recv_sspi_close_notify) { result = CURLE_RECV_ERROR; @@ -2378,8 +2371,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* it is debatable what to return when !len. We could return whatever error we got from decryption but instead we override here so the return is - consistent. - */ + consistent. */ if(!len) return CURLE_OK; diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index b65ff79926d1..242f6a0f4496 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -147,9 +147,7 @@ struct schannel_cert_share { struct curltime time; /* when the cached store was created */ }; -/* -* size of the structure: 20 bytes. -*/ +/* size of the structure: 20 bytes */ struct num_ip_data { DWORD size; /* 04 bytes */ union { diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 816912a13534..167c65763ec6 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -36,7 +36,7 @@ "SSL/TLS Strong Encryption: An Introduction" https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html -*/ + */ #include "curl_setup.h" diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index dfc1549c10b0..3e26e0bc8c75 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -36,7 +36,7 @@ "SSL/TLS Strong Encryption: An Introduction" https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html -*/ + */ #include "curl_setup.h" diff --git a/lib/ws.c b/lib/ws.c index 335986c06af5..cdaec3ba9356 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -43,16 +43,15 @@ #include "curlx/strparse.h" #include "curlx/strcopy.h" -/*** - RFC 6455 Section 5.2 - - 0 1 2 3 4 5 6 7 - +-+-+-+-+-------+ - |F|R|R|R| opcode| - |I|S|S|S| (4) | - |N|V|V|V| | - | |1|2|3| | -*/ +/* RFC 6455 Section 5.2 + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-------+ + |F|R|R|R| opcode| + |I|S|S|S| (4) | + |N|V|V|V| | + | |1|2|3| | + */ #define WSBIT_FIN 0x80 #define WSBIT_RSV1 0x40 #define WSBIT_RSV2 0x20 @@ -854,28 +853,27 @@ static void ws_enc_init(struct ws_encoder *enc) ws_enc_reset(enc); } -/*** - RFC 6455 Section 5.2 - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ -*/ +/* RFC 6455 Section 5.2 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + */ static CURLcode ws_enc_add_frame(struct Curl_easy *data, struct ws_encoder *enc, @@ -1415,14 +1413,13 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, The sent value is the base64 encoded version of a SHA-1 hash done on the |Sec-WebSocket-Key| header field concatenated with - the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". - */ + the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". */ /* If the response includes a |Sec-WebSocket-Extensions| header field and this header field indicates the use of an extension that was not present in the client's handshake (the server has indicated an extension not requested by the client), the client MUST Fail the WebSocket Connection. - */ + */ /* If the response includes a |Sec-WebSocket-Protocol| header field and this header field indicates the use of a subprotocol that was diff --git a/projects/vms/curl_crtl_init.c b/projects/vms/curl_crtl_init.c index a044ee72e042..9c8e46761ef9 100644 --- a/projects/vms/curl_crtl_init.c +++ b/projects/vms/curl_crtl_init.c @@ -168,9 +168,7 @@ static int sys_crelnm(const char *logname, const char *value) /* Start of DECC RTL Feature handling */ -/* -** Sets default value for a feature -*/ +/* Sets default value for a feature */ #ifdef __VAX static void set_feature_default(const char *name, const char *value) { @@ -309,9 +307,9 @@ void (* const iniarray[])(void) = { set_features }; #endif /* -** Force a reference to LIB$INITIALIZE to ensure it -** exists in the image. -*/ + * Force a reference to LIB$INITIALIZE to ensure it + * exists in the image. + */ int LIB$INITIALIZE(void); #ifdef __DECC #pragma extern_model strict_refdef diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index cd046dcbad08..2a69ce0f1179 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -123,8 +123,8 @@ static void dump(const char *timebuf, const char *idsbuf, const char *text, #define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ CURL_FORMAT_CURL_OFF_T "] " /* -** callback for CURLOPT_DEBUGFUNCTION -*/ + * callback for CURLOPT_DEBUGFUNCTION + */ int tool_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userdata) diff --git a/src/tool_cb_dbg.h b/src/tool_cb_dbg.h index d78afb36eaa1..8df54a240f54 100644 --- a/src/tool_cb_dbg.h +++ b/src/tool_cb_dbg.h @@ -26,9 +26,8 @@ #include "tool_setup.h" /* -** callback for CURLOPT_DEBUGFUNCTION -*/ - + * callback for CURLOPT_DEBUGFUNCTION + */ int tool_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userdata); diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 918fe1907e12..c54037f130fc 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -411,10 +411,10 @@ static size_t content_disposition(const char *str, const char *end, } /* -** callback for CURLOPT_HEADERFUNCTION -* -* 'size' is always 1 -*/ + * callback for CURLOPT_HEADERFUNCTION + * + * 'size' is always 1 + */ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { struct per_transfer *per = userdata; diff --git a/src/tool_cb_hdr.h b/src/tool_cb_hdr.h index 37be591e70ee..07b50eb0ddae 100644 --- a/src/tool_cb_hdr.h +++ b/src/tool_cb_hdr.h @@ -52,9 +52,8 @@ struct HdrCbData { int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream); /* -** callback for CURLOPT_HEADERFUNCTION -*/ - + * callback for CURLOPT_HEADERFUNCTION + */ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata); #endif /* HEADER_CURL_TOOL_CB_HDR_H */ diff --git a/src/tool_cb_prg.c b/src/tool_cb_prg.c index b570923c4129..60709426e42c 100644 --- a/src/tool_cb_prg.c +++ b/src/tool_cb_prg.c @@ -36,8 +36,7 @@ my $pi = 3.1415; foreach my $i (1 .. 200) { printf "%d, ", sin($i / 200 * 2 * $pi) * 500000 + 500000; - } -*/ + } */ static const int sinus[] = { 515704, 531394, 547052, 562664, 578214, 593687, 609068, 624341, 639491, 654504, 669364, 684057, 698568, 712883, 726989, 740870, 754513, 767906, @@ -104,9 +103,8 @@ static void fly(struct ProgressData *bar, bool moved) } /* -** callback for CURLOPT_XFERINFOFUNCTION -*/ - + * callback for CURLOPT_XFERINFOFUNCTION + */ static void update_width(struct ProgressData *bar) { int cols = get_terminal_columns(); diff --git a/src/tool_cb_prg.h b/src/tool_cb_prg.h index 9c2dda2d7e35..5939d158da87 100644 --- a/src/tool_cb_prg.h +++ b/src/tool_cb_prg.h @@ -45,9 +45,8 @@ struct OperationConfig; void progressbarinit(struct ProgressData *bar, struct OperationConfig *config); /* -** callback for CURLOPT_PROGRESSFUNCTION -*/ - + * callback for CURLOPT_PROGRESSFUNCTION + */ int tool_progress_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); diff --git a/src/tool_cb_rea.c b/src/tool_cb_rea.c index b5bc53a46ed6..bf9bd15ad640 100644 --- a/src/tool_cb_rea.c +++ b/src/tool_cb_rea.c @@ -68,9 +68,8 @@ static void waitfd(int waitms, int fd) #endif /* -** callback for CURLOPT_READFUNCTION -*/ - + * callback for CURLOPT_READFUNCTION + */ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) { ssize_t rc = 0; @@ -153,9 +152,8 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) } /* -** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads -*/ - + * callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads + */ int tool_readbusy_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) diff --git a/src/tool_cb_rea.h b/src/tool_cb_rea.h index 06899d3ef342..c44ceaaa0d02 100644 --- a/src/tool_cb_rea.h +++ b/src/tool_cb_rea.h @@ -26,15 +26,13 @@ #include "tool_setup.h" /* -** callback for CURLOPT_READFUNCTION -*/ - + * callback for CURLOPT_READFUNCTION + */ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata); /* -** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads -*/ - + * callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads + */ int tool_readbusy_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); diff --git a/src/tool_cb_see.c b/src/tool_cb_see.c index bb33bd22890d..50e630d60d79 100644 --- a/src/tool_cb_see.c +++ b/src/tool_cb_see.c @@ -28,12 +28,11 @@ #include "tool_cb_see.h" /* -** callback for CURLOPT_SEEKFUNCTION -** -** Notice that this is not supposed to return the resulting offset. This -** shall only return CURL_SEEKFUNC_* return codes. -*/ - + * callback for CURLOPT_SEEKFUNCTION + * + * Notice that this is not supposed to return the resulting offset. This + * shall only return CURL_SEEKFUNC_* return codes. + */ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) { struct per_transfer *per = userdata; diff --git a/src/tool_cb_see.h b/src/tool_cb_see.h index e473223526ae..1cac0ace5cfb 100644 --- a/src/tool_cb_see.h +++ b/src/tool_cb_see.h @@ -26,8 +26,8 @@ #include "tool_setup.h" /* -** callback for CURLOPT_SEEKFUNCTION -*/ + * callback for CURLOPT_SEEKFUNCTION + */ int tool_seek_cb(void *userdata, curl_off_t offset, int whence); #endif /* HEADER_CURL_TOOL_CB_SEE_H */ diff --git a/src/tool_cb_soc.c b/src/tool_cb_soc.c index 0df0a1e6fb24..ddc11ac86857 100644 --- a/src/tool_cb_soc.c +++ b/src/tool_cb_soc.c @@ -30,11 +30,10 @@ #include "tool_cb_soc.h" /* -** callback for CURLOPT_OPENSOCKETFUNCTION -** -** Notice that only Linux is supported for the moment. -*/ - + * callback for CURLOPT_OPENSOCKETFUNCTION + * + * Notice that only Linux is supported for the moment. + */ curl_socket_t tool_socket_open_mptcp_cb(void *clientp, curlsocktype purpose, struct curl_sockaddr *addr) diff --git a/src/tool_cb_soc.h b/src/tool_cb_soc.h index f02150aa8284..d45d72a361e1 100644 --- a/src/tool_cb_soc.h +++ b/src/tool_cb_soc.h @@ -26,9 +26,8 @@ #include "tool_setup.h" /* -** callback for CURLOPT_OPENSOCKETFUNCTION -*/ - + * callback for CURLOPT_OPENSOCKETFUNCTION + */ curl_socket_t tool_socket_open_mptcp_cb(void *clientp, curlsocktype purpose, struct curl_sockaddr *addr); diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index 133573a8b38e..c67e3975d2d1 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -237,9 +237,8 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, #endif /* _WIN32 */ /* -** callback for CURLOPT_WRITEFUNCTION -*/ - + * callback for CURLOPT_WRITEFUNCTION + */ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) { size_t rc; diff --git a/src/tool_cb_wrt.h b/src/tool_cb_wrt.h index 55502f440dcf..c99bd7c5e789 100644 --- a/src/tool_cb_wrt.h +++ b/src/tool_cb_wrt.h @@ -26,9 +26,8 @@ #include "tool_setup.h" /* -** callback for CURLOPT_WRITEFUNCTION -*/ - + * callback for CURLOPT_WRITEFUNCTION + */ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata); /* create a local file for writing, return TRUE on success */ diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 7e6d625cb251..6d41711a406e 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -69,9 +69,8 @@ static struct tool_mime *tool_mime_new_data(struct tool_mime *parent, } /* -** unsigned size_t to signed curl_off_t -*/ - + * unsigned size_t to signed curl_off_t + */ #define CURL_MASK_UCOFFT ((unsigned CURL_TYPEOF_CURL_OFF_T)~0) #define CURL_MASK_SCOFFT (CURL_MASK_UCOFFT >> 1) diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 9d34ffad205c..5499b1ff360e 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -1719,8 +1719,7 @@ static ParameterError parse_upload_flags(struct OperationConfig *config, } /* if 'toggle' is TRUE, set the 'bits' in 'modify'. - if 'toggle' is FALSE, clear the 'bits' in 'modify' -*/ + if 'toggle' is FALSE, clear the 'bits' in 'modify' */ static void togglebit(bool toggle, unsigned long *modify, unsigned long bits) { if(toggle) diff --git a/src/tool_helpers.c b/src/tool_helpers.c index a23c3a80e75b..b5e9b5412128 100644 --- a/src/tool_helpers.c +++ b/src/tool_helpers.c @@ -29,8 +29,8 @@ #include "tool_helpers.h" /* -** Helper functions that are used from more than one source file. -*/ + * Helper functions that are used from more than one source file. + */ const char *param2text(ParameterError error) { diff --git a/src/tool_main.c b/src/tool_main.c index 4ed2f41d580d..d017f702578c 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -129,8 +129,8 @@ static void memory_tracking_init(void) #endif /* -** curl tool main function. -*/ + * curl tool main function. + */ #ifdef _UNICODE #ifdef CURL_HAVE_DIAG /* GCC does not know about wmain() */ diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c index 18e2d1a62578..57a49ef97f6a 100644 --- a/src/tool_paramhlp.c +++ b/src/tool_paramhlp.c @@ -64,8 +64,7 @@ struct getout *new_getout(struct OperationConfig *config) countcrlf TRUE - return number of bytes from the start that are ONLY CR or LF or NULL. - -*/ + */ static size_t memcrlf(char *orig, bool countcrlf, /* TRUE if we count CRLF, FALSE if we count non-CRLF */ diff --git a/src/tool_progress.c b/src/tool_progress.c index 2f15e8f6ae8d..c30644c0e380 100644 --- a/src/tool_progress.c +++ b/src/tool_progress.c @@ -30,8 +30,8 @@ but never longer than 5 columns (+ one zero byte). Add suffix k, M, G when suitable... - Unit test @1622 -*/ + Unit test 1622 + */ UNITTEST char *max5data(curl_off_t bytes, char *max5, size_t mlen) { /* a signed 64-bit value is 8192 petabytes maximum */ @@ -90,7 +90,7 @@ int xferinfo_cb(void *clientp, /* Provide a time string that is 8 letters long (plus the zero byte) - Unit test @1622 + Unit test 1622 */ UNITTEST void timebuf(char *r, size_t rlen, curl_off_t seconds) { @@ -152,9 +152,9 @@ static void add_offt(curl_off_t *val, curl_off_t add) } /* - |DL% UL% Dled Uled Xfers Live Total Current Left Speed - | 6 -- 9.9G 0 2 2 0:00:40 0:00:02 0:00:37 4087M -*/ + |DL% UL% Dled Uled Xfers Live Total Current Left Speed + | 6 -- 9.9G 0 2 2 0:00:40 0:00:02 0:00:37 4087M + */ bool progress_meter(CURLM *multi, struct curltime *start, bool final) { static struct curltime stamp; diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index 92bf9c43a5e6..c4b112437018 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -425,8 +425,7 @@ static CURLcode add_glob(struct URLGlob *glob, size_t pos) } /* returns the named glob pattern (case sensitively) if it exists, otherwise - NULL -*/ + NULL */ static struct URLPattern *glob_find_name(struct URLGlob *glob, struct Curl_str *name) { diff --git a/src/tool_writeout.c b/src/tool_writeout.c index ac8d5434b8d4..63e93ff52dcb 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -569,7 +569,7 @@ static const char *outtime(const char *ptr, /* %time{ ... */ - +0000 for %z because it is otherwise not portable - UTC for %Z because it is otherwise not portable - Keep '%%' as-is so that strftime() makes a single % out of them - */ + */ for(i = 0; !result && i < vlen; i++) { if((i < vlen - 1) && ptr[i] == '%') { switch(ptr[i + 1]) { diff --git a/src/tool_writeout_json.c b/src/tool_writeout_json.c index 7ea18860a5b1..3b6f7df73fd8 100644 --- a/src/tool_writeout_json.c +++ b/src/tool_writeout_json.c @@ -32,8 +32,7 @@ /* provide the given string in dynbuf as a quoted json string, but without the outer quotes. The buffer is not inited by this function. - Return 0 on success, non-zero on error. -*/ + Return 0 on success, non-zero on error. */ int jsonquoted(const char *in, size_t len, struct dynbuf *out, bool lowercase) { const unsigned char *i = (const unsigned char *)in; diff --git a/tests/libtest/lib1156.c b/tests/libtest/lib1156.c index b8339c245613..6b13374f0424 100644 --- a/tests/libtest/lib1156.c +++ b/tests/libtest/lib1156.c @@ -23,16 +23,14 @@ ***************************************************************************/ #include "first.h" -/* - Check range/resume returned error codes and data presence. - - The input parameters are: - - CURLOPT_RANGE/CURLOPT_RESUME_FROM - - CURLOPT_FAILONERROR - - Returned http code (2xx/416) - - Content-Range header present in reply. - -*/ +/* Check range/resume returned error codes and data presence. + + The input parameters are: + - CURLOPT_RANGE/CURLOPT_RESUME_FROM + - CURLOPT_FAILONERROR + - Returned http code (2xx/416) + - Content-Range header present in reply. + */ #define F_RESUME (1 << 0) /* resume/range. */ #define F_HTTP416 (1 << 1) /* Server returns http code 416. */ diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index fd8073c87608..0b20780b7e61 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -300,12 +300,11 @@ static const struct testcase get_parts_list[] = { "| [16] | [17]", 0, CURLU_URLDECODE, CURLUE_OK }, #ifdef USE_IDN - /* - https://sv.wikipedia.org/wiki/R%c3%a4ksm%c3%b6rg%c3%a5s - https://codepoints.net/U+00E4 Latin Small Letter A with Diaeresis - https://codepoints.net/U+00F6 Latin Small Letter O with Diaeresis - https://codepoints.net/U+00E5 Latin Small Letter A with Ring Above - */ + /* https://sv.wikipedia.org/wiki/R%c3%a4ksm%c3%b6rg%c3%a5s + https://codepoints.net/U+00E4 Latin Small Letter A with Diaeresis + https://codepoints.net/U+00F6 Latin Small Letter O with Diaeresis + https://codepoints.net/U+00E5 Latin Small Letter A with Ring Above + */ { "https://r\xc3\xa4ksm\xc3\xb6rg\xc3\xa5s.se", "https | [11] | [12] | [13] | xn--rksmrgs-5wao1o.se | " "[15] | / | [16] | [17]", 0, CURLU_PUNYCODE, CURLUE_OK }, @@ -323,15 +322,14 @@ static const struct testcase get_parts_list[] = { "https | [11] | [12] | [13] | [30] | [15] | / | [16] | [17]", 0, CURLU_PUNYCODE, CURLUE_OK }, #endif - /* - https://codepoints.net/U+2102 Double-Struck Capital C - https://codepoints.net/U+1d64 Latin Subscript Small Letter U - https://codepoints.net/U+24c7 Circled Latin Capital Letter R - https://codepoints.net/U+2112 Script Capital L - https://codepoints.net/U+3002 Ideographic Full Stop - https://codepoints.net/U+1d412 Mathematical Bold Capital S - https://codepoints.net/U+1f134 Squared Latin Capital Letter E - */ + /* https://codepoints.net/U+2102 Double-Struck Capital C + https://codepoints.net/U+1d64 Latin Subscript Small Letter U + https://codepoints.net/U+24c7 Circled Latin Capital Letter R + https://codepoints.net/U+2112 Script Capital L + https://codepoints.net/U+3002 Ideographic Full Stop + https://codepoints.net/U+1d412 Mathematical Bold Capital S + https://codepoints.net/U+1f134 Squared Latin Capital Letter E + */ {"https://" "%e2%84%82%e1%b5%a4%e2%93%87%e2%84%92%e3%80%82%f0%9d%90%92%f0%9f%84%b4", "https | [11] | [12] | [13] | " @@ -1132,7 +1130,7 @@ static int checkurl(const char *org, const char *url, const char *out) /* 1. Set the URL 2. Set components 3. Extract all components (not URL) -*/ + */ static const struct setgetcase setget_parts_list[] = { {"https://example.com/", "query=\"\",", @@ -1748,7 +1746,7 @@ static int set_url(void) /* 1. Set a URL 2. Set one or more parts 3. Extract and compare all parts - not the URL -*/ + */ static int setget_parts(bool has_utf8) { int i; diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index 5a6a44a048cf..e98ce3ca813f 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -59,7 +59,7 @@ static CURLcode test_lib1978(const char *URL) same value 7. merging works for headers all with no values 8. merging works for headers some with no values - */ + */ list = curl_slist_append(list, "x-amz-meta-test: test2"); if(!list) diff --git a/tests/libtest/lib514.c b/tests/libtest/lib514.c index 1fe9adc8d543..fb4b7e2385c1 100644 --- a/tests/libtest/lib514.c +++ b/tests/libtest/lib514.c @@ -45,12 +45,10 @@ static CURLcode test_lib514(const char *URL) /* Based on a bug report by Niels van Tongeren on June 29, 2004: - A weird situation occurs when request 1 is a POST request and the request - 2 is a HEAD request. For the POST request we set the CURLOPT_POSTFIELDS, - CURLOPT_POSTFIELDSIZE and CURLOPT_POST options. For the HEAD request we - set the CURLOPT_NOBODY option to '1'. - - */ + A weird situation occurs when request 1 is a POST request and the request + 2 is a HEAD request. For the POST request we set the CURLOPT_POSTFIELDS, + CURLOPT_POSTFIELDSIZE and CURLOPT_POST options. For the HEAD request we + set the CURLOPT_NOBODY option to '1'. */ easy_setopt(curl, CURLOPT_POSTFIELDS, "moo"); easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 3L); diff --git a/tests/libtest/lib590.c b/tests/libtest/lib590.c index 3a83e62701ab..88614c9c3f45 100644 --- a/tests/libtest/lib590.c +++ b/tests/libtest/lib590.c @@ -23,19 +23,18 @@ ***************************************************************************/ #include "first.h" -/* - Based on a bug report recipe by Rene Bernhardt in - https://curl.se/mail/lib-2011-10/0323.html +/* Based on a bug report recipe by Rene Bernhardt in + https://curl.se/mail/lib-2011-10/0323.html - It is reproducible by the following steps: + It is reproducible by the following steps: - - Use a proxy that offers NTLM and Negotiate - (CURLOPT_PROXY and CURLOPT_PROXYPORT) - - Tell libcurl NOT to use Negotiate - curl_easy_setopt(CURLOPT_PROXYAUTH, - CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM) - - Start the request -*/ + - Use a proxy that offers NTLM and Negotiate + (CURLOPT_PROXY and CURLOPT_PROXYPORT) + - Tell libcurl NOT to use Negotiate + curl_easy_setopt(CURLOPT_PROXYAUTH, + CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM) + - Start the request + */ static CURLcode test_lib590(const char *URL) { diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 87f32384eea6..a9b869d7715e 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -424,22 +424,22 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, uint8_t header[12] = { 0x80, 0xea, /* ID, overwrite */ 0x81, 0x80, - /* - Flags: 0x8180 Standard query response, No error - 1... .... .... .... = Response: Message is a response - .000 0... .... .... = Opcode: Standard query (0) - .... .0.. .... .... = Authoritative: Server is not an authority for - domain - .... ..0. .... .... = Truncated: Message is not truncated - .... ...1 .... .... = Recursion desired: Do query recursively - .... .... 1... .... = Recursion available: Server can do recursive - queries - .... .... .0.. .... = Z: reserved (0) - .... .... ..0. .... = Answer authenticated: Answer/authority portion - was not authenticated by the server - .... .... ...0 .... = Non-authenticated data: Unacceptable - .... .... .... 0000 = Reply code: No error (0) - */ + /* Flags: 0x8180 Standard query response, No error + + 1... .... .... .... = Response: Message is a response + .000 0... .... .... = Opcode: Standard query (0) + .... .0.. .... .... = Authoritative: Server is not an authority for + domain + .... ..0. .... .... = Truncated: Message is not truncated + .... ...1 .... .... = Recursion desired: Do query recursively + .... .... 1... .... = Recursion available: Server can do recursive + queries + .... .... .0.. .... = Z: reserved (0) + .... .... ..0. .... = Answer authenticated: Answer/authority portion + was not authenticated by the server + .... .... ...0 .... = Non-authenticated data: Unacceptable + .... .... .... 0000 = Reply code: No error (0) + */ 0x0, 0x1, /* QDCOUNT a single question */ 0x0, 0x0, /* ANCOUNT number of answers */ 0x0, 0x0, /* NSCOUNT */ diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 286495a63e11..189ac41bec7b 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -286,7 +286,7 @@ static int disconnect(FILE *dump, curl_socket_t fd) } /* - do + do encodedByte = X MOD 128 X = X DIV 128 @@ -294,16 +294,13 @@ static int disconnect(FILE *dump, curl_socket_t fd) // if there are more data to encode, set the top bit of this byte if ( X > 0 ) - encodedByte = encodedByte OR 128 + endif - endif - - 'output' encodedByte - - while ( X > 0 ) + 'output' encodedByte -*/ + while ( X > 0 ) + */ /* return number of bytes used */ static size_t encode_length(size_t packetlen, @@ -684,12 +681,10 @@ static curl_socket_t mqttit(curl_socket_t fd) return CURL_SOCKET_BAD; } -/* - sockfdp is a pointer to an established stream or CURL_SOCKET_BAD +/* sockfdp is a pointer to an established stream or CURL_SOCKET_BAD - if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must - accept() -*/ + if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must + accept() */ static bool mqttd_incoming(curl_socket_t listenfd) { fd_set fds_read; diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index d25195af2d94..32838f987c96 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -911,12 +911,10 @@ static bool disc_handshake(void) return TRUE; } -/* - sockfdp is a pointer to an established stream or CURL_SOCKET_BAD +/* sockfdp is a pointer to an established stream or CURL_SOCKET_BAD - if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must - accept() -*/ + if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must + accept() */ static bool juggle(curl_socket_t *sockfdp, curl_socket_t listenfd, enum sockmode *mode) @@ -1041,7 +1039,7 @@ static bool juggle(curl_socket_t *sockfdp, Commands: DATA - plain pass-through data - */ + */ if(!read_stdin(buffer, 5)) return FALSE; diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 9e1caedc2d34..b0b5b6771c5c 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -167,7 +167,7 @@ static void socksd_getconfig(void) o 0x00 NO AUTHENTICATION REQUIRED o 0x01 GSSAPI o 0x02 USERNAME/PASSWORD - */ + */ else if(!strcmp(key, "method")) { pval = value; if(!curlx_str_number(&pval, &num, 0xff)) { @@ -366,7 +366,7 @@ static curl_socket_t sockit(curl_socket_t fd) +----+------+----------+------+----------+ | 1 | 1 | 1 to 255 | 1 | 1 to 255 | +----+------+----------+------+----------+ - */ + */ unsigned char ulen; unsigned char plen; bool login = TRUE; @@ -438,10 +438,10 @@ static curl_socket_t sockit(curl_socket_t fd) return CURL_SOCKET_BAD; } /* ATYP: - o IPv4 address: 0x01 - o domain name: 0x03 - o IPv6 address: 0x04 - */ + o IPv4 address: 0x01 + o domain name: 0x03 + o IPv6 address: 0x04 + */ type = buffer[SOCKS5_ATYP]; address = &buffer[SOCKS5_DSTADDR]; switch(type) { @@ -518,19 +518,18 @@ static curl_socket_t sockit(curl_socket_t fd) response[SOCKS5_VERSION] = s_config.responseversion; - /* - o REP Reply field: - o 0x00 succeeded - o 0x01 general SOCKS server failure - o 0x02 connection not allowed by ruleset - o 0x03 Network unreachable - o 0x04 Host unreachable - o 0x05 Connection refused - o 0x06 TTL expired - o 0x07 Command not supported - o 0x08 Address type not supported - o 0x09 to 0xFF unassigned - */ + /* o REP Reply field: + o 0x00 succeeded + o 0x01 general SOCKS server failure + o 0x02 connection not allowed by ruleset + o 0x03 Network unreachable + o 0x04 Host unreachable + o 0x05 Connection refused + o 0x06 TTL expired + o 0x07 Command not supported + o 0x08 Address type not supported + o 0x09 to 0xFF unassigned + */ response[SOCKS5_REP] = rep; response[SOCKS5_RESERVED] = 0; /* must be zero */ response[SOCKS5_ATYP] = type; /* address type */ @@ -602,12 +601,10 @@ static int tunnel(struct perclient *cp, fd_set *fds) return 0; } -/* - sockfdp is a pointer to an established stream or CURL_SOCKET_BAD +/* sockfdp is a pointer to an established stream or CURL_SOCKET_BAD - if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must - accept() -*/ + if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must + accept() */ static bool socksd_incoming(curl_socket_t listenfd) { fd_set fds_read; diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index 1132a1a7cfc3..d7970a5cc6fe 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -35,36 +35,36 @@ static CURLcode t1609_setup(void) /* CURLOPT_RESOLVE address parsing test - to test the following defect fix: - 1. if there is already existing host:port pair in the DNS cache and - we call CURLOPT_RESOLVE, it should also replace addresses. - for example, if there is "test.com:80" with address "1.1.1.1" - and we called CURLOPT_RESOLVE with address "2.2.2.2", then DNS entry - needs to reflect that. - - 2. when cached address is already there and close to expire, then by the - time request is made, it can get expired. This happens because, when - we set address using CURLOPT_RESOLVE, - it usually marks as permanent (by setting timestamp to zero). However, - if address already exists in the cache, then it does not mark it, but - leaves it as it is. - We are fixing this by setting timestamp to zero if address already - exists too. - - Test: - - - insert new entry - - verify that timestamp is not zero - - call set options with CURLOPT_RESOLVE - - then, call Curl_loadhostpairs - - expected result: cached address has zero timestamp. - - - call set options with CURLOPT_RESOLVE with same host:port pair, - different address. - - then, call Curl_loadhostpairs - - expected result: cached address has zero timestamp and new address -*/ + 1. if there is already existing host:port pair in the DNS cache and + we call CURLOPT_RESOLVE, it should also replace addresses. + for example, if there is "test.com:80" with address "1.1.1.1" + and we called CURLOPT_RESOLVE with address "2.2.2.2", then DNS entry + needs to reflect that. + + 2. when cached address is already there and close to expire, then by the + time request is made, it can get expired. This happens because, when + we set address using CURLOPT_RESOLVE, + it usually marks as permanent (by setting timestamp to zero). However, + if address already exists in the cache, then it does not mark it, but + leaves it as it is. + We are fixing this by setting timestamp to zero if address already + exists too. + + Test: + + - insert new entry + - verify that timestamp is not zero + - call set options with CURLOPT_RESOLVE + - then, call Curl_loadhostpairs + + expected result: cached address has zero timestamp. + + - call set options with CURLOPT_RESOLVE with same host:port pair, + different address. + - then, call Curl_loadhostpairs + + expected result: cached address has zero timestamp and new address + */ static CURLcode test_unit1609(const char *arg) { diff --git a/tests/unit/unit1667.c b/tests/unit/unit1667.c index 345bd052cf76..5fedfd2b40af 100644 --- a/tests/unit/unit1667.c +++ b/tests/unit/unit1667.c @@ -103,18 +103,17 @@ static CURLcode test_unit1667(const char *arg) { 31, "abcde", 5, "", CURLE_BAD_FUNCTION_ARGUMENT }, { 99, "abcde", 5, "", CURLE_BAD_FUNCTION_ARGUMENT }, - /* - (many different) strings: - - CURL_ASN1_UTF8_STRING - CURL_ASN1_NUMERIC_STRING - CURL_ASN1_PRINTABLE_STRING - CURL_ASN1_TELETEX_STRING - CURL_ASN1_IA5_STRING - CURL_ASN1_VISIBLE_STRING - CURL_ASN1_UNIVERSAL_STRING - CURL_ASN1_BMP_STRING - */ + /* (many different) strings: + + CURL_ASN1_UTF8_STRING + CURL_ASN1_NUMERIC_STRING + CURL_ASN1_PRINTABLE_STRING + CURL_ASN1_TELETEX_STRING + CURL_ASN1_IA5_STRING + CURL_ASN1_VISIBLE_STRING + CURL_ASN1_UNIVERSAL_STRING + CURL_ASN1_BMP_STRING + */ { CURL_ASN1_UTF8_STRING, "abcde", 5, "abcde", CURLE_OK }, /* a with ring, a with umlaut, o with umlaut in UTF-8 encoding */ { CURL_ASN1_UTF8_STRING, "\xc3\xa5\xc3\xa4\xc3\xb6", 6, From 4b0cf12ab1aa7fa4fbf2f8964f49628f0c163778 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 29 Jun 2026 11:27:38 +0200 Subject: [PATCH 0703/1062] vms: fix symbol typo and missing closing quotes in `config_h.com` Closes #22375 --- projects/vms/config_h.com | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/projects/vms/config_h.com b/projects/vms/config_h.com index a19bc1affa1f..49f6a3a51612 100644 --- a/projects/vms/config_h.com +++ b/projects/vms/config_h.com @@ -381,7 +381,7 @@ $! Manual check for LL on $!----------------------------------------------- $ if key2 .eqs. "LL" $ then -$ write tf "#ifndef __VAX +$ write tf "#ifndef __VAX" $ write tf "#define HAVE_''key2' 1" $ write tf "#endif" $ goto cfgh_in_loop1 @@ -1143,7 +1143,7 @@ $!----------------------------------------------------------------------- $ if keysym .eqs. "STRINGIZE" $ then $ write tf "#ifndef HAVE_STRINGIZE" -$ write tf "#define HAVE_STRINGSIZE 1" +$ write tf "#define HAVE_STRINGIZE 1" $ write tf "#endif" $ goto cfgh_in_loop1 $ endif @@ -1241,7 +1241,7 @@ $ then $ write tf "#ifndef HAVE_''keysym'" $ write tf "#define HAVE_''keysym' 1" $if p2 .nes. "" then write sys$output "''decc_shr' #define ''keysym' 1" -$ write tf "#endif +$ write tf "#endif" $ goto cfgh_in_loop1 $ endif $! @@ -1374,11 +1374,11 @@ $ then $ if key2b .eqs. "INO" .and. key2_h .eqs. "_T" $ then $ write tf "#ifndef SIZEOF_INO_T" -$ write tf "#if !__USING_STD_STAT +$ write tf "#if !__USING_STD_STAT" $ write tf "#define SIZEOF_INO_T 6" -$ write tf "#else +$ write tf "#else" $ write tf "#define SIZEOF_INO_T 8" -$ write tf "#endif +$ write tf "#endif" $ write tf "#endif" $ goto cfgh_in_loop1 $ endif From 0ada20387c31c638cfd7f6b4ae7e5cab5b318caf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 29 Jun 2026 19:01:04 +0200 Subject: [PATCH 0704/1062] tidy-up: fix Perl syntax and formatting nits Closes #22376 --- .github/scripts/cleancmd.pl | 10 ++--- .github/scripts/randcurl.pl | 16 ++++---- .github/scripts/trimmarkdownheader.pl | 4 +- .github/scripts/verify-examples.pl | 4 +- docs/examples/version-check.pl | 4 +- docs/libcurl/symbols.pl | 2 +- scripts/extract-unit-protos | 2 +- scripts/managen | 2 +- scripts/mk-ca-bundle.pl | 14 +++---- scripts/release-notes.pl | 2 +- src/mkhelp.pl | 2 +- tests/devtest.pl | 2 +- tests/ftpserver.pl | 18 ++++----- tests/getpart.pm | 2 +- tests/libtest/test610.pl | 2 +- tests/libtest/test613.pl | 18 ++++----- tests/memanalyze.pl | 2 +- tests/runner.pm | 2 +- tests/runtests.pl | 2 +- tests/secureserver.pl | 2 +- tests/servers.pm | 8 ++-- tests/sshserver.pl | 54 +++++++++++++-------------- tests/test1119.pl | 2 +- tests/test1135.pl | 2 +- tests/test1165.pl | 8 ++-- tests/test1175.pl | 2 +- tests/test1477.pl | 4 +- tests/test1488.pl | 2 +- tests/testcurl.pl | 6 +-- tests/testutil.pm | 4 +- 30 files changed, 102 insertions(+), 102 deletions(-) diff --git a/.github/scripts/cleancmd.pl b/.github/scripts/cleancmd.pl index 722015b08fdc..c362484054aa 100755 --- a/.github/scripts/cleancmd.pl +++ b/.github/scripts/cleancmd.pl @@ -15,7 +15,7 @@ my @asyms; -open(S, "<./docs/libcurl/symbols-in-versions") +open(S, "<", './docs/libcurl/symbols-in-versions') or die "cannot find symbols-in-versions"; while() { if(/^([^ ]*) /) { @@ -29,7 +29,7 @@ '--ftp-ssl-reqd', # old alias ); -open(O, "<./docs/options-in-versions") +open(O, "<", './docs/options-in-versions') or die "cannot find options-in-versions"; while() { chomp; @@ -49,7 +49,7 @@ } close(O); -open(C, "<./.github/scripts/spellcheck.curl") +open(C, "<", './.github/scripts/spellcheck.curl') or die "cannot find spellcheck.curl"; while() { if(/^\#/) { @@ -75,7 +75,7 @@ sub process { my $sepcount = 0; my $out; my $line = 0; - open(F, "<$f") or die; + open(F, "<", $f) or die; while() { $line++; @@ -115,7 +115,7 @@ sub process { map { $out =~ s/\b$_\b//g; } (@syms); if(!$ignore) { - open(O, ">$f") or die; + open(O, ">", $f) or die; print O $out; close(O); } diff --git a/.github/scripts/randcurl.pl b/.github/scripts/randcurl.pl index 2c24a6b5007b..eaf957545676 100755 --- a/.github/scripts/randcurl.pl +++ b/.github/scripts/randcurl.pl @@ -80,7 +80,7 @@ sub addarg { "0123456789-"; my $len = getnum(20) + 2; my $o; - for (1 .. $len) { + for(1 .. $len) { $o .= substr($nice, getnum(length($nice)), 1); } return "--$o"; @@ -93,7 +93,7 @@ sub randarg { ",-?#$%!@ "; my $len = getnum(20); my $o = ''; - for (1 .. $len) { + for(1 .. $len) { $o .= substr($nice, getnum(length($nice)), 1); } return "\'$o\'"; @@ -122,7 +122,7 @@ sub runone { $totalargs += $nargs; $totalcmds++; - for (1 .. $nargs) { + for(1 .. $nargs) { my $o = getnum($nopts); my $option = $opt[$o]; my $ar = ""; @@ -154,7 +154,7 @@ sub runone { print "CMD: $cmd\n"; print "RC: $rc\n"; print "== curl-output == \n"; - open(D, "; print @out; close(D); @@ -166,11 +166,11 @@ sub runconfig { my $a; my $nargs = getnum(80) + 1; - open(C, ">config"); + open(C, ">", 'config'); $totalargs += $nargs; $totalcmds++; - for (1 .. $nargs) { + for(1 .. $nargs) { my $o = getnum($nopts); my $option = $opt[$o]; my $ar = ""; @@ -204,12 +204,12 @@ sub runconfig { print "CMD: $cmd\n"; print "RC: $rc\n"; print "== config == \n"; - open(D, "; print @all; close(D); print "\n== curl-output == \n"; - open(D, "; print @out; close(D); diff --git a/.github/scripts/trimmarkdownheader.pl b/.github/scripts/trimmarkdownheader.pl index 89b2e7d9fd05..36bc8de46c69 100755 --- a/.github/scripts/trimmarkdownheader.pl +++ b/.github/scripts/trimmarkdownheader.pl @@ -12,7 +12,7 @@ my $f = $ARGV[0] || ''; -open(F, "<$f") or die; +open(F, "<", $f) or die; my @out; my $line = 0; @@ -37,7 +37,7 @@ } close(F); -open(O, ">$f") or die; +open(O, ">", $f) or die; for my $l (@out) { print O $l; } diff --git a/.github/scripts/verify-examples.pl b/.github/scripts/verify-examples.pl index a23dc412f08b..a734eefc9625 100755 --- a/.github/scripts/verify-examples.pl +++ b/.github/scripts/verify-examples.pl @@ -54,8 +54,8 @@ sub extract { my $l = 0; my $iline = 0; my $fail = 0; - open(F, "<$f") or die "failed opening input file $f : $!"; - open(O, ">$cfile") or die "failed opening output file $cfile : $!"; + open(F, "<", $f) or die "failed opening input file $f : $!"; + open(O, ">", $cfile) or die "failed opening output file $cfile : $!"; print O "#include \n"; while() { $iline++; diff --git a/docs/examples/version-check.pl b/docs/examples/version-check.pl index 125c3b5f7eb2..680bbb358b02 100755 --- a/docs/examples/version-check.pl +++ b/docs/examples/version-check.pl @@ -41,7 +41,7 @@ use strict; use warnings; -open(S, "<../libcurl/symbols-in-versions") or die; +open(S, "<", '../libcurl/symbols-in-versions') or die; my %doc; my %rem; @@ -70,7 +70,7 @@ sub age { } my %used; -open(C, "<$ARGV[0]") or die; +open(C, "<", $ARGV[0]) or die; while() { if(/\W(CURL[_A-Z0-9v]+)\W/) { diff --git a/docs/libcurl/symbols.pl b/docs/libcurl/symbols.pl index 2717ac3a5550..1472d59bb188 100755 --- a/docs/libcurl/symbols.pl +++ b/docs/libcurl/symbols.pl @@ -47,7 +47,7 @@ use strict; use warnings; -open F, ") { if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) { $version = $1; diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index 494f1d21d192..e2e9acf629a7 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -224,7 +224,7 @@ ($$@) } split(',', $param_string); # Find all values which are not in the list of valid values or "ALL" - my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values; + my @invalid = grep { !is_in_list($_, "ALL", @valid_values) } @values; if(scalar(@invalid) > 0) { # Tell the user which parameters were invalid and print the standard help @@ -233,7 +233,7 @@ ($$@) HELP_MESSAGE(); } - @values = @valid_values if(is_in_list("ALL",@values)); + @values = @valid_values if(is_in_list("ALL", @values)); return @values; } @@ -403,7 +403,7 @@ (%) if($stdout) { open(CRT, '> -') or die "Could not open STDOUT: $!\n"; } else { - open(CRT,">$crt.~") or die "Could not open $crt.~: $!\n"; + open(CRT, ">", "$crt.~") or die "Could not open $crt.~: $!\n"; } print CRT <>$crt.~") or die "Could not open $crt.~: $!"; + open(CRT, ">>", "$crt.~") or die "Could not open $crt.~: $!"; } } $pipe = "|$openssl x509 -text -inform PEM"; @@ -647,7 +647,7 @@ (%) print TMP $pem; close(TMP) or die "Could not close openssl pipe: $!"; if(!$stdout) { - open(CRT, ">>$crt.~") or die "Could not open $crt.~: $!"; + open(CRT, ">>", "$crt.~") or die "Could not open $crt.~: $!"; } } report "Processed: $caname" if($opt_v); diff --git a/scripts/release-notes.pl b/scripts/release-notes.pl index c26c5037e8ea..706f34435919 100755 --- a/scripts/release-notes.pl +++ b/scripts/release-notes.pl @@ -261,7 +261,7 @@ sub onecommit { } } -open(O, ">RELEASE-NOTES"); +open(O, ">", 'RELEASE-NOTES'); for my $l (@o) { print O $l; } diff --git a/src/mkhelp.pl b/src/mkhelp.pl index 9c60d7a64b6c..013ddc51c80b 100755 --- a/src/mkhelp.pl +++ b/src/mkhelp.pl @@ -92,7 +92,7 @@ if(!($c % 12)) { print " "; } - printf(" 0x%02x,", 0+$num); + printf(" 0x%02x,", 0 + $num); if(!(++$c % 12)) { print "\n"; } diff --git a/tests/devtest.pl b/tests/devtest.pl index b71689c857e5..322b6904b6da 100755 --- a/tests/devtest.pl +++ b/tests/devtest.pl @@ -108,7 +108,7 @@ sub parseprotocols { # Initialize @protocols from the curl binary under test # sub init_protocols { - for (qx($CURL -V 2>$dev_null)) { + for(qx($CURL -V 2>$dev_null)) { if(m/^Protocols: (.*)$/) { parseprotocols($1); } diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 0cb8cdc3442c..958160646f29 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -180,9 +180,9 @@ BEGIN # that they are closely related and relationship is a bit awkward. # my $datasockf_state = 'STOPPED'; # see datasockf_state() sub -my $datasockf_mode = 'none'; # ['none','active','passive'] -my $datasockf_runs = 'no'; # ['no','yes'] -my $datasockf_conn = 'no'; # ['no','yes'] +my $datasockf_mode = 'none'; # ['none', 'active', 'passive'] +my $datasockf_runs = 'no'; # ['no', 'yes'] +my $datasockf_conn = 'no'; # ['no', 'yes'] #********************************************************************** # global vars used for signal handling @@ -330,7 +330,7 @@ sub read_mainsockf { $timeout = $sockfilt_timeout + ($nbytes >> 12); } if(eXsysread($FH, $scalar, $nbytes, $timeout) != $nbytes) { - my ($fcaller, $lcaller) = (caller)[1,2]; + my ($fcaller, $lcaller) = (caller)[1, 2]; logmsg "Error: read_mainsockf() failure at $fcaller " . "line $lcaller. Due to eXsysread() failure\n"; return 0; @@ -354,7 +354,7 @@ sub read_datasockf { $timeout = $sockfilt_timeout + ($nbytes >> 12); } if(eXsysread($FH, $scalar, $nbytes, $timeout) != $nbytes) { - my ($fcaller, $lcaller) = (caller)[1,2]; + my ($fcaller, $lcaller) = (caller)[1, 2]; logmsg "Error: read_datasockf() failure at $fcaller " . "line $lcaller. Due to eXsysread() failure\n"; return 0; @@ -373,7 +373,7 @@ sub sysread_or_die { $result = sysread($$FH, $$scalar, $length); if(not defined $result) { - ($fcaller, $lcaller) = (caller)[1,2]; + ($fcaller, $lcaller) = (caller)[1, 2]; logmsg "Failed to read input\n"; logmsg "Error: $srvrname server, sysread error: $!\n"; logmsg "Exited from sysread_or_die() at $fcaller " . @@ -388,7 +388,7 @@ sub sysread_or_die { exit; } elsif($result == 0) { - ($fcaller, $lcaller) = (caller)[1,2]; + ($fcaller, $lcaller) = (caller)[1, 2]; logmsg "Failed to read input\n"; logmsg "Error: $srvrname server, read zero\n"; logmsg "Exited from sysread_or_die() at $fcaller " . @@ -520,7 +520,7 @@ sub senddata { } else { # pause between each byte - for (split(//,$l)) { + for(split(//, $l)) { sockfiltsecondary $_; Time::HiRes::sleep($datadelay); } @@ -1252,7 +1252,7 @@ sub FETCH_imap { @data = getreplydata($selected); } - for (@data) { + for(@data) { $size += length($_); } diff --git a/tests/getpart.pm b/tests/getpart.pm index 10838b2675f0..7bdf9817460d 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -392,7 +392,7 @@ sub writearray { my ($filename, $arrayref) = @_; open(my $temp, ">", $filename) or die "Failure writing file"; - binmode($temp,":raw"); # Cygwin fix + binmode($temp, ":raw"); # Cygwin fix for(@$arrayref) { print $temp $_; } diff --git a/tests/libtest/test610.pl b/tests/libtest/test610.pl index af63c3e6b3b6..2834ba4ecab4 100755 --- a/tests/libtest/test610.pl +++ b/tests/libtest/test610.pl @@ -46,7 +46,7 @@ } elsif($cmd eq "move") { my $arg2 = shift @ARGV; - move($arg,$arg2) or die "$!"; + move($arg, $arg2) or die "$!"; } elsif($cmd eq "gone") { ! -e $arg or die "Path $arg exists"; diff --git a/tests/libtest/test613.pl b/tests/libtest/test613.pl index a404bb613925..8647c9add2ad 100755 --- a/tests/libtest/test613.pl +++ b/tests/libtest/test613.pl @@ -50,30 +50,30 @@ sub errout { mkdir "asubdir" or errout "$!"; chmod 0777, "asubdir"; - open(FILE, ">plainfile.txt") or errout "$!"; + open(FILE, ">", 'plainfile.txt') or errout "$!"; binmode FILE; print FILE "Test file to support curl test suite\n"; close(FILE); # The mtime is specifically chosen to be an even number so that it can be # represented exactly on a FAT file system. - utime time, timegm(0,0,12,1,0,100), "plainfile.txt"; + utime time, timegm(0, 0, 12, 1, 0, 100), "plainfile.txt"; chmod 0666, "plainfile.txt"; - open(FILE, ">emptyfile.txt") or errout "$!"; + open(FILE, ">", 'emptyfile.txt') or errout "$!"; binmode FILE; close(FILE); # The mtime is specifically chosen to be an even number so that it can be # represented exactly on a FAT file system. - utime time, timegm(0,0,12,1,0,100), "emptyfile.txt"; + utime time, timegm(0, 0, 12, 1, 0, 100), "emptyfile.txt"; chmod 0666, "emptyfile.txt"; - open(FILE, ">rofile.txt") or errout "$!"; + open(FILE, ">", 'rofile.txt') or errout "$!"; binmode FILE; print FILE "Read-only test file to support curl test suite\n"; close(FILE); # The mtime is specifically chosen to be an even number so that it can be # represented exactly on a FAT file system. - utime time, timegm(0,0,12,31,11,100), "rofile.txt"; + utime time, timegm(0, 0, 12, 31, 11, 100), "rofile.txt"; chmod 0444, "rofile.txt"; if($^O eq 'cygwin') { system('chattr', ('+r', 'rofile.txt')); @@ -120,7 +120,7 @@ sub errout { # -r-?r-?r-? 12 U U 47 Dec 31 2000 rofile.txt my @canondir; - open(IN, "<$logfile") or die "$!"; + open(IN, "<", $logfile) or die "$!"; while() { /^(.)(..).(..).(..).\s*(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\S+\s+\S+\s+\S+)\s+(.*)$/; if($1 eq "d") { @@ -137,7 +137,7 @@ sub errout { # some systems (e.g. on Windows) # Erase user and group names, as they are not consistent across # all test systems - my $line = sprintf("%s%s???????%5d U U %15d %s %s\n", $1,$2,$5,$6,$7,$8); + my $line = sprintf("%s%s???????%5d U U %15d %s %s\n", $1, $2, $5, $6, $7, $8); push @canondir, $line; } else { # Unexpected format; pass it through and let the test fail @@ -148,7 +148,7 @@ sub errout { @canondir = sort {substr($a, 57) cmp substr($b, 57)} @canondir; my $newfile = $logfile . ".new"; - open(OUT, ">$newfile") or die "$!"; + open(OUT, ">", $newfile) or die "$!"; print OUT join('', @canondir); close(OUT); diff --git a/tests/memanalyze.pl b/tests/memanalyze.pl index 15382e527af4..c1988500fa16 100755 --- a/tests/memanalyze.pl +++ b/tests/memanalyze.pl @@ -55,6 +55,6 @@ my @res = memanalyze($file, $verbose, $trace, $showlimit); -for (@res) { +for(@res) { print $_; } diff --git a/tests/runner.pm b/tests/runner.pm index 594e383f7417..7cbc5067e1c1 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -486,7 +486,7 @@ sub torture { logmsg " $count functions to make fail\n"; } - for (@torture_tests) { + for(@torture_tests) { my $limit = $_; my $fail; my $dumped_core; diff --git a/tests/runtests.pl b/tests/runtests.pl index 449500119428..d7582ded96ab 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2759,7 +2759,7 @@ sub pickrunner { # Output information about the curl build # if(!$listonly && $buildinfo) { - if(open(my $fd, "<", "../buildinfo.txt")) { + if(open(my $fd, "<", '../buildinfo.txt')) { while(my $line = <$fd>) { chomp $line; if($line && $line !~ /^#/) { diff --git a/tests/secureserver.pl b/tests/secureserver.pl index 5aa82f4a0f00..9b12b4425a6c 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -337,7 +337,7 @@ sub exit_signal_handler { print uc($proto) ." server (stunnel $ver_major.$ver_minor)\n"; print "cmd: $cmd\n"; print "stunnel config at $conffile:\n"; - open (my $writtenconf, '<', $conffile) or die "$ssltext could not open the config file after writing\n"; + open(my $writtenconf, '<', $conffile) or die "$ssltext could not open the config file after writing\n"; print <$writtenconf>; print "\n"; close ($writtenconf); diff --git a/tests/servers.pm b/tests/servers.pm index 7aff9be429b4..99db988124e1 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -334,7 +334,7 @@ sub startnew { if(not defined $child) { logmsg "startnew: fork() failure detected\n"; - return (-1,-1); + return (-1, -1); } if(0 == $child) { @@ -366,7 +366,7 @@ sub startnew { Time::HiRes::sleep($timeout); if(checkdied($child)) { logmsg "startnew: child process has failed to start\n" if($verbose); - return (-1,-1); + return (-1, -1); } } @@ -384,7 +384,7 @@ sub startnew { logmsg "startnew: child process has died, server might start up\n" if($verbose); # We cannot abort waiting for the server with a - # return (-1,-1); + # return (-1, -1); # because the server might have forked and could still start # up normally. Instead, reduce the amount of time we remain # waiting. @@ -1671,7 +1671,7 @@ sub runsshserver { my $sshd = find_sshd(); if($sshd) { - ($sshdid,$sshdvernum,$sshdverstr,$sshderror) = sshversioninfo($sshd); + ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd); logmsg $sshderror if($sshderror); } diff --git a/tests/sshserver.pl b/tests/sshserver.pl index 2461b668b114..27a7fa81db6a 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -698,33 +698,33 @@ sub sshd_supports_opt { #*************************************************************************** # Kerberos Authentication support may have not been built into sshd # -if(sshd_supports_opt('KerberosAuthentication','no')) { +if(sshd_supports_opt('KerberosAuthentication', 'no')) { push @cfgarr, 'KerberosAuthentication no'; } -if(sshd_supports_opt('KerberosGetAFSToken','no')) { +if(sshd_supports_opt('KerberosGetAFSToken', 'no')) { push @cfgarr, 'KerberosGetAFSToken no'; } -if(sshd_supports_opt('KerberosOrLocalPasswd','no')) { +if(sshd_supports_opt('KerberosOrLocalPasswd', 'no')) { push @cfgarr, 'KerberosOrLocalPasswd no'; } -if(sshd_supports_opt('KerberosTgtPassing','no')) { +if(sshd_supports_opt('KerberosTgtPassing', 'no')) { push @cfgarr, 'KerberosTgtPassing no'; } -if(sshd_supports_opt('KerberosTicketCleanup','yes')) { +if(sshd_supports_opt('KerberosTicketCleanup', 'yes')) { push @cfgarr, 'KerberosTicketCleanup yes'; } #*************************************************************************** # Andrew File System support may have not been built into sshd # -if(sshd_supports_opt('AFSTokenPassing','no')) { +if(sshd_supports_opt('AFSTokenPassing', 'no')) { push @cfgarr, 'AFSTokenPassing no'; } #*************************************************************************** # S/Key authentication support may have not been built into sshd # -if(sshd_supports_opt('SkeyAuthentication','no')) { +if(sshd_supports_opt('SkeyAuthentication', 'no')) { push @cfgarr, 'SkeyAuthentication no'; } @@ -732,23 +732,23 @@ sub sshd_supports_opt { # GSSAPI Authentication support may have not been built into sshd # my $sshd_builtwith_GSSAPI; -if(sshd_supports_opt('GSSAPIAuthentication','no')) { +if(sshd_supports_opt('GSSAPIAuthentication', 'no')) { push @cfgarr, 'GSSAPIAuthentication no'; $sshd_builtwith_GSSAPI = 1; } -if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) { +if(sshd_supports_opt('GSSAPICleanupCredentials', 'yes')) { push @cfgarr, 'GSSAPICleanupCredentials yes'; } -if(sshd_supports_opt('GSSAPIKeyExchange','no')) { +if(sshd_supports_opt('GSSAPIKeyExchange', 'no')) { push @cfgarr, 'GSSAPIKeyExchange no'; } -if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) { +if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials', 'no')) { push @cfgarr, 'GSSAPIStoreDelegatedCredentials no'; } -if(sshd_supports_opt('GSSCleanupCreds','yes')) { +if(sshd_supports_opt('GSSCleanupCreds', 'yes')) { push @cfgarr, 'GSSCleanupCreds yes'; } -if(sshd_supports_opt('GSSUseSessionCredCache','no')) { +if(sshd_supports_opt('GSSUseSessionCredCache', 'no')) { push @cfgarr, 'GSSUseSessionCredCache no'; } push @cfgarr, '#'; @@ -760,54 +760,54 @@ sub sshd_supports_opt { # Address family must be specified before ListenAddress splice @cfgarr, 11, 0, 'AddressFamily any'; } -if(sshd_supports_opt('Compression','no')) { +if(sshd_supports_opt('Compression', 'no')) { push @cfgarr, 'Compression no'; } -if(sshd_supports_opt('KbdInteractiveAuthentication','no')) { +if(sshd_supports_opt('KbdInteractiveAuthentication', 'no')) { push @cfgarr, 'KbdInteractiveAuthentication no'; } -if(sshd_supports_opt('KeepAlive','no')) { +if(sshd_supports_opt('KeepAlive', 'no')) { push @cfgarr, 'KeepAlive no'; } -if(sshd_supports_opt('LookupClientHostnames','no')) { +if(sshd_supports_opt('LookupClientHostnames', 'no')) { push @cfgarr, 'LookupClientHostnames no'; } if(sshd_supports_opt('MaxAuthTries','10')) { push @cfgarr, 'MaxAuthTries 10'; } -if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) { +if(sshd_supports_opt('PAMAuthenticationViaKbdInt', 'no')) { push @cfgarr, 'PAMAuthenticationViaKbdInt no'; } -if(sshd_supports_opt('PermitTunnel','no')) { +if(sshd_supports_opt('PermitTunnel', 'no')) { push @cfgarr, 'PermitTunnel no'; } -if(sshd_supports_opt('PermitUserEnvironment','no')) { +if(sshd_supports_opt('PermitUserEnvironment', 'no')) { push @cfgarr, 'PermitUserEnvironment no'; } -if(sshd_supports_opt('RhostsAuthentication','no')) { +if(sshd_supports_opt('RhostsAuthentication', 'no')) { push @cfgarr, 'RhostsAuthentication no'; } -if(sshd_supports_opt('TCPKeepAlive','no')) { +if(sshd_supports_opt('TCPKeepAlive', 'no')) { push @cfgarr, 'TCPKeepAlive no'; } -if(sshd_supports_opt('UseDNS','no')) { +if(sshd_supports_opt('UseDNS', 'no')) { push @cfgarr, 'UseDNS no'; } -if(sshd_supports_opt('UsePAM','no')) { +if(sshd_supports_opt('UsePAM', 'no')) { push @cfgarr, 'UsePAM no'; } if($sshdid =~ /OpenSSH/) { # http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415 - if(sshd_supports_opt('UsePrivilegeSeparation','no')) { + if(sshd_supports_opt('UsePrivilegeSeparation', 'no')) { push @cfgarr, 'UsePrivilegeSeparation no'; } } -if(sshd_supports_opt('VerifyReverseMapping','no')) { +if(sshd_supports_opt('VerifyReverseMapping', 'no')) { push @cfgarr, 'VerifyReverseMapping no'; } -if(sshd_supports_opt('X11UseLocalhost','yes')) { +if(sshd_supports_opt('X11UseLocalhost', 'yes')) { push @cfgarr, 'X11UseLocalhost yes'; } push @cfgarr, '#'; diff --git a/tests/test1119.pl b/tests/test1119.pl index 47ec214120c1..a1b09d059457 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -66,7 +66,7 @@ # included by it, which *should* be all headers sub scanenum { my ($file) = @_; - open my $h_in, "-|", "$Cpreprocessor $i$file" or die "Cannot preprocess $file"; + open(my $h_in, "-|", "$Cpreprocessor $i$file") or die "Cannot preprocess $file"; while(<$h_in>) { if(/enum\s+(\S+\s+)?{/ .. /}/) { s/^\s+//; diff --git a/tests/test1135.pl b/tests/test1135.pl index 907a08d20bb2..ceb313cf8c2e 100755 --- a/tests/test1135.pl +++ b/tests/test1135.pl @@ -68,7 +68,7 @@ my @out; foreach my $f (@incs) { - open H, "<$f" or die; + open(H, "<", $f) or die; my $first = ""; while() { s/CURL_DEPRECATED\(.*"\)//; diff --git a/tests/test1165.pl b/tests/test1165.pl index 99979dff06b4..259e13cc8783 100755 --- a/tests/test1165.pl +++ b/tests/test1165.pl @@ -43,7 +43,7 @@ sub scanconf { my ($f) = @_; - open S, "<$f"; + open(S, "<", $f); while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { my ($sym) = ($1); @@ -68,7 +68,7 @@ sub scan_configure { sub scanconf_cmake { my ($hashr, $f) = @_; - open S, "<$f"; + open(S, "<", $f); while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { my ($sym) = ($1); @@ -95,7 +95,7 @@ sub scan_cmake_config_h { sub scan_file { my ($source) = @_; - open F, "<$source"; + open(F, "<", $source); while() { while(s/(CURL_DISABLE_[A-Z0-9_]+)//) { my ($sym) = ($1); @@ -127,7 +127,7 @@ sub scan_sources { } sub scan_docs { - open F, "<$root/docs/$DOCS"; + open(F, "<", "$root/docs/$DOCS"); my $line = 0; while() { $line++; diff --git a/tests/test1175.pl b/tests/test1175.pl index b929ff41ee86..06db7c7d4c13 100755 --- a/tests/test1175.pl +++ b/tests/test1175.pl @@ -52,7 +52,7 @@ sub getincludeerrors { open(my $f, "<", "$root/docs/libcurl/symbols-in-versions"); while(<$f>) { if($_ =~ /^(CURL[EM]_[^ \t]*)[ \t]*([0-9.]+)[ \t]*(.*)/) { - my ($symbol, $added, $rest) = ($1,$2,$3); + my ($symbol, $added, $rest) = ($1, $2, $3); if($rest =~ /^([0-9.]+)/) { # removed! } diff --git a/tests/test1477.pl b/tests/test1477.pl index 0973c9c95135..a561cb8cde92 100755 --- a/tests/test1477.pl +++ b/tests/test1477.pl @@ -43,7 +43,7 @@ sub scanheader { my ($file) = @_; - open H, "<$file"; + open(H, "<", $file); my $line = 0; while() { $line++; @@ -63,7 +63,7 @@ sub scanheader { sub scanmanpage { my ($file) = @_; - open H, "<$file"; + open(H, "<", $file); my $line = 0; while() { $line++; diff --git a/tests/test1488.pl b/tests/test1488.pl index b940fb7f14d0..8542c6ee7f41 100755 --- a/tests/test1488.pl +++ b/tests/test1488.pl @@ -111,7 +111,7 @@ sub scanman_md_dir { scanman_md_dir("$root/docs/libcurl"); scanman_md_dir("$root/docs/libcurl/opts"); -open my $s, "<", "$root/docs/libcurl/symbols-in-versions"; +open(my $s, "<", "$root/docs/libcurl/symbols-in-versions"); while(<$s>) { chomp; if(/^(\S+) +([0-9.]*)/) { diff --git a/tests/testcurl.pl b/tests/testcurl.pl index ae836e441135..0c7068a0eedc 100755 --- a/tests/testcurl.pl +++ b/tests/testcurl.pl @@ -443,7 +443,7 @@ sub get_host_triplet { # get the last 5 commits for show (even if no pull was made) @commits = qx(git log --pretty=oneline --abbrev-commit -5); logit "The most recent ares git commits:"; - for (@commits) { + for(@commits) { chomp ($_); logit " $_"; } @@ -479,7 +479,7 @@ sub get_host_triplet { } else { # Show snapshot git commit when available - if(open (my $f, '<', "docs/tarball-commit.txt")) { + if(open(my $f, '<', "docs/tarball-commit.txt")) { my $commit = <$f>; chomp $commit; logit "The most recent curl git commits:"; @@ -490,7 +490,7 @@ sub get_host_triplet { # Set timestamp to the one in curlver.h if this is not a git test build. if((-f "include/curl/curlver.h") && - (open(my $f, "<", "include/curl/curlver.h"))) { + open(my $f, "<", "include/curl/curlver.h")) { while(<$f>) { chomp; if($_ =~ /^\#define\s+LIBCURL_TIMESTAMP\s+\"(.+)\".*$/) { diff --git a/tests/testutil.pm b/tests/testutil.pm index 2deed4ce9528..bfff47db2a55 100644 --- a/tests/testutil.pm +++ b/tests/testutil.pm @@ -243,7 +243,7 @@ sub exerunner { sub get_sha256_base64 { my ($file_path) = @_; - return encode_base64(sha256(do { local $/; open my $fh, '<:raw', $file_path or die $!; <$fh> }), ""); + return encode_base64(sha256(do { local $/; open(my $fh, '<:raw', $file_path) or die $!; <$fh> }), ""); } sub subsha256base64file { @@ -260,7 +260,7 @@ sub subsha256base64file { sub get_file_content { my ($file_path) = @_; - my $content = do { local $/; open my $fh, '<', $file_path or die $!; <$fh> }; + my $content = do { local $/; open(my $fh, '<', $file_path) or die $!; <$fh> }; $content =~ s/(^|-----END .*?-----[\r\n]?)(.*?)(-----BEGIN .*?-----|$)/$1$3/gs; $content =~ s/\r\n/\n/g; chomp($content); From 932e63aab9618436e0f0a88f0abd841c18ebe12a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 28 Jun 2026 20:32:50 +0200 Subject: [PATCH 0705/1062] md5: replace magic numbers with `MD5_DIGEST_LEN` Closes #22377 --- lib/md5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/md5.c b/lib/md5.c index 9f995c7393d0..358f0744ea32 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -68,7 +68,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) #if NETTLE_VERSION_MAJOR >= 4 md5_digest(ctx, digest); #else - md5_digest(ctx, 16, digest); + md5_digest(ctx, MD5_DIGEST_LEN, digest); #endif } @@ -144,7 +144,7 @@ static void my_md5_update(void *ctx, static void my_md5_final(unsigned char *digest, void *ctx) { size_t actual_length; - (void)psa_hash_finish(ctx, digest, 16, &actual_length); + (void)psa_hash_finish(ctx, digest, MD5_DIGEST_LEN, &actual_length); } #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ @@ -221,7 +221,7 @@ static void my_md5_final(unsigned char *digest, void *in) my_md5_ctx *ctx = (my_md5_ctx *)in; unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); - if(length == 16) + if(length == MD5_DIGEST_LEN) CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); if(ctx->hHash) CryptDestroyHash(ctx->hHash); @@ -530,7 +530,7 @@ const struct HMAC_params Curl_HMAC_MD5 = { my_md5_final, /* Hash computation end function. */ sizeof(my_md5_ctx), /* Size of hash context structure. */ 64, /* Maximum key length. */ - 16 /* Result size. */ + MD5_DIGEST_LEN /* Result size. */ }; const struct MD5_params Curl_DIGEST_MD5 = { @@ -538,7 +538,7 @@ const struct MD5_params Curl_DIGEST_MD5 = { my_md5_update, /* Digest update function */ my_md5_final, /* Digest computation end function */ sizeof(my_md5_ctx), /* Size of digest context struct */ - 16 /* Result size */ + MD5_DIGEST_LEN /* Result size */ }; /* From 8734b088834c74ae3d77193387dbe37ccda6d00d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 11 Jul 2026 00:44:25 +0200 Subject: [PATCH 0706/1062] tidy-up: miscellaneous - CMake/CurlTests.c: sync indent with rest of file and most of code. - cmake: sync term in `_CURL_PREFILL` description with rest of code. - curl_setup.h: document function relying on `_CRT_SECURE_NO_WARNINGS`. - tests/http/scorecard.py: typo in message. - 'Quiche' -> 'quiche'. - fix comment and formatting nits. Closes #22378 --- CMake/CurlTests.c | 48 +++++++++---------- CMakeLists.txt | 2 +- configure.ac | 2 +- docs/EXPERIMENTAL.md | 2 +- lib/curl_setup.h | 2 +- lib/curlx/fopen.c | 2 +- lib/vssh/libssh2.c | 4 +- lib/vtls/schannel.c | 2 +- tests/http/scorecard.py | 2 +- .../http/testenv/mod_curltest/mod_curltest.c | 12 ++--- 10 files changed, 37 insertions(+), 41 deletions(-) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index c9d0b93d21d9..f43521bfd658 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -133,7 +133,7 @@ int main(void) #ifdef HAVE_IOCTLSOCKET #ifdef _WIN32 -# include +#include #endif int main(void) { @@ -160,7 +160,7 @@ int main(void) #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO #include #ifdef HAVE_SYS_IOCTL_H -# include +#include #endif int main(void) { @@ -175,7 +175,7 @@ int main(void) #ifdef HAVE_IOCTLSOCKET_FIONBIO #ifdef _WIN32 -# include +#include #endif int main(void) { @@ -190,19 +190,19 @@ int main(void) #ifdef HAVE_IOCTL_FIONBIO /* headers for FIONBIO test */ #ifdef HAVE_SYS_TYPES_H -# include +#include #endif #ifdef HAVE_UNISTD_H -# include +#include #endif #ifndef _WIN32 -# include +#include #endif #ifdef HAVE_SYS_IOCTL_H -# include +#include #endif #ifdef HAVE_STROPTS_H -# include +#include #endif int main(void) { @@ -217,19 +217,19 @@ int main(void) #ifdef HAVE_IOCTL_SIOCGIFADDR /* headers for SIOCGIFADDR test */ #ifdef HAVE_SYS_TYPES_H -# include +#include #endif #ifdef HAVE_UNISTD_H -# include +#include #endif #ifndef _WIN32 -# include +#include #endif #ifdef HAVE_SYS_IOCTL_H -# include +#include #endif #ifdef HAVE_STROPTS_H -# include +#include #endif #include int main(void) @@ -244,13 +244,13 @@ int main(void) #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK #ifdef _WIN32 -# include +#include #endif #ifdef HAVE_SYS_TYPES_H -# include +#include #endif #ifndef _WIN32 -# include +#include #endif int main(void) { @@ -338,13 +338,13 @@ int main(void) #ifdef HAVE_ATOMIC #ifdef HAVE_SYS_TYPES_H -# include +#include #endif #ifdef HAVE_UNISTD_H -# include +#include #endif #ifdef HAVE_STDATOMIC_H -# include +#include #endif int main(void) { @@ -356,10 +356,10 @@ int main(void) #ifdef HAVE_WIN32_WINNT #ifdef _WIN32 -# ifndef NOGDI -# define NOGDI -# endif -# include +#ifndef NOGDI +#define NOGDI +#endif +#include #endif #define enquote(x) #x @@ -374,7 +374,7 @@ int main(void) #ifdef MINGW64_VERSION #ifdef __MINGW32__ -# include <_mingw.h> +#include <_mingw.h> #endif #define enquote(x) #x diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c49e3cb37d9..54032401a2a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -617,7 +617,7 @@ include(CheckSymbolExists) include(CheckTypeSize) include(CheckCSourceCompiles) -option(_CURL_PREFILL "Fast-track known feature detection results (Windows, some Apple)" "${WIN32}") +option(_CURL_PREFILL "Pre-fill known feature detection results (Windows, some Apple)" "${WIN32}") mark_as_advanced(_CURL_PREFILL) if(_CURL_PREFILL) if(WIN32) diff --git a/configure.ac b/configure.ac index 9958bdbe6942..6b87ee6e3e5f 100644 --- a/configure.ac +++ b/configure.ac @@ -3814,7 +3814,7 @@ if test "$want_quiche" != "no"; then AC_CHECK_LIB(quiche, quiche_conn_send_ack_eliciting, [ AC_CHECK_HEADERS(quiche.h, - experimental="$experimental Quiche" + experimental="$experimental quiche" AC_MSG_NOTICE([HTTP3 support is experimental]) curl_h3_msg="enabled (quiche)" AC_DEFINE(USE_QUICHE, 1, [if quiche is in use]) diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index 9e39a9b36cb5..b1b472c5ba43 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -53,7 +53,7 @@ Graduation requirements: - implementation stability over time with no known severe regressions -### The Quiche backend +### The quiche backend Graduation requirements: diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 487a3c5e391b..00e4694bc7bc 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -90,7 +90,7 @@ /* Disable Visual Studio warnings: 4127 "conditional expression is constant" */ #pragma warning(disable:4127) #ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS /* for getenv(), sscanf() */ +#define _CRT_SECURE_NO_WARNINGS /* for getenv(), sscanf(), vsnprintf() */ #endif #endif /* _MSC_VER */ diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index c3601af06ac4..30754b310a6d 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -110,7 +110,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out) const wchar_t *in_w; wchar_t *fbuf = NULL; - /* MS documented "approximate" limit for the maximum path length */ + /* MS-documented "approximate" limit for the maximum path length */ const size_t max_path_len = 32767; #ifndef _UNICODE diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index a17aa9400462..96c1157669d6 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -373,8 +373,8 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, (conn->origin->port != PORT_SSH) ? conn->origin->port : -1, remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit, &host); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 6bbeb0487bbf..17c1c8d195db 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1368,7 +1368,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, /* check if the handshake needs to be continued */ result = CURLE_OK; for(i = 0; i < 3; i++) { - /* search for handshake tokens that need to be send */ + /* search for handshake tokens that need to be sent */ if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { size_t written = 0; DEBUGF(infof(data, "schannel: sending next handshake data: " diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 60b1a35f4c2d..5bc24cf2c9c0 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -474,7 +474,7 @@ def downloads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict if self._limit_rate: title = f'Download Speed ({self.protocol}), limit={Card.fmt_speed(self._limit_rate_num)}, from {meta["server"]}' else: - title = f'Downloads ({self.protocol})from {meta["server"]}' + title = f'Downloads ({self.protocol}) from {meta["server"]}' if self._socks_args: title += f' via {self._socks_args}' return { diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 3eec35381d5f..0bb0c78a363f 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -394,8 +394,7 @@ static int curltest_tweak_handler(request_rec *r) continue; } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " - "understood: '%s' in %s", - arg, r->args); + "understood: '%s' in %s", arg, r->args); ap_die(HTTP_BAD_REQUEST, r); return OK; } @@ -574,8 +573,7 @@ static int curltest_put_handler(request_rec *r) } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " - "understood: '%s' in %s", - arg, r->args); + "understood: '%s' in %s", arg, r->args); ap_die(HTTP_BAD_REQUEST, r); return OK; } @@ -760,8 +758,7 @@ static int curltest_sslinfo_handler(request_rec *r) continue; } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " - "understood: '%s' in %s", - arg, r->args); + "understood: '%s' in %s", arg, r->args); ap_die(HTTP_BAD_REQUEST, r); return OK; } @@ -869,8 +866,7 @@ static int curltest_limit_handler(request_rec *r) } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not " - "understood: '%s' in %s", - arg, r->args); + "understood: '%s' in %s", arg, r->args); ap_die(HTTP_BAD_REQUEST, r); return OK; } From 090056522b494feee8bb2e127a3df087b8753e56 Mon Sep 17 00:00:00 2001 From: Ross Burton Date: Thu, 23 Jul 2026 13:09:13 +0100 Subject: [PATCH 0707/1062] configure: only check in the watt library if WATT_ROOT is set Only look for gethostbyname in libwatt in $WATT_ROOT/lib if WATT_ROOT has actually been set. This avoids configure trying to search in /lib, which won't every succeed and can cause problems in cross builds which check that host paths are not being searched. Closes #22380 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6b87ee6e3e5f..2456589b61a1 100644 --- a/configure.ac +++ b/configure.ac @@ -1277,7 +1277,7 @@ if test "$HAVE_GETHOSTBYNAME" != "1"; then ) fi -if test "$HAVE_GETHOSTBYNAME" != "1"; then +if test "$HAVE_GETHOSTBYNAME" != "1" && test -n "$WATT_ROOT"; then dnl gethostbyname in the watt lib? clean_CPPFLAGS=$CPPFLAGS clean_LDFLAGS=$LDFLAGS From 489a4c1b48096db7df4875af0bedf4f7d0973382 Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Thu, 23 Jul 2026 12:09:04 +0530 Subject: [PATCH 0708/1062] ctype: exclude control bytes from ISPRINT and ISGRAPH Closes #22371 --- lib/curl_ctype.h | 6 ++---- tests/unit/unit1307.c | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/curl_ctype.h b/lib/curl_ctype.h index f3291ad8184b..fd6b7d723cf5 100644 --- a/lib/curl_ctype.h +++ b/lib/curl_ctype.h @@ -30,10 +30,8 @@ #define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) #define IS7F(x) ((x) == 0x7f) -#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d)) - -#define ISPRINT(x) (ISLOWPRINT(x) || (((x) >= ' ') && ((x) <= 0x7e))) -#define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e))) +#define ISPRINT(x) (((x) >= ' ') && ((x) <= 0x7e)) +#define ISGRAPH(x) (((x) > ' ') && ((x) <= 0x7e)) #define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) #define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) #define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALPHA(x) || ISUPHEXALPHA(x)) diff --git a/tests/unit/unit1307.c b/tests/unit/unit1307.c index 833319b611ec..c45dc81c770e 100644 --- a/tests/unit/unit1307.c +++ b/tests/unit/unit1307.c @@ -174,10 +174,15 @@ static CURLcode test_unit1307(const char *arg) { "[[:print:]]", "L", MATCH }, { "[[:print:]]", "\10", NOMATCH }, { "[[:print:]]", "\10", NOMATCH }, + { "[[:print:]]", "\t", NOMATCH }, + { "[[:print:]]", "\n", NOMATCH }, + { "[[:print:]]", "\r", NOMATCH }, { "[[:space:]]", " ", MATCH }, { "[[:space:]]", "x", NOMATCH }, { "[[:graph:]]", " ", NOMATCH }, { "[[:graph:]]", "x", MATCH }, + { "[[:graph:]]", "\t", NOMATCH }, + { "[[:graph:]]", "\r", NOMATCH }, { "[[:blank:]]", "\t", MATCH }, { "[[:blank:]]", " ", MATCH }, { "[[:blank:]]", "\r", NOMATCH }, From 545cdd4b504310df7d8823a3acf6764b77b4f05b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 21 Jul 2026 13:28:59 +0200 Subject: [PATCH 0709/1062] asyn-thrdd: retry link-local ipv6 if missing scope id When the threaded resolver gets AAAA results that carry a link-local address without scope-id, it now re-queues a query with AF_UNSPEC and strips ipv4 addresses from that result. Whatever the resulting addresses and scope-ids are, this becomes the result of the resolve. Fixes #22330 Reported-by: Bartel Sielski Closes #22368 --- lib/asyn-thrdd.c | 177 +++++++++++++++++++++++++++------- lib/asyn.h | 6 +- lib/cf-dns.c | 2 +- lib/thrdqueue.c | 43 ++++----- lib/thrdqueue.h | 3 - tests/http/test_21_resolve.py | 25 +++++ tests/unit/unit3301.c | 4 +- tests/unit/unit3306.c | 2 +- 8 files changed, 195 insertions(+), 67 deletions(-) diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index fa06550a5836..0941c08f5ea9 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -293,8 +293,12 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, curlx_safefree(async->thrdd.rr.https_name); Curl_httpsrr_destroy(async->thrdd.rr.hinfo); #endif + async_thrdd_item_destroy(async->thrdd.inc_A); + async->thrdd.inc_A = NULL; async_thrdd_item_destroy(async->thrdd.res_A); async->thrdd.res_A = NULL; + async_thrdd_item_destroy(async->thrdd.inc_AAAA); + async->thrdd.inc_AAAA = NULL; async_thrdd_item_destroy(async->thrdd.res_AAAA); async->thrdd.res_AAAA = NULL; } @@ -309,28 +313,32 @@ CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; timediff_t milli, ms; + CURLcode result = CURLE_AGAIN; if(!thrdd) return CURLE_FAILED_INIT; - while(async->queries_ongoing && !async->done) { - Curl_async_thrdd_multi_process(data->multi); - if(async->done) - break; - - ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); - if(ms < 3) - milli = 0; - else if(ms <= 50) - milli = ms / 3; - else if(ms <= 250) - milli = 50; - else - milli = 200; - CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); - curlx_wait_ms(milli); + while(result == CURLE_AGAIN) { + while(async->queries_ongoing && !async->done) { + Curl_async_thrdd_multi_process(data->multi); + if(async->done) + break; + + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + if(ms < 3) + milli = 0; + else if(ms <= 50) + milli = ms / 3; + else if(ms <= 250) + milli = 50; + else + milli = 200; + CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); + curlx_wait_ms(milli); + } + result = Curl_async_take_result(data, async, pdns); } - return Curl_async_take_result(data, async, pdns); + return result; } #ifdef HAVE_GETADDRINFO @@ -469,7 +477,7 @@ CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, { CURLcode result; DEBUGASSERT(!multi->resolv_thrdq); - result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, + result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", min_threads, max_threads, idle_time_ms, async_thrdd_item_free, async_thrdd_item_process, @@ -561,30 +569,25 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi) if(data) async = Curl_async_get(data, item->resolv_id); if(async) { - struct async_thrdd_item **pdest = &async->thrdd.res_A; - - async->dns_responses |= item->dns_queries; - --async->queries_ongoing; - async->done = !async->queries_ongoing; + struct async_thrdd_item **pdest = &async->thrdd.inc_A; #ifdef CURLRES_IPV6 if(item->dns_queries & CURL_DNSQ_AAAA) - pdest = &async->thrdd.res_AAAA; + pdest = &async->thrdd.inc_AAAA; #endif if(!*pdest) { - VERBOSE(async_thrdd_report_item(data, item)); *pdest = item; item = NULL; } else DEBUGASSERT(0); /* should not receive duplicates here */ + + --async->queries_ongoing; Curl_multi_mark_dirty(data); } async_thrdd_item_free(item); } -#ifdef CURLVERBOSE - Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); -#endif + VERBOSE(Curl_thrdq_trace(multi->resolv_thrdq, multi->admin)); } CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, @@ -592,7 +595,7 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, uint32_t max_threads, uint32_t idle_time_ms) { - return Curl_thrdq_set_props(multi->resolv_thrdq, 0, + return Curl_thrdq_set_props(multi->resolv_thrdq, min_threads, max_threads, idle_time_ms); } @@ -723,6 +726,113 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, return CURLE_OK; } +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +static bool async_thrdd_item_missing_scope(struct Curl_easy *data, + struct async_thrdd_item *item) +{ + const struct Curl_addrinfo *ai; + + /* scope id already globally set */ + if(data->conn && data->conn->scope_id) + return FALSE; + + for(ai = item->res; ai; ai = ai->ai_next) { + if(ai->ai_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (void *)ai->ai_addr; + if(IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) && !sa6->sin6_scope_id) + return TRUE; + } + } + return FALSE; +} + +static void async_thrdd_item_strip_results(struct async_thrdd_item *item, + int ai_family) +{ + struct Curl_addrinfo *ai = item->res, **panchor = &item->res; + while(ai) { + if(ai->ai_family == ai_family) { + *panchor = ai->ai_next; + ai->ai_next = NULL; + Curl_freeaddrinfo(ai); + ai = *panchor; + } + else { + panchor = &ai->ai_next; + ai = ai->ai_next; + } + } +} + +#endif /* USE_IPV6 && HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID */ + +static CURLcode async_thrdd_check_done(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + struct async_thrdd_ctx *thrdd = &async->thrdd; + + (void)data; + if(thrdd->inc_A) { + thrdd->res_A = thrdd->inc_A; + thrdd->inc_A = NULL; + VERBOSE(async_thrdd_report_item(data, thrdd->res_A)); + async->dns_responses |= thrdd->res_A->dns_queries; + } + + if(thrdd->inc_AAAA) { + if(thrdd->res_AAAA) { + DEBUGASSERT(0); /* should not happen */ + return CURLE_FAILED_INIT; + } +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + /* do we accept the incoming AAAA response? */ + if(!(thrdd->inc_AAAA->dns_queries & CURL_DNSQ_A) && + async_thrdd_item_missing_scope(data, thrdd->inc_AAAA)) { + /* We queried "only" AF_INET6. This may be problematic when the + * result has ipv6 link-local addresses and did not give + * any scope id for it. glibc has a long outstanding bug + * + * that gives scope ids only on AF_UNSPEC queries. */ + struct async_thrdd_item *item = thrdd->inc_AAAA; + CURLcode result; + + /* Reuse the item and queue it again, this time with added + * CURL_DNSQ_A which resolves using AF_UNSPEC. */ + thrdd->inc_AAAA = NULL; + item->dns_queries |= CURL_DNSQ_A; + if(item->res) { + Curl_freeaddrinfo(item->res); + item->res = NULL; + } + + CURL_TRC_DNS(data, "re-queueing query %s for AF_UNSPEC resolve", + item->description); + result = Curl_thrdq_send(data->multi->resolv_thrdq, item, + async_item_description(item), + async->timeout_ms); + if(result) { + async_thrdd_item_free(item); + return result; + } + async->queries_ongoing++; + return CURLE_AGAIN; + } + /* accepting the AAAA result, strip it of any AF_INET entries, + * as we might have resolved it with AF_UNSPEC. */ + async_thrdd_item_strip_results(thrdd->inc_AAAA, AF_INET); +#endif /* USE_IPV6 && HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID */ + thrdd->res_AAAA = thrdd->inc_AAAA; + thrdd->inc_AAAA = NULL; + VERBOSE(async_thrdd_report_item(data, thrdd->res_AAAA)); + async->dns_responses |= thrdd->res_AAAA->dns_queries; + } + + if(async->queries_ongoing) + return CURLE_AGAIN; + async->done = TRUE; + return CURLE_OK; +} + /* * Curl_async_take_result() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if @@ -738,10 +848,6 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, DEBUGASSERT(pdns); *pdns = NULL; - if(!async->queries_ongoing && !async->done) { - DEBUGASSERT(0); - return CURLE_FAILED_INIT; - } #ifdef USE_HTTPSRR_ARES /* best effort, ignore errors */ @@ -752,8 +858,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, Curl_async_thrdd_multi_process(data->multi); #endif - if(!async->done) - return CURLE_AGAIN; + result = async_thrdd_check_done(data, async); + if(result) + return result; Curl_expire_done(data, EXPIRE_ASYNC_NAME); diff --git a/lib/asyn.h b/lib/asyn.h index 76996f888aa0..43e1dc646302 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -140,8 +140,10 @@ struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - struct async_thrdd_item *res_A; /* IPv4 result */ - struct async_thrdd_item *res_AAAA; /* IPv6 result */ + struct async_thrdd_item *inc_A; /* IPv4 incoming result */ + struct async_thrdd_item *res_A; /* IPv4 final result */ + struct async_thrdd_item *inc_AAAA; /* IPv6 incoming result */ + struct async_thrdd_item *res_AAAA; /* IPv6 final result */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 4152a00c1788..e3693e3acc24 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -291,7 +291,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, if(ctx->dns && !ctx->announced) { ctx->announced = TRUE; - if(cf->sockindex == FIRSTSOCKET) { + if((cf->sockindex == FIRSTSOCKET) && ip_query) { cf->conn->bits.dns_resolved = TRUE; Curl_pgrsTime(data, TIMER_NAMELOOKUP); } diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c index f7da23f3854d..d1b5bcd646a2 100644 --- a/lib/thrdqueue.c +++ b/lib/thrdqueue.c @@ -47,7 +47,6 @@ struct curl_thrdq { Curl_thrdq_item_process_cb *fn_process; Curl_thrdq_ev_cb *fn_event; void *fn_user_data; - uint32_t send_max_len; BIT(aborted); }; @@ -198,7 +197,6 @@ static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join) CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, const char *name, - uint32_t max_len, uint32_t min_threads, uint32_t max_threads, uint32_t idle_time_ms, @@ -222,7 +220,6 @@ CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, tqueue->fn_process = fn_process; tqueue->fn_event = fn_event; tqueue->fn_user_data = user_data; - tqueue->send_max_len = max_len; tqueue->name = curlx_strdup(name); if(!tqueue->name) @@ -253,11 +250,18 @@ void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join) thrdq_unlink(tqueue, TRUE, join); } +static uint32_t thrdq_get_signals(struct curl_thrdq *tqueue) +{ + size_t qlen = Curl_llist_count(&tqueue->sendq); + return (qlen <= UINT32_MAX) ? (uint32_t)qlen : UINT32_MAX; +} + CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, const char *description, timediff_t timeout_ms) { - CURLcode result = CURLE_AGAIN; - size_t signals = 0; + struct thrdq_item *qitem; + CURLcode result = CURLE_OK; + uint32_t signals = 0; Curl_mutex_acquire(&tqueue->lock); if(tqueue->aborted) { @@ -270,19 +274,13 @@ CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, goto out; } - if(!tqueue->send_max_len || - (Curl_llist_count(&tqueue->sendq) < tqueue->send_max_len)) { - struct thrdq_item *qitem = thrdq_item_create(tqueue, item, description, - timeout_ms); - if(!qitem) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - item = NULL; - Curl_llist_append(&tqueue->sendq, qitem, &qitem->node); - signals = Curl_llist_count(&tqueue->sendq); - result = CURLE_OK; + qitem = thrdq_item_create(tqueue, item, description, timeout_ms); + if(!qitem) { + result = CURLE_OUT_OF_MEMORY; + goto out; } + Curl_llist_append(&tqueue->sendq, qitem, &qitem->node); + signals = thrdq_get_signals(tqueue); out: Curl_mutex_release(&tqueue->lock); @@ -310,7 +308,7 @@ CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) { CURLcode result = CURLE_AGAIN; struct Curl_llist_node *e; - size_t signals = 0; + uint32_t signals = 0; *pitem = NULL; Curl_mutex_acquire(&tqueue->lock); @@ -329,7 +327,8 @@ CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) result = CURLE_OK; } else - signals = Curl_llist_count(&tqueue->sendq); + signals = thrdq_get_signals(tqueue); + out: Curl_mutex_release(&tqueue->lock); /* Signal thread pool unlocked to avoid deadlocks. If items await @@ -384,17 +383,15 @@ UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, #endif CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, - uint32_t max_len, uint32_t min_threads, uint32_t max_threads, uint32_t idle_time_ms) { CURLcode result; - size_t signals; + uint32_t signals; Curl_mutex_acquire(&tqueue->lock); - tqueue->send_max_len = max_len; - signals = Curl_llist_count(&tqueue->sendq); + signals = thrdq_get_signals(tqueue); Curl_mutex_release(&tqueue->lock); result = Curl_thrdpool_set_props(tqueue->tpool, min_threads, diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h index b6dd92a25d9f..0d84f6f6f144 100644 --- a/lib/thrdqueue.h +++ b/lib/thrdqueue.h @@ -54,7 +54,6 @@ typedef void Curl_thrdq_item_free_cb(void *item); */ CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, const char *name, - uint32_t max_len, /* 0 for unlimited */ uint32_t min_threads, uint32_t max_threads, uint32_t idle_time_ms, @@ -74,7 +73,6 @@ void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join); * to "item" on success, e.g. the queue takes ownership. * `description` is an optional string describing the item for tracing * purposes. It needs to have the same lifetime as `item`. - * Returns CURLE_AGAIN when the queue has already been full. * * With`timeout_ms` != 0, items that get stuck that long in the send * queue are removed and added to the receive queue right away. @@ -115,7 +113,6 @@ CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, uint32_t timeout_ms); CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, - uint32_t max_len, /* 0 for unlimited */ uint32_t min_threads, uint32_t max_threads, uint32_t idle_time_ms); diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index eaaf5c6006e2..940952400a7f 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -26,6 +26,7 @@ # import logging import os +import re from datetime import timedelta from typing import Generator @@ -283,6 +284,30 @@ def test_21_15_resolv_thread_start_fails(self, env: Env, httpd, nghttpx): r.check_stats(count=1, http_status=0, exitcode=6) assert r.duration < timedelta(seconds=20), f'{r}' + # dnsd with one answer for AAAA, delayed one for A + @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") + @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") + @pytest.mark.skipif(condition=not Env.curl_resolv_threaded(), reason="no threaded resolver") + def test_21_16_dnsd_link_local(self, env: Env, httpd, dnsd): + dnsd.set_answers(addr_aaaa=['[fe80::1]']) + run_env = os.environ.copy() + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env['CURL_QUICK_EXIT'] = '1' + run_env['CURL_DEBUG'] = 'dns' + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' + r = curl.http_download(urls=[url], with_stats=True, extra_args=[ + '--connect-timeout', '1' + ]) + # should fail with CURLE_OPERATION_TIMEOUT or COULDNT_CONNECT + assert r.exit_code in (7, 28), f'{r.dump_logs()}' + af_unspec_resolves = [line for line in r.trace_lines if + re.match(r'.* \[DNS] re-queueing query .+ for AF_UNSPEC resolve', line)] + assert len(af_unspec_resolves) == 1, f'{r.dump_logs()}' + aaaa_resolves = [line for line in r.trace_lines if + re.match(r'.* \* IPv6: fe80::1', line)] + assert len(aaaa_resolves) == 1, f'{r.dump_logs()}' + def _clean_files(self, files): for file in files: if os.path.exists(file): diff --git a/tests/unit/unit3301.c b/tests/unit/unit3301.c index 67b126200dd3..4ed9e81bd489 100644 --- a/tests/unit/unit3301.c +++ b/tests/unit/unit3301.c @@ -88,7 +88,7 @@ static CURLcode test_unit3301(const char *arg) /* create and teardown queue */ memset(&ctx, 0, sizeof(ctx)); - result = Curl_thrdq_create(&tqueue, "unit3301-a", 0, 0, 2, 1, + result = Curl_thrdq_create(&tqueue, "unit3301-a", 0, 2, 1, unit3301_item_free, unit3301_process, unit3301_event, &ctx); fail_unless(!result, "queue-a create"); @@ -99,7 +99,7 @@ static CURLcode test_unit3301(const char *arg) /* create queue, have it process `count` items */ count = 10; memset(&ctx, 0, sizeof(ctx)); - result = Curl_thrdq_create(&tqueue, "unit3301-b", 0, 0, 2, 1, + result = Curl_thrdq_create(&tqueue, "unit3301-b", 0, 2, 1, unit3301_item_free, unit3301_process, unit3301_event, &ctx); fail_unless(!result, "queue-b create"); diff --git a/tests/unit/unit3306.c b/tests/unit/unit3306.c index 5b26ba8244c8..85b587dda277 100644 --- a/tests/unit/unit3306.c +++ b/tests/unit/unit3306.c @@ -76,7 +76,7 @@ static CURLcode test_unit3306(const char *arg) fail_unless(getenv("CURL_DBG_THRDPOOL_FAIL_STARTS"), "CURL_DBG_THRDPOOL_FAIL_STARTS must be set for this test"); - result = Curl_thrdq_create(&tqueue, "unit3306", 0, 0, 2, 1, + result = Curl_thrdq_create(&tqueue, "unit3306", 0, 2, 1, unit3306_item_free, unit3306_process, NULL, NULL); fail_unless(!result, "queue create"); From c8860532ddac762c0cc9f9cb2ddbf9c479f136cb Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sun, 12 Jul 2026 18:47:32 +0100 Subject: [PATCH 0710/1062] tests: fix the FTP check for unexpected RST - In vsftpd ignore unrelated RST by matching tcpdump RSTs to the data connection port pair. Prior to this change an unrelated RST on a recycled ephemeral port could cause test failure. Closes https://github.com/curl/curl/pull/22305 --- tests/http/test_30_vsftpd.py | 4 ++-- tests/http/test_31_vsftpds.py | 4 ++-- tests/http/test_32_ftps_vsftpd.py | 4 ++-- tests/http/testenv/curl.py | 19 ++++++++++++++++--- tests/http/testenv/vsftpd.py | 9 +++++---- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index d625852249a8..6627be2613b7 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -161,7 +161,7 @@ def test_30_06_shutdown_download(self, env: Env, vsftpd: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpd.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' # check with `tcpdump` if curl causes any TCP RST packets @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @@ -182,7 +182,7 @@ def test_30_07_shutdown_upload(self, env: Env, vsftpd: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpd.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' def test_30_08_active_download(self, env: Env, vsftpd: VsFTPD): docname = 'data-10k' diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 18f93dede457..1d8a897fbdfd 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -177,7 +177,7 @@ def test_31_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpds.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' # check with `tcpdump` if curl causes any TCP RST packets @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @@ -197,7 +197,7 @@ def test_31_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpds.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): docname = 'upload-ascii' diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index df1258c2d210..1e2e9652927e 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -187,7 +187,7 @@ def test_32_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpds.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' # check with `tcpdump` if curl causes any TCP RST packets @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @@ -207,7 +207,7 @@ def test_32_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): # look only at ports from DATA connection. data_ports = vsftpds.get_data_ports(r) assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}' - assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets' + assert len(r.tcpdump.get_rsts(port_pairs=data_ports)) == 0, 'Unexpected TCP RST packets' def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): docname = 'upload-ascii' diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 90b7b2885673..2abbbbe49cb0 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -36,7 +36,7 @@ from functools import cmp_to_key from statistics import fmean, mean from threading import Thread -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Tuple, Union from urllib.parse import urlparse import psutil @@ -188,9 +188,19 @@ def __init__(self, env, run_dir): self._stdoutfile = os.path.join(self._run_dir, 'tcpdump.out') self._stderrfile = os.path.join(self._run_dir, 'tcpdump.err') - def get_rsts(self, ports: Optional[List[int]] = None) -> Optional[List[str]]: + def get_rsts(self, ports: Optional[List[int]] = None, + port_pairs: Optional[List[Tuple[int, int]]] = None + ) -> Optional[List[str]]: if self._proc: raise Exception('tcpdump still running') + # a pair matches only a RST between exactly these two ports, while + # a port in `ports` matches any RST it is involved in + pairs = None + if port_pairs is not None: + pairs = set() + for p1, p2 in port_pairs: + pairs.add((p1, p2)) + pairs.add((p2, p1)) lines = [] with open(self._stdoutfile) as fd: for line in fd: @@ -198,7 +208,10 @@ def get_rsts(self, ports: Optional[List[int]] = None) -> Optional[List[str]]: if m: sport = int(m.group(1)) dport = int(m.group(2)) - if ports is None or sport in ports or dport in ports: + if pairs is not None: + if (sport, dport) in pairs: + lines.append(line) + elif ports is None or sport in ports or dport in ports: lines.append(line) return lines diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index daaf904f1e4e..e2c33edee33b 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -31,7 +31,7 @@ import subprocess import time from datetime import datetime, timedelta -from typing import Dict, List +from typing import Dict, List, Tuple from .curl import CurlClient, ExecResult from .env import Env @@ -226,6 +226,7 @@ def _write_config(self): with open(self._conf_file, 'w') as fd: fd.write("\n".join(conf)) - def get_data_ports(self, r: ExecResult) -> List[int]: - return [int(m.group(1)) for line in r.trace_lines if - (m := re.match(r'.*Established 2nd connection to .* \(\S+ port (\d+)\)', line))] + def get_data_ports(self, r: ExecResult) -> List[Tuple[int, int]]: + return [(int(m.group(1)), int(m.group(2))) for line in r.trace_lines if + (m := re.match(r'.*Established 2nd connection to .* ' + r'\(\S+ port (\d+)\) from \S+ port (\d+)', line))] From a479459ea39ac0e7de37237f9f0ba9a0c9d7f470 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 24 Jul 2026 14:20:58 +0200 Subject: [PATCH 0711/1062] http: fix non-tunneling proxy hostname use Make sure hostname used in URL to proxy is IDN decoded form, but keep the original hostname in case it was ipv6. Fixes #22382 Reported-by: RMMoreton on github Closes #22385 --- lib/http.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/http.c b/lib/http.c index bc3212036d60..d8eca68198e0 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2115,7 +2115,7 @@ static CURLcode http_target(struct Curl_easy *data, /* The path sent to the proxy is in fact the entire URL, but if the remote host is a IDN-name, we must make sure that the request we produce only - uses the encoded hostname! */ + uses the decoded hostname! */ /* and no fragment part */ CURLUcode uc; @@ -2124,7 +2124,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(data->state.origin->user_hostname != data->state.origin->hostname) { + if(!data->state.origin->ipv6 && + (data->state.origin->user_hostname != data->state.origin->hostname)) { uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0); if(uc) { curl_url_cleanup(h); From 20c7877dcb0aba0a198ea3e66b29f72cc5e8de85 Mon Sep 17 00:00:00 2001 From: AlanKingPL Date: Wed, 8 Jul 2026 13:13:40 +0200 Subject: [PATCH 0712/1062] terminal: Enhance terminal size detection for multiple outputs - Get the terminal size from STDOUT or STDERR when the terminal size of STDIN is not available. Closes https://github.com/curl/curl/pull/22276 --- src/terminal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/terminal.c b/src/terminal.c index b25c0e6e8006..2e28d9232309 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -62,6 +62,10 @@ unsigned int get_terminal_columns(void) struct winsize ts; if(!ioctl(STDIN_FILENO, TIOCGWINSZ, &ts)) cols = (int)ts.ws_col; + else if(!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ts)) + cols = (int)ts.ws_col; + else if(!ioctl(STDERR_FILENO, TIOCGWINSZ, &ts)) + cols = (int)ts.ws_col; #elif defined(_WIN32) && !defined(CURL_WINDOWS_UWP) { HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); From 474ebb524708ecddf4fe0fdb5493e8d638bdc04d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 22 Jul 2026 11:06:39 +0200 Subject: [PATCH 0713/1062] api-guard: check lock on session cache Add a property to easy/multi API calls that prohibit calling the function when the involved SSL session cache is under lock by the current thread. Checks are only in effect when pthreads/Windows threads are available. Closes #22367 --- lib/api.c | 106 +++++++++++++++++++++++++++-------------- lib/curl_threads.c | 21 ++++++++ lib/curl_threads.h | 6 +++ lib/vtls/vtls_scache.c | 50 ++++++++++++++++--- lib/vtls/vtls_scache.h | 4 +- 5 files changed, 141 insertions(+), 46 deletions(-) diff --git a/lib/api.c b/lib/api.c index adcc51b3f893..930350297833 100644 --- a/lib/api.c +++ b/lib/api.c @@ -25,33 +25,37 @@ #include "urldata.h" #include "api.h" +#include "curl_threads.h" #include "multiif.h" +#include "vtls/vtls_scache.h" struct Curl_eapi_fn_props { Curl_eapi_fn fn; uint8_t data_is_killed; /* easy handle is killed in call */ uint8_t recurse; /* may be called when another call is in progress */ uint8_t no_event_cb; /* may not be called during a multi event callback */ + uint8_t no_scache_lock; /* may not be called with easy's vtls_scache + locked by current thread */ }; static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { - /* function kill rec !ev */ - { CURL_EAPI_FN_easy_cleanup, 1, 0, 0 }, - { CURL_EAPI_FN_easy_duphandle, 0, 1, 0 }, - { CURL_EAPI_FN_easy_getinfo, 0, 1, 0 }, - { CURL_EAPI_FN_easy_pause, 0, 1, 1 }, - { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0 }, - { CURL_EAPI_FN_easy_perform, 0, 0, 0 }, - { CURL_EAPI_FN_easy_recv, 0, 0, 0 }, - { CURL_EAPI_FN_easy_reset, 0, 0, 0 }, - { CURL_EAPI_FN_easy_send, 0, 0, 0 }, - { CURL_EAPI_FN_easy_setopt, 0, 1, 0 }, - { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0 }, - { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0 }, - { CURL_EAPI_FN_easy_upkeep, 0, 0, 0 }, - { CURL_EAPI_FN_ws_recv, 0, 1, 0 }, - { CURL_EAPI_FN_ws_send, 0, 1, 0 }, - { CURL_EAPI_FN_ws_start_frame, 0, 1, 0 }, + /* function kill rec !ev !scach */ + { CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 }, + { CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 }, + { CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 }, + { CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 }, + { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_recv, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_reset, 0, 0, 0, 0 }, + { CURL_EAPI_FN_easy_send, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_setopt, 0, 1, 0, 0 }, + { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0, 1 }, + { CURL_EAPI_FN_easy_upkeep, 0, 0, 0, 1 }, + { CURL_EAPI_FN_ws_recv, 0, 1, 0, 1 }, + { CURL_EAPI_FN_ws_send, 0, 1, 0, 1 }, + { CURL_EAPI_FN_ws_start_frame, 0, 1, 0, 0 }, }; struct Curl_mapi_fn_props { @@ -59,29 +63,31 @@ struct Curl_mapi_fn_props { uint8_t multi_is_killed; /* multi handle is killed during call */ uint8_t recurse; /* may be called when another call is in progress */ uint8_t allow_ntfy_cb; /* may be called during a notify callback */ + uint8_t no_scache_lock; /* may not be called with multi's vtls_scache + locked by current thread */ }; static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = { - /* function kill rec ntfy */ - { CURL_MAPI_FN_multi_add_handle, 0, 0, 1 }, - { CURL_MAPI_FN_multi_assign, 0, 1, 1 }, - { CURL_MAPI_FN_multi_cleanup, 1, 0, 0 }, - { CURL_MAPI_FN_multi_fdset, 0, 0, 1 }, - { CURL_MAPI_FN_multi_get_handles, 0, 1, 1 }, - { CURL_MAPI_FN_multi_get_offt, 0, 1, 1 }, - { CURL_MAPI_FN_multi_info_read, 0, 1, 1 }, - { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1 }, - { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1 }, - { CURL_MAPI_FN_multi_perform, 0, 0, 0 }, - { CURL_MAPI_FN_multi_poll, 0, 0, 1 }, - { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1 }, - { CURL_MAPI_FN_multi_setopt, 0, 0, 1 }, - { CURL_MAPI_FN_multi_socket_action, 0, 0, 0 }, - { CURL_MAPI_FN_multi_socket_all, 0, 0, 0 }, - { CURL_MAPI_FN_multi_socket, 0, 0, 0 }, - { CURL_MAPI_FN_multi_timeout, 0, 0, 1 }, - { CURL_MAPI_FN_multi_wait, 0, 0, 1 }, - { CURL_MAPI_FN_multi_waitfds, 0, 0, 1 }, + /* function kill rec ntfy !scach */ + { CURL_MAPI_FN_multi_add_handle, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_assign, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_cleanup, 1, 0, 0, 1 }, + { CURL_MAPI_FN_multi_fdset, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_get_handles, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_get_offt, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_info_read, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1, 0 }, + { CURL_MAPI_FN_multi_perform, 0, 0, 0, 1 }, + { CURL_MAPI_FN_multi_poll, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_setopt, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_socket_action, 0, 0, 0, 1 }, + { CURL_MAPI_FN_multi_socket_all, 0, 0, 0, 1 }, + { CURL_MAPI_FN_multi_socket, 0, 0, 0, 1 }, + { CURL_MAPI_FN_multi_timeout, 0, 0, 1, 1 }, + { CURL_MAPI_FN_multi_wait, 0, 0, 1, 0 }, + { CURL_MAPI_FN_multi_waitfds, 0, 0, 1, 0 }, }; struct Curl_cbapi_fn_props { @@ -248,6 +254,19 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, goto out; } +#if defined(USE_SSL) && defined(USE_MUTEX) + if(fn_props->no_scache_lock && + Curl_ssl_scache_is_locked_by_current_thread(data)) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u while vtls_scache is locked by " + "current thread\n", (uint16_t)fn)); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } +#endif + /* all fine, add to data's callstack */ if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) { result = CURLE_RECURSIVE_API_CALL; @@ -318,6 +337,19 @@ bool Curl_mapi_enter(struct Curl_mapi_guard *guard, goto out; } +#if defined(USE_SSL) && defined(USE_MUTEX) + if(fn_props->no_scache_lock && multi->ssl_scache && + Curl_ssl_scache_is_locked_by_current_thread(multi->admin)) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "MAPI guard: calling %u while its vtls_scache is locked by " + "current thread\n", (uint16_t)fn)); +#endif + mresult = CURLM_RECURSIVE_API_CALL; + goto out; + } +#endif + /* all fine, add to data's callstack */ if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) { mresult = CURLM_RECURSIVE_API_CALL; diff --git a/lib/curl_threads.c b/lib/curl_threads.c index 01041f63c3ac..16f14a364cf6 100644 --- a/lib/curl_threads.c +++ b/lib/curl_threads.c @@ -186,6 +186,16 @@ CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK; } +curl_thread_id_t Curl_thread_get_current_id(void) +{ + return pthread_self(); +} + +bool Curl_thread_is_current(curl_thread_id_t tid) +{ + return !!pthread_equal(tid, pthread_self()); +} + #elif defined(_WIN32) void Curl_cond_signal(CONDITION_VARIABLE *c) @@ -208,6 +218,17 @@ CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, } return CURLE_OK; } + +curl_thread_id_t Curl_thread_get_current_id(void) +{ + return GetCurrentThreadId(); +} + +bool Curl_thread_is_current(curl_thread_id_t tid) +{ + return tid == GetCurrentThreadId(); +} + #else #error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif diff --git a/lib/curl_threads.h b/lib/curl_threads.h index 081d155695f8..452d5eb44f3e 100644 --- a/lib/curl_threads.h +++ b/lib/curl_threads.h @@ -31,6 +31,7 @@ # define CURL_STDCALL # define curl_mutex_t pthread_mutex_t # define curl_thread_t pthread_t * +# define curl_thread_id_t pthread_t # define curl_thread_t_null (pthread_t *)0 # define Curl_mutex_init(m) pthread_mutex_init(m, NULL) # define Curl_mutex_acquire(m) pthread_mutex_lock(m) @@ -44,6 +45,7 @@ # define CURL_STDCALL WINAPI # define curl_mutex_t CRITICAL_SECTION # define curl_thread_t HANDLE +# define curl_thread_id_t DWORD # define curl_thread_t_null (HANDLE)0 # define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) # define Curl_mutex_acquire(m) EnterCriticalSection(m) @@ -61,6 +63,10 @@ void Curl_cond_wait(curl_cond_t *c, curl_mutex_t *m); /* Returns CURLE_OPERATION_TIMEDOUT on timeout */ CURLcode Curl_cond_timedwait(curl_cond_t *c, curl_mutex_t *m, uint32_t timeout_ms); + +curl_thread_id_t Curl_thread_get_current_id(void); +bool Curl_thread_is_current(curl_thread_id_t tid); + #endif /* USE_MUTEX */ #ifdef USE_THREADS diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 7148bf3297e7..3c0881262fb9 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -41,6 +41,7 @@ #include "url.h" #include "llist.h" #include "curl_share.h" +#include "curl_threads.h" #include "curl_trc.h" #include "curl_sha256.h" #include "rand.h" @@ -360,6 +361,10 @@ struct Curl_ssl_scache { size_t peer_count; int default_lifetime_secs; long age; +#ifdef USE_MUTEX + curl_mutex_t mutex; + curl_thread_id_t locking_thread; +#endif BIT(is_locked); }; @@ -649,7 +654,9 @@ CURLcode Curl_ssl_scache_create(size_t max_peers, Curl_llist_init(&scache->peers[i].sessions, cf_ssl_scache_session_ldestroy); } - +#ifdef USE_MUTEX + Curl_mutex_init(&scache->mutex); +#endif *pscache = scache; return CURLE_OK; } @@ -663,6 +670,9 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) cf_ssl_scache_clear_peer(&scache->peers[i]); } curlx_free(scache->peers); +#ifdef USE_MUTEX + Curl_mutex_destroy(&scache->mutex); +#endif curlx_free(scache); } } @@ -684,8 +694,16 @@ void Curl_ssl_scache_lock(struct Curl_easy *data) if(CURL_SHARE_ssl_scache(data)) Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); +#ifdef USE_MUTEX + Curl_mutex_acquire(&scache->mutex); + scache->locking_thread = Curl_thread_get_current_id(); + DEBUGASSERT(!scache->is_locked); + scache->is_locked = TRUE; + Curl_mutex_release(&scache->mutex); +#else DEBUGASSERT(!scache->is_locked); scache->is_locked = TRUE; +#endif } } @@ -694,13 +712,37 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data) { struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); if(scache) { +#ifdef USE_MUTEX + Curl_mutex_acquire(&scache->mutex); + scache->locking_thread = 0; + DEBUGASSERT(scache->is_locked); + scache->is_locked = FALSE; + Curl_mutex_release(&scache->mutex); +#else DEBUGASSERT(scache->is_locked); scache->is_locked = FALSE; +#endif if(CURL_SHARE_ssl_scache(data)) Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); } } +bool Curl_ssl_scache_is_locked_by_current_thread(struct Curl_easy *data) +{ + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + bool locked = FALSE; + if(!scache) + return FALSE; +#ifdef USE_MUTEX + Curl_mutex_acquire(&scache->mutex); + locked = scache->is_locked && Curl_thread_is_current(scache->locking_thread); + Curl_mutex_release(&scache->mutex); +#else + locked = (bool)scache->is_locked; +#endif + return locked; +} + static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { @@ -1096,12 +1138,6 @@ void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, #define CURL_SSL_TICKET_MAX (16 * 1024) -bool Curl_ssl_scache_is_locked(struct Curl_easy *data) -{ - struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); - return scache && scache->is_locked; -} - static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) { CURLcode result; diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index e6f359395b18..c9f35e800f62 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -202,9 +202,9 @@ void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key); -#ifdef USE_SSLS_EXPORT +bool Curl_ssl_scache_is_locked_by_current_thread(struct Curl_easy *data); -bool Curl_ssl_scache_is_locked(struct Curl_easy *data); +#ifdef USE_SSLS_EXPORT CURLcode Curl_ssl_session_import(struct Curl_easy *data, const char *ssl_peer_key, From a954d87f0b41e4424f5565f74be5f2bd08d3347b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 23 Jul 2026 11:20:19 +0200 Subject: [PATCH 0714/1062] httpsrr: DoH with HTTPS, fix response handling Fix handling of DoH response that only asks for HTTPS records. Add test 2117 for checking that a HTTPS-RR resolve is processed, even though the actual answer is invalid. Closes #22372 --- docs/libcurl/curl_global_trace.md | 9 +-- lib/curl_trc.c | 13 ++-- lib/curl_trc.h | 3 + lib/doh.c | 36 ++++++----- lib/httpsrr.c | 6 +- lib/multi.c | 22 +++---- lib/urldata.h | 8 +-- tests/data/Makefile.am | 2 +- tests/data/test2117 | 102 ++++++++++++++++++++++++++++++ 9 files changed, 156 insertions(+), 45 deletions(-) create mode 100644 tests/data/test2117 diff --git a/docs/libcurl/curl_global_trace.md b/docs/libcurl/curl_global_trace.md index f78f2ca0b06f..e4cf8e05a564 100644 --- a/docs/libcurl/curl_global_trace.md +++ b/docs/libcurl/curl_global_trace.md @@ -101,6 +101,11 @@ trace. Tracing of DNS operations to resolve hostnames and HTTPS records. +## `doh` + +Tracing of DoH operations (DNS over HTTPS) to resolve hostnames and +HTTPS records. + ## `lib-ids` Adds transfer and connection identifiers as prefix to every call to @@ -116,10 +121,6 @@ connection. The command line tool `curl`uses the same format for its transfers but have no own way to identify in trace output which transfer a trace event is connected to. -## `doh` - -Former name for DNS-over-HTTP operations. Now an alias for `dns`. - ## `multi` Traces multi operations managing transfers' state changes and sockets poll diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 1400707a7ad8..dd3cd7bdd543 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -219,6 +219,12 @@ struct curl_trc_feat Curl_trc_feat_dns = { "DNS", CURL_LOG_LVL_NONE, }; +#ifndef CURL_DISABLE_DOH +struct curl_trc_feat Curl_trc_feat_doh = { + "DoH", + CURL_LOG_LVL_NONE, +}; +#endif struct curl_trc_feat Curl_trc_feat_timer = { "TIMER", CURL_LOG_LVL_NONE, @@ -530,6 +536,9 @@ static struct trc_feat_def trc_feats[] = { { &Curl_trc_feat_read, TRC_CT_NONE }, { &Curl_trc_feat_write, TRC_CT_NONE }, { &Curl_trc_feat_dns, TRC_CT_NETWORK }, +#ifndef CURL_DISABLE_DOH + { &Curl_trc_feat_doh, TRC_CT_NETWORK }, +#endif { &Curl_trc_feat_timer, TRC_CT_NETWORK }, #ifdef USE_THREADS { &Curl_trc_feat_threads, TRC_CT_NONE }, @@ -652,10 +661,6 @@ static CURLcode trc_opt(const char *config) trc_apply_level_by_category(TRC_CT_NETWORK, lvl); else if(curlx_str_casecompare(&out, "proxy")) trc_apply_level_by_category(TRC_CT_PROXY, lvl); - else if(curlx_str_casecompare(&out, "doh")) { - struct Curl_str dns = { "dns", 3 }; - trc_apply_level_by_name(&dns, lvl); - } else trc_apply_level_by_name(&out, lvl); diff --git a/lib/curl_trc.h b/lib/curl_trc.h index b4ae8e5314cd..fc0e6dedcf96 100644 --- a/lib/curl_trc.h +++ b/lib/curl_trc.h @@ -307,6 +307,9 @@ extern struct curl_trc_feat Curl_trc_feat_multi; extern struct curl_trc_feat Curl_trc_feat_read; extern struct curl_trc_feat Curl_trc_feat_write; extern struct curl_trc_feat Curl_trc_feat_dns; +#ifndef CURL_DISABLE_DOH +extern struct curl_trc_feat Curl_trc_feat_doh; +#endif extern struct curl_trc_feat Curl_trc_feat_timer; #ifdef USE_THREADS extern struct curl_trc_feat Curl_trc_feat_threads; diff --git a/lib/doh.c b/lib/doh.c index 776a96fea886..6d39f6ff0eba 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -217,8 +217,8 @@ static void doh_print_buf(struct Curl_easy *data, * This looks up the probe response at its meta CURL_EZM_DOH_PROBE * and copies the response body over to the struct at the master's * meta at CURL_EZM_DOH_MASTER. */ -static void doh_probe_done(struct Curl_easy *data, - struct Curl_easy *doh, CURLcode result) +static void doh_probe_done(struct Curl_easy *doh, + struct Curl_easy *master, CURLcode result) { struct Curl_resolv_async *async = NULL; struct doh_probes *dohp = NULL; @@ -227,13 +227,14 @@ static void doh_probe_done(struct Curl_easy *data, doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); if(!doh_req) { + /* transfer `doh` is not a DoH probe. */ DEBUGASSERT(0); return; } - async = Curl_async_get(data, doh_req->resolv_id); + async = Curl_async_get(master, doh_req->resolv_id); if(!async) { - CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response", + CURL_TRC_DNS(master, "[%u] ignoring outdated DoH response", doh_req->resolv_id); return; } @@ -246,7 +247,7 @@ static void doh_probe_done(struct Curl_easy *data, /* We really should have found the slot where to store the response */ if(i >= DOH_SLOT_COUNT) { DEBUGASSERT(0); - failf(data, "DoH: unknown sub request done"); + failf(master, "DoH: unknown sub request done"); return; } @@ -267,8 +268,8 @@ static void doh_probe_done(struct Curl_easy *data, infof(doh, "DoH request %s", curl_easy_strerror(result)); if(!dohp->pending) { - /* DoH completed, run the transfer picking up the results */ - Curl_multi_mark_dirty(data); + /* DoH completed, run master to act on results */ + Curl_multi_mark_dirty(master); } } @@ -344,7 +345,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ - VERBOSE(doh->state.feat = &Curl_trc_feat_dns); + VERBOSE(doh->state.feat = &Curl_trc_feat_doh); ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb); @@ -367,7 +368,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share); if(data->set.err && data->set.err != stderr) ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_doh)) ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); if(data->set.no_signal) ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); @@ -423,6 +424,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ + doh->sub_xfer_done = doh_probe_done; result = Curl_meta_set(doh, CURL_EZM_DOH_PROBE, doh_req, doh_probe_dtor); doh_req = NULL; /* call took ownership */ @@ -479,9 +481,6 @@ CURLcode Curl_doh(struct Curl_easy *data, dohp->host = async->hostname; dohp->port = async->port; - /* We are making sub easy handles and want to be called back when - * one is done. */ - data->sub_xfer_done = doh_probe_done; /* create IPv4 DoH request */ if(async->dns_queries & CURL_DNSQ_A) { @@ -1207,7 +1206,8 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, if(!dohp) return CURLE_OUT_OF_MEMORY; - if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && + if(CURL_DNSQ_IS_ADDR(async->dns_queries) && + dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { failf(data, "Could not DoH-resolve: %s", dohp->host); return async->for_proxy ? @@ -1237,7 +1237,9 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, if(rc[slot] && (rc[slot] != DOH_DNS_NXDOMAIN)) negative = FALSE; if(rc[slot]) { - CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), + CURL_TRC_DNS(data, "[%s] [DoH] error: %s type %s for %s", + Curl_resolv_query_str(async->dns_queries), + doh_strerror(rc[slot]), doh_type2name(p->dnstype), dohp->host); } } /* next slot */ @@ -1247,7 +1249,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, /* we have an address, of one kind or other */ struct Curl_addrinfo *ai; - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_doh)) { CURL_TRC_DNS(data, "hostname: %s", dohp->host); doh_show(data, &de); } @@ -1283,6 +1285,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, /* Now add and HTTPSRR information if we have */ struct Curl_https_rrinfo *hrr = NULL; + CURL_TRC_DNS(data, "[HTTPS] got %d records", de.numhttps_rrs); if(de.numhttps_rrs > 0 && result == CURLE_OK) { result = doh_resp_decode_httpsrr(data, de.https_rrs->val, de.https_rrs->len, &hrr); @@ -1338,11 +1341,12 @@ static void doh_close(struct Curl_easy *data, doh->probe_resp[slot].probe_mid)); continue; } + probe_data->sub_xfer_done = NULL; /* No longer interested in result */ /* data->multi might already be reset at this time */ Curl_multi_remove_handle(data->multi, probe_data); Curl_close(&probe_data); } - data->sub_xfer_done = NULL; + CURL_TRC_DNS(data, "[DoH] probe done"); } } diff --git a/lib/httpsrr.c b/lib/httpsrr.c index a05a5cb5c15c..441fe350b117 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -98,15 +98,15 @@ void Curl_httpsrr_trace(struct Curl_easy *data, CURLcode result; if(!rr) { - CURL_TRC_DNS(data, "[HTTPS-RR] not available"); + CURL_TRC_DNS(data, "[HTTPS] no record available"); return; } curlx_dyn_init(&tmp, 1024); result = Curl_httpsrr_print(&tmp, rr); if(!result) - CURL_TRC_DNS(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); + CURL_TRC_DNS(data, "[HTTPS] record: %s", curlx_dyn_ptr(&tmp)); else - CURL_TRC_DNS(data, "Error printing HTTPS-RR information"); + CURL_TRC_DNS(data, "[HTTPS] error printing information"); curlx_dyn_free(&tmp); } diff --git a/lib/multi.c b/lib/multi.c index 0732c7bfbae1..77cb306d31ae 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2445,20 +2445,16 @@ static void handle_completed(struct Curl_multi *multi, CURLcode result) { if(data->master_mid != UINT32_MAX) { - /* A sub transfer, not for msgsent to application */ - struct Curl_easy *mdata; - - CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); - mdata = Curl_multi_get_easy(multi, data->master_mid); - if(mdata) { - if(mdata->sub_xfer_done) - mdata->sub_xfer_done(mdata, data, result); + /* A sub transfer, not for msgsent to application. Is anyone still + * interested in processing its results? */ + if(data->sub_xfer_done) { + struct Curl_easy *master = Curl_multi_get_easy(multi, data->master_mid); + + CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); + if(master) + data->sub_xfer_done(data, master, result); else - CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", - data->master_mid); - } - else { - CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); + CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); } } else { diff --git a/lib/urldata.h b/lib/urldata.h index b0c7af234415..5d6b3c2fdf4a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1192,10 +1192,10 @@ struct UserDefined { #define IS_MIME_POST(a) FALSE #endif -/* callback that gets called when a sub easy (data->master_mid set) is - DONE. Called on the master easy. */ -typedef void multi_sub_xfer_done_cb(struct Curl_easy *master_easy, - struct Curl_easy *sub_easy, +/* callback that gets called when the transfer `data` is done and + * `data->master_mid` is set to an existing easy handle. */ +typedef void multi_sub_xfer_done_cb(struct Curl_easy *data, + struct Curl_easy *master, CURLcode result); /* diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index fc15cbc399c6..2e001ec3c8ef 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 test2093 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 test2110 test2113 test2114 test2115 test2116 \ +test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2117 b/tests/data/test2117 new file mode 100644 index 000000000000..a5b800bc0e22 --- /dev/null +++ b/tests/data/test2117 @@ -0,0 +1,102 @@ + + + + +HTTP +HTTP GET +DOH +httpsrr + + + +# Server-side + + +# This is the DoH response for foo.example.com A 127.0.0.1. It will be sent +# for all DoH requests, so the AAAA and HTTPS resolves will fail with wrong +# data. But it does verify that the responses are acted upon. +# This requires that the test server is accessible at that address! + + +SFRUUC8xLjEgMjAwIE9LCkRhdGU6IFRodSwgMDkgTm92IDIwMTAgMTQ6NDk6MDAgR01UClNlcnZl +cjogdGVzdC1zZXJ2ZXIvZmFrZQpDb25uZWN0aW9uOiBjbG9zZQpDb250ZW50LVR5cGU6IGFwcGxp +Y2F0aW9uL2Rucy1tZXNzYWdlCkNvbnRlbnQtTGVuZ3RoOiA0OQoKAAABAAABAAEAAAAAA2Zvbwdl +eGFtcGxlA2NvbQAAAQABwAwAAQABAAAANwAEfwAAAQ== + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http +https + + +# requires Debug so that it can use the DoH server without https +# requires IPv6 so that we can assume and compare both DoH requests + + +Debug +DoH +IPv6 +ECH +HTTPSRR + + +HTTP GET using DoH with mandatory HTTPS-RR response + + +https://foo.example.com:%HTTPSPORT/%TESTNUMBER --insecure --ech true --connect-timeout 20 --doh-insecure --doh-url https://%HOSTIP:%HTTPSPORT/%TESTNUMBER0001 + + + +# Verify data after the test has been "shot" + + +# To make the test ignore the order of the two outgoing DoH requests, strip +# the family byte + + +s/com\x00\x00(\x1c|\x01)/com-00-00!/g; + + +POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT +Accept: */* +Content-Type: application/dns-message +Content-Length: 47 + +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPSPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT +Accept: */* +Content-Type: application/dns-message +Content-Length: 33 + +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT +Accept: */* +Content-Type: application/dns-message +Content-Length: 33 + +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%GET /%TESTNUMBER HTTP/1.1 +Host: foo.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* + + + + From e093c67f1cc3fbedefc49fd2e3f9520afcfa769f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 23 Jul 2026 14:17:13 +0200 Subject: [PATCH 0715/1062] connect: connection close tweaks - connclose/streamclose/connkeep() remove description string that was never used anywhere. Add trace statements where reasons for closing were not already traced and maybe not obvious. - multi_remove_handle: only lookup former connection in pool when transfer is set to connect only - test1554: adapt expectations now that pool is less often locked Closes #22379 --- lib/cfilters.c | 6 +- lib/conncache.c | 34 ----------- lib/conncache.h | 8 --- lib/connect.c | 50 ++++++++-------- lib/connect.h | 44 +++++--------- lib/easy.c | 8 +-- lib/ftp.c | 17 +++--- lib/gopher.c | 2 +- lib/http.c | 35 +++++------ lib/http2.c | 10 ++-- lib/imap.c | 3 +- lib/ldap.c | 2 +- lib/mqtt.c | 2 +- lib/multi.c | 112 ++++++++++++++++-------------------- lib/pop3.c | 3 +- lib/sendf.c | 4 +- lib/smb.c | 24 +++++--- lib/smtp.c | 3 +- lib/tftp.c | 2 +- lib/transfer.c | 2 +- lib/vquic/cf-ngtcp2-proxy.c | 2 +- lib/vquic/cf-ngtcp2.c | 2 +- lib/vquic/cf-quiche.c | 2 +- lib/vssh/libssh.c | 2 +- lib/vssh/libssh2.c | 2 +- tests/data/test1554 | 6 -- 26 files changed, 159 insertions(+), 228 deletions(-) diff --git a/lib/cfilters.c b/lib/cfilters.c index 238698d3320e..bef39e6f5b62 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -600,11 +600,9 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) { struct Curl_cfilter *cf; - if(!CONN_SOCK_IDX_VALID(sockindex)) + if(!conn || !CONN_SOCK_IDX_VALID(sockindex)) return FALSE; - cf = conn ? conn->cfilter[sockindex] : NULL; - - for(; cf; cf = cf->next) { + for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) { if(cf->cft->flags & CF_TYPE_MULTIPLEX) return TRUE; if(cf->cft->flags & (CF_TYPE_IP_CONNECT | CF_TYPE_SSL)) diff --git a/lib/conncache.c b/lib/conncache.c index 17e8d715b0dc..eb88d67dc8f7 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -811,40 +811,6 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, return fctx.conn; } -struct cpool_do_conn_ctx { - curl_off_t id; - Curl_cpool_conn_do_cb *cb; - void *cbdata; -}; - -static int cpool_do_conn(struct Curl_easy *data, - struct connectdata *conn, void *param) -{ - struct cpool_do_conn_ctx *dctx = param; - - if(conn->connection_id == dctx->id) { - dctx->cb(conn, data, dctx->cbdata); - return 1; - } - return 0; -} - -void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id, - Curl_cpool_conn_do_cb *cb, void *cbdata) -{ - struct cpool *cpool = cpool_get_instance(data); - struct cpool_do_conn_ctx dctx; - - if(!cpool) - return; - dctx.id = conn_id; - dctx.cb = cb; - dctx.cbdata = cbdata; - CPOOL_LOCK(cpool, data); - cpool_foreach(data, cpool, &dctx, cpool_do_conn); - CPOOL_UNLOCK(cpool, data); -} - void Curl_cpool_do_locked(struct Curl_easy *data, struct connectdata *conn, Curl_cpool_conn_do_cb *cb, void *cbdata) diff --git a/lib/conncache.h b/lib/conncache.h index 71940d724c02..78ccd8282fa2 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -143,14 +143,6 @@ typedef void Curl_cpool_conn_do_cb(struct connectdata *conn, struct Curl_easy *data, void *cbdata); -/** - * Invoke the callback on the pool's connection with the - * given connection id (if it exists). - */ -void Curl_cpool_do_by_id(struct Curl_easy *data, - curl_off_t conn_id, - Curl_cpool_conn_do_cb *cb, void *cbdata); - /** * Invoked the callback for the given data + connection under the * connection pool's lock. diff --git a/lib/connect.c b/lib/connect.c index ca81849ce786..c22047538907 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -192,43 +192,41 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, conn = Curl_cpool_get_conn(data, data->state.lastconnect_id); if(!conn) { data->state.lastconnect_id = -1; + if(connp) + *connp = NULL; return CURL_SOCKET_BAD; } if(connp) - /* only store this if the caller cares for it */ *connp = conn; return conn->sock[FIRSTSOCKET]; } + if(connp) + *connp = NULL; return CURL_SOCKET_BAD; } -/* - * Curl_conncontrol() marks streams or connection for closure. - */ -void Curl_conncontrol(struct connectdata *conn, - int ctrl /* see defines in header */ -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - , const char *reason -#endif - ) +void Curl_conncontrol(struct connectdata *conn, int ctrl) { - /* close if a connection, or a stream that is not multiplexed. */ - /* This function will be called both before and after this connection is - associated with a transfer. */ - bool closeit, is_multiplex; - DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - (void)reason; /* useful for debugging */ -#endif - is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); - closeit = (ctrl == CONNCTRL_CONNECTION) || - ((ctrl == CONNCTRL_STREAM) && !is_multiplex); - if((ctrl == CONNCTRL_STREAM) && is_multiplex) - ; /* stream signal on multiplex conn never affects close state */ - else if((curl_bit)closeit != conn->bits.close) { - conn->bits.close = closeit; /* the only place in the source code that - should assign this bit */ + if(!conn) { + DEBUGASSERT(0); + return; + } + switch(ctrl) { + case CONNCTRL_CONN_KEEP: + conn->bits.close = FALSE; + break; + case CONNCTRL_CONN_CLOSE: + conn->bits.close = TRUE; + break; + case CONNCTRL_STREAM_CLOSE: + /* stream close when multiplexing does not affect connection */ + if(!Curl_conn_is_multiplex(conn, FIRSTSOCKET)) + conn->bits.close = TRUE; + break; + default: + DEBUGASSERT(0); + break; } } diff --git a/lib/connect.h b/lib/connect.h index 20eedaf10e1d..91cd92490112 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -70,37 +70,23 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); /* - * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' - * argument specifies if it is the end of a connection or a stream. - * - * For stream-based protocols (such as HTTP/2), a stream close will not cause - * a connection close. Other protocols will close the connection for both - * cases. - * - * It sets the bit.close bit to TRUE (with an explanation for debug builds), - * when the connection will close. + * Curl_conncontrol() manipulates the `conn->bits.close` bit on + * a connection: + * - CONNCTRL_CONN_KEEP: clear the bit + * - CONNCTRL_CONN_CLOSE: set the bit + * - CONNCTRL_STREAM_CLOSE: set the bit when the connection is not + * multiplexed + * The call does *NOT* cause any immediate connection close. */ +#define CONNCTRL_CONN_KEEP 0 +#define CONNCTRL_CONN_CLOSE 1 +#define CONNCTRL_STREAM_CLOSE 2 + +void Curl_conncontrol(struct connectdata *conn, int ctrl); -#define CONNCTRL_KEEP 0 /* undo a marked closure */ -#define CONNCTRL_CONNECTION 1 -#define CONNCTRL_STREAM 2 - -void Curl_conncontrol(struct connectdata *conn, - int ctrl -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - , const char *reason -#endif - ); - -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) -#define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) -#else /* !DEBUGBUILD || !CURLVERBOSE */ -#define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) -#endif +#define streamclose(x) Curl_conncontrol((x), CONNCTRL_STREAM_CLOSE) +#define connclose(x) Curl_conncontrol((x), CONNCTRL_CONN_CLOSE) +#define connkeep(x) Curl_conncontrol((x), CONNCTRL_CONN_KEEP) /** * Setup the cfilters at `sockindex` in connection `conn`. diff --git a/lib/easy.c b/lib/easy.c index 7321e738782e..88b4c194c29e 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -758,13 +758,9 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* if the handle has a connection still attached (it is/was a connect-only handle) then disconnect before performing */ if(data->conn) { - struct connectdata *c; - curl_socket_t s; + struct connectdata *conn = data->conn; Curl_detach_connection(data); - s = Curl_getconnectinfo(data, &c); - if((s != CURL_SOCKET_BAD) && c) { - Curl_conn_terminate(data, c, TRUE); - } + Curl_conn_terminate(data, conn, TRUE); DEBUGASSERT(!data->conn); } diff --git a/lib/ftp.c b/lib/ftp.c index c5725c48ec51..ff9fed98afc5 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -3610,7 +3610,7 @@ static CURLcode ftp_sendquote(struct Curl_easy *data, return CURLE_OK; } -static CURLcode ftp_done_status(struct connectdata *conn, +static CURLcode ftp_done_status(struct Curl_easy *data, struct ftp_conn *ftpc, CURLcode status, bool premature) { @@ -3641,7 +3641,8 @@ static CURLcode ftp_done_status(struct connectdata *conn, ftpc->ctl_valid = FALSE; ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the current path, as this connection is going */ - connclose(conn, "FTP ended with bad error code"); + CURL_TRC_FTP(data, "FTP ended with bad error code"); + connclose(data->conn); return status; /* use the already set error code */ } return CURLE_OK; @@ -3669,7 +3670,7 @@ static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc, /* We can limp along anyway (and should try to since we may already be in * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ + connclose(conn); /* mark for connection closure */ curlx_safefree(ftpc->prevpath); /* no path remembering */ } else { /* remember working directory for connection reuse */ @@ -3712,7 +3713,7 @@ static CURLcode ftp_done_secondary_socket(struct Curl_easy *data, failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "ABOR command failed"); /* connection closure */ + connclose(conn); /* connection closure */ } } @@ -3744,7 +3745,7 @@ static CURLcode ftp_done_control_reply(struct Curl_easy *data, if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { failf(data, "control connection looks dead"); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ + connclose(conn); /* close */ } if(result) @@ -3754,7 +3755,7 @@ static CURLcode ftp_done_control_reply(struct Curl_easy *data, /* we have sent ABOR and there is no reliable way to check if it was * successful or not; we have to close the connection now */ infof(data, "partial download completed, closing connection"); - connclose(conn, "Partial download with no ability to check"); + connclose(conn); return result; } @@ -3838,7 +3839,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(!ftp || !ftpc) return CURLE_OK; - result = ftp_done_status(data->conn, ftpc, status, premature); + result = ftp_done_status(data, ftpc, status, premature); ftp_done_wildcard(data, ftpc); ftp_done_path(data, ftpc, result); @@ -4299,7 +4300,7 @@ static CURLcode ftp_quit(struct Curl_easy *data, failf(data, "Failure sending QUIT command: %s", curl_easy_strerror(result)); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(data->conn, "QUIT command failed"); /* mark for closure */ + connclose(data->conn); /* mark for closure */ ftp_state(data, ftpc, FTP_STOP); return result; } diff --git a/lib/gopher.c b/lib/gopher.c index 9781ffff631c..57dfa263e08e 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -51,7 +51,7 @@ static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) - connclose(conn, "Failed TLS connection"); + connclose(conn); *done = TRUE; return result; } diff --git a/lib/http.c b/lib/http.c index d8eca68198e0..91dcdb9716c7 100644 --- a/lib/http.c +++ b/lib/http.c @@ -464,7 +464,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, ongoing_auth ? ongoing_auth : "", ongoing_auth ? " send, " : ""); /* We decided to abort the ongoing transfer */ - streamclose(conn, "Mid-auth HTTP and much data left to send"); + streamclose(conn); data->req.size = 0; /* do not download any more than 0 bytes */ data->req.http_bodyless = TRUE; } @@ -577,7 +577,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authhost.picked == CURLAUTH_NTLM && (data->req.httpversion_sent > 11)) { infof(data, "Forcing HTTP/1.1 for NTLM"); - connclose(conn, "Force HTTP/1.1 connection"); + connclose(conn); data->state.http_neg.wanted = CURL_HTTP_V1x; data->state.http_neg.allowed = CURL_HTTP_V1x; } @@ -1678,7 +1678,7 @@ CURLcode Curl_http_done(struct Curl_easy *data, return an error here */ failf(data, "Empty reply from server"); /* Mark it as closed to avoid the "left intact" message */ - streamclose(conn, "Empty reply from server"); + streamclose(conn); return CURLE_GOT_NOTHING; } @@ -2711,7 +2711,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data) /* The resume point is at the end of file, consider this fine even if it does not allow resume from here. */ infof(data, "The entire document is already downloaded"); - streamclose(conn, "already downloaded"); + streamclose(conn); /* Abort download */ CURL_REQ_CLEAR_RECV(data); k->done = TRUE; @@ -2739,7 +2739,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data) infof(data, "Simulate an HTTP 304 response"); /* we abort the transfer before it is completed == we ruin the reuse ability. Close the connection */ - streamclose(conn, "Simulated 304 handling"); + streamclose(conn); return CURLE_OK; } } /* we have a time condition */ @@ -3272,7 +3272,7 @@ static CURLcode http_header_c(struct Curl_easy *data, failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; } - streamclose(conn, "overflow content-length"); + streamclose(conn); infof(data, "Overflow Content-Length: value"); return CURLE_OK; } @@ -3333,7 +3333,7 @@ static CURLcode http_header_c(struct Curl_easy *data, * the connection will close when this request has been * served. */ - connclose(conn, "Connection: close used"); + connclose(conn); return CURLE_OK; } if((k->httpversion == 10) && @@ -3344,7 +3344,7 @@ static CURLcode http_header_c(struct Curl_easy *data, * pleasure. Default action for 1.0 is to close. * * [RFC2068, section 19.7.1] */ - connkeep(conn, "Connection keep-alive"); + connkeep(conn); infof(data, "HTTP/1.0 connection set to keep alive"); return CURLE_OK; } @@ -3460,7 +3460,7 @@ static CURLcode http_header_p(struct Curl_easy *data, * connection will be kept alive for our pleasure. * Default action for 1.0 is to close. */ - connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ + connkeep(conn); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } else if((k->httpversion == 11) && conn->http_proxy.peer && @@ -3469,7 +3469,7 @@ static CURLcode http_header_p(struct Curl_easy *data, * We get an HTTP/1.1 response from a proxy and it says it will * close down after this transfer. */ - connclose(conn, "Proxy-Connection: asked to close after done"); + connclose(conn); infof(data, "HTTP/1.1 proxy connection set close"); } return CURLE_OK; @@ -3634,7 +3634,8 @@ static CURLcode http_header_t(struct Curl_easy *data, /* if this is not chunked, only close can signal the end of this * transfer as Content-Length is said not to be trusted for * transfer-encoding! */ - connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); + CURL_TRC_M(data, "HTTP/1.1 transfer-encoding without chunks"); + connclose(conn); k->ignore_cl = TRUE; } return CURLE_OK; @@ -3780,7 +3781,7 @@ static CURLcode http_statusline(struct Curl_easy *data, we get one of those fancy headers that tell us the server keeps it open for us! */ infof(data, "HTTP 1.0, assume close after body"); - connclose(conn, "HTTP/1.0 close after body"); + connclose(conn); } k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; @@ -4078,7 +4079,7 @@ static CURLcode http_handle_send_error(struct Curl_easy *data) } else { infof(data, "Got HTTP failure 417 while sending data"); - streamclose(conn, "Stop sending data before everything sent"); + streamclose(conn); result = http_perhapsrewind(data, conn); if(result) return result; @@ -4096,7 +4097,7 @@ static CURLcode http_handle_send_error(struct Curl_easy *data) } else { infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); + streamclose(conn); result = Curl_req_abort_sending(data); if(result) return result; @@ -4172,7 +4173,7 @@ static CURLcode http_on_response(struct Curl_easy *data, according to RFC2616 section 4.4 point 5, we assume that the server will close the connection to signal the end of the document. */ infof(data, "no chunk, no close, no size. Assume close to signal end"); - streamclose(conn, "HTTP: No end-of-message indicator"); + streamclose(conn); } http_check_auth_closure(data, conn); @@ -4508,7 +4509,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, /* this is not the beginning of a protocol first header line. * Cannot be 0.9 if version was detected or connection was reused. */ k->header = FALSE; - streamclose(conn, "bad HTTP: No end-of-message indicator"); + streamclose(conn); if((k->httpversion >= 10) || conn->bits.reuse) { failf(data, "Invalid status line"); return CURLE_WEIRD_SERVER_REPLY; @@ -4545,7 +4546,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, /* the first read "header", the status line */ statusline st = checkprotoprefix(data, conn, hd, hlen); if(st == STATUS_BAD) { - streamclose(conn, "bad HTTP: No end-of-message indicator"); + streamclose(conn); /* this is not the beginning of a protocol first header line. * Cannot be 0.9 if version was detected or connection was reused. */ if((k->httpversion >= 10) || conn->bits.reuse) { diff --git a/lib/http2.c b/lib/http2.c index 8612efc30298..a63fc4a59c84 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -512,7 +512,8 @@ static CURLcode h2_process_pending_input(struct Curl_cfilter *cf, the connection may not be reused. This is set when a GOAWAY frame has been received or when the limit of stream identifiers has been reached. */ - connclose(cf->conn, "http/2: No new requests allowed"); + CURL_TRC_M(data, "http/2: No new requests allowed"); + connclose(cf->conn); } return CURLE_OK; @@ -1688,7 +1689,7 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, if(stream->error == NGHTTP2_REFUSED_STREAM) { infof(data, "HTTP/2 stream %d refused by server, try again on a new " "connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + connclose(cf->conn); /* do not use this anymore */ data->state.refused_stream = TRUE; return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } @@ -1930,8 +1931,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, } if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - connclose(cf->conn, ctx->rcvd_goaway ? "server closed with GOAWAY" : - "server closed abruptly"); + CURL_TRC_CF(data, cf, "server closed %s", + ctx->rcvd_goaway ? "with GOAWAY" : "abruptly"); + connclose(cf->conn); } CURL_TRC_CF(data, cf, "[0] ingress: done"); diff --git a/lib/imap.c b/lib/imap.c index fb23cfef41bf..6a48f8515280 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -2010,7 +2010,8 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status, return CURLE_OK; if(status) { - connclose(conn, "IMAP done with bad status"); /* marked for closure */ + CURL_TRC_M(data, "IMAP done with bad status"); + connclose(conn); /* marked for closure */ result = status; /* use the already set error code */ } else if(!data->set.connect_only && diff --git a/lib/ldap.c b/lib/ldap.c index b4b107a57266..63a40784a807 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -652,7 +652,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* no data to transfer */ Curl_xfer_setup_nop(data); - connclose(conn, "LDAP connection always disable reuse"); + connclose(conn); return result; } diff --git a/lib/mqtt.c b/lib/mqtt.c index ad397929875f..a9cdf214984b 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -975,7 +975,7 @@ static CURLcode mqtts_connecting(struct Curl_easy *data, bool *done) result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) - connclose(conn, "Failed TLS connection"); + connclose(conn); return result; } diff --git a/lib/multi.c b/lib/multi.c index 77cb306d31ae..f68124436f09 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -78,7 +78,7 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, static void multi_timeout(struct Curl_multi *multi, struct curltime *expire_time, long *timeout_ms); -static void process_pending_handles(struct Curl_multi *multi); +static void multi_schedule_pending(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); #ifdef DEBUGBUILD static void multi_xfer_tbl_dump(struct Curl_multi *multi); @@ -666,14 +666,14 @@ static void multi_done_locked(struct connectdata *conn, conn->connection_id, conn->destination, data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); - connclose(conn, "disconnecting"); + connclose(conn); Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" " by server, not reusing", conn->connection_id, conn->destination); - connclose(conn, "server shutdown"); + connclose(conn); Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else { @@ -751,7 +751,7 @@ static CURLcode multi_done(struct Curl_easy *data, if(conn) Curl_conn_ev_data_done(data, premature); - process_pending_handles(data->multi); /* connection / multiplex */ + multi_schedule_pending(data->multi); /* connection / multiplex */ if(!result) result = Curl_req_done(&data->req, data, premature); @@ -771,16 +771,6 @@ static CURLcode multi_done(struct Curl_easy *data, return result; } -static void close_connect_only(struct connectdata *conn, - struct Curl_easy *data, - void *userdata) -{ - (void)userdata; - (void)data; - if(conn->bits.connect_only) - connclose(conn, "Removing connect-only easy handle"); -} - CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, struct Curl_easy *data) { @@ -808,20 +798,14 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, premature = (data->mstate < MSTATE_COMPLETED); - /* If the 'state' is not INIT or COMPLETED, we might need to do something - nice to put the easy_handle in a good known state when this returns. */ - if(data->conn && - data->mstate > MSTATE_DO && - data->mstate < MSTATE_COMPLETED) { - /* Set connection owner so that the DONE function closes it. We can - safely do this here since connection is killed. */ - streamclose(data->conn, "Removed with partial response"); - } - if(data->conn) { + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + if(premature && (data->mstate > MSTATE_DO)) + streamclose(data->conn); + /* multi_done() clears the association between the easy handle and the connection. - Note that this ignores the return code because there is nothing really useful to do with it anyway! */ (void)multi_done(data, data->result, premature); @@ -835,6 +819,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; + if(data->state.really_alive) { data->state.really_alive = FALSE; --multi->xfers_really_alive; @@ -852,26 +837,29 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, /* Tell event handling that this transfer is definitely going away */ Curl_multi_ev_xfer_done(multi, data); - if(data->set.connect_only && !data->multi_easy) { - /* This removes a handle that was part the multi interface that used - CONNECT_ONLY, that connection is now left alive but since this handle - has bits.close set nothing can use that transfer anymore and it is - forbidden from reuse. This easy handle cannot find the connection - anymore once removed from the multi handle - - Better close the connection here, at once. */ - struct connectdata *c; - curl_socket_t s; - s = Curl_getconnectinfo(data, &c); - if((s != CURL_SOCKET_BAD) && c) { - Curl_conn_terminate(data, c, TRUE); + if(data->set.connect_only) { + if(data->multi_easy) { + if(data->state.lastconnect_id != -1) { + /* Mark any connect-only connection for closure */ + struct connectdata *conn; + (void)Curl_getconnectinfo(data, &conn); + if(conn && conn->bits.connect_only) + connclose(conn); + } + } + else { + /* This removes a handle that was part the multi interface that used + CONNECT_ONLY, that connection is now left alive but since this handle + has bits.close set nothing can use that connection anymore and it is + forbidden from reuse. This easy handle cannot find the connection + anymore once removed from the multi handle + + Better close the connection here, at once. */ + struct connectdata *conn; + (void)Curl_getconnectinfo(data, &conn); + if(conn) + Curl_conn_terminate(data, conn, TRUE); } - } - - if(data->state.lastconnect_id != -1) { - /* Mark any connect-only connection for closure */ - Curl_cpool_do_by_id(data, data->state.lastconnect_id, - close_connect_only, NULL); } #ifdef USE_LIBPSL @@ -904,10 +892,8 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, data->mid = UINT32_MAX; data->master_mid = UINT32_MAX; - /* NOTE NOTE NOTE - We do not touch the easy handle here! */ - process_pending_handles(multi); - + /* A pending transfer *might* be able to run now. */ + multi_schedule_pending(multi); mresult = Curl_update_timer(multi); if(mresult) return mresult; @@ -1872,7 +1858,7 @@ static bool multi_handle_timeout(struct Curl_easy *data, if(data->conn) { /* Force connection closed if the connection has indeed been used */ if(data->mstate > MSTATE_DO) { - streamclose(data->conn, "Disconnect due to timeout"); + streamclose(data->conn); *stream_error = TRUE; } (void)multi_done(data, *result, TRUE); @@ -2077,7 +2063,7 @@ static CURLMcode multistate_performing(struct Curl_easy *data, if(!ret) { infof(data, "Downgrades to HTTP/1.1"); - streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); + streamclose(data->conn); data->state.http_neg.wanted = CURL_HTTP_V1x; data->state.http_neg.allowed = CURL_HTTP_V1x; /* clear the error message bit too as we ignore the one we got */ @@ -2112,7 +2098,7 @@ static CURLMcode multistate_performing(struct Curl_easy *data, if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); + streamclose(data->conn); multi_posttransfer(data); multi_done(data, result, TRUE); @@ -2323,7 +2309,7 @@ static CURLMcode multistate_ratelimiting(struct Curl_easy *data, if(result) { if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); + streamclose(data->conn); multi_posttransfer(data); multi_done(data, result, TRUE); @@ -2357,7 +2343,7 @@ static CURLMcode multistate_connect(struct Curl_multi *multi, return mresult; } else - process_pending_handles(data->multi); + multi_schedule_pending(data->multi); if(!result) { /* after the connect has been sent off, go WAITCONNECT unless the @@ -2369,7 +2355,7 @@ static CURLMcode multistate_connect(struct Curl_multi *multi, if(!data->conn->bits.reuse && Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); + multi_schedule_pending(data->multi); } multistate(data, MSTATE_PROTOCONNECT); } @@ -2398,7 +2384,7 @@ static CURLcode is_finished(struct Curl_multi *multi, connection detach and termination happens only here */ /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* connection */ + multi_schedule_pending(multi); /* connection */ if(data->conn) { if(stream_error) { @@ -2428,7 +2414,7 @@ static CURLcode is_finished(struct Curl_multi *multi, if(result) { /* aborted due to progress callback return code must close the connection */ - streamclose(data->conn, "Aborted by callback"); + streamclose(data->conn); /* if not yet in DONE state, go there, otherwise COMPLETED */ multistate(data, (data->mstate < MSTATE_DONE) ? @@ -2542,7 +2528,7 @@ static CURLMcode multistate_connecting(struct Curl_easy *data, if(!data->conn->bits.reuse && Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); + multi_schedule_pending(data->multi); } multistate(data, MSTATE_PROTOCONNECT); return CURLM_CALL_MULTI_PERFORM; @@ -2678,7 +2664,7 @@ static CURLMcode multistate_did(struct Curl_multi *multi, DEBUGASSERT(data->conn); if(data->conn->bits.multiplex) /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* multiplexed */ + multi_schedule_pending(multi); /* multiplexed */ /* Only perform the transfer if there is a good socket to work with. Having both BAD is a signal to skip immediately to DONE */ @@ -2770,7 +2756,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(multi_ischanged(multi, TRUE)) { CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue"); - process_pending_handles(multi); /* multiplexed */ + multi_schedule_pending(multi); /* multiplexed */ } if(data->mstate > MSTATE_CONNECT && @@ -2923,7 +2909,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi, sigpipe_restore(&sigpipe_ctx); if(multi_ischanged(multi, TRUE)) - process_pending_handles(multi); + multi_schedule_pending(multi); if(!returncode && CURL_MNTFY_HAS_ENTRIES(multi)) returncode = Curl_mntfy_dispatch_all(multi); @@ -3330,7 +3316,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, sigpipe_restore(&pipe_ctx); if(multi_ischanged(multi, TRUE)) - process_pending_handles(multi); + multi_schedule_pending(multi); if(!mresult && CURL_MNTFY_HAS_ENTRIES(multi)) mresult = Curl_mntfy_dispatch_all(multi); @@ -3879,7 +3865,7 @@ static void move_pending_to_connect(struct Curl_multi *multi, Curl_multi_mark_dirty(data); /* make it run */ } -/* process_pending_handles() moves a handle from PENDING back into the process +/* multi_schedule_pending() moves a handle from PENDING back into the process list and change state to CONNECT. We do not move all transfers because that can be a significant amount. @@ -3892,7 +3878,7 @@ static void move_pending_to_connect(struct Curl_multi *multi, We could consider an improvement where we store the queue reason and allow more pipewait rechecks than others. */ -static void process_pending_handles(struct Curl_multi *multi) +static void multi_schedule_pending(struct Curl_multi *multi) { uint32_t mid = multi->last_pending_mid; diff --git a/lib/pop3.c b/lib/pop3.c index 7557c7435056..071872687cd8 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -1478,7 +1478,8 @@ static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, return CURLE_OK; if(status) { - connclose(data->conn, "POP3 done with bad status"); + CURL_TRC_M(data, "POP3 done with bad status"); + connclose(data->conn); result = status; /* use the already set error code */ } diff --git a/lib/sendf.c b/lib/sendf.c index 449a4bc44739..27f305521477 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -210,7 +210,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(data->req.no_body && nbytes > 0) { /* BODY arrives although we want none, bail out */ - streamclose(data->conn, "ignoring body"); + streamclose(data->conn); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " "did not want a BODY", (unsigned int)type, nbytes); data->req.download_done = TRUE; @@ -277,7 +277,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, ", bytecount = %" FMT_OFF_T, excess_len, data->req.size, data->req.maxdownload, data->req.bytecount); - connclose(data->conn, "excess found in a read"); + connclose(data->conn); } } else if((nwrite < nbytes) && !data->req.ignorebody) { diff --git a/lib/smb.c b/lib/smb.c index 70af8d597090..2d1a859aa169 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -912,7 +912,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) result = smb_send_negotiate(data, smbc, req); if(result) { - connclose(conn, "SMB: failed to send negotiate message"); + CURL_TRC_M(data, "SMB: failed to send negotiate message"); + connclose(conn); return result; } @@ -922,7 +923,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) /* Send the previous message and check for a response */ result = smb_send_and_recv(data, smbc, &msg); if(result && result != CURLE_AGAIN) { - connclose(conn, "SMB: failed to communicate"); + CURL_TRC_M(data, "SMB: failed to communicate"); + connclose(conn); return result; } @@ -935,7 +937,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) case SMB_NEGOTIATE: if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) || h->status) { - connclose(conn, "SMB: negotiation failed"); + CURL_TRC_M(data, "SMB: negotiation failed"); + connclose(conn); return CURLE_COULDNT_CONNECT; } nrsp = msg; @@ -952,7 +955,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) smbc->session_key = smb_swap32(nrsp->session_key); result = smb_send_setup(data); if(result) { - connclose(conn, "SMB: failed to send setup message"); + CURL_TRC_M(data, "SMB: failed to send setup message"); + connclose(conn); return result; } conn_state(data, smbc, SMB_SETUP); @@ -960,7 +964,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) case SMB_SETUP: if(h->status) { - connclose(conn, "SMB: authentication failed"); + CURL_TRC_M(data, "SMB: authentication failed"); + connclose(conn); return CURLE_LOGIN_DENIED; } smbc->uid = smb_swap16(h->uid); @@ -1025,7 +1030,8 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) if(req->state == SMB_REQUESTING) { result = smb_send_tree_connect(data, smbc, req); if(result) { - connclose(conn, "SMB: failed to send tree connect message"); + CURL_TRC_M(data, "SMB: failed to send tree connect message"); + connclose(conn); return result; } @@ -1035,7 +1041,8 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) /* Send the previous message and check for a response */ result = smb_send_and_recv(data, smbc, &msg); if(result && result != CURLE_AGAIN) { - connclose(conn, "SMB: failed to communicate"); + CURL_TRC_M(data, "SMB: failed to communicate"); + connclose(conn); return result; } @@ -1180,7 +1187,8 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) } if(result) { - connclose(conn, "SMB: failed to send message"); + CURL_TRC_M(data, "SMB: failed to send message"); + connclose(conn); return result; } diff --git a/lib/smtp.c b/lib/smtp.c index f5d922ddb48c..a37a5733811e 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -1739,7 +1739,8 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, curlx_safefree(smtp->custom); if(status) { - connclose(conn, "SMTP done with bad status"); /* marked for closure */ + CURL_TRC_M(data, "SMTP done with bad status"); + connclose(conn); /* marked for closure */ result = status; /* use the already set error code */ } else if(!data->set.connect_only && data->set.mail_rcpt && diff --git a/lib/tftp.c b/lib/tftp.c index 00e89752bddc..d5c85a3100af 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -942,7 +942,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) /* we do not keep TFTP connections up because there is none or little gain * for UDP */ - connclose(conn, "TFTP"); + connclose(conn); state->data = data; state->sockfd = conn->sock[FIRSTSOCKET]; diff --git a/lib/transfer.c b/lib/transfer.c index b7902de9a172..3a7b6bc1360f 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -658,7 +658,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) if(!*url) return CURLE_OUT_OF_MEMORY; - connclose(conn, "retry"); /* close this connection */ + connclose(conn); /* close this connection */ conn->bits.retry = TRUE; /* mark this as a connection we are about to retry. Marking it this way should prevent i.e HTTP transfers to return error because nothing diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 9a43a443cc9e..3a7023f300e1 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -665,7 +665,7 @@ static ssize_t cf_h3_proxy_recv_closed_stream(struct Curl_cfilter *cf, if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " "on a new connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); + connclose(cf->conn); data->state.refused_stream = TRUE; *err = CURLE_RECV_ERROR; goto out; diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 9a905a745a46..980516d05eaa 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -449,7 +449,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " "on a new connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + connclose(cf->conn); /* do not use this anymore */ data->state.refused_stream = TRUE; return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index e0016d37d28d..c23b81025449 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -866,7 +866,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { infof(data, "HTTP/3 stream %" PRIu64 " refused by server, try again " "on a new connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + connclose(cf->conn); /* do not use this anymore */ data->state.refused_stream = TRUE; return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 89d2f33bcc3c..f1a206ba2d1d 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -2238,7 +2238,7 @@ static CURLcode myssh_in_SESSION_FREE(struct Curl_easy *data, /* the code we are about to return */ result = sshc->actualcode; memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(data->conn, "SSH session free"); + connclose(data->conn); sshc->state = SSH_SESSION_FREE; /* current */ sshc->nextstate = SSH_NO_STATE; myssh_to(data, sshc, SSH_STOP); diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 96c1157669d6..effcc9beaf4f 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -2850,7 +2850,7 @@ static CURLcode ssh_state_session_free(struct Curl_easy *data, if(result) return result; memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); + connclose(conn); sshc->state = SSH_SESSION_FREE; /* current */ myssh_to(data, sshc, SSH_STOP); return CURLE_OK; diff --git a/tests/data/test1554 b/tests/data/test1554 index fa071b09b245..f76678564775 100644 --- a/tests/data/test1554 +++ b/tests/data/test1554 @@ -33,8 +33,6 @@ run 1: foobar and so on fun! run 1: foobar and so on fun! -] Mutex lock CONNECT [- Mutex unlock CONNECT --] Mutex lock CONNECT -[- Mutex unlock CONNECT -] Mutex lock SHARE [- Mutex unlock SHARE -] Mutex lock SHARE @@ -48,8 +46,6 @@ run 1: foobar and so on fun! run 1: foobar and so on fun! -] Mutex lock CONNECT [- Mutex unlock CONNECT --] Mutex lock CONNECT -[- Mutex unlock CONNECT -] Mutex lock SHARE [- Mutex unlock SHARE -] Mutex lock SHARE @@ -63,8 +59,6 @@ run 1: foobar and so on fun! run 1: foobar and so on fun! -] Mutex lock CONNECT [- Mutex unlock CONNECT --] Mutex lock CONNECT -[- Mutex unlock CONNECT -] Mutex lock SHARE [- Mutex unlock SHARE -] Mutex lock SHARE From c4dcdb838883ad060c44a30680328451decdad1b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 24 Jul 2026 20:26:01 +0200 Subject: [PATCH 0716/1062] spacecheck: cap number of lines per file To prevent merging large text files by accident. Set the cap at 10k lines. The current line number top list is: ``` 5577 configure.ac 5561 lib/vtls/openssl.c 5077 lib/http.c 4517 lib/ftp.c 4284 lib/multi.c ``` Closes #22387 --- scripts/spacecheck.pl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/scripts/spacecheck.pl b/scripts/spacecheck.pl index 18543dfd9a02..44274d0a21b4 100755 --- a/scripts/spacecheck.pl +++ b/scripts/spacecheck.pl @@ -84,10 +84,7 @@ sub fn_match { } sub eol_detect { - my ($content) = @_; - - my $cr = () = $content =~ /\r/g; - my $lf = () = $content =~ /\n/g; + my ($cr, $lf) = @_; if($cr > 0 && $lf == 0) { return 'cr'; @@ -107,6 +104,7 @@ sub eol_detect { my $max_repeat_space = 79; my $max_line_len = 192; +my $max_lines = 10000; my $max_path_len = 64; my $max_filename_len = 48; @@ -140,7 +138,10 @@ sub eol_detect { push @err, 'content: has tab'; } - my $eol = eol_detect($content); + my $cnt_cr = () = $content =~ /\r/g; + my $cnt_lf = () = $content =~ /\n/g; + + my $eol = eol_detect($cnt_cr, $cnt_lf); if($eol eq '') { push @err, 'content: has mixed EOL types'; @@ -156,6 +157,13 @@ sub eol_detect { push @err, 'content: must use LF EOL for this file type'; } + if($cnt_cr > $max_lines) { + push @err, sprintf('content: too many lines (%d > %d)', $cnt_cr, $max_lines); + } + elsif($cnt_lf > $max_lines) { + push @err, sprintf('content: too many lines (%d > %d)', $cnt_lf, $max_lines); + } + if($content =~ /[ \t]\n/) { my $line; for my $l (split(/\n/, $content)) { From ebc5212dacd3db8f5315b5a2d676415848e936d5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 28 Jun 2026 13:10:38 +0200 Subject: [PATCH 0717/1062] tidy-up: drop redundant includes `sys/types.h` and `sys/socket.h` (non-Win32). They are included via `curl/curl.h` and `curl_setup.h`. This drops `HAVE_SYS_TYPES_H` guards from the codebase. It's safe because `sys/types.h` (POSIX) is already required unconditionally by `curl/curl.h`. It remains used in feature checks by both autotools and cmake; to be reviewed in a future step. Closes #22374 --- lib/curl_setup.h | 9 ++++----- lib/file.c | 4 ---- lib/vtls/vtls.c | 4 ---- lib/vtls/vtls_config.c | 4 ---- lib/vtls/vtls_scache.c | 4 ---- src/tool_xattr.h | 1 - tests/unit/unit1961.c | 6 ------ 7 files changed, 4 insertions(+), 28 deletions(-) diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 00e4694bc7bc..c8743108bdec 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -506,12 +506,15 @@ #endif #include +/* Include after setting system macros that may affect type sizes + (e.g. 'off_t' or 'time_t'), or suppress warnings + (e.g. '_CRT_SECURE_NO_WARNINGS`), but before including sys/stat.h */ +#include #ifdef _WIN32 # ifdef HAVE_IO_H # include # endif -# include # include /* Large file (>2Gb) support using Win32 functions. */ # define curl_lseek _lseeki64 @@ -830,10 +833,6 @@ #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif - #include #if !defined(_WIN32) || defined(__MINGW32__) diff --git a/lib/file.c b/lib/file.c index 9c151643f98e..979204bf44e0 100644 --- a/lib/file.c +++ b/lib/file.c @@ -47,10 +47,6 @@ #include #endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif - #ifdef HAVE_DIRENT_H #include #endif diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 167c65763ec6..2da004adba0b 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -40,10 +40,6 @@ #include "curl_setup.h" -#ifdef HAVE_SYS_TYPES_H -#include -#endif - #include "urldata.h" #include "cfilters.h" #include "cf-dns.h" diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index 3e26e0bc8c75..252c5ad885ce 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -40,10 +40,6 @@ #include "curl_setup.h" -#ifdef HAVE_SYS_TYPES_H -#include -#endif - #include "urldata.h" #include "setopt.h" #include "strcase.h" diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 3c0881262fb9..3a4c467699a9 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -25,10 +25,6 @@ #ifdef USE_SSL -#ifdef HAVE_SYS_TYPES_H -#include -#endif - #include "urldata.h" #include "cfilters.h" diff --git a/src/tool_xattr.h b/src/tool_xattr.h index f2ccfe209a37..0795ba8a6704 100644 --- a/src/tool_xattr.h +++ b/src/tool_xattr.h @@ -30,7 +30,6 @@ # define USE_XATTR #elif (defined(__FreeBSD_version) && (__FreeBSD_version > 500000)) || \ defined(__MidnightBSD_version) -# include # include # define USE_XATTR #elif defined(_WIN32) diff --git a/tests/unit/unit1961.c b/tests/unit/unit1961.c index 135386256a04..338eeb176b43 100644 --- a/tests/unit/unit1961.c +++ b/tests/unit/unit1961.c @@ -23,12 +23,6 @@ ***************************************************************************/ #include "unitcheck.h" -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifndef _WIN32 -#include -#endif #ifdef HAVE_ARPA_INET_H #include #endif From a55731050e8c3dbea0b96205cf916443118f6acb Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Fri, 24 Jul 2026 22:49:52 +0200 Subject: [PATCH 0718/1062] httpsig: add RFC 9421 HTTP Message Signatures support Add support for signing outgoing HTTP requests per RFC 9421 using Ed25519 or HMAC-SHA256 algorithms. New libcurl options: - CURLOPT_HTTPSIG: signing algorithm ("ed25519" or "hmac-sha256") - CURLOPT_HTTPSIG_KEY: path to hex-encoded key file - CURLOPT_HTTPSIG_KEYID: key identifier for Signature-Input - CURLOPT_HTTPSIG_HEADERS: space-separated components to sign New CLI flags: --httpsig, --httpsig-key, --httpsig-keyid, --httpsig-headers The crypto layer follows the sha256.c multi-backend pattern with implementations for OpenSSL (EVP_DigestSign) and wolfSSL (wc_ed25519_sign_msg). HMAC-SHA256 uses the existing Curl_hmacit() infrastructure which works on all backends. Verified by test 5000 to 5021 Assisted-by: Daniel Stenberg Signed-off-by: Sameeh Jubran Closes #22386 Closes #21239 --- .github/scripts/pyspelling.words | 5 + .github/workflows/linux.yml | 4 +- CMakeLists.txt | 3 + configure.ac | 28 + docs/CURL-DISABLE.md | 4 + docs/EXPERIMENTAL.md | 19 + docs/cmdline-opts/.gitignore | 1 + docs/cmdline-opts/Makefile.inc | 4 + docs/cmdline-opts/httpsig-algo.md | 36 + docs/cmdline-opts/httpsig-headers.md | 45 ++ docs/cmdline-opts/httpsig-key.md | 35 + docs/cmdline-opts/httpsig-keyid.md | 23 + docs/libcurl/curl_easy_setopt.md | 16 + docs/libcurl/curl_version_info.md | 7 + docs/libcurl/opts/CURLOPT_HTTPAUTH.md | 7 + .../libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md | 89 +++ docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md | 108 +++ docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md | 90 +++ docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md | 76 ++ docs/libcurl/opts/Makefile.inc | 4 + docs/libcurl/symbols-in-versions | 8 + docs/options-in-versions | 4 + include/curl/curl.h | 24 +- include/curl/typecheck-gcc.h | 3 + lib/Makefile.inc | 4 + lib/curl_config-cmake.h.in | 3 + lib/curl_ed25519.c | 170 +++++ lib/curl_ed25519.h | 44 ++ lib/curl_hmac.h | 1 + lib/curl_setup.h | 3 + lib/curl_sha256.h | 3 +- lib/easyoptions.c | 6 +- lib/http.c | 17 + lib/http_httpsig.c | 692 ++++++++++++++++++ lib/http_httpsig.h | 35 + lib/setopt.c | 24 + lib/urldata.h | 6 + lib/version.c | 3 + projects/OS400/README.OS400 | 3 + projects/OS400/ccsidcurl.c | 3 + scripts/managen | 9 +- src/config2setopts.c | 78 ++ src/curlinfo.c | 7 + src/tool_cfgable.c | 4 + src/tool_cfgable.h | 4 + src/tool_getparam.c | 32 + src/tool_getparam.h | 4 + src/tool_libinfo.c | 2 + src/tool_libinfo.h | 1 + src/tool_listhelp.c | 22 + tests/data/Makefile.am | 9 +- tests/data/data-httpsig-ed25519.key | 1 + tests/data/data-httpsig-hmac-sha256.key | 1 + tests/data/test5000 | 57 ++ tests/data/test5001 | 55 ++ tests/data/test5002 | 55 ++ tests/data/test5003 | 55 ++ tests/data/test5004 | 66 ++ tests/data/test5005 | 56 ++ tests/data/test5006 | 56 ++ tests/data/test5007 | 58 ++ tests/data/test5008 | 46 ++ tests/data/test5009 | 46 ++ tests/data/test5010 | 46 ++ tests/data/test5011 | 55 ++ tests/data/test5012 | 58 ++ tests/data/test5013 | 58 ++ tests/data/test5014 | 58 ++ tests/data/test5015 | 62 ++ tests/data/test5016 | 60 ++ tests/data/test5017 | 55 ++ tests/data/test5018 | 63 ++ tests/data/test5019 | 46 ++ tests/data/test5020 | 46 ++ tests/data/test5021 | 57 ++ tests/libtest/Makefile.inc | 3 +- tests/libtest/lib5000.c | 66 ++ tests/libtest/lib5004.c | 85 +++ 78 files changed, 3192 insertions(+), 10 deletions(-) create mode 100644 docs/cmdline-opts/httpsig-algo.md create mode 100644 docs/cmdline-opts/httpsig-headers.md create mode 100644 docs/cmdline-opts/httpsig-key.md create mode 100644 docs/cmdline-opts/httpsig-keyid.md create mode 100644 docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md create mode 100644 docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md create mode 100644 docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md create mode 100644 docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md create mode 100644 lib/curl_ed25519.c create mode 100644 lib/curl_ed25519.h create mode 100644 lib/http_httpsig.c create mode 100644 lib/http_httpsig.h create mode 100644 tests/data/data-httpsig-ed25519.key create mode 100644 tests/data/data-httpsig-hmac-sha256.key create mode 100644 tests/data/test5000 create mode 100644 tests/data/test5001 create mode 100644 tests/data/test5002 create mode 100644 tests/data/test5003 create mode 100644 tests/data/test5004 create mode 100644 tests/data/test5005 create mode 100644 tests/data/test5006 create mode 100644 tests/data/test5007 create mode 100644 tests/data/test5008 create mode 100644 tests/data/test5009 create mode 100644 tests/data/test5010 create mode 100644 tests/data/test5011 create mode 100644 tests/data/test5012 create mode 100644 tests/data/test5013 create mode 100644 tests/data/test5014 create mode 100644 tests/data/test5015 create mode 100644 tests/data/test5016 create mode 100644 tests/data/test5017 create mode 100644 tests/data/test5018 create mode 100644 tests/data/test5019 create mode 100644 tests/data/test5020 create mode 100644 tests/data/test5021 create mode 100644 tests/libtest/lib5000.c create mode 100644 tests/libtest/lib5004.c diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 7563fcd6c726..ce0f4cbfca3b 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -368,6 +368,9 @@ httpget HttpGet HTTPS https +httpsig +HTTPSIG +CURLHTTPSIG HTTPSRR hyper's IANA @@ -428,6 +431,7 @@ KDE keepalive Keil kerberos +keyid Keychain keychain KiB @@ -996,6 +1000,7 @@ WinIDN WinLDAP winsock Wireshark +wolfCrypt wolfSSH wolfSSL ws diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 86383de69140..47c887651c95 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -200,7 +200,7 @@ jobs: install_steps: wolfssl-opensslextra-arm tflags: '--min=815 1 to 1000' LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug --enable-httpsig - name: 'wolfssl-opensslextra valgrind 2' image: ubuntu-26.04-arm @@ -208,7 +208,7 @@ jobs: install_steps: wolfssl-opensslextra-arm tflags: '--min=835 1001 to 9999' LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug --enable-httpsig - name: 'openssl default' install_steps: pytest diff --git a/CMakeLists.txt b/CMakeLists.txt index 54032401a2a1..f36b49034e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,8 @@ option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) option(CURL_DISABLE_AWS "Disable aws-sigv4" OFF) mark_as_advanced(CURL_DISABLE_AWS) +option(CURL_DISABLE_HTTPSIG "Disable HTTP Message Signatures (RFC 9421)" ON) +mark_as_advanced(CURL_DISABLE_HTTPSIG) option(CURL_DISABLE_DICT "Disable DICT" OFF) mark_as_advanced(CURL_DISABLE_DICT) option(CURL_DISABLE_DOH "Disable DNS-over-HTTPS" OFF) @@ -2037,6 +2039,7 @@ curl_add_if("SSPI" USE_WINDOWS_SSPI) curl_add_if("GSS-API" HAVE_GSSAPI) curl_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) curl_add_if("HSTS" NOT CURL_DISABLE_HSTS) +curl_add_if("HTTPSIG" NOT CURL_DISABLE_HTTPSIG) curl_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND diff --git a/configure.ac b/configure.ac index 2456589b61a1..eed980f0ebb3 100644 --- a/configure.ac +++ b/configure.ac @@ -4566,6 +4566,29 @@ AS_HELP_STRING([--disable-aws],[Disable AWS sig support]), AC_MSG_RESULT(yes) ) +dnl ************************************************************ +dnl disable httpsig (RFC 9421) +dnl +AC_MSG_CHECKING([whether to enable HTTP Message Signatures (RFC 9421)]) +AC_ARG_ENABLE(httpsig, +AS_HELP_STRING([--enable-httpsig],[Enable HTTP Message Signatures support]) +AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (default)]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_HTTPSIG, 1, [to disable HTTP Message Signatures support]) + want_httpsig="no" + ;; + *) + AC_MSG_RESULT(yes) + want_httpsig="yes" + ;; + esac ], + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_HTTPSIG, 1, [to disable HTTP Message Signatures support]) + want_httpsig="no" +) + dnl ************************************************************ dnl disable NTLM support dnl @@ -5313,6 +5336,11 @@ if test "$want_httpsrr" != "no"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPSRR" fi +if test "$want_httpsig" = "yes"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPSIG" + experimental="$experimental HTTPSIG" +fi + if test "$SSLS_EXPORT_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSLS-EXPORT" fi diff --git a/docs/CURL-DISABLE.md b/docs/CURL-DISABLE.md index a6a1ea16614c..83494cf18b4f 100644 --- a/docs/CURL-DISABLE.md +++ b/docs/CURL-DISABLE.md @@ -42,6 +42,10 @@ Disable support for the negotiate authentication methods. Disable **aws-sigv4** support. +## `CURL_DISABLE_HTTPSIG` + +Disable RFC 9421 HTTP Message Signatures support. + ## `CURL_DISABLE_CA_SEARCH` Disable unsafe CA bundle search in PATH on Windows. diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index b1b472c5ba43..ff37422172fa 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -105,3 +105,22 @@ Graduation requirements: - HTTPS records can control ALPN and port number, at least - There are options to control HTTPS use + +## HTTP Message Signatures (RFC 9421) + +Sign outgoing HTTP requests according to RFC 9421 using the +`--httpsig-algo`, `--httpsig-key`, `--httpsig-keyid` and +`--httpsig-headers` command line options, or the corresponding +`CURLOPT_HTTPSIG_*` libcurl options. Built only when configured with +`--enable-httpsig`. + +Graduation requirements: + +- the option set (names, arguments, defaults) is settled + +- interoperability has been verified against at least two independent + RFC 9421 implementations + +- no test cases are disabled for the feature + +- feedback from users saying the API works for their use cases diff --git a/docs/cmdline-opts/.gitignore b/docs/cmdline-opts/.gitignore index 8d42e2c53d51..b903c3bf7781 100644 --- a/docs/cmdline-opts/.gitignore +++ b/docs/cmdline-opts/.gitignore @@ -3,3 +3,4 @@ # SPDX-License-Identifier: curl curl.txt +asciipage.tmp.* diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index f8fd01ccd63e..c6ffc06a5ea7 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -134,6 +134,10 @@ DPAGES = \ http2.md \ http3.md \ http3-only.md \ + httpsig-algo.md \ + httpsig-headers.md \ + httpsig-key.md \ + httpsig-keyid.md \ ignore-content-length.md \ insecure.md \ interface.md \ diff --git a/docs/cmdline-opts/httpsig-algo.md b/docs/cmdline-opts/httpsig-algo.md new file mode 100644 index 000000000000..c97814738ed2 --- /dev/null +++ b/docs/cmdline-opts/httpsig-algo.md @@ -0,0 +1,36 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: httpsig-algo +Protocols: HTTP +Arg: +Help: Algorithm for HTTP Message Signatures +Category: auth http +Added: 8.22.0 +Multi: single +Experimental: yes +See-also: + - httpsig-key + - httpsig-keyid + - httpsig-headers +Example: + - --httpsig-key key.hex --httpsig-keyid "my-key" $URL + - --httpsig-algo hmac-sha256 --httpsig-key secret.hex --httpsig-keyid "shared" $URL +--- + +# `--httpsig-algo` + +Sign outgoing HTTP requests using RFC 9421 HTTP Message Signatures. + +This option specifies which signing algorithm to use. Supported values are +**ed25519** and **hmac-sha256**. If not specified, **ed25519** is used. Any +other value causes curl to exit with an error. + +HTTP Message Signatures are enabled when any of --httpsig-algo, +--httpsig-key, --httpsig-keyid or --httpsig-headers is given. When enabled, +--httpsig-key and --httpsig-keyid are required. Without any of these options +no signing is performed. + +By default, the signed components are `method`, `authority`, `path`, and +`query` (when a query string is present). Use --httpsig-headers to override +the set of components included in the signature. diff --git a/docs/cmdline-opts/httpsig-headers.md b/docs/cmdline-opts/httpsig-headers.md new file mode 100644 index 000000000000..7b590c98540f --- /dev/null +++ b/docs/cmdline-opts/httpsig-headers.md @@ -0,0 +1,45 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: httpsig-headers +Protocols: HTTP +Arg: +Help: Components to sign for HTTP Message Signatures +Category: auth http +Added: 8.22.0 +Multi: single +Experimental: yes +See-also: + - httpsig-algo + - httpsig-key + - httpsig-keyid +Example: + - --httpsig-algo ed25519 --httpsig-key key.hex --httpsig-keyid "my-key" --httpsig-headers "method authority content-type:" $URL +--- + +# `--httpsig-headers` + +Space-separated list of components to include in the RFC 9421 HTTP Message +Signature. Derived components are given as bare names: `method`, `authority`, +`path`, and `query`. HTTP header fields are given with a trailing colon, for +example `content-type:` and `content-digest:`. + +If not specified, the default set is `method authority path` (plus `query` +when a query string is present in the URL). + +## Signing request headers + +Header components are taken from `-H` / `--header` options only. Headers curl +adds by default (such as `User-Agent`) are not signed unless you set them +explicitly, for example: + + curl --httpsig-algo ed25519 \ + --httpsig-key k.hex \ + --httpsig-keyid mykey \ + -H "User-Agent: MyApp/1.0" \ + --httpsig-headers \ + "method authority path user-agent:" \ + $URL + +Each component may appear only once. Duplicate identifiers in +`--httpsig-headers` cause curl to exit with an error. diff --git a/docs/cmdline-opts/httpsig-key.md b/docs/cmdline-opts/httpsig-key.md new file mode 100644 index 000000000000..a2d8f611d849 --- /dev/null +++ b/docs/cmdline-opts/httpsig-key.md @@ -0,0 +1,35 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: httpsig-key +Protocols: HTTP +Arg: +Help: Key file for HTTP Message Signatures +Category: auth http +Added: 8.22.0 +Multi: single +Experimental: yes +See-also: + - httpsig-algo + - httpsig-keyid +Example: + - --httpsig-algo ed25519 --httpsig-key key.hex --httpsig-keyid "my-key" $URL +--- + +# `--httpsig-key` + +Path to the key file used for RFC 9421 HTTP Message Signatures. + +The file must contain a hex-encoded key on its first line. For **ed25519**, +this is the 32-byte private seed (64 hex characters). For **hmac-sha256**, +this is the shared secret. PEM files are not supported. + +## Generating Ed25519 keys + +With OpenSSL 3: + + openssl genpkey -algorithm ED25519 -out k.pem + openssl pkey -in k.pem -outform RAW -out k.raw + xxd -p -c 64 k.raw | tr -d '\n' > k.hex + +Use `k.hex` with `--httpsig-key`. diff --git a/docs/cmdline-opts/httpsig-keyid.md b/docs/cmdline-opts/httpsig-keyid.md new file mode 100644 index 000000000000..2dc305fcfbd5 --- /dev/null +++ b/docs/cmdline-opts/httpsig-keyid.md @@ -0,0 +1,23 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: httpsig-keyid +Protocols: HTTP +Arg: +Help: Key identifier for HTTP Message Signatures +Category: auth http +Added: 8.22.0 +Multi: single +Experimental: yes +See-also: + - httpsig-algo + - httpsig-key +Example: + - --httpsig-algo ed25519 --httpsig-key key.hex --httpsig-keyid "my-key" $URL +--- + +# `--httpsig-keyid` + +The key identifier to include in the `Signature-Input` header when using RFC +9421 HTTP Message Signatures. This value appears as the `keyid` parameter and +allows the server to look up the correct verification key. diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 44d2132577b0..23eda3520333 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -474,6 +474,22 @@ See CURLOPT_HTTPPOST(3) Tunnel through the HTTP proxy. CURLOPT_HTTPPROXYTUNNEL(3) +## CURLOPT_HTTPSIG_ALGORITHM + +RFC 9421 HTTP Message Signatures algorithm. See CURLOPT_HTTPSIG_ALGORITHM(3) + +## CURLOPT_HTTPSIG_HEADERS + +Components to sign for HTTP Message Signatures. See CURLOPT_HTTPSIG_HEADERS(3) + +## CURLOPT_HTTPSIG_KEY + +Hex-encoded key for HTTP Message Signatures. See CURLOPT_HTTPSIG_KEY(3) + +## CURLOPT_HTTPSIG_KEYID + +Key identifier for HTTP Message Signatures. See CURLOPT_HTTPSIG_KEYID(3) + ## CURLOPT_HTTP_CONTENT_DECODING Disable Content decoding. See CURLOPT_HTTP_CONTENT_DECODING(3) diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index 5b1822216995..6b1d61121b38 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -232,6 +232,13 @@ HTTP/3 and QUIC support are built-in (Added in 7.66.0) libcurl was built with support for HTTPS-proxy. +## `HTTPSIG` + +*features* mask bit: non-existent + +libcurl was built with support for RFC 9421 HTTP Message Signatures (Added in +8.22.0) + ## `HTTPSRR` *features* mask bit: non-existent diff --git a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md index 692178c72e53..925033281f12 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md +++ b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md @@ -118,6 +118,11 @@ single auth algorithm is acceptable. provides AWS V4 signature authentication on HTTPS header see CURLOPT_AWS_SIGV4(3). +## CURLAUTH_HTTPSIG + +provides RFC 9421 HTTP Message Signatures on outgoing requests, +see CURLOPT_HTTPSIG_ALGORITHM(3). + # DEFAULT CURLAUTH_BASIC @@ -158,6 +163,8 @@ CURLAUTH_AWS_SIGV4 was added in 7.74.0 CURLAUTH_DIGEST_IE does nothing since 8.21.0 +CURLAUTH_HTTPSIG was added in 8.22.0 + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md new file mode 100644 index 000000000000..2501ec33eb01 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md @@ -0,0 +1,89 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_HTTPSIG_ALGORITHM +Section: 3 +Source: libcurl +See-also: + - CURLOPT_HTTPSIG_HEADERS (3) + - CURLOPT_HTTPSIG_KEY (3) + - CURLOPT_HTTPSIG_KEYID (3) + - CURLOPT_HTTPAUTH (3) +Protocol: + - HTTP +Added-in: 8.22.0 +--- + +# NAME + +CURLOPT_HTTPSIG_ALGORITHM - RFC 9421 HTTP Message Signatures algorithm + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPSIG_ALGORITHM, + long algorithm); +~~~ + +# DESCRIPTION + +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. + +Enable RFC 9421 HTTP Message Signatures on outgoing requests. Pass a long +set to one of the values below to select the signing algorithm. + +## CURLHTTPSIG_NONE (0) + +Disable HTTP Message Signatures. + +## CURLHTTPSIG_ED25519 (1) + +Sign with Ed25519 (RFC 8032). Requires a TLS backend with Ed25519 support. + +## CURLHTTPSIG_HMAC_SHA256 (2) + +Sign with HMAC-SHA256. + +## + +Setting this option to a non-zero value also sets CURLOPT_HTTPAUTH(3) to +CURLAUTH_HTTPSIG. The options CURLOPT_HTTPSIG_KEY(3) and +CURLOPT_HTTPSIG_KEYID(3) must also be set. + +# DEFAULT + +CURLHTTPSIG_NONE (0) + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, + CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "my-key-id"); + curl_easy_perform(curl); + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md new file mode 100644 index 000000000000..2e2cc4f77ea4 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md @@ -0,0 +1,108 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_HTTPSIG_HEADERS +Section: 3 +Source: libcurl +See-also: + - CURLOPT_HTTPSIG_ALGORITHM (3) + - CURLOPT_HTTPSIG_KEY (3) + - CURLOPT_HTTPSIG_KEYID (3) +Protocol: + - HTTP +Added-in: 8.22.0 +--- + +# NAME + +CURLOPT_HTTPSIG_HEADERS - components to sign for HTTP Message Signatures + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPSIG_HEADERS, + char *components); +~~~ + +# DESCRIPTION + +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. + +Pass a space-separated list of component identifiers to include in the +RFC 9421 HTTP Message Signature. + +Derived components are given as bare names: + +- **method** - the HTTP method (GET, POST, etc.) +- **authority** - the host and optional port +- **path** - the request path +- **query** - the query string including the leading `?` + +HTTP header fields are given with a trailing colon, for example `content-type:` +or `content-digest:`. This mirrors how a header looks and keeps a leading `@` +free for its usual curl meaning (read the value from a file). + +If this option is not set, the default components are **method**, **authority**, +**path** (plus **query** when a query string is present). + +## Signing request headers + +Header components are resolved from the list set with CURLOPT_HTTPHEADER(3) +only. Headers that libcurl adds later (such as the default `User-Agent`) are +**not** visible to the signer unless the application supplies them explicitly. + +To sign `User-Agent`, supply it via CURLOPT_HTTPHEADER(3) together with this +option before the transfer; see EXAMPLE. + +Each component identifier may appear at most once (RFC 9421 Section 2). +Listing the same component twice returns `CURLE_BAD_FUNCTION_ARGUMENT`. + +At most 16 components are accepted; supplying more returns +`CURLE_BAD_FUNCTION_ARGUMENT`. + +The application does not have to keep the string around after setting this +option. + +# DEFAULT + +NULL (uses the default component set) + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + + if(curl) { + headers = curl_slist_append(headers, "User-Agent: MyApp/1.0"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, + CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "my-key-id"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_HEADERS, + "method authority path content-type: user-agent:"); + curl_easy_perform(curl); + curl_slist_free_all(headers); + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md new file mode 100644 index 000000000000..e81378f9cc88 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md @@ -0,0 +1,90 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_HTTPSIG_KEY +Section: 3 +Source: libcurl +See-also: + - CURLOPT_HTTPSIG_ALGORITHM (3) + - CURLOPT_HTTPSIG_KEYID (3) +Protocol: + - HTTP +Added-in: 8.22.0 +--- + +# NAME + +CURLOPT_HTTPSIG_KEY - hex-encoded key for HTTP Message Signatures + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPSIG_KEY, char *key); +~~~ + +# DESCRIPTION + +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. + +Pass a null-terminated string containing the hex-encoded private key or +shared secret used for RFC 9421 HTTP Message Signatures. + +For **ed25519**, this is the 32-byte private seed (64 hex characters). For +**hmac-sha256**, this is the shared secret as hex; the decoded length is half +the number of hex digits, up to `CURL_MAX_INPUT_LENGTH / 2` bytes (the same +upper bound as other libcurl string options). + +PEM and other encodings are not supported; pass the raw key material as hex. + +## Generating Ed25519 keys + +With OpenSSL 3: + + openssl genpkey -algorithm ED25519 -out ed25519.pem + openssl pkey -in ed25519.pem -outform RAW | xxd -p -c 64 | tr -d '\n' > key.hex + +The `key.hex` file is one line of 64 hexadecimal digits. + +The application does not have to keep the string around after setting this +option. + +Using this option multiple times makes the last set string override the +previous ones. Set it to NULL to disable its use again. + +# DEFAULT + +NULL + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, + CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "my-key-id"); + curl_easy_perform(curl); + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md new file mode 100644 index 000000000000..d921db89a4ba --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md @@ -0,0 +1,76 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_HTTPSIG_KEYID +Section: 3 +Source: libcurl +See-also: + - CURLOPT_HTTPSIG_ALGORITHM (3) + - CURLOPT_HTTPSIG_KEY (3) +Protocol: + - HTTP +Added-in: 8.22.0 +--- + +# NAME + +CURLOPT_HTTPSIG_KEYID - key identifier for HTTP Message Signatures + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPSIG_KEYID, char *keyid); +~~~ + +# DESCRIPTION + +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. + +Pass a null-terminated string that identifies the key used for RFC 9421 HTTP +Message Signatures. This value is included as the `keyid` parameter in the +`Signature-Input` header, allowing the server to look up the corresponding +verification key. + +The application does not have to keep the string around after setting this +option. + +Using this option multiple times makes the last set string override the +previous ones. Set it to NULL to disable its use again. + +# DEFAULT + +NULL + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, + CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "my-key-id"); + curl_easy_perform(curl); + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 1eb628b800a4..f0dc077a9423 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -227,6 +227,10 @@ man_MANS = \ CURLOPT_HTTPHEADER.3 \ CURLOPT_HTTPPOST.3 \ CURLOPT_HTTPPROXYTUNNEL.3 \ + CURLOPT_HTTPSIG_ALGORITHM.3 \ + CURLOPT_HTTPSIG_HEADERS.3 \ + CURLOPT_HTTPSIG_KEY.3 \ + CURLOPT_HTTPSIG_KEYID.3 \ CURLOPT_IGNORE_CONTENT_LENGTH.3 \ CURLOPT_INFILESIZE.3 \ CURLOPT_INFILESIZE_LARGE.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index faa3b5494696..a3d92a2637d8 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -202,6 +202,7 @@ CURLALTSVC_READONLYFILE 7.64.1 CURLAUTH_ANY 7.10.6 CURLAUTH_ANYSAFE 7.10.6 CURLAUTH_AWS_SIGV4 7.75.0 +CURLAUTH_HTTPSIG 8.22.0 CURLAUTH_BASIC 7.10.6 CURLAUTH_BEARER 7.61.0 CURLAUTH_DIGEST 7.10.6 @@ -420,6 +421,9 @@ CURLHEADER_SEPARATE 7.37.0 CURLHEADER_UNIFIED 7.37.0 CURLHSTS_ENABLE 7.74.0 CURLHSTS_READONLYFILE 7.74.0 +CURLHTTPSIG_ED25519 8.22.0 +CURLHTTPSIG_HMAC_SHA256 8.22.0 +CURLHTTPSIG_NONE 8.22.0 CURLINFO_ACTIVESOCKET 7.45.0 CURLINFO_APPCONNECT_TIME 7.19.0 CURLINFO_APPCONNECT_TIME_T 7.61.0 @@ -697,6 +701,10 @@ CURLOPT_HTTPHEADER 7.1 CURLOPT_HTTPPOST 7.1 7.56.0 CURLOPT_HTTPPROXYTUNNEL 7.3 CURLOPT_HTTPREQUEST 7.1 - 7.15.5 +CURLOPT_HTTPSIG_ALGORITHM 8.22.0 +CURLOPT_HTTPSIG_HEADERS 8.22.0 +CURLOPT_HTTPSIG_KEY 8.22.0 +CURLOPT_HTTPSIG_KEYID 8.22.0 CURLOPT_IGNORE_CONTENT_LENGTH 7.14.1 CURLOPT_INFILE 7.1 7.9.7 CURLOPT_INFILESIZE 7.1 diff --git a/docs/options-in-versions b/docs/options-in-versions index fa20b2dd29b2..595cb05caab5 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -98,6 +98,10 @@ --http2-prior-knowledge 7.49.0 --http3 7.66.0 --http3-only 7.88.0 +--httpsig-algo 8.22.0 +--httpsig-headers 8.22.0 +--httpsig-key 8.22.0 +--httpsig-keyid 8.22.0 --ignore-content-length 7.14.1 --ip-tos 8.9.0 --ipfs-gateway 8.4.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 326804eb1851..0797c92cad07 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -843,12 +843,20 @@ typedef enum { #endif #define CURLAUTH_BEARER (((unsigned long)1) << 6) #define CURLAUTH_AWS_SIGV4 (((unsigned long)1) << 7) +#define CURLAUTH_HTTPSIG (((unsigned long)1) << 8) #define CURLAUTH_ONLY (((unsigned long)1) << 31) -#define CURLAUTH_ANY ((~CURLAUTH_DIGEST_IE) & \ +#define CURLAUTH_ANY ((~(CURLAUTH_DIGEST_IE | \ + CURLAUTH_HTTPSIG)) & \ ((unsigned long)0xffffffff)) -#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) & \ +#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE | \ + CURLAUTH_HTTPSIG)) & \ ((unsigned long)0xffffffff)) +/* constants for CURLOPT_HTTPSIG_ALGORITHM */ +#define CURLHTTPSIG_NONE 0L +#define CURLHTTPSIG_ED25519 1L +#define CURLHTTPSIG_HMAC_SHA256 2L + /* all types supported by server */ #define CURLSSH_AUTH_ANY ((unsigned long)0xffffffff) #define CURLSSH_AUTH_NONE 0L /* none allowed, silly but complete */ @@ -2266,6 +2274,18 @@ typedef enum { /* set TLS supported signature algorithms */ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + /* RFC 9421 HTTP Message Signatures algorithm */ + CURLOPT(CURLOPT_HTTPSIG_ALGORITHM, CURLOPTTYPE_VALUES, 329), + + /* Hex-encoded key for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_KEY, CURLOPTTYPE_STRINGPOINT, 330), + + /* Key identifier for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_KEYID, CURLOPTTYPE_STRINGPOINT, 331), + + /* Space-separated list of components to sign for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_HEADERS, CURLOPTTYPE_STRINGPOINT, 332), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index d600c73cdcbd..c70bd17cb84e 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -496,6 +496,9 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_USERAGENT || \ (option) == CURLOPT_USERNAME || \ (option) == CURLOPT_AWS_SIGV4 || \ + (option) == CURLOPT_HTTPSIG_HEADERS || \ + (option) == CURLOPT_HTTPSIG_KEY || \ + (option) == CURLOPT_HTTPSIG_KEYID || \ (option) == CURLOPT_USERPWD || \ (option) == CURLOPT_XOAUTH2_BEARER || \ 0) diff --git a/lib/Makefile.inc b/lib/Makefile.inc index aff75e28a54d..e00e599d6ccc 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -222,6 +222,8 @@ LIB_CFILES = \ http1.c \ http2.c \ http_aws_sigv4.c \ + http_httpsig.c \ + curl_ed25519.c \ http_chunks.c \ http_digest.c \ http_negotiate.c \ @@ -363,6 +365,8 @@ LIB_HFILES = \ http1.h \ http2.h \ http_aws_sigv4.h \ + http_httpsig.h \ + curl_ed25519.h \ http_chunks.h \ http_digest.h \ http_negotiate.h \ diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 97d8f54f5ac7..ffcce8273c8e 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -61,6 +61,9 @@ /* disables aws-sigv4 */ #cmakedefine CURL_DISABLE_AWS 1 +/* disables HTTP Message Signatures (RFC 9421) */ +#cmakedefine CURL_DISABLE_HTTPSIG 1 + /* disables DICT */ #cmakedefine CURL_DISABLE_DICT 1 diff --git a/lib/curl_ed25519.c b/lib/curl_ed25519.c new file mode 100644 index 000000000000..318c70ed2110 --- /dev/null +++ b/lib/curl_ed25519.c @@ -0,0 +1,170 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HTTPSIG) + +/* Please keep the SSL backend-specific #if branches in this order: + * + * 1. USE_OPENSSL + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS + */ + +#include "curl_ed25519.h" + +#ifdef USE_OPENSSL +#include + +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen) +{ + EVP_PKEY *pkey; + EVP_MD_CTX *mdctx; + size_t slen; + int rc; + + if(keylen != CURL_ED25519_KEYLEN) + return CURLE_BAD_FUNCTION_ARGUMENT; + + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, key, keylen); + if(!pkey) + return CURLE_AUTH_ERROR; + + mdctx = EVP_MD_CTX_new(); + if(!mdctx) { + EVP_PKEY_free(pkey); + return CURLE_OUT_OF_MEMORY; + } + + rc = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey); + if(rc != 1) { + EVP_MD_CTX_free(mdctx); + EVP_PKEY_free(pkey); + return CURLE_AUTH_ERROR; + } + + slen = CURL_ED25519_SIGLEN; + rc = EVP_DigestSign(mdctx, sig, &slen, msg, msglen); + + EVP_MD_CTX_free(mdctx); + EVP_PKEY_free(pkey); + + if(rc != 1) + return CURLE_AUTH_ERROR; + + *siglen = slen; + return CURLE_OK; +} + +#elif defined(USE_WOLFSSL) +#include +#include +#if (defined(HAVE_ED25519) || defined(WOLFSSL_CURVE25519_USE_ED25519)) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_SIGN) +#include +#include +#include + +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen) +{ + int ret; + WC_RNG rng; + ed25519_key edkey; + word32 outlen; + unsigned char pubkey[ED25519_PUB_KEY_SIZE]; + + if(keylen != ED25519_KEY_SIZE) + return CURLE_BAD_FUNCTION_ARGUMENT; + + ret = wc_InitRng(&rng); + if(ret) + return CURLE_AUTH_ERROR; + + ret = wc_ed25519_init(&edkey); + if(ret) { + wc_FreeRng(&rng); + return CURLE_AUTH_ERROR; + } + + ret = wc_ed25519_import_private_only(key, ED25519_KEY_SIZE, &edkey); + if(ret) + goto fail; + + ret = wc_ed25519_make_public(&edkey, pubkey, ED25519_PUB_KEY_SIZE); + if(ret) + goto fail; + + ret = wc_ed25519_import_private_key(key, ED25519_KEY_SIZE, + pubkey, ED25519_PUB_KEY_SIZE, &edkey); + if(ret) + goto fail; + + outlen = ED25519_SIG_SIZE; + ret = wc_ed25519_sign_msg(msg, (word32)msglen, sig, &outlen, &edkey); + if(ret) + goto fail; + + *siglen = (size_t)outlen; + wc_ed25519_free(&edkey); + wc_FreeRng(&rng); + return CURLE_OK; + +fail: + wc_ed25519_free(&edkey); + wc_FreeRng(&rng); + return CURLE_AUTH_ERROR; +} + +#else /* USE_WOLFSSL but no Ed25519 sign/import in this wolfSSL build */ + +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen) +{ + (void)key; (void)keylen; (void)msg; (void)msglen; + (void)sig; (void)siglen; + return CURLE_NOT_BUILT_IN; +} + +#endif /* wolfSSL Ed25519 */ + +#else /* no Ed25519-capable backend */ + +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen) +{ + (void)key; (void)keylen; (void)msg; (void)msglen; + (void)sig; (void)siglen; + return CURLE_NOT_BUILT_IN; +} + +#endif /* Ed25519 backends */ + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_HTTPSIG */ diff --git a/lib/curl_ed25519.h b/lib/curl_ed25519.h new file mode 100644 index 000000000000..381b6511eba2 --- /dev/null +++ b/lib/curl_ed25519.h @@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_ED25519_H +#define HEADER_CURL_ED25519_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HTTPSIG) + +#define CURL_ED25519_SIGLEN 64 +#define CURL_ED25519_KEYLEN 32 + +/* Sign with Ed25519 (RFC 8032). + * key/keylen: raw 32-byte private seed + * msg/msglen: data to sign + * sig: output buffer (at least CURL_ED25519_SIGLEN bytes) + * siglen: out - actual signature length on success + * Returns CURLE_OK or CURLE_NOT_BUILT_IN if no backend supports Ed25519. */ +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen); + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_HTTPSIG */ +#endif /* HEADER_CURL_ED25519_H */ diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index d9a697a0a595..4fd8ec47bbe3 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -26,6 +26,7 @@ #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_HTTPSIG) || \ defined(USE_LIBSSH2) || defined(USE_SSL) #define HMAC_MD5_LENGTH 16 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index c8743108bdec..a91b912f3e22 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -298,6 +298,9 @@ # ifndef CURL_DISABLE_AWS # define CURL_DISABLE_AWS # endif +# ifndef CURL_DISABLE_HTTPSIG +# define CURL_DISABLE_HTTPSIG +# endif # ifndef CURL_DISABLE_DOH # define CURL_DISABLE_DOH # endif diff --git a/lib/curl_sha256.h b/lib/curl_sha256.h index 6c48a8e1764c..fb823169f7fe 100644 --- a/lib/curl_sha256.h +++ b/lib/curl_sha256.h @@ -26,7 +26,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_HTTPSIG) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) #include "curl_hmac.h" diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 4c0b4f0ce4ad..1643ec32568b 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -140,6 +140,10 @@ const struct curl_easyoption Curl_easyopts[] = { { "HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0 }, { "HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0 }, { "HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0 }, + { "HTTPSIG_ALGORITHM", CURLOPT_HTTPSIG_ALGORITHM, CURLOT_VALUES, 0 }, + { "HTTPSIG_HEADERS", CURLOPT_HTTPSIG_HEADERS, CURLOT_STRING, 0 }, + { "HTTPSIG_KEY", CURLOPT_HTTPSIG_KEY, CURLOT_STRING, 0 }, + { "HTTPSIG_KEYID", CURLOPT_HTTPSIG_KEYID, CURLOT_STRING, 0 }, { "HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0 }, { "HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CURLOT_LONG, 0 }, @@ -385,6 +389,6 @@ const struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return (CURLOPT_LASTENTRY % 10000) != (328 + 1); + return (CURLOPT_LASTENTRY % 10000) != (332 + 1); } #endif diff --git a/lib/http.c b/lib/http.c index 91dcdb9716c7..d8bac2e5ccf4 100644 --- a/lib/http.c +++ b/lib/http.c @@ -61,6 +61,7 @@ #include "http_ntlm.h" #include "http_negotiate.h" #include "http_aws_sigv4.h" +#include "http_httpsig.h" #include "url.h" #include "urlapi-int.h" #include "curl_share.h" @@ -376,6 +377,10 @@ static bool pickoneauth(struct auth *pick, unsigned long mask, #ifndef CURL_DISABLE_AWS else if(avail & CURLAUTH_AWS_SIGV4) pick->picked = CURLAUTH_AWS_SIGV4; +#endif +#ifndef CURL_DISABLE_HTTPSIG + else if(avail & CURLAUTH_HTTPSIG) + pick->picked = CURLAUTH_HTTPSIG; #endif else { pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ @@ -665,6 +670,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data, } else #endif +#ifndef CURL_DISABLE_HTTPSIG + if((authstatus->picked == CURLAUTH_HTTPSIG) && !proxy) { + auth = "HTTPSIG"; + result = Curl_output_httpsig(data); + if(result) + return result; + } + else +#endif #ifdef USE_SPNEGO if(authstatus->picked == CURLAUTH_NEGOTIATE) { auth = "Negotiate"; @@ -792,6 +806,9 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, #ifdef USE_SPNEGO !(authhost->want & CURLAUTH_NEGOTIATE) && !(authproxy->want & CURLAUTH_NEGOTIATE) && +#endif +#ifndef CURL_DISABLE_HTTPSIG + !(authhost->want & CURLAUTH_HTTPSIG) && #endif !data->state.creds) { /* no authentication with no user or password */ diff --git a/lib/http_httpsig.c b/lib/http_httpsig.c new file mode 100644 index 000000000000..bcf796900f6c --- /dev/null +++ b/lib/http_httpsig.c @@ -0,0 +1,692 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HTTPSIG) + +#include "urldata.h" +#include "http_httpsig.h" +#include "curl_ed25519.h" +#include "curl_hmac.h" +#include "curl_sha256.h" +#include "http.h" +#include "transfer.h" +#include "curl_trc.h" +#include "slist.h" +#include "curlx/dynbuf.h" +#include "curlx/base64.h" +#include "curlx/strdup.h" +#include "curlx/strparse.h" +#include "strcase.h" + +#include + +#define HTTPSIG_MAX_SIG_BASE CURL_MAX_HTTP_HEADER +#define HTTPSIG_MAX_COMPONENTS 16 +#define HTTPSIG_MAX_RAW_SIG CURL_ED25519_SIGLEN +#define HTTPSIG_DEFAULT_LABEL "sig1" + +enum httpsig_alg { + HTTPSIG_ALG_ED25519, + HTTPSIG_ALG_HMAC_SHA256, + HTTPSIG_ALG_UNKNOWN +}; + +static const char *alg_to_str(enum httpsig_alg alg) +{ + switch(alg) { + case HTTPSIG_ALG_ED25519: + return "ed25519"; + case HTTPSIG_ALG_HMAC_SHA256: + return "hmac-sha256"; + default: + break; + } + return NULL; +} + +static enum httpsig_alg id_to_alg(uint8_t val) +{ + switch(val) { + case CURLHTTPSIG_ED25519: + return HTTPSIG_ALG_ED25519; + case CURLHTTPSIG_HMAC_SHA256: + return HTTPSIG_ALG_HMAC_SHA256; + default: + break; + } + return HTTPSIG_ALG_UNKNOWN; +} + +static CURLcode decode_hex_key(struct Curl_easy *data, + const char *hexstr, + unsigned char **keyout, + size_t *keylen) +{ + size_t len, i; + unsigned char *keybuf; + + *keyout = NULL; + *keylen = 0; + + len = strlen(hexstr); + while(len > 0 && ISNEWLINE(hexstr[len - 1])) + len--; + + if(len == 0 || (len & 1) != 0) { + failf(data, "httpsig: invalid hex key (length %zu)", len); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(len > CURL_MAX_INPUT_LENGTH) { + failf(data, "httpsig: hex key too long"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + keybuf = curlx_malloc(len / 2); + if(!keybuf) + return CURLE_OUT_OF_MEMORY; + + for(i = 0; i < len; i += 2) { + if(!ISXDIGIT(hexstr[i]) || !ISXDIGIT(hexstr[i + 1])) { + failf(data, "httpsig: invalid hex at position %zu ('%c%c')", + i, hexstr[i], hexstr[i + 1]); + curlx_free(keybuf); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + keybuf[i / 2] = (unsigned char)((curlx_hexval(hexstr[i]) << 4) | + curlx_hexval(hexstr[i + 1])); + } + + *keyout = keybuf; + *keylen = len / 2; + return CURLE_OK; +} + +/* @authority matches the Host header field-value when available (RFC 9421). + data->state.aptr.host is produced by http_set_aptr_host() before auth. */ +static CURLcode httpsig_authority(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *authority_buf) +{ + const char *h = data->state.aptr.host; + + if(h && curl_strnequal(h, "host:", 5)) { + const char *value = h + 5; + const char *end; + + while(ISBLANK(*value)) + value++; + if(*value) { + CURLcode result; + + end = value; + while(*end && !ISNEWLINE(*end)) + end++; + while(end > value && ISBLANK(end[-1])) + end--; + result = curlx_dyn_addn(authority_buf, value, (size_t)(end - value)); + if(result) + return result; + return CURLE_OK; + } + } + + { + const char *hostname = conn->origin->hostname; + uint16_t port = conn->origin->port; + + if((conn->given->defport != port) && port) + return curlx_dyn_addf(authority_buf, "%s:%u", hostname, port); + return curlx_dyn_add(authority_buf, hostname); + } +} + +static CURLcode sf_append_quoted(struct dynbuf *buf, const char *str) +{ + CURLcode result = curlx_dyn_addn(buf, "\"", 1); + if(result) + return result; + while(*str) { + if(ISCNTRL(*str)) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(*str == '\\' || *str == '"') { + result = curlx_dyn_addn(buf, "\\", 1); + if(result) + return result; + } + result = curlx_dyn_addn(buf, str, 1); + if(result) + return result; + str++; + } + return curlx_dyn_addn(buf, "\"", 1); +} + +/* base64-encode raw bytes into an RFC 8941 byte sequence (:base64:) */ +static CURLcode sf_encode_byte_seq(const unsigned char *raw, size_t rawlen, + struct dynbuf *out) +{ + CURLcode result; + size_t b64len; + char *b64; + + result = curlx_base64_encode(raw, rawlen, &b64, &b64len); + if(result) + return result; + + result = curlx_dyn_addn(out, ":", 1); + if(!result) + result = curlx_dyn_addn(out, b64, b64len); + if(!result) + result = curlx_dyn_addn(out, ":", 1); + + curlx_free(b64); + return result; +} + +static CURLcode build_sig_params(struct dynbuf *params, + const char **components, size_t count, + time_t created, const char *keyid, + enum httpsig_alg alg) +{ + CURLcode result; + size_t i; + + result = curlx_dyn_addn(params, "(", 1); + if(result) + return result; + + for(i = 0; i < count; i++) { + if(i > 0) { + result = curlx_dyn_addn(params, " ", 1); + if(result) + return result; + } + result = sf_append_quoted(params, components[i]); + if(result) + return result; + } + + result = curlx_dyn_addn(params, ")", 1); + if(result) + return result; + + result = curlx_dyn_addf(params, ";created=%lld", (long long)created); + if(result) + return result; + + if(keyid && *keyid) { + result = curlx_dyn_add(params, ";keyid="); + if(result) + return result; + result = sf_append_quoted(params, keyid); + if(result) + return result; + } + + result = curlx_dyn_addf(params, ";alg=\"%s\"", alg_to_str(alg)); + return result; +} + +/* strings defined by RFC 9421 */ +#define SIG_METHOD "@method" +#define SIG_AUTHORITY "@authority" +#define SIG_PATH "@path" +#define SIG_QUERY "@query" + +/* Resolve a component identifier to its value. + * For headers, we walk the full user-supplied header list to combine + * duplicate field values with ", " per RFC 9421 Section 2.1. Each + * individual value is trimmed of leading/trailing OWS and the trailing + * \r\n. The combined result is written into the caller-provided buffer. */ +static CURLcode resolve_component(const char *name, + const char *method, + const char *authority, + const char *path, + const char *query, + struct Curl_easy *data, + struct dynbuf *valbuf, + const char **out) +{ + *out = NULL; + + if(name[0] == '@') { + if(curl_strequal(name, SIG_METHOD)) + *out = method; + else if(curl_strequal(name, SIG_AUTHORITY)) + *out = authority; + else if(curl_strequal(name, SIG_PATH)) + *out = path; + else if(curl_strequal(name, SIG_QUERY)) + *out = query; + else { + failf(data, "httpsig: unsupported derived component '%s'", name); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!*out) { + failf(data, "httpsig: derived component '%s' has no value", name); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + return CURLE_OK; + } + else { + /* RFC 9421 Section 2.1: walk all user-supplied headers and combine + duplicate field values with ", " per HTTP field combination rules. */ + struct curl_slist *head; + size_t namelen = strlen(name); + bool found = FALSE; + + curlx_dyn_reset(valbuf); + + for(head = data->set.headers; head; head = head->next) { + if(curl_strnequal(head->data, name, namelen) && + Curl_headersep(head->data[namelen])) { + const char *p = strchr(head->data, ':'); + if(p) { + CURLcode result; + struct Curl_str content; + curlx_str_assign(&content, p + 1, strlen(p + 1)); + curlx_str_trimblanks(&content); + if(found) { + result = curlx_dyn_addn(valbuf, ", ", 2); + if(result) + return result; + } + result = curlx_dyn_addn(valbuf, curlx_str(&content), + curlx_strlen(&content)); + if(result) + return result; + found = TRUE; + } + } + } + + if(found) { + *out = curlx_dyn_ptr(valbuf); + return CURLE_OK; + } + failf(data, "httpsig: header '%s' not found in request", name); + return CURLE_BAD_FUNCTION_ARGUMENT; + } +} + +static CURLcode build_sig_base(struct dynbuf *base, + const char **components, size_t count, + const char *method, + const char *authority, + const char *path, + const char *query, + struct Curl_easy *data, + const char *sig_params) +{ + CURLcode result; + size_t i; + struct dynbuf hdrvalbuf; + + curlx_dyn_init(&hdrvalbuf, CURL_MAX_HTTP_HEADER); + + for(i = 0; i < count; i++) { + const char *val = NULL; + result = resolve_component(components[i], method, + authority, path, query, data, + &hdrvalbuf, &val); + if(result || !val) { + failf(data, "httpsig: cannot resolve component '%s'", components[i]); + curlx_dyn_free(&hdrvalbuf); + return result ? result : CURLE_BAD_FUNCTION_ARGUMENT; + } + + result = curlx_dyn_addf(base, "\"%s\": %s\n", components[i], val); + if(result) { + curlx_dyn_free(&hdrvalbuf); + return result; + } + } + + curlx_dyn_free(&hdrvalbuf); + result = curlx_dyn_addf(base, "\"@signature-params\": %s", sig_params); + return result; +} + +static CURLcode parse_components(struct Curl_easy *data, + const char *query, + const char **components, + size_t *ncomp_out, + char **hdrs_copy_out) +{ + const char *hdrs = data->set.str[STRING_HTTPSIG_HEADERS]; + size_t ncomp = 0; + + *hdrs_copy_out = NULL; + if(hdrs && *hdrs) { + char *p; + char *hdrs_copy = curlx_strdup(hdrs); + if(!hdrs_copy) + return CURLE_OUT_OF_MEMORY; + *hdrs_copy_out = hdrs_copy; + p = hdrs_copy; + while(*p && ncomp < HTTPSIG_MAX_COMPONENTS) { + char *start; + size_t tlen = 0; + const char *p2 = p; + + curlx_str_passblanks(&p2); + if(!*p2) + break; + p = start = CURL_UNCONST(p2); + while(*p && !ISBLANK(*p)) { + if((*p == '\"') || (*p == '\\')) + return CURLE_BAD_FUNCTION_ARGUMENT; + p++; + tlen++; + } + if(*p) + *p++ = '\0'; + + if(tlen && start[tlen - 1] == ':') { + /* Header field: drop the trailing ':' marker. RFC 9421 field + names are canonically lowercase (Section 2.1). */ + start[--tlen] = '\0'; + if(!tlen) { + failf(data, "httpsig: empty header component name"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + Curl_strntolower(start, start, tlen); + components[ncomp++] = start; + } + else { + /* Derived component: map the bare name to its canonical RFC 9421 + '@'-prefixed identifier (Section 2.2). */ + Curl_strntolower(start, start, tlen); + if(!strcmp(start, "method")) + components[ncomp++] = SIG_METHOD; + else if(!strcmp(start, "authority")) + components[ncomp++] = SIG_AUTHORITY; + else if(!strcmp(start, "path")) + components[ncomp++] = SIG_PATH; + else if(!strcmp(start, "query")) + components[ncomp++] = SIG_QUERY; + else { + failf(data, "httpsig: unknown component '%s'", start); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + } + if(!ncomp) { + failf(data, "httpsig: no signature components specified"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(*p) { + failf(data, "httpsig: too many signature components (max %u)", + (unsigned int)HTTPSIG_MAX_COMPONENTS); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + /* RFC 9421 Section 2: each covered component MUST occur only once */ + { + size_t i, j; + + for(i = 0; i < ncomp; i++) { + for(j = i + 1; j < ncomp; j++) { + if(!strcmp(components[i], components[j])) { + failf(data, "httpsig: duplicate signature component '%s'", + components[i]); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + } + } + } + else { + components[ncomp++] = SIG_METHOD; + components[ncomp++] = SIG_AUTHORITY; + components[ncomp++] = SIG_PATH; + if(query) + components[ncomp++] = SIG_QUERY; + } + + *ncomp_out = ncomp; + return CURLE_OK; +} + +static time_t httpsig_get_created(void) +{ +#ifdef DEBUGBUILD + char *force = getenv("CURL_FORCETIME"); + if(force && *force) { + char *sigts = getenv("CURL_HTTPSIG_CREATED"); + if(sigts && *sigts) { + const char *p = sigts; + curl_off_t num; + if(!curlx_str_number(&p, &num, CURL_OFF_T_MAX)) + return (time_t)num; + } + return 0; + } +#endif + return time(NULL); +} + +static CURLcode httpsig_sign_base(struct Curl_easy *data, + enum httpsig_alg alg, + const unsigned char *keybuf, + size_t keylen, + const struct dynbuf *sig_base, + unsigned char *raw_sig, + size_t *raw_sig_len) +{ + CURLcode result; + + switch(alg) { + case HTTPSIG_ALG_ED25519: + result = Curl_ed25519_sign( + keybuf, keylen, + (const unsigned char *)curlx_dyn_ptr(sig_base), + curlx_dyn_len(sig_base), + raw_sig, raw_sig_len); + break; + case HTTPSIG_ALG_HMAC_SHA256: + result = Curl_hmacit(&Curl_HMAC_SHA256, keybuf, keylen, + (const unsigned char *)curlx_dyn_ptr(sig_base), + curlx_dyn_len(sig_base), raw_sig); + if(!result) + *raw_sig_len = CURL_SHA256_DIGEST_LENGTH; + break; + default: + result = CURLE_BAD_FUNCTION_ARGUMENT; + break; + } + + if(result && result == CURLE_NOT_BUILT_IN) { + failf(data, "httpsig: algorithm '%s' not supported by TLS backend", + alg_to_str(alg)); + } + return result; +} + +CURLcode Curl_output_httpsig(struct Curl_easy *data) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + struct connectdata *conn = data->conn; + const char *path; + const char *query; + Curl_HttpReq httpreq; + const char *method = NULL; + const char *hexkey; + const char *keyid; + enum httpsig_alg alg; + time_t created; + struct dynbuf sig_params; + struct dynbuf sig_base; + struct dynbuf sig_hdr; + struct dynbuf input_hdr; + struct dynbuf authority_buf; + const char *authority; + const char *components[HTTPSIG_MAX_COMPONENTS]; + size_t ncomp = 0; + unsigned char *keybuf = NULL; + size_t keylen = 0; + unsigned char raw_sig[HTTPSIG_MAX_RAW_SIG]; + size_t raw_sig_len = 0; + char *auth_headers = NULL; + char *hdrs_copy = NULL; + struct dynbuf query_dyn; + + alg = id_to_alg(data->set.httpsig_algorithm); + if(alg == HTTPSIG_ALG_UNKNOWN) { + failf(data, "httpsig: CURLOPT_HTTPSIG_ALGORITHM is required"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + hexkey = data->set.str[STRING_HTTPSIG_KEY]; + keyid = data->set.str[STRING_HTTPSIG_KEYID]; + + if(!hexkey || !*hexkey) { + failf(data, "httpsig: CURLOPT_HTTPSIG_KEY is required"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!keyid || !*keyid) { + failf(data, "httpsig: CURLOPT_HTTPSIG_KEYID is required"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + curlx_dyn_init(&sig_params, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&sig_base, HTTPSIG_MAX_SIG_BASE); + curlx_dyn_init(&sig_hdr, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&input_hdr, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&authority_buf, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&query_dyn, CURL_MAX_HTTP_HEADER); + + if(Curl_checkheaders(data, STRCONST("Signature")) || + Curl_checkheaders(data, STRCONST("Signature-Input"))) { + /* user provides their own Signature / Signature-Input headers, consider + this done */ + goto done; + } + + result = decode_hex_key(data, hexkey, &keybuf, &keylen); + if(result) + goto fail; + + if(alg == HTTPSIG_ALG_ED25519 && keylen != 32) { + failf(data, "httpsig: ed25519 requires a 32-byte key (got %zu)", keylen); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto fail; + } + + Curl_http_method(data, &method, &httpreq); + + path = data->state.up.path; + if(!path || !*path) + path = "/"; + + query = data->state.up.query; + + result = httpsig_authority(data, conn, &authority_buf); + if(result) + goto fail; + authority = curlx_dyn_ptr(&authority_buf); + + /* Build @query value: RFC 9421 Section 2.2.7 - always starts with "?" */ + if(query && *query) + result = curlx_dyn_addf(&query_dyn, "?%s", query); + else + result = curlx_dyn_add(&query_dyn, "?"); + if(result) + goto fail; + + result = parse_components(data, query, components, &ncomp, &hdrs_copy); + if(result) + goto fail; + + created = httpsig_get_created(); + + result = build_sig_params(&sig_params, components, ncomp, + created, keyid, alg); + if(result) + goto fail; + + infof(data, "httpsig: Signature-Input params: %s", + curlx_dyn_ptr(&sig_params)); + + result = build_sig_base(&sig_base, components, ncomp, + method, authority, path, + curlx_dyn_ptr(&query_dyn), + data, curlx_dyn_ptr(&sig_params)); + if(result) + goto fail; + + infof(data, "httpsig: Signature base: [%s]", + curlx_dyn_ptr(&sig_base)); + + result = httpsig_sign_base(data, alg, keybuf, keylen, &sig_base, + raw_sig, &raw_sig_len); + if(result) + goto fail; + + result = curlx_dyn_add(&sig_hdr, HTTPSIG_DEFAULT_LABEL "="); + if(result) + goto fail; + result = sf_encode_byte_seq(raw_sig, raw_sig_len, &sig_hdr); + if(result) + goto fail; + + result = curlx_dyn_addf(&input_hdr, "%s=%s", HTTPSIG_DEFAULT_LABEL, + curlx_dyn_ptr(&sig_params)); + if(result) + goto fail; + + auth_headers = curl_maprintf("Signature-Input: %s\r\n" + "Signature: %s\r\n", + curlx_dyn_ptr(&input_hdr), + curlx_dyn_ptr(&sig_hdr)); + if(!auth_headers) + goto fail; + + infof(data, "httpsig: Signature-Input: %s", curlx_dyn_ptr(&input_hdr)); + infof(data, "httpsig: Signature: %s", curlx_dyn_ptr(&sig_hdr)); +done: + curlx_free(data->req.hd_auth); + data->req.hd_auth = auth_headers; + data->state.authhost.done = TRUE; + result = CURLE_OK; + +fail: + if(keybuf) { + memset(keybuf, 0, keylen); + curlx_free(keybuf); + } + memset(raw_sig, 0, sizeof(raw_sig)); + curlx_free(hdrs_copy); + curlx_dyn_free(&sig_params); + curlx_dyn_free(&sig_base); + curlx_dyn_free(&sig_hdr); + curlx_dyn_free(&input_hdr); + curlx_dyn_free(&authority_buf); + curlx_dyn_free(&query_dyn); + return result; +} + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_HTTPSIG */ diff --git a/lib/http_httpsig.h b/lib/http_httpsig.h new file mode 100644 index 000000000000..410755d7e0af --- /dev/null +++ b/lib/http_httpsig.h @@ -0,0 +1,35 @@ +#ifndef HEADER_CURL_HTTP_HTTPSIG_H +#define HEADER_CURL_HTTP_HTTPSIG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HTTPSIG) + +#include "urldata.h" + +CURLcode Curl_output_httpsig(struct Curl_easy *data); + +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_HTTPSIG */ +#endif /* HEADER_CURL_HTTP_HTTPSIG_H */ diff --git a/lib/setopt.c b/lib/setopt.c index 51cd58a7f9f8..a030b5489e95 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1103,6 +1103,19 @@ static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, #else result = CURLE_NOT_BUILT_IN; break; +#endif +#ifndef CURL_DISABLE_HTTPSIG + case CURLOPT_HTTPSIG_ALGORITHM: + if(arg != CURLHTTPSIG_NONE && + arg != CURLHTTPSIG_ED25519 && + arg != CURLHTTPSIG_HMAC_SHA256) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->httpsig_algorithm = (uint8_t)arg; + if(arg) + s->httpauth = (uint32_t)CURLAUTH_HTTPSIG; + else + s->httpauth &= ~(uint32_t)CURLAUTH_HTTPSIG; + break; #endif default: return CURLE_UNKNOWN_OPTION; @@ -2081,6 +2094,17 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, if(s->str[STRING_AWS_SIGV4]) s->httpauth = CURLAUTH_AWS_SIGV4; break; +#endif +#ifndef CURL_DISABLE_HTTPSIG + case CURLOPT_HTTPSIG_KEY: + result = Curl_setstropt(&s->str[STRING_HTTPSIG_KEY], ptr); + break; + case CURLOPT_HTTPSIG_KEYID: + result = Curl_setstropt(&s->str[STRING_HTTPSIG_KEYID], ptr); + break; + case CURLOPT_HTTPSIG_HEADERS: + result = Curl_setstropt(&s->str[STRING_HTTPSIG_HEADERS], ptr); + break; #endif case CURLOPT_REFERER: /* diff --git a/lib/urldata.h b/lib/urldata.h index 5d6b3c2fdf4a..1be8da2dd64f 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -849,6 +849,11 @@ enum dupstring { #ifndef CURL_DISABLE_AWS STRING_AWS_SIGV4, /* Parameters for V4 signature */ #endif +#ifndef CURL_DISABLE_HTTPSIG + STRING_HTTPSIG_KEY, /* hex-encoded key data */ + STRING_HTTPSIG_KEYID, /* key identifier */ + STRING_HTTPSIG_HEADERS, /* space-separated components to sign */ +#endif #ifndef CURL_DISABLE_PROXY STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ #endif @@ -890,6 +895,7 @@ struct UserDefined { void *writeheader; /* write the header to this if non-NULL */ uint32_t httpauth; /* kind of HTTP authentication to use (bitmask) */ uint32_t proxyauth; /* kind of proxy authentication to use (bitmask) */ + uint8_t httpsig_algorithm; /* CURLHTTPSIG_* algorithm for RFC 9421 */ void *postfields; /* if POST, set the fields' values here */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead diff --git a/lib/version.c b/lib/version.c index 33dbe3c43697..c4cdde6377bb 100644 --- a/lib/version.c +++ b/lib/version.c @@ -469,6 +469,9 @@ static const struct feat features_table[] = { !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif +#ifndef CURL_DISABLE_HTTPSIG + FEATURE("HTTPSIG", NULL, 0), +#endif #ifdef USE_HTTPSRR FEATURE("HTTPSRR", NULL, 0), #endif diff --git a/projects/OS400/README.OS400 b/projects/OS400/README.OS400 index 5d4b643a5e31..71a78990f378 100644 --- a/projects/OS400/README.OS400 +++ b/projects/OS400/README.OS400 @@ -71,6 +71,9 @@ options: CURLOPT_FTP_ALTERNATIVE_TO_USER CURLOPT_HAPROXY_CLIENT_IP CURLOPT_HSTS + CURLOPT_HTTPSIG_HEADERS + CURLOPT_HTTPSIG_KEY + CURLOPT_HTTPSIG_KEYID CURLOPT_INTERFACE CURLOPT_ISSUERCERT CURLOPT_KEYPASSWD diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index cba89f8642ea..1dcf9543f9c5 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -1060,6 +1060,9 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) case CURLOPT_FTP_ALTERNATIVE_TO_USER: case CURLOPT_HAPROXY_CLIENT_IP: case CURLOPT_HSTS: + case CURLOPT_HTTPSIG_HEADERS: + case CURLOPT_HTTPSIG_KEY: + case CURLOPT_HTTPSIG_KEYID: case CURLOPT_INTERFACE: case CURLOPT_ISSUERCERT: case CURLOPT_KEYPASSWD: diff --git a/scripts/managen b/scripts/managen index 267b4cae3f2c..4e21474ea021 100755 --- a/scripts/managen +++ b/scripts/managen @@ -1143,7 +1143,14 @@ HEAD elsif(length($desc) > 78) { print STDERR "WARN: the --$long description is too long\n"; } - print $line; + my $ifdef = ""; + my $endif = ""; + + if($long =~ /^(ipfs|httpsig)/) { + $ifdef = sprintf "#ifndef CURL_DISABLE_%s\n", uc($1); + $endif = "#endif\n"; + } + print $ifdef.$line.$endif; } print <httpsig_algorithm || config->httpsig_key || + config->httpsig_keyid || config->httpsig_headers) { + long httpsig_alg = CURLHTTPSIG_NONE; + + if(!config->httpsig_key) { + errorf("--httpsig-key is required"); + return CURLE_FAILED_INIT; + } + if(!config->httpsig_keyid) { + errorf("--httpsig-keyid is required"); + return CURLE_FAILED_INIT; + } + + if(!config->httpsig_algorithm || + curl_strequal(config->httpsig_algorithm, "ed25519")) + httpsig_alg = CURLHTTPSIG_ED25519; + else if(curl_strequal(config->httpsig_algorithm, "hmac-sha256")) + httpsig_alg = CURLHTTPSIG_HMAC_SHA256; + else { + errorf("--httpsig-algo: unsupported algorithm '%s'", + config->httpsig_algorithm); + return CURLE_FAILED_INIT; + } + my_setopt_long(curl, CURLOPT_HTTPSIG_ALGORITHM, httpsig_alg); + MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_HEADERS, config->httpsig_headers); + MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_KEYID, config->httpsig_keyid); + { + FILE *keyf = curlx_fopen(config->httpsig_key, FOPEN_READTEXT); + if(keyf) { + char *hexdata = NULL; + ParameterError pe = file2string(&hexdata, keyf); + + curlx_fclose(keyf); + if(pe == PARAM_NO_MEM) { + curlx_safefree(hexdata); + return CURLE_OUT_OF_MEMORY; + } + if(pe == PARAM_READ_ERROR) { + curlx_safefree(hexdata); + errorf("httpsig: cannot read key file '%s'", config->httpsig_key); + return CURLE_READ_ERROR; + } + if(!hexdata || !*hexdata) { + curlx_safefree(hexdata); + errorf("httpsig: key file '%s' is empty", config->httpsig_key); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + /* can't use the MY_SETOPT_STR() macro here since it returns on error + and we must free the hexdata */ + result = my_setopt_str(curl, CURLOPT_HTTPSIG_KEY, hexdata); + curlx_safefree(hexdata); + if(setopt_bad(result)) + return result; + } + else { + errorf("httpsig: cannot open key file '%s'", config->httpsig_key); + return CURLE_READ_ERROR; + } + } + } + return CURLE_OK; +} +#else +#define httpsig_setopts(x,y) CURLE_OK +#endif + /* only for HTTP transfers */ static CURLcode http_setopts(struct OperationConfig *config, CURL *curl, const char *use_proto) @@ -579,6 +654,9 @@ static CURLcode http_setopts(struct OperationConfig *config, CURL *curl, #ifndef CURL_DISABLE_AWS MY_SETOPT_STR(curl, CURLOPT_AWS_SIGV4, config->aws_sigv4); #endif + result = httpsig_setopts(config, curl); + if(result) + return result; my_setopt_long(curl, CURLOPT_AUTOREFERER, config->autoreferer); if(config->proxyheaders) { diff --git a/src/curlinfo.c b/src/curlinfo.c index 94a02e965f21..36eabeb78fb4 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -93,6 +93,13 @@ static const char *disabled[] = { "OFF" #else "ON" +#endif + , + "httpsig: " +#ifdef CURL_DISABLE_HTTPSIG + "OFF" +#else + "ON" #endif , "DoH: " diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index b0a5990177bd..5423944a2990 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -181,6 +181,10 @@ static void free_config_fields(struct OperationConfig *config) curlx_safefree(config->ftp_account); curlx_safefree(config->ftp_alternative_to_user); curlx_safefree(config->aws_sigv4); + curlx_safefree(config->httpsig_algorithm); + curlx_safefree(config->httpsig_headers); + curlx_safefree(config->httpsig_key); + curlx_safefree(config->httpsig_keyid); curlx_safefree(config->ech); curlx_safefree(config->ech_config); curlx_safefree(config->ech_public); diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index e21c3c5b2d75..7a587f6675c8 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -153,6 +153,10 @@ struct OperationConfig { char *unix_socket_path; /* path to Unix domain socket */ char *haproxy_clientip; /* client IP for HAProxy protocol */ char *aws_sigv4; + char *httpsig_algorithm; + char *httpsig_headers; + char *httpsig_key; + char *httpsig_keyid; char *ech; /* Config set by --ech keywords */ char *ech_config; /* Config set by "--ech esl:" option */ char *ech_public; /* Config set by "--ech pn:" option */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 5499b1ff360e..9364c8683905 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -170,6 +170,12 @@ static const struct LongShort aliases[] = { {"http2-prior-knowledge", ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE}, {"http3", ARG_NONE|ARG_TLS, ' ', C_HTTP3}, {"http3-only", ARG_NONE|ARG_TLS, ' ', C_HTTP3_ONLY}, +#ifndef CURL_DISABLE_HTTPSIG + {"httpsig-algo", ARG_STRG, ' ', C_HTTPSIG_ALGORITHM}, + {"httpsig-headers", ARG_STRG, ' ', C_HTTPSIG_HEADERS}, + {"httpsig-key", ARG_FILE, ' ', C_HTTPSIG_KEY}, + {"httpsig-keyid", ARG_STRG, ' ', C_HTTPSIG_KEYID}, +#endif {"ignore-content-length", ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH}, {"include", ARG_BOOL, ' ', C_INCLUDE}, {"insecure", ARG_BOOL, 'k', C_INSECURE}, @@ -2363,6 +2369,12 @@ static ParameterError opt_file(struct OperationConfig *config, case C_UPLOAD_FILE: /* --upload-file */ err = parse_upload_file(config, nextarg); break; + case C_HTTPSIG_KEY: /* --httpsig-key */ + if(!feature_httpsig) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->httpsig_key, nextarg, DENY_BLANK); + break; } return err; } @@ -2557,6 +2569,26 @@ static ParameterError opt_string(struct OperationConfig *config, config->authtype |= CURLAUTH_AWS_SIGV4; err = getstr(&config->aws_sigv4, nextarg, ALLOW_BLANK); break; + case C_HTTPSIG_ALGORITHM: /* --httpsig-algo */ + if(!feature_httpsig) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + config->authtype |= CURLAUTH_HTTPSIG; + err = getstr(&config->httpsig_algorithm, nextarg, DENY_BLANK); + } + break; + case C_HTTPSIG_KEYID: /* --httpsig-keyid */ + if(!feature_httpsig) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->httpsig_keyid, nextarg, DENY_BLANK); + break; + case C_HTTPSIG_HEADERS: /* --httpsig-headers */ + if(!feature_httpsig) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->httpsig_headers, nextarg, DENY_BLANK); + break; case C_INTERFACE: /* --interface */ /* interface */ err = getstr(&config->iface, nextarg, DENY_BLANK); diff --git a/src/tool_getparam.h b/src/tool_getparam.h index 32476d377691..92495a4de670 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -125,6 +125,10 @@ typedef enum { C_HTTP2_PRIOR_KNOWLEDGE, C_HTTP3, C_HTTP3_ONLY, + C_HTTPSIG_ALGORITHM, + C_HTTPSIG_HEADERS, + C_HTTPSIG_KEY, + C_HTTPSIG_KEYID, C_IGNORE_CONTENT_LENGTH, C_INCLUDE, C_INSECURE, diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 2eec25f0835d..bce6bb7d6ee6 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -69,6 +69,7 @@ bool feature_brotli = FALSE; bool feature_hsts = FALSE; bool feature_http2 = FALSE; bool feature_http3 = FALSE; +bool feature_httpsig = FALSE; bool feature_httpsproxy = FALSE; bool feature_libz = FALSE; bool feature_ntlm = FALSE; @@ -97,6 +98,7 @@ static struct feature_name_presentp { { "HTTP2", &feature_http2, CURL_VERSION_HTTP2 }, { "HTTP3", &feature_http3, CURL_VERSION_HTTP3 }, { "HTTPS-proxy", &feature_httpsproxy, CURL_VERSION_HTTPS_PROXY }, + { "HTTPSIG", &feature_httpsig, 0 }, { "IDN", NULL, CURL_VERSION_IDN }, { "IPv6", NULL, CURL_VERSION_IPV6 }, { "Kerberos", NULL, CURL_VERSION_KERBEROS5 }, diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index 481db1e58e1c..4ca507ad66cb 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -52,6 +52,7 @@ extern bool feature_brotli; extern bool feature_hsts; extern bool feature_http2; extern bool feature_http3; +extern bool feature_httpsig; extern bool feature_httpsproxy; extern bool feature_libz; extern bool feature_ntlm; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index c0b0af792f9e..63cc990622b3 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -303,6 +303,26 @@ const struct helptxt helptext[] = { { " --http3-only", "Use HTTP/3 only", CURLHELP_HTTP }, +#ifndef CURL_DISABLE_HTTPSIG + { " --httpsig-algo ", + "Algorithm for HTTP Message Signatures", + CURLHELP_AUTH | CURLHELP_HTTP }, +#endif +#ifndef CURL_DISABLE_HTTPSIG + { " --httpsig-headers ", + "Components to sign for HTTP Message Signatures", + CURLHELP_AUTH | CURLHELP_HTTP }, +#endif +#ifndef CURL_DISABLE_HTTPSIG + { " --httpsig-key ", + "Key file for HTTP Message Signatures", + CURLHELP_AUTH | CURLHELP_HTTP }, +#endif +#ifndef CURL_DISABLE_HTTPSIG + { " --httpsig-keyid ", + "Key identifier for HTTP Message Signatures", + CURLHELP_AUTH | CURLHELP_HTTP }, +#endif { " --ignore-content-length", "Ignore the size of the remote resource", CURLHELP_HTTP | CURLHELP_FTP }, @@ -315,9 +335,11 @@ const struct helptxt helptext[] = { { " --ip-tos ", "Set IP Type of Service or Traffic Class", CURLHELP_CONNECTION }, +#ifndef CURL_DISABLE_IPFS { " --ipfs-gateway ", "Gateway for IPFS", CURLHELP_CURL }, +#endif { "-4, --ipv4", "Resolve names to IPv4 addresses", CURLHELP_CONNECTION | CURLHELP_DNS }, diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 2e001ec3c8ef..f012cc58c074 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -290,11 +290,16 @@ test3300 test3301 test3302 test3303 test3304 test3305 test3306 \ \ test3400 test3401 \ \ -test4000 test4001 +test4000 test4001 \ +\ +test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ +test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ +test5016 test5017 test5018 test5019 test5020 test5021 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ data1400.c data1401.c data1402.c data1403.c data1404.c data1405.c data1406.c \ data1407.c data1420.c data1465.c data1481.c \ data1705-1.md data1705-2.md data1705-3.md data1705-4.md data1705-stdout.1 \ -data1706-1.md data1706-2.md data1706-3.md data1706-4.md data1706-stdout.txt +data1706-1.md data1706-2.md data1706-3.md data1706-4.md data1706-stdout.txt \ +data-httpsig-ed25519.key data-httpsig-hmac-sha256.key diff --git a/tests/data/data-httpsig-ed25519.key b/tests/data/data-httpsig-ed25519.key new file mode 100644 index 000000000000..f0b6688b67d2 --- /dev/null +++ b/tests/data/data-httpsig-ed25519.key @@ -0,0 +1 @@ +9f8362f87a484a954e6e740c5b4c0e84229139a20aa8ab56ff66586f6a7d29c5 diff --git a/tests/data/data-httpsig-hmac-sha256.key b/tests/data/data-httpsig-hmac-sha256.key new file mode 100644 index 000000000000..eef9161772c3 --- /dev/null +++ b/tests/data/data-httpsig-hmac-sha256.key @@ -0,0 +1 @@ +0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef diff --git a/tests/data/test5000 b/tests/data/test5000 new file mode 100644 index 000000000000..b935ecdf8aaf --- /dev/null +++ b/tests/data/test5000 @@ -0,0 +1,57 @@ + + + + +HTTP +CURLOPT_HTTPSIG_ALGORITHM +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/html +Content-Length: 0 + + + + +# Client-side + + +http + +# this relies on the Debug feature which allows tests to set the time + +Debug +httpsig + + + +HTTP RFC 9421 Message Signatures: basic GET with Ed25519 + + +lib%TESTNUMBER + + + +http://example.com:9000/%TESTNUMBER/resource example.com:9000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:9000 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:Olp3ugYSVwc47PTNBMrkV2P+8p5w2VPFzURxNaG44UA2On6OFG2FzFLEl8QEN1khiOdRNlFDouZsOwpJoPHvBA==: +Accept: */* + + + + diff --git a/tests/data/test5001 b/tests/data/test5001 new file mode 100644 index 000000000000..705289019efc --- /dev/null +++ b/tests/data/test5001 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: CLI GET with query + + +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:RQniOeqmwdRzGvoDIMJ8XJha75evJWgqo5/66EeuJeEGczZtnP2U/F52Lzd/y7Vd1DCb8oUcCKHrKi2VJI7lBA==: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5002 b/tests/data/test5002 new file mode 100644 index 000000000000..0d002f831f75 --- /dev/null +++ b/tests/data/test5002 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 +hmac-sha256 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: CLI GET with HMAC-SHA256 + + +"http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:7000 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="shared-key-1";alg="hmac-sha256" +Signature: sig1=:jvajJheXE4H/TqWfAn8m0d0Rx0XWYKADN/1P0qn+FLw=: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5003 b/tests/data/test5003 new file mode 100644 index 000000000000..9d8bbfb06fc6 --- /dev/null +++ b/tests/data/test5003 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 +httpsig-headers + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: custom --httpsig-headers + + +"http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:6000 +Signature-Input: sig1=("@method" "@authority");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:q+8VHrGBOhkJm9d9dxKv3BfhyVWKIFiYXqdLZVx8yGrTzZO6Q6zJicFFuLvwJex6DS+Pw6YEMQzmtyj7dKOCCw==: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5004 b/tests/data/test5004 new file mode 100644 index 000000000000..c916131762a7 --- /dev/null +++ b/tests/data/test5004 @@ -0,0 +1,66 @@ + + + + +HTTP +CURLOPT_HTTPSIG_ALGORITHM +RFC9421 +ed25519 +B.2.6 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/html +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + + +HTTP RFC 9421 B.2.6: Ed25519 POST with headers (RFC test vector) + + +lib%TESTNUMBER + + + +CURL_HTTPSIG_CREATED=1618884473 + + + +http://example.com/%TESTNUMBER/foo example.com:80:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER/foo HTTP/1.1 +Host: example.com +Signature-Input: sig1=("date" "@method" "@path" "@authority" "content-type" "content-length");created=1618884473;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:6QriGerNuao/A02UMre01lQOwlVQ0L9Cx3WJPQonQpUdKxSNg/XXXqjKdcC9PELzmAKJ10loLIq10yYLTzmzCA==: +Accept: */* +Date: Tue, 20 Apr 2021 02:07:55 GMT +Content-Type: application/json +Content-Length: 18 + +{"hello": "world"} + + + diff --git a/tests/data/test5005 b/tests/data/test5005 new file mode 100644 index 000000000000..f52c94d8d569 --- /dev/null +++ b/tests/data/test5005 @@ -0,0 +1,56 @@ + + + + +HTTP +httpsig +RFC9421 +header-signing + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: sign with a regular HTTP header + + +"http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5000 +Signature-Input: sig1=("@method" "@authority" "content-type");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:lyo8O1BjxYmPJchwmIO6ZwOEycijlfsLsqVpVVMiuPFAWrZ7zm0eoIF0X0wET8bsO9jiDRWjDx0b0dPK7FHPCA==: +User-Agent: curl/%VERSION +Accept: */* +Content-Type: application/json + + + + diff --git a/tests/data/test5006 b/tests/data/test5006 new file mode 100644 index 000000000000..fee77488ea06 --- /dev/null +++ b/tests/data/test5006 @@ -0,0 +1,56 @@ + + + + +HTTP +httpsig +RFC9421 +case-normalization + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: component name lowercasing + + +"http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5100 +Signature-Input: sig1=("@method" "content-type");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:tu3WEN25K59evFpiHY5GZurVRtbaex5QM1W9b+P1rPCgNDsEEOepNZap/+nm48QZSensuc4tOpy1gbvAj25gAg==: +User-Agent: curl/%VERSION +Accept: */* +Content-Type: text/plain + + + + diff --git a/tests/data/test5007 b/tests/data/test5007 new file mode 100644 index 000000000000..42d72bfd1b74 --- /dev/null +++ b/tests/data/test5007 @@ -0,0 +1,58 @@ + + + + +HTTP +HTTP POST +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: POST method + + +"http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5200 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:fqvzBFSBFu9oys7DawypZIrt2VS+nOnN1TQ7aTLcPUbEv7Zqm4leYd0q5r9FwW+kvabAjMj8J8N8F7FmPpIWCA==: +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 8 +Content-Type: application/x-www-form-urlencoded + +postbody + + + diff --git a/tests/data/test5008 b/tests/data/test5008 new file mode 100644 index 000000000000..01e628322412 --- /dev/null +++ b/tests/data/test5008 @@ -0,0 +1,46 @@ + + + + +HTTP +httpsig +RFC9421 +error + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: error - missing --httpsig-key + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +2 + + + diff --git a/tests/data/test5009 b/tests/data/test5009 new file mode 100644 index 000000000000..ef7bc5e6a75f --- /dev/null +++ b/tests/data/test5009 @@ -0,0 +1,46 @@ + + + + +HTTP +httpsig +RFC9421 +error + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: error - unsupported algorithm + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "rsa-pss-sha512" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +2 + + + diff --git a/tests/data/test5010 b/tests/data/test5010 new file mode 100644 index 000000000000..ff77e59ceb41 --- /dev/null +++ b/tests/data/test5010 @@ -0,0 +1,46 @@ + + + + +HTTP +httpsig +RFC9421 +error + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: error - non-existent key file + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key /nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +26 + + + diff --git a/tests/data/test5011 b/tests/data/test5011 new file mode 100644 index 000000000000..94206854a2e6 --- /dev/null +++ b/tests/data/test5011 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 +query-special-chars + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: query string with special characters + + +"http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad HTTP/1.1 +Host: example.com:5300 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:ZFaJcDMTJEBbr90c0FaqupdJYDhNSz1i/eVCpMTDJ+1phLql2U4ROLiO26SGL6D0Nd5rr/3VXbDcWJ2vnV6IBA==: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5012 b/tests/data/test5012 new file mode 100644 index 000000000000..e119c29d5827 --- /dev/null +++ b/tests/data/test5012 @@ -0,0 +1,58 @@ + + + + +HTTP +httpsig +RFC9421 +default-port + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: default port omitted from @authority + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5013 b/tests/data/test5013 new file mode 100644 index 000000000000..9cad55190c1e --- /dev/null +++ b/tests/data/test5013 @@ -0,0 +1,58 @@ + + + + +HTTP +httpsig +RFC9421 +non-default-port + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: non-default port in @authority + + +"http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:9000 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5014 b/tests/data/test5014 new file mode 100644 index 000000000000..6033e40bec61 --- /dev/null +++ b/tests/data/test5014 @@ -0,0 +1,58 @@ + + + + +HTTP +httpsig +RFC9421 +empty-query + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: @query explicitly requested, no query in URL + + +"http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5400 +Signature-Input: sig1=("@method" "@authority" "@query");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5015 b/tests/data/test5015 new file mode 100644 index 000000000000..f9459be37a04 --- /dev/null +++ b/tests/data/test5015 @@ -0,0 +1,62 @@ + + + + +HTTP +HTTP POST +httpsig +RFC9421 +hmac-sha256 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: POST with HMAC-SHA256 + + +"http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +POST /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5500 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="shared-key-1";alg="hmac-sha256" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 8 +Content-Type: application/x-www-form-urlencoded + +postbody + + + diff --git a/tests/data/test5016 b/tests/data/test5016 new file mode 100644 index 000000000000..f6fbe2b0139c --- /dev/null +++ b/tests/data/test5016 @@ -0,0 +1,60 @@ + + + + +HTTP +httpsig +RFC9421 +hmac-sha256 +header-signing + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: HMAC-SHA256 with custom headers + + +"http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5600 +Signature-Input: sig1=("@method" "@authority" "content-type");created=0;keyid="shared-key-1";alg="hmac-sha256" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* +Content-Type: application/json + + + + diff --git a/tests/data/test5017 b/tests/data/test5017 new file mode 100644 index 000000000000..04a582e0d34e --- /dev/null +++ b/tests/data/test5017 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 +preexisting-signature + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: user-supplied Signature header skips signing + + +"http://example.com:5700/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -H "Signature: preexisting" -H "Signature-Input: preexisting" --connect-to example.com:5700:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5700 +User-Agent: curl/%VERSION +Accept: */* +Signature: preexisting +Signature-Input: preexisting + + + + diff --git a/tests/data/test5018 b/tests/data/test5018 new file mode 100644 index 000000000000..417c1f0b5bfc --- /dev/null +++ b/tests/data/test5018 @@ -0,0 +1,63 @@ + + + + +HTTP +HTTP PUT +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +putdata + + +HTTP RFC 9421 Message Signatures: PUT upload + + +"http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +PUT /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:5800 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 8 + +putdata + + + diff --git a/tests/data/test5019 b/tests/data/test5019 new file mode 100644 index 000000000000..b07df9aa185d --- /dev/null +++ b/tests/data/test5019 @@ -0,0 +1,46 @@ + + + + +HTTP +httpsig +RFC9421 +error + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: error - duplicate component + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --httpsig-headers "method method" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +43 + + + diff --git a/tests/data/test5020 b/tests/data/test5020 new file mode 100644 index 000000000000..f6c27e877d1b --- /dev/null +++ b/tests/data/test5020 @@ -0,0 +1,46 @@ + + + + +HTTP +httpsig +RFC9421 +error + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 + + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: error - rejects legacy '@' component syntax + + +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --httpsig-headers "@method authority" --connect-to example.com::%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +43 + + + diff --git a/tests/data/test5021 b/tests/data/test5021 new file mode 100644 index 000000000000..da5f5af672a7 --- /dev/null +++ b/tests/data/test5021 @@ -0,0 +1,57 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: default algorithm (ed25519) when --httpsig-algo omitted + + +"http://example.com:6100/%TESTNUMBER/resource" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +s/Signature: sig1=:.*:/Signature: sig1=:STRIPPED:/ + + +GET /%TESTNUMBER/resource HTTP/1.1 +Host: example.com:6100 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="test-key-ed25519";alg="ed25519" +Signature: sig1=:STRIPPED: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 7b959e0df498..89f6a8254634 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -124,4 +124,5 @@ TESTS_C = \ lib2700.c \ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ lib3100.c lib3101.c lib3102.c lib3103.c lib3104.c lib3105.c \ - lib3207.c lib3208.c + lib3207.c lib3208.c \ + lib5000.c lib5004.c diff --git a/tests/libtest/lib5000.c b/tests/libtest/lib5000.c new file mode 100644 index 000000000000..a0eb92f0aebb --- /dev/null +++ b/tests/libtest/lib5000.c @@ -0,0 +1,66 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static CURLcode test_lib5000(const char *URL) +{ + CURL *curl; + CURLcode result = TEST_ERR_MAJOR_BAD; + struct curl_slist *connect_to = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, (long)CURLHTTPSIG_ED25519); + easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "test-key-ed25519"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); + if(libtest_arg2) { + connect_to = curl_slist_append(connect_to, libtest_arg2); + } + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + + result = curl_easy_perform(curl); + +test_cleanup: + + curl_slist_free_all(connect_to); + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return result; +} diff --git a/tests/libtest/lib5004.c b/tests/libtest/lib5004.c new file mode 100644 index 000000000000..1f6fb1a22399 --- /dev/null +++ b/tests/libtest/lib5004.c @@ -0,0 +1,85 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* RFC 9421 Appendix B.2.6 test vector: Ed25519 signing of a POST request + * with headers. Uses CURL_HTTPSIG_CREATED=1618884473 to match the RFC + * expected timestamp. */ + +#include "first.h" + +static CURLcode test_lib5004(const char *URL) +{ + CURL *curl; + CURLcode result = TEST_ERR_MAJOR_BAD; + struct curl_slist *connect_to = NULL; + struct curl_slist *headers = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, (long)CURLHTTPSIG_ED25519); + easy_setopt(curl, CURLOPT_HTTPSIG_KEY, + "9f8362f87a484a954e6e740c5b4c0e84" + "229139a20aa8ab56ff66586f6a7d29c5"); + easy_setopt(curl, CURLOPT_HTTPSIG_KEYID, "test-key-ed25519"); + easy_setopt(curl, CURLOPT_HTTPSIG_HEADERS, + "date: method path authority content-type: content-length:"); + easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"hello\": \"world\"}"); + + headers = curl_slist_append(headers, + "Date: Tue, 20 Apr 2021 02:07:55 GMT"); + headers = curl_slist_append(headers, + "Content-Type: application/json"); + headers = curl_slist_append(headers, + "Content-Length: 18"); + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); + if(libtest_arg2) { + connect_to = curl_slist_append(connect_to, libtest_arg2); + } + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + + result = curl_easy_perform(curl); + +test_cleanup: + + curl_slist_free_all(headers); + curl_slist_free_all(connect_to); + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return result; +} From 9bcc64c39b9f4b57361d3535829743d6999d4aad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 25 Jul 2026 17:03:22 +0200 Subject: [PATCH 0719/1062] curl: make --httpsig-key take a key OR a file name for key Verified by test 5022 Closes #22392 --- docs/cmdline-opts/httpsig-key.md | 19 ++++++----- src/config2setopts.c | 19 ++++++++--- src/tool_listhelp.c | 4 +-- tests/data/Makefile.am | 2 +- tests/data/test5001 | 2 +- tests/data/test5002 | 2 +- tests/data/test5003 | 2 +- tests/data/test5005 | 2 +- tests/data/test5006 | 2 +- tests/data/test5007 | 2 +- tests/data/test5010 | 2 +- tests/data/test5011 | 2 +- tests/data/test5012 | 2 +- tests/data/test5013 | 2 +- tests/data/test5014 | 2 +- tests/data/test5015 | 2 +- tests/data/test5016 | 2 +- tests/data/test5018 | 2 +- tests/data/test5021 | 2 +- tests/data/test5022 | 55 ++++++++++++++++++++++++++++++++ 20 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 tests/data/test5022 diff --git a/docs/cmdline-opts/httpsig-key.md b/docs/cmdline-opts/httpsig-key.md index a2d8f611d849..c0a04051485f 100644 --- a/docs/cmdline-opts/httpsig-key.md +++ b/docs/cmdline-opts/httpsig-key.md @@ -3,8 +3,8 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: httpsig-key Protocols: HTTP -Arg: -Help: Key file for HTTP Message Signatures +Arg: +Help: Key for HTTP Message Signatures Category: auth http Added: 8.22.0 Multi: single @@ -13,16 +13,19 @@ See-also: - httpsig-algo - httpsig-keyid Example: - - --httpsig-algo ed25519 --httpsig-key key.hex --httpsig-keyid "my-key" $URL + - --httpsig-algo ed25519 --httpsig-key @key.hex --httpsig-keyid "my-key" $URL + - --httpsig-key 123a56fb72197633bc --httpsig-keyid "my-key" $URL --- # `--httpsig-key` -Path to the key file used for RFC 9421 HTTP Message Signatures. +The key to use for RFC 9421 HTTP Message Signatures. Provide it as-is, or as +`@filename`. If the argument starts with an `@`, the rest is treated as a file +name for the key. -The file must contain a hex-encoded key on its first line. For **ed25519**, -this is the 32-byte private seed (64 hex characters). For **hmac-sha256**, -this is the shared secret. PEM files are not supported. +The key is formatted as a series of hexadecimal digits in a single line. For +**ed25519**, this is the 32-byte private seed (64 hex characters). For +**hmac-sha256**, this is the shared secret. PEM files are not supported. ## Generating Ed25519 keys @@ -32,4 +35,4 @@ With OpenSSL 3: openssl pkey -in k.pem -outform RAW -out k.raw xxd -p -c 64 k.raw | tr -d '\n' > k.hex -Use `k.hex` with `--httpsig-key`. +Use `@k.hex` with `--httpsig-key`. diff --git a/src/config2setopts.c b/src/config2setopts.c index 40543637def0..47b67aed0b7a 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -599,8 +599,9 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) my_setopt_long(curl, CURLOPT_HTTPSIG_ALGORITHM, httpsig_alg); MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_HEADERS, config->httpsig_headers); MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_KEYID, config->httpsig_keyid); - { - FILE *keyf = curlx_fopen(config->httpsig_key, FOPEN_READTEXT); + + if(config->httpsig_key[0] == '@') { + FILE *keyf = curlx_fopen(&config->httpsig_key[1], FOPEN_READTEXT); if(keyf) { char *hexdata = NULL; ParameterError pe = file2string(&hexdata, keyf); @@ -612,12 +613,13 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) } if(pe == PARAM_READ_ERROR) { curlx_safefree(hexdata); - errorf("httpsig: cannot read key file '%s'", config->httpsig_key); + errorf("httpsig: cannot read key file '%s'", + &config->httpsig_key[1]); return CURLE_READ_ERROR; } if(!hexdata || !*hexdata) { curlx_safefree(hexdata); - errorf("httpsig: key file '%s' is empty", config->httpsig_key); + errorf("httpsig: key file '%s' is empty", &config->httpsig_key[1]); return CURLE_BAD_FUNCTION_ARGUMENT; } /* can't use the MY_SETOPT_STR() macro here since it returns on error @@ -628,10 +630,17 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) return result; } else { - errorf("httpsig: cannot open key file '%s'", config->httpsig_key); + errorf("httpsig: cannot open key file '%s'", &config->httpsig_key[1]); return CURLE_READ_ERROR; } } + else { + if(!config->httpsig_key[0]) { + errorf("httpsig: key is empty"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_KEY, config->httpsig_key); + } } return CURLE_OK; } diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 63cc990622b3..6232fa8b5270 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -314,8 +314,8 @@ const struct helptxt helptext[] = { CURLHELP_AUTH | CURLHELP_HTTP }, #endif #ifndef CURL_DISABLE_HTTPSIG - { " --httpsig-key ", - "Key file for HTTP Message Signatures", + { " --httpsig-key ", + "Key for HTTP Message Signatures", CURLHELP_AUTH | CURLHELP_HTTP }, #endif #ifndef CURL_DISABLE_HTTPSIG diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f012cc58c074..209cc43598b5 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -294,7 +294,7 @@ test4000 test4001 \ \ test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ -test5016 test5017 test5018 test5019 test5020 test5021 +test5016 test5017 test5018 test5019 test5020 test5021 test5022 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ diff --git a/tests/data/test5001 b/tests/data/test5001 index 705289019efc..7465aab2b7ff 100644 --- a/tests/data/test5001 +++ b/tests/data/test5001 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: CLI GET with query -"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5002 b/tests/data/test5002 index 0d002f831f75..143d06d12217 100644 --- a/tests/data/test5002 +++ b/tests/data/test5002 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: CLI GET with HMAC-SHA256 -"http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT +"http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5003 b/tests/data/test5003 index 9d8bbfb06fc6..6468aa60e3f8 100644 --- a/tests/data/test5003 +++ b/tests/data/test5003 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: custom --httpsig-headers -"http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT +"http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5005 b/tests/data/test5005 index f52c94d8d569..e090c943de3e 100644 --- a/tests/data/test5005 +++ b/tests/data/test5005 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: sign with a regular HTTP header -"http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT +"http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5006 b/tests/data/test5006 index fee77488ea06..046f499648ca 100644 --- a/tests/data/test5006 +++ b/tests/data/test5006 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: component name lowercasing -"http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT +"http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5007 b/tests/data/test5007 index 42d72bfd1b74..1184ebcffb06 100644 --- a/tests/data/test5007 +++ b/tests/data/test5007 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: POST method -"http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT +"http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5010 b/tests/data/test5010 index ff77e59ceb41..51fd8dd888c0 100644 --- a/tests/data/test5010 +++ b/tests/data/test5010 @@ -33,7 +33,7 @@ httpsig HTTP RFC 9421 Message Signatures: error - non-existent key file -"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key /nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @/nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5011 b/tests/data/test5011 index 94206854a2e6..c883bbe249e1 100644 --- a/tests/data/test5011 +++ b/tests/data/test5011 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: query string with special characters -"http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT +"http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5012 b/tests/data/test5012 index e119c29d5827..8f183738e51d 100644 --- a/tests/data/test5012 +++ b/tests/data/test5012 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: default port omitted from @authority -"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5013 b/tests/data/test5013 index 9cad55190c1e..beb3a1cf7014 100644 --- a/tests/data/test5013 +++ b/tests/data/test5013 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: non-default port in @authority -"http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT +"http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5014 b/tests/data/test5014 index 6033e40bec61..51fab716bd02 100644 --- a/tests/data/test5014 +++ b/tests/data/test5014 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: @query explicitly requested, no query in URL -"http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT +"http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5015 b/tests/data/test5015 index f9459be37a04..5f6af68bc07a 100644 --- a/tests/data/test5015 +++ b/tests/data/test5015 @@ -37,7 +37,7 @@ httpsig HTTP RFC 9421 Message Signatures: POST with HMAC-SHA256 -"http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT +"http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5016 b/tests/data/test5016 index f6fbe2b0139c..01a12a1ab0e4 100644 --- a/tests/data/test5016 +++ b/tests/data/test5016 @@ -37,7 +37,7 @@ httpsig HTTP RFC 9421 Message Signatures: HMAC-SHA256 with custom headers -"http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT +"http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5018 b/tests/data/test5018 index 417c1f0b5bfc..ab037470703c 100644 --- a/tests/data/test5018 +++ b/tests/data/test5018 @@ -39,7 +39,7 @@ putdata HTTP RFC 9421 Message Signatures: PUT upload -"http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT +"http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5021 b/tests/data/test5021 index da5f5af672a7..9370943853eb 100644 --- a/tests/data/test5021 +++ b/tests/data/test5021 @@ -35,7 +35,7 @@ httpsig HTTP RFC 9421 Message Signatures: default algorithm (ed25519) when --httpsig-algo omitted -"http://example.com:6100/%TESTNUMBER/resource" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT +"http://example.com:6100/%TESTNUMBER/resource" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5022 b/tests/data/test5022 new file mode 100644 index 000000000000..a27ab7701fb4 --- /dev/null +++ b/tests/data/test5022 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: pass key directly + + +"http://example.com:8000/5001/resource?action=read" --httpsig-algo "ed25519" --variable key@%SRCDIR/data/data-httpsig-ed25519.key --expand-httpsig-key '{{key:trim}}' --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /5001/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:RQniOeqmwdRzGvoDIMJ8XJha75evJWgqo5/66EeuJeEGczZtnP2U/F52Lzd/y7Vd1DCb8oUcCKHrKi2VJI7lBA==: +User-Agent: curl/%VERSION +Accept: */* + + + + From 163f0cbc8bd1b0ecd2a885594bdb4e41cebb90b3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 25 Jul 2026 22:22:21 +0200 Subject: [PATCH 0720/1062] cd2nroff: fix backslashes for 4-space indent lines They were previously only properly escaped for ~~~ quotes. Spotted for the CURLOPT_HTTPSIG_KEY man page. Closes #22393 --- scripts/cd2nroff | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 3f5a447929c8..02f97bc41efc 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -200,6 +200,17 @@ my %knowntls = ( 'none' => 1, ); +sub quoted { + my ($d) = @_; + + # in verbatim/quoted blocks, make backslashes literal + $d =~ s/\\/\\\\/g; + + # lines starting with a period or apostrophe need it escaped + $d =~ s/^([.'])/\\&$1/; + return $d; +} + sub single { my @seealso; my @proto; @@ -391,28 +402,27 @@ sub single { if($quote == 4) { # remove the indentation if($d =~ /^ (.*)/) { - push @desc, "$1\n"; + $d = quoted($1); + push @desc, "$d\n"; next; } - else { + # end of quote + $quote = 0; + push @desc, ".fi\n"; + + # fall-through + } + else { + if(/^~~~/) { # end of quote $quote = 0; push @desc, ".fi\n"; next; } - } - if(/^~~~/) { - # end of quote - $quote = 0; - push @desc, ".fi\n"; + $d = quoted($d); + push @desc, $d; next; } - # convert single backslashes to doubles - $d =~ s/\\/\\\\/g; - # lines starting with a period needs it escaped - $d =~ s/^\./\\&./; - push @desc, $d; - next; } # remove single line HTML comments @@ -512,7 +522,8 @@ sub single { $quote = 4; push @desc, "\n" if($blankline && !$header); $header = 0; - push @desc, ".nf\n$1\n"; + my $d = quoted($1); + push @desc, ".nf\n$d\n"; } elsif(/^[ \t]*\n/) { # count and ignore blank lines From c5d683b961d49dbfa297c61c53b58466b960e202 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 25 Jul 2026 23:06:29 +0200 Subject: [PATCH 0721/1062] tool_getparam: clear the --httpsig-key argument To hide it somewhat from process listings. Closes #22394 --- src/tool_getparam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 9364c8683905..479168e0bdb1 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -173,7 +173,7 @@ static const struct LongShort aliases[] = { #ifndef CURL_DISABLE_HTTPSIG {"httpsig-algo", ARG_STRG, ' ', C_HTTPSIG_ALGORITHM}, {"httpsig-headers", ARG_STRG, ' ', C_HTTPSIG_HEADERS}, - {"httpsig-key", ARG_FILE, ' ', C_HTTPSIG_KEY}, + {"httpsig-key", ARG_FILE|ARG_CLEAR, ' ', C_HTTPSIG_KEY}, {"httpsig-keyid", ARG_STRG, ' ', C_HTTPSIG_KEYID}, #endif {"ignore-content-length", ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH}, From 12532713d654d265bb7cb1c0c62e8a942d3a64d5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Jul 2026 16:58:54 +0200 Subject: [PATCH 0722/1062] build: tidy up httpsig options - say 'experimental'. - cmake: add to documentation. - cmake: alpha-sort. Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 Closes #22391 --- CMakeLists.txt | 4 ++-- configure.ac | 4 ++-- docs/INSTALL-CMAKE.md | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f36b49034e8d..663ca5408157 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,8 +435,6 @@ option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) option(CURL_DISABLE_AWS "Disable aws-sigv4" OFF) mark_as_advanced(CURL_DISABLE_AWS) -option(CURL_DISABLE_HTTPSIG "Disable HTTP Message Signatures (RFC 9421)" ON) -mark_as_advanced(CURL_DISABLE_HTTPSIG) option(CURL_DISABLE_DICT "Disable DICT" OFF) mark_as_advanced(CURL_DISABLE_DICT) option(CURL_DISABLE_DOH "Disable DNS-over-HTTPS" OFF) @@ -457,6 +455,8 @@ option(CURL_DISABLE_HTTP "Disable HTTP" OFF) mark_as_advanced(CURL_DISABLE_HTTP) option(CURL_DISABLE_HTTP_AUTH "Disable all HTTP authentication methods" OFF) mark_as_advanced(CURL_DISABLE_HTTP_AUTH) +option(CURL_DISABLE_HTTPSIG "Disable HTTP Message Signatures (RFC 9421) (experimental)" ON) +mark_as_advanced(CURL_DISABLE_HTTPSIG) option(CURL_DISABLE_IMAP "Disable IMAP" OFF) mark_as_advanced(CURL_DISABLE_IMAP) option(CURL_DISABLE_LDAP "Disable LDAP" OFF) diff --git a/configure.ac b/configure.ac index eed980f0ebb3..0d2ee2b3ff32 100644 --- a/configure.ac +++ b/configure.ac @@ -4571,8 +4571,8 @@ dnl disable httpsig (RFC 9421) dnl AC_MSG_CHECKING([whether to enable HTTP Message Signatures (RFC 9421)]) AC_ARG_ENABLE(httpsig, -AS_HELP_STRING([--enable-httpsig],[Enable HTTP Message Signatures support]) -AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (default)]), +AS_HELP_STRING([--enable-httpsig],[Enable HTTP Message Signatures support (experimental)]) +AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (experimental) (default)]), [ case "$enableval" in no) AC_MSG_RESULT(no) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index c95ce3366bd7..59b8c9ecac00 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -278,6 +278,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_HSTS`: Disable HSTS support. Default: `OFF` - `CURL_DISABLE_HTTP`: Disable HTTP. Default: `OFF` - `CURL_DISABLE_HTTP_AUTH`: Disable all HTTP authentication methods. Default: `OFF` +- `CURL_DISABLE_HTTPSIG`: Disable HTTP Message Signatures (RFC 9421) (experimental). Default: `ON` - `CURL_DISABLE_IMAP`: Disable IMAP. Default: `OFF` - `CURL_DISABLE_INSTALL`: Disable installation targets. Default: `OFF` - `CURL_DISABLE_IPFS`: Disable IPFS. Default: `OFF` From b2a5369e708982d1050ccec76967e8fc42f6e2dd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Jul 2026 14:18:07 +0200 Subject: [PATCH 0723/1062] runtests: allow comments in `setenv` section, merge sections in test433 Closes #22389 --- tests/data/test433 | 6 ++---- tests/runner.pm | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/data/test433 b/tests/data/test433 index 85cf29ee4967..1ef5e804451c 100644 --- a/tests/data/test433 +++ b/tests/data/test433 @@ -31,14 +31,12 @@ http XDG_CONFIG_HOME=%PWD/%LOGDIR HOME CURL_HOME +# set the terminal wide to avoid word wrap in the message +COLUMNS=300 Verify XDG_CONFIG_HOME use to find curlrc -# set the terminal wide to avoid word wrap in the message - -COLUMNS=300 - %HOSTIP:%HTTPPORT/%TESTNUMBER --no-progress-meter diff --git a/tests/runner.pm b/tests/runner.pm index 7cbc5067e1c1..230a688bd081 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -665,7 +665,7 @@ sub singletest_setenv { my @setenv = getpart("client", "setenv"); foreach my $s (@setenv) { chomp $s; - if($s =~ /([^=]*)(.*)/) { + if($s !~ /^#/ && $s =~ /([^=]*)(.*)/) { my ($var, $content) = ($1, $2); # remember current setting, to restore it once test runs $oldenv{$var} = $ENV{$var} ? $ENV{$var} : 'notset'; From e416e2948b5433251e8939f65f57734bfd4d07bf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 26 Jul 2026 16:27:34 +0200 Subject: [PATCH 0724/1062] vquic: initialize new callback slot for nghttp3 v1.18.0+ Closes #22399 --- lib/vquic/cf-ngtcp2-proxy.c | 3 +++ lib/vquic/cf-ngtcp2.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 3a7023f300e1..2592b05dab2e 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -625,6 +625,9 @@ static nghttp3_callbacks ngh3_proxy_callbacks = { #ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ NULL, /* recv_settings2 */ #endif +#ifdef NGHTTP3_CALLBACKS_V4 /* nghttp3 v1.18.0+ */ + NULL, /* stream_close2 */ +#endif }; static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 980516d05eaa..9a2b64f235e2 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -410,6 +410,9 @@ static nghttp3_callbacks ngh3_callbacks = { #ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ NULL, /* recv_settings2 */ #endif +#ifdef NGHTTP3_CALLBACKS_V4 /* nghttp3 v1.18.0+ */ + NULL, /* stream_close2 */ +#endif }; static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, From 6b1ff5407ee00f8160434885cc2d4e31f7df4aee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:13:55 +0000 Subject: [PATCH 0725/1062] GHA/http3-linux: update dependency ngtcp2/nghttp3 to v1.18.0 Closes #22397 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a690ecd9aa55..e996a68e26f6 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -56,7 +56,7 @@ env: # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.2 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - NGHTTP3_VERSION: 1.17.0 + NGHTTP3_VERSION: 1.18.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com NGTCP2_VERSION: 1.24.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com From acf4498381cfd4dbaa2a2de6d868eb2f71bfb2b1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 25 Jul 2026 23:48:07 +0200 Subject: [PATCH 0726/1062] http: fix httpsig with auth-redir Do not let unrelated credentials from a redirected URL bypass the cross-host auth boundary Verified by test 5023 to 5025 Follow-up to a55731050e8c3dbea0 Closes #22395 --- lib/http.c | 15 +++++++--- tests/data/Makefile.am | 3 +- tests/data/test5023 | 66 ++++++++++++++++++++++++++++++++++++++++ tests/data/test5024 | 68 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test5025 | 66 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 tests/data/test5023 create mode 100644 tests/data/test5024 create mode 100644 tests/data/test5025 diff --git a/lib/http.c b/lib/http.c index d8bac2e5ccf4..c8c008423366 100644 --- a/lib/http.c +++ b/lib/http.c @@ -672,10 +672,17 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #endif #ifndef CURL_DISABLE_HTTPSIG if((authstatus->picked == CURLAUTH_HTTPSIG) && !proxy) { - auth = "HTTPSIG"; - result = Curl_output_httpsig(data); - if(result) - return result; + /* HTTPSIG uses its own configured key material rather than + data->state.creds. Do not let unrelated credentials from a + redirected URL bypass the cross-host auth boundary. */ + if(Curl_auth_allowed_to_host(data)) { + auth = "HTTPSIG"; + result = Curl_output_httpsig(data); + if(result) + return result; + } + else + authstatus->done = TRUE; } else #endif diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 209cc43598b5..728858dafa2d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -294,7 +294,8 @@ test4000 test4001 \ \ test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ -test5016 test5017 test5018 test5019 test5020 test5021 test5022 +test5016 test5017 test5018 test5019 test5020 test5021 test5022 test5023 \ +test5024 test5025 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ diff --git a/tests/data/test5023 b/tests/data/test5023 new file mode 100644 index 000000000000..1d59c239943f --- /dev/null +++ b/tests/data/test5023 @@ -0,0 +1,66 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 301 OK +Content-Length: 0 +Location: http://example.org:9000/%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP Message Signature with cross-origin redirect + + +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --connect-to example.org:9000:%HOSTIP:%HTTPPORT --location + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:faTRQjDfWWm39STQkXyafoAp6ee2FCPh2KMefXOB51WAvoOdc6/Uwp9LW4zNWI7r+ivTKY7oSDb5kqcPJ/aSAQ==: +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: example.org:9000 +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5024 b/tests/data/test5024 new file mode 100644 index 000000000000..930b0de65d0a --- /dev/null +++ b/tests/data/test5024 @@ -0,0 +1,68 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 301 OK +Content-Length: 0 +Location: /%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP Message Signature with same-origin redirect + + +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --location + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:pxRrwEySHVgwXOI93y5KxKytfoXU6leQpC7i82Uodh8+oI7ycqCogD4PUU1p0RUAzoqeT2RHVWIN7Qg3k4A9DQ==: +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:QVx0WyxuyHqb3j0YihCLaWYdZOySKoIezj6FmTzsJprSqgm6yUsFbcWek4r6pALF1/jKGwNB1Rbyv3DE/pn/BA==: +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test5025 b/tests/data/test5025 new file mode 100644 index 000000000000..10c48073def8 --- /dev/null +++ b/tests/data/test5025 @@ -0,0 +1,66 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 301 OK +Content-Length: 0 +Location: http://bob:alice@example.org:9000/%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP Message Signature with cross-origin + user auth redirect + + +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --connect-to example.org:9000:%HOSTIP:%HTTPPORT --location + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:OUXXFiQlk/CEUksEkxvuivcFqWxL3xoPIZhB9FKTrx4MbhQqCRYMT0CQCUA9wCeRGkzmpGComBk5oPwKZr+MDg==: +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: example.org:9000 +User-Agent: curl/%VERSION +Accept: */* + + + + From 6f39c854c8e9df067e0acf0cccf631035d40ca7c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Jul 2026 13:43:33 +0200 Subject: [PATCH 0727/1062] runtests: fix `mode="warn"` tests passing unconditionally, fix test 1752 Fix test 1712 to pass curl C by setting `COLUMNS` to the highest accepted value, and adjust expected results. To avoid envs with varying lengths of `LOGDIR` affect the outcome. Apply the same fix to test 459, though it wasn't affected in curl CI. Also sync up test 433 `COLUMNS` value with these two tests for consistency. Ref: #22381 Follow-up to 8e3a2a64d103a46508e17cde76595993de96ea6c #20666 Closes #22388 --- tests/data/test1712 | 6 ++++-- tests/data/test433 | 2 +- tests/data/test459 | 6 ++++-- tests/runtests.pl | 4 ---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/data/test1712 b/tests/data/test1712 index 5cdc642d0906..03fc9a729092 100644 --- a/tests/data/test1712 +++ b/tests/data/test1712 @@ -30,6 +30,9 @@ Funny-head: yesyes http + +COLUMNS=10000 + config file with argument using single quotes @@ -54,8 +57,7 @@ Content-Type: application/x-www-form-urlencoded 'arg-with-quote' -Warning: %LOGDIR/config:1 Option 'data' uses argument with leading single quote.%SP -It is probably a mistake. Consider double quotes. +Warning: %LOGDIR/config:1 Option 'data' uses argument with leading single quote. It is probably a mistake. Consider double quotes. diff --git a/tests/data/test433 b/tests/data/test433 index 1ef5e804451c..8a3cee80a1d7 100644 --- a/tests/data/test433 +++ b/tests/data/test433 @@ -32,7 +32,7 @@ XDG_CONFIG_HOME=%PWD/%LOGDIR HOME CURL_HOME # set the terminal wide to avoid word wrap in the message -COLUMNS=300 +COLUMNS=10000 Verify XDG_CONFIG_HOME use to find curlrc diff --git a/tests/data/test459 b/tests/data/test459 index 91f2d67bca18..f5649922cd86 100644 --- a/tests/data/test459 +++ b/tests/data/test459 @@ -30,6 +30,9 @@ Funny-head: yesyes http + +COLUMNS=10000 + config file with argument using whitespace missing quotes @@ -54,8 +57,7 @@ Content-Type: application/x-www-form-urlencoded arg -Warning: %LOGDIR/config:1 Option 'data' uses argument with unquoted whitespace.%SP -Warning: This may cause side-effects. Consider double quotes. +Warning: %LOGDIR/config:1 Option 'data' uses argument with unquoted whitespace. This may cause side-effects. Consider double quotes. diff --git a/tests/runtests.pl b/tests/runtests.pl index d7582ded96ab..eee5c2950e6a 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -1372,10 +1372,6 @@ sub singletest_check { s/\r//; s/\n/ /; } - my $v = join(@validstderr, ""); - my $a = join(@actual, ""); - @validstderr = $v; - @actual = $a; } if($hash{'nonewline'}) { From 16d49ac659659bb4fd0c4d7d0e6add87a5762ec6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 27 Jul 2026 01:41:07 +0200 Subject: [PATCH 0728/1062] GHA/windows: bump stunnel to 5.79 Closes #22403 --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 267a2a108a37..bcee2e7330e2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,8 +39,8 @@ env: OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 - STUNNEL_VERSION: '5.78' - STUNNEL_SHA256: 32a88dcc5654f955266109be8bf10fd7d56fa4e125cab821ee508230570e46c5 + STUNNEL_VERSION: '5.79' + STUNNEL_SHA256: 3d946e9759d5a6c6fc5aadb6b56e7de1a81d210ad028235340616e471afeb6f6 jobs: build-cache: From 17855dd44795afa8856facefb6d0e69026526250 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 27 Jul 2026 09:40:05 +0200 Subject: [PATCH 0729/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 68 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 2c0f27e4e09c..b750535fa1d2 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,24 +1,27 @@ curl and libcurl 8.22.0 Public curl releases: 276 - Command line options: 274 - curl_easy_setopt() options: 308 + Command line options: 278 + curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1497 - Contributors: 3749 + Authors: 1500 + Contributors: 3753 This release includes the following changes: o gssapi: add support for Apple GSS Framework [72] o hardening: add API guards [64] + o RFC 9421 HTTP Message Signatures support [108] o TLS: drop support for TLS-SRP [71] This release includes the following bugfixes: + o asyn-thrdd: retry link-local ipv6 if missing scope id [118] o autotools: minor fixes and improvements [33] o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] o build: drop superfluous `STDC_HEADERS` macro [51] o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] + o cd2nroff: fix backslashes for 4-space indent lines [104] o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] o cfilters: fix event-based connection shutdown [91] @@ -31,24 +34,29 @@ This release includes the following bugfixes: o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] o configure: fix misleading error messages [42] o configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows [79] + o configure: only check in the watt library if WATT_ROOT is set [120] o configure: remove double check for GnuTLS [21] o configure: set ldap lib to no by default for non-finds [18] o conncache: apply multi limits to transfers using a shared pool [41] o conncache: connection alive checks intervals [20] + o connect: connection close tweaks [112] o content_encoding: give a clear error on multi-member gzip [46] o CREDENTIALS.md: remove comment about empty user/pass [50] + o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] - o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization' instead of 'Authentication' [74] + o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gopher: reject CR and LF in the selector [1] o hostip: only cache negative resolves for authoritative answers [16] o http: avoid length underflow in Curl_compareheader [78] + o http: fix non-tunneling proxy hostname use [116] o http: stop dropping large custom headers [69] o http: trim custom header name before the Authorization drop [17] + o httpsrr: DoH with HTTPS, fix response handling [113] o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] @@ -59,11 +67,13 @@ This release includes the following bugfixes: o lib: fix 'ns' -> 'us' in trace messages [57] o lib: ratelimit timestamps [14] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] + o md5: replace magic numbers with `MD5_DIGEST_LEN` [122] o mime.c: avoid integer overflow in base64 size calculation [105] o mime: reject CR and LF in mail part name and filename [30] o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] + o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] o openssl+sectrust: fix session reuse [4] @@ -72,25 +82,33 @@ This release includes the following bugfixes: o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] o quiche: set the max field section size [100] + o runtests: allow comments in `setenv` section, merge sections in test433 [89] + o runtests: fix `mode="warn"` tests passing unconditionally, fix test 1752 [66] o runtests: flush cached test parts when (re)loading a file [95] o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] + o scripts: replace/extend `--` with `--end-of-options` in git commands [128] o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] o setopt: error for CURLOPT_SHARE when easy handle is used [68] o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] + o spacecheck: cap number of lines per file [111] o ssls: fix potential memory leak on import [96] o sws: allow connection-monitor to log all disconnects [2] + o terminal: Enhance terminal size detection for multiple outputs [115] o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] o test1560: allow to build and run without LDAP support [109] o test798: force IPv4 to avoid cross-runner port aliasing [97] o test: adjust test_06_13 for 0100::/64 being blackholed [13] + o tests: fix the FTP check for unexpected RST [117] o tests: fix type promotion on 32-bit arches in http test code [88] o tests: remove test1701 [58] o tests: skip test 311 for wolfSSL 5.9.2 [63] o thrdpool: retry failed thread starts while items wait [62] o tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) [102] + o tidy-up: drop redundant includes [110] + o tidy-up: fix Perl syntax and formatting nits [123] o tidy-up: typos, comment nits [60] o timeval: make `Curl_freq` variable static (Windows) [90] o tool: do not flush on out-null [38] @@ -98,14 +116,18 @@ This release includes the following bugfixes: o tool: init progress bar on demand [39] o tool_cb_hdr: de-duplicate filename setter [24] o tool_cb_prg: avoid integer overflows [93] + o tool_doswin: add stdin relay auth [130] o tool_operate: remove call to abort() [23] + o tool_xattr: add support for Windows alternate data stream [129] o uint-spbset: reused empty chunks [67] o unit3214: fix to pass on systems with >=128-bit pointers [107] o url: reject control codes in credentials set via CURLOPT [70] o urlapi: allow URLs to not have userauth (hostname) [92] o urlapi: do not keep an internal port string [31] o urlapi: preserve empty markers in relative URLs [61] + o vms: fix symbol typo and missing closing quotes in `config_h.com` [124] o vquic: add Curl_ prefix to some global functions [76] + o vquic: initialize new callback slot for nghttp3 v1.18.0+ [87] o vssh: keyfile use cleanups [83] o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] o wolfssl: fix build for wolfssl without bio chain support [75] @@ -130,16 +152,18 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Alb3e3, Alhuda Khan, Bigtang on hackerone, Bill Mill, Bryan Henderson, + 11soda11, AlanKingPL, Alb3e3, Alhuda Khan, Bartel Sielski, + Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, Christian Ullrich, Christoph Reiter, Collin Funk, Dan Fandrich, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, - Keng-Yu Lin, Laurent Sabourin, Memduh Çelik, Patrick Monnerat, - pszemus on github, Ray Satiro, renovate[bot], Roger Leigh, Sam James, - Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, - stze on hackerone, Viktor Szakats, xmoezzz on github - (37 contributors) + Keng-Yu Lin, Laurent Sabourin, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, + pszemus on github, Ray Satiro, renovate[bot], RMMoreton on github, + Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, Samuel Dainard, + Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, stze on hackerone, + Viktor Szakats, xmoezzz on github + (44 contributors) References to bug reports and discussions on issues: @@ -207,6 +231,7 @@ References to bug reports and discussions on issues: [62] = https://curl.se/bug/?i=22303 [63] = https://curl.se/bug/?i=22286 [64] = https://curl.se/bug/?i=22237 + [66] = https://curl.se/bug/?i=22388 [67] = https://curl.se/bug/?i=22340 [68] = https://curl.se/bug/?i=22253 [69] = https://curl.se/bug/?i=22336 @@ -225,7 +250,9 @@ References to bug reports and discussions on issues: [83] = https://curl.se/bug/?i=22243 [84] = https://curl.se/bug/?i=22245 [86] = https://curl.se/bug/?i=22272 + [87] = https://curl.se/bug/?i=22399 [88] = https://curl.se/bug/?i=22210 + [89] = https://curl.se/bug/?i=22389 [90] = https://curl.se/bug/?i=22346 [91] = https://curl.se/bug/?i=22282 [92] = https://curl.se/bug/?i=22279 @@ -238,6 +265,25 @@ References to bug reports and discussions on issues: [101] = https://curl.se/bug/?i=22333 [102] = https://curl.se/bug/?i=22334 [103] = https://curl.se/bug/?i=22326 + [104] = https://curl.se/bug/?i=22393 [105] = https://curl.se/bug/?i=22320 [107] = https://curl.se/bug/?i=22299 + [108] = https://curl.se/bug/?i=22386 [109] = https://curl.se/bug/?i=22312 + [110] = https://curl.se/bug/?i=22374 + [111] = https://curl.se/bug/?i=22387 + [112] = https://curl.se/bug/?i=22379 + [113] = https://curl.se/bug/?i=22372 + [115] = https://curl.se/bug/?i=22276 + [116] = https://curl.se/bug/?i=22382 + [117] = https://curl.se/bug/?i=22305 + [118] = https://curl.se/bug/?i=22330 + [119] = https://curl.se/bug/?i=22371 + [120] = https://curl.se/bug/?i=22380 + [122] = https://curl.se/bug/?i=22377 + [123] = https://curl.se/bug/?i=22376 + [124] = https://curl.se/bug/?i=22375 + [126] = https://curl.se/bug/?i=22363 + [128] = https://curl.se/bug/?i=22369 + [129] = https://curl.se/bug/?i=22354 + [130] = https://curl.se/bug/?i=21467 From e90c4397c8f522e8e395707f3aba0f201e41a1c7 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 26 Jul 2026 16:57:51 +0200 Subject: [PATCH 0730/1062] vquic: silence `-Wmissing-field-initializers` for nghttp3/ngtcp2 callback tables To avoid a breakage in CI and curl-for-win builds on upstream updates extending the callback lists. Each such breakage needed patching curl, rolling these patches into curl-for-win, and doing it in near real-time, to keep CI and builds working (and still causing some red CI jobs). Bring calmness here by suppressing the warnings and allowing time to extend the callback tables as/if needed and at a convenient moment. Closes #22400 --- lib/vquic/cf-ngtcp2-cmn.c | 7 +++++++ lib/vquic/cf-ngtcp2-proxy.c | 7 +++++++ lib/vquic/cf-ngtcp2.c | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index e6efd2bd64e6..c474fdbdc549 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -546,6 +546,10 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, return 0; } +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ @@ -600,6 +604,9 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* recv_stop_sending */ #endif }; +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif #if defined(_MSC_VER) && defined(_DLL) #pragma warning(pop) diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 2592b05dab2e..ec6de93368af 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -601,6 +601,10 @@ static nghttp3_ssize cb_h3_tunnel_read_data(nghttp3_conn *conn, return (nghttp3_ssize)nvecs; } +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif static nghttp3_callbacks ngh3_proxy_callbacks = { cb_h3_proxy_acked_req_body, /* acked_stream_data */ cb_h3_proxy_stream_close, @@ -629,6 +633,9 @@ static nghttp3_callbacks ngh3_proxy_callbacks = { NULL, /* stream_close2 */ #endif }; +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, struct Curl_easy *data, diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 9a2b64f235e2..4d5c122183fb 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -386,6 +386,10 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, return 0; } +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif static nghttp3_callbacks ngh3_callbacks = { cb_h3_acked_req_body, /* acked_stream_data */ cb_h3_stream_close, @@ -414,6 +418,9 @@ static nghttp3_callbacks ngh3_callbacks = { NULL, /* stream_close2 */ #endif }; +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, struct Curl_easy *data, From f9dc57a149a8ba38557b5d0e1f7e31ce9b93c810 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 26 Jul 2026 21:15:33 +0200 Subject: [PATCH 0731/1062] pytest: update two H3 tests for nghttp3 1.18.0+ Fixing: ``` FAILED ../../tests/http/test_02_download.py::TestDownload::test_02_36_looong_urls[65536-h3] - AssertionError: expected exit code 0, got 56 FAILED ../../tests/http/test_14_auth.py::TestAuth::test_14_05_basic_large_pw[h3] - AssertionError: expected exit code 0, got 56 ``` Ref: https://github.com/curl/curl/actions/runs/30207198835/job/89807247058?pr=22400 Refs: https://github.com/ngtcp2/nghttp3/pull/539 https://github.com/ngtcp2/nghttp3/commit/a5872645446cdc07e897fea33b61a3c665563da9 Bug: https://github.com/curl/curl/pull/22397#issuecomment-5084927935 Closes #22402 --- tests/http/test_02_download.py | 14 +++++++------- tests/http/test_14_auth.py | 12 +++++++++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index de2f1a3e0935..413d518a6ea4 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -720,11 +720,11 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): # h2 is unable to send such large headers (frame limits) r.check_exit_code(55) elif proto == 'h3': - if url_junk <= 64 * 1024: - r.check_exit_code(0) - # nghttpx reports 431 Request Header Field too Large + # nghttpx reports 431 Request Header Field too Large + # or destroys the connection with internal error + # ERR_QPACK_HEADER_TOO_LARGE, + # depending on nghttp3 version and payload size + assert r.exit_code in (0, 56), f'expected exit code 0 or 56, '\ + f'got {r.exit_code}\n{r.dump_logs()}' + if r.exit_code == 0: r.check_response(http_status=431) - else: - # nghttpx destroys the connection with internal error - # ERR_QPACK_HEADER_TOO_LARGE - r.check_exit_code(56) diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 69866baedf98..095dc382baf7 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -92,7 +92,7 @@ def test_14_04_digest_large_pw(self, env: Env, httpd, nghttpx, proto): # PUT data, basic auth large pw @pytest.mark.parametrize("proto", Env.http_mplx_protos()) def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto): - if proto == 'h3' and not env.curl_uses_lib('ngtcp2'): + if proto == 'h3' and env.curl_uses_lib('quiche'): # See pytest.skip("quiche has problems with large requests") # large enough that nghttp2 will submit @@ -105,8 +105,14 @@ def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto): '--trace-config', 'http/2,http/3' ]) # but apache either denies on length limit or gives a 400 - r.check_exit_code(0) - assert r.stats[0]['http_code'] in [400, 431] + if proto == 'h3': + # depends on nghttp3 version + assert r.exit_code in (0, 56), f'expected exit code 0 or 56, '\ + f'got {r.exit_code}\n{r.dump_logs()}' + else: + r.check_exit_code(0) + if r.exit_code == 0: + assert r.stats[0]['http_code'] in [400, 431] # PUT data, basic auth with very large pw @pytest.mark.parametrize("proto", Env.http_mplx_protos()) From 7484874fdf799398523fc57022c3fd2cc85835f3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 27 Jul 2026 02:42:09 +0200 Subject: [PATCH 0732/1062] cmake: verify if options are listed in `INSTALL-CMAKE.md` Also: - add one debug option to pass the test. Ref: https://github.com/curl/curl/discussions/14885#discussioncomment-10632311 Closes #22404 --- .github/workflows/checksrc.yml | 6 ++++-- docs/INSTALL-CMAKE.md | 1 + scripts/Makefile.am | 6 +++--- scripts/cmakeopts.sh | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100755 scripts/cmakeopts.sh diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 213aa94c2af7..8baeac288749 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -84,9 +84,11 @@ jobs: source ~/venv/bin/activate scripts/cmakelint.sh + - name: 'cmake options in documentation' + run: scripts/cmakeopts.sh + - name: 'perlcheck' - run: | - scripts/perlcheck.sh + run: scripts/perlcheck.sh - name: 'ruff' run: | diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 59b8c9ecac00..d241efb0077b 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -203,6 +203,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_COMPLETION_FISH_DIR`: Custom fish completion install directory. - `CURL_COMPLETION_ZSH`: Install zsh completions. Default: `OFF` - `CURL_COMPLETION_ZSH_DIR`: Custom zsh completion install directory. +- `CURL_DEBUG_GLOBAL_MEM`: Debug `curl_global_init_mem`. Default: `OFF` - `CURL_DEFAULT_SSL_BACKEND`: Override default TLS backend in MultiSSL builds. Accepted values in order of default priority: `wolfssl`, `gnutls`, `mbedtls`, `openssl`, `schannel`, `rustls` diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 7ffa98ed9bb6..6d0e92432d81 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -25,9 +25,9 @@ EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ checksrc-all.pl mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd \ cdall cd2cd managen dmaketgz maketgz release-tools.sh verify-release \ - cmakelint.sh mdlinkcheck CMakeLists.txt perlcheck.sh pythonlint.sh \ - spacecheck.pl randdisable wcurl top-complexity extract-unit-protos \ - .checksrc badwords badwords-all badwords.txt top-length + cmakelint.sh cmakeopts.sh mdlinkcheck CMakeLists.txt perlcheck.sh \ + pythonlint.sh spacecheck.pl randdisable wcurl top-complexity \ + extract-unit-protos .checksrc badwords badwords-all badwords.txt top-length dist_bin_SCRIPTS = wcurl diff --git a/scripts/cmakeopts.sh b/scripts/cmakeopts.sh new file mode 100755 index 000000000000..a58e086b4c73 --- /dev/null +++ b/scripts/cmakeopts.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Copyright (C) Viktor Szakats +# +# SPDX-License-Identifier: curl + +# Verify if CMake options are documented + +set -eu + +cd -- "$(dirname "$0")"/.. + +anyerr=0 +while read -r opt; do + if ! grep -q -F -- "- \`$opt\`: " docs/INSTALL-CMAKE.md; then + echo "CMake option missing from documentation: '$opt'" + anyerr=1 + fi +done < <( + { + git grep -o -E 'option\([A-Z][A-Z0-9_]+' ':!tests/cmake' | + grep -i cmake | + sed -E -e 's/^.+://g' -e 's/option\(//g' + + git grep -h -o -E 'curl_dependency_option\([A-Z][A-Z0-9_]+' | + sed -e 's/curl_dependency_option(//g' + + git grep -h -o -E "^# - \`[A-Z0-9_]+\`: " ':CMake/Find**' | + grep -o -E '[A-Z0-9_]+' | + grep -v -E '(_FOUND|_VERSION|NGTCP2_CRYPTO_BACKEND)' + } | sort -u +) + +exit "${anyerr}" From 6350eb01ed3f4a1ad2e1c6a1d4cdcd855eac3a10 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 26 Jul 2026 20:48:02 +0200 Subject: [PATCH 0733/1062] cf-ngtcp2-cmn: de-duplicate `ngtcp2_conn_client_new()` call code Closes #22401 --- lib/vquic/cf-ngtcp2-cmn.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index c474fdbdc549..9cc3cad5c0f7 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -951,17 +951,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->q.local_addrlen); ngtcp2_addr_init(&ctx->connected_path.remote, &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; } else { /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address @@ -995,18 +984,18 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ngtcp2_addr_init(&ctx->connected_path.remote, &remote->curl_sa_addr, (socklen_t)remote->addrlen); + } - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - } + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer, &ALPN_SPEC_H3, From ed3508b1ee43534d65299b9440d5ba7906f4fcf2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:20:01 +0000 Subject: [PATCH 0734/1062] GHA: update dependency ngtcp2/ngtcp2 to v1.25.0 Closes #22398 --- .github/workflows/http3-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index e996a68e26f6..157bf6758394 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -58,7 +58,7 @@ env: # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com NGHTTP3_VERSION: 1.18.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - NGTCP2_VERSION: 1.24.0 + NGTCP2_VERSION: 1.25.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 # no tagged releases From 4eb4b0d080723d328c4bf88c3be86faf02a39c5f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 20 Jul 2026 14:37:13 +0200 Subject: [PATCH 0735/1062] vquic: use ngtcp2 v1.25.0 new close2 callback Forward only the app error code from the receiving side to the h3 layer. This only takes effect when building against ngtcp2 v1.25.0 or higher. Fixes #22270 Closes #22356 --- lib/vquic/cf-ngtcp2-cmn.c | 39 ++++++++++++++++++++++++++++++++++++++ tests/http/test_14_auth.py | 10 ++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 9cc3cad5c0f7..ea214ff8b06b 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -388,6 +388,42 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, return 0; } +#ifdef NGTCP2_CALLBACKS_V5 /* ngtcp2 v1.25.0+ */ +static int cb_stream_close2(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, + uint64_t rx_app_error_code, + uint64_t tx_app_error_code, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + uint64_t h3_app_error_code = NGHTTP3_H3_NO_ERROR; + int rv; + + (void)tconn; + (void)tx_app_error_code; + /* stream is closed... */ + if(!data) + data = CF_DATA_CURRENT(cf); + if(!data) + return NGTCP2_ERR_CALLBACK_FAILURE; + + if(flags & NGTCP2_STREAM_CLOSE2_FLAG_RX_APP_ERROR_CODE_SET) + h3_app_error_code = rx_app_error_code; + + rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, h3_app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" + PRIu64 ") -> %d", stream_id, h3_app_error_code, rv); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + Curl_cf_ngtcp2_h3_err_set(cf, data, rv); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} +#endif + static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) @@ -603,6 +639,9 @@ static ngtcp2_callbacks ng_callbacks = { #ifdef NGTCP2_CALLBACKS_V4 /* ngtcp2 v1.24.0+ */ NULL, /* recv_stop_sending */ #endif +#ifdef NGTCP2_CALLBACKS_V5 /* ngtcp2 v1.25.0+ */ + cb_stream_close2, /* is called instead of cb_stream_close when set */ +#endif }; #ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 095dc382baf7..ca808e007795 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -104,14 +104,12 @@ def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto): '--basic', '--user', f'test:{password}', '--trace-config', 'http/2,http/3' ]) - # but apache either denies on length limit or gives a 400 - if proto == 'h3': - # depends on nghttp3 version - assert r.exit_code in (0, 56), f'expected exit code 0 or 56, '\ - f'got {r.exit_code}\n{r.dump_logs()}' + if proto == 'h3' and r.exit_code != 0: + # nghttpx violently closes the connection now + assert r.exit_code in [55, 56, 95], f'{r.dump_logs()}' else: + # but apache either denies on length limit or gives a 400 r.check_exit_code(0) - if r.exit_code == 0: assert r.stats[0]['http_code'] in [400, 431] # PUT data, basic auth with very large pw From c5ad90bf9a8471987ebb47a05881a51108bcf87f Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 27 Jul 2026 13:17:56 +0200 Subject: [PATCH 0736/1062] typecheck-gcc: allow passing `char[]` as callback data E.g. `TEST_DATA_STRING` in test 655. Cherry-picked from #22406 Closes #22409 --- include/curl/typecheck-gcc.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index c70bd17cb84e..f2e4542772c5 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -608,9 +608,10 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, * == or whatsoever. */ -/* XXX: should evaluate to true if expr is a pointer */ +/* XXX: should evaluate to true if expr is a pointer or a char[] array */ #define curlcheck_any_ptr(expr) \ - (sizeof(expr) == sizeof(void *)) + (sizeof(expr) == sizeof(void *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) /* evaluates to true if expr is NULL */ /* XXX: must not evaluate expr, so this check is not accurate */ @@ -677,7 +678,7 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ -/* XXX: also check size of an char[] array? */ +/* XXX: also check size of a char[] array? */ #define curlcheck_error_buffer(expr) \ (curlcheck_NULL(expr) || \ __builtin_types_compatible_p(__typeof__(expr), char *) || \ From c9ead9bd1cdc7fa03d5e9eb20ebed37e27110407 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 27 Jul 2026 09:49:33 +0200 Subject: [PATCH 0737/1062] conncache: conn upkeep/alive: move and enhance - move `Curl_conn_seems_dead()` into conncache.c - move `Curl_conn_upkeep()` into conncache.c - when upkeep gives an error on a connection not in use, terminate it Closes #21806 --- lib/cfilters.c | 22 +++++----- lib/cfilters.h | 5 +-- lib/conncache.c | 99 ++++++++++++++++++++++++++++++++++++++++-- lib/conncache.h | 7 +++ lib/url.c | 113 ++---------------------------------------------- lib/url.h | 19 +------- 6 files changed, 122 insertions(+), 143 deletions(-) diff --git a/lib/cfilters.c b/lib/cfilters.c index bef39e6f5b62..0af8c4bede88 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -450,9 +450,9 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, int event, int arg1, void *arg2) { CURLcode result = CURLE_OK; - size_t i; + int i; - for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { + for(i = 0; i < (int)CURL_ARRAYSIZE(conn->cfilter); ++i) { result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, event, arg1, arg2); if(!ignore_result && result) @@ -753,7 +753,7 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, * connect or shutdown does not add poll events for the other. Check * against the transfer's own interest, before any chain added sockets * of its own. */ - for(i = 0; (i < 2) && !result; ++i) { + for(i = 0; (i < (int)CURL_ARRAYSIZE(conn->cfilter)) && !result; ++i) { if(conn->cfilter[i] && (want_io || !Curl_conn_is_connected(conn, i) || Curl_shutdown_started(conn, i))) @@ -978,15 +978,17 @@ bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, } CURLcode Curl_conn_keep_alive(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) + struct connectdata *conn) { - struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + int i; - if(!CONN_SOCK_IDX_VALID(sockindex)) - return CURLE_BAD_FUNCTION_ARGUMENT; - cf = conn->cfilter[sockindex]; - return cf ? cf->cft->keep_alive(cf, data) : CURLE_OK; + for(i = 0; (i < (int)CURL_ARRAYSIZE(conn->cfilter)) && !result; ++i) { + struct Curl_cfilter *cf = conn->cfilter[i]; + if(cf) + result = cf->cft->keep_alive(cf, data); + } + return result; } size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, diff --git a/lib/cfilters.h b/lib/cfilters.h index f55a572e2e93..cfdb5782ee7a 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -558,11 +558,10 @@ bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, bool *input_pending); /** - * Try to upkeep the connection filters at sockindex. + * Try to upkeep the connection filters. */ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); + struct connectdata *conn); /** * Get the remote hostname and port that the connection is currently diff --git a/lib/conncache.c b/lib/conncache.c index eb88d67dc8f7..897b41cc4251 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -715,7 +715,7 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, if(!terminate) { reaper->checked++; - terminate = Curl_conn_seems_dead(conn, data, &reaper->now); + terminate = Curl_cpool_conn_seems_dead(conn, data, &reaper->now); } if(terminate) { /* stop the iteration here, pass back the connection that was pruned */ @@ -761,7 +761,21 @@ static int conn_upkeep(struct Curl_easy *data, void *param) { (void)param; - Curl_conn_upkeep(data, conn); + if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) >= + data->set.upkeep_interval_ms) { + CURLcode result; + + /* briefly attach for action */ + Curl_attach_connection(data, conn); + result = Curl_conn_keep_alive(data, conn); + conn->keepalive = *Curl_pgrs_now(data); + Curl_detach_connection(data); + + if(result && !CONN_INUSE(conn)) { + Curl_conn_terminate(data, conn, FALSE); + return 1; + } + } return 0; /* continue iteration */ } @@ -773,7 +787,8 @@ CURLcode Curl_cpool_upkeep(struct Curl_easy *data) return CURLE_OK; CPOOL_LOCK(cpool, data); - cpool_foreach(data, cpool, NULL, conn_upkeep); + while(cpool_foreach(data, cpool, NULL, conn_upkeep)) + ; CPOOL_UNLOCK(cpool, data); return CURLE_OK; } @@ -858,6 +873,84 @@ void Curl_cpool_nw_changed(struct Curl_easy *data) } } +/* A connection has to have been idle for less than 'conn_max_idle_ms' + (the success rate is too low after this), or created less than + 'conn_max_age_ms' ago, to be subject for reuse. */ +static bool cpool_conn_maxage(struct Curl_easy *data, + struct connectdata *conn, + const struct curltime *pnow) +{ + timediff_t age_ms; + + if(data->set.conn_max_idle_ms) { + age_ms = curlx_ptimediff_ms(pnow, &conn->lastused); + if(age_ms > data->set.conn_max_idle_ms) { + infof(data, "Too old connection (%" FMT_TIMEDIFF_T + " ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it", + age_ms, data->set.conn_max_idle_ms); + return TRUE; + } + } + + if(data->set.conn_max_age_ms) { + age_ms = curlx_ptimediff_ms(pnow, &conn->created); + if(age_ms > data->set.conn_max_age_ms) { + infof(data, + "Too old connection (created %" FMT_TIMEDIFF_T + " ms ago, max lifetime is %" FMT_TIMEDIFF_T " ms), disconnect it", + age_ms, data->set.conn_max_age_ms); + return TRUE; + } + } + + return FALSE; +} + +/* + * Return TRUE iff the given connection is considered dead. + */ +bool Curl_cpool_conn_seems_dead(struct connectdata *conn, + struct Curl_easy *data, + const struct curltime *pnow) +{ + bool input_pending = FALSE; + bool dead = FALSE; + + DEBUGASSERT(!data->conn); + /* The check only makes sense only if the connection is not in use */ + if(CONN_INUSE(conn)) + return FALSE; + else if(cpool_conn_maxage(data, conn, pnow)) /* too old? */ + return TRUE; + else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) + return FALSE; + else if(conn->scheme->run->connection_is_dead) { + Curl_attach_connection(data, conn); + dead = conn->scheme->run->connection_is_dead(data, conn); + Curl_detach_connection(data); + } + else { + Curl_attach_connection(data, conn); + dead = !Curl_conn_is_alive(data, conn, &input_pending); + Curl_detach_connection(data); + } + + if(input_pending) { + /* For reuse, we want a "clean" connection state. This includes + * that we expect - in general - no waiting input data. Input + * waiting might be a TLS Notify Close, for example. We reject + * that. + * For protocols where data from other end may arrive at + * any time (HTTP/2 PING for example), the protocol handler needs + * to install its own `connection_check` callback. + */ + DEBUGF(infof(data, "connection has input pending, not reusable")); + dead = TRUE; + } + + return dead; +} + #if 0 /* Useful for debugging the connection pool */ void Curl_cpool_print(struct cpool *cpool) diff --git a/lib/conncache.h b/lib/conncache.h index 78ccd8282fa2..6798a3b8b3e7 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -156,4 +156,11 @@ void Curl_cpool_do_locked(struct Curl_easy *data, /* Close all unused connections, prevent reuse of existing ones. */ void Curl_cpool_nw_changed(struct Curl_easy *data); +/** + * Return TRUE iff the given connection is considered dead. + */ +bool Curl_cpool_conn_seems_dead(struct connectdata *conn, + struct Curl_easy *data, + const struct curltime *pnow); + #endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/lib/url.c b/lib/url.c index 1c837c586e8a..4cb25350338c 100644 --- a/lib/url.c +++ b/lib/url.c @@ -564,115 +564,6 @@ static bool proxy_info_matches(const struct proxy_info *data, } #endif -/* A connection has to have been idle for less than 'conn_max_idle_ms' - (the success rate is too low after this), or created less than - 'conn_max_age_ms' ago, to be subject for reuse. */ -static bool conn_maxage(struct Curl_easy *data, - struct connectdata *conn, - struct curltime now) -{ - timediff_t age_ms; - - if(data->set.conn_max_idle_ms) { - age_ms = curlx_ptimediff_ms(&now, &conn->lastused); - if(age_ms > data->set.conn_max_idle_ms) { - infof(data, "Too old connection (%" FMT_TIMEDIFF_T - " ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it", - age_ms, data->set.conn_max_idle_ms); - return TRUE; - } - } - - if(data->set.conn_max_age_ms) { - age_ms = curlx_ptimediff_ms(&now, &conn->created); - if(age_ms > data->set.conn_max_age_ms) { - infof(data, - "Too old connection (created %" FMT_TIMEDIFF_T - " ms ago, max lifetime is %" FMT_TIMEDIFF_T " ms), disconnect it", - age_ms, data->set.conn_max_age_ms); - return TRUE; - } - } - - return FALSE; -} - -/* - * Return TRUE iff the given connection is considered dead. - */ -bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - const struct curltime *pnow) -{ - DEBUGASSERT(!data->conn); - if(!CONN_INUSE(conn)) { - /* The check for a dead socket makes sense only if the connection is not in - use */ - bool dead; - - if(conn_maxage(data, conn, *pnow)) { - /* avoid check if already too old */ - dead = TRUE; - } - else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) - dead = FALSE; - else if(conn->scheme->run->connection_is_dead) { - /* The protocol has a special method for checking the state of the - connection. Use it to check if the connection is dead. */ - /* briefly attach the connection for the check */ - Curl_attach_connection(data, conn); - dead = conn->scheme->run->connection_is_dead(data, conn); - Curl_detach_connection(data); - conn->lastchecked = *pnow; - } - else { - bool input_pending = FALSE; - - Curl_attach_connection(data, conn); - dead = !Curl_conn_is_alive(data, conn, &input_pending); - if(input_pending) { - /* For reuse, we want a "clean" connection state. This includes - * that we expect - in general - no waiting input data. Input - * waiting might be a TLS Notify Close, for example. We reject - * that. - * For protocols where data from other end may arrive at - * any time (HTTP/2 PING for example), the protocol handler needs - * to install its own `connection_check` callback. - */ - DEBUGF(infof(data, "connection has input pending, not reusable")); - dead = TRUE; - } - Curl_detach_connection(data); - conn->lastchecked = *pnow; - } - - if(dead) { - /* remove connection from cpool */ - infof(data, "Connection %" FMT_OFF_T " seems to be dead", - conn->connection_id); - return TRUE; - } - } - return FALSE; -} - -CURLcode Curl_conn_upkeep(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) <= - data->set.upkeep_interval_ms) - return result; - - /* briefly attach for action */ - Curl_attach_connection(data, conn); - result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - Curl_detach_connection(data); - - conn->keepalive = *Curl_pgrs_now(data); - return result; -} - #ifdef USE_SSH static bool ssh_config_matches(struct connectdata *one, struct connectdata *two) @@ -1172,8 +1063,10 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_limits(conn, m)) return FALSE; - if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, &m->now)) { + if(Curl_cpool_conn_seems_dead(conn, m->data, &m->now)) { /* remove and disconnect. */ + infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating", + conn->connection_id); Curl_conn_terminate(m->data, conn, FALSE); return FALSE; } diff --git a/lib/url.h b/lib/url.h index 02f1bd3943ab..c1df2fe81193 100644 --- a/lib/url.h +++ b/lib/url.h @@ -71,23 +71,8 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key); #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ -/** - * Return TRUE iff the given connection is considered dead. - */ -bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - const struct curltime *pnow); - -/** - * Perform upkeep operations on the connection. - */ -CURLcode Curl_conn_upkeep(struct Curl_easy *data, - struct connectdata *conn); - -/** - * Always eval all arguments, return the first - * result != (CURLE_OK | CURLE_AGAIN) or `r1`. - */ +/* Always eval all arguments, return the first + * result != (CURLE_OK | CURLE_AGAIN) or `r1`. */ CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2); #if defined(USE_HTTP2) || defined(USE_HTTP3) From a8881e5e1d2e22d4b085d45ab6c8ce1df251d602 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 13 Apr 2026 12:58:52 +0100 Subject: [PATCH 0738/1062] spnego: block NTLM fallback in SPNEGO negotiation - Switch the Windows SSPI identity struct to SEC_WINNT_AUTH_IDENTITY_EX to use !ntlm in PackageList to prevent NTLM from being offered. - For GSS filter out NTLMSSP OID, and restrict via gss_set_neg_mechs() to prevent NTLM from being offered. - Extend the GSS-API debug stub layer to support the NTLM blocking logic without a real Kerberos environment. - Update test 2057 to check that negotiate auth is silently skipped with no Authorization header when only NTLM stub credentials are available. - Add SPNEGO NTLM blocking test 2093 which verifies that Kerberos credentials still succeed when NTLM is blocked within SPNEGO. - Suppress tests valgrind leak for MIT krb5 gss_display_status, since the leak is in the library and not in curl. To suppress the tests valgrind leak, the wildcard '...' bridges over an anonymous frame inside libgssapi_krb5.so that valgrind reports as '???'. Signed-off-by: Matthew John Cheetham Aided-by: Johannes Schindelin Closes https://github.com/curl/curl/pull/21315 Closes https://github.com/curl/curl/pull/22410 --- CMakeLists.txt | 5 + configure.ac | 1 + docs/INSTALL-CMAKE.md | 1 + lib/curl_config-cmake.h.in | 3 + lib/curl_gssapi.c | 267 ++++++++++++++++++++++++++++++++++++- lib/curl_gssapi.h | 28 +++- lib/curl_sspi.c | 6 +- lib/curl_sspi.h | 6 +- lib/ldap.c | 2 +- lib/socks_gssapi.c | 3 +- lib/vauth/digest_sspi.c | 10 +- lib/vauth/krb5_gssapi.c | 3 +- lib/vauth/spnego_gssapi.c | 83 +++++++++++- lib/vauth/spnego_sspi.c | 26 ++++ lib/vauth/vauth.h | 13 +- tests/data/Makefile.am | 2 +- tests/data/test2057 | 55 ++------ tests/data/test2094 | 68 ++++++++++ tests/valgrind.supp | 11 ++ 19 files changed, 524 insertions(+), 69 deletions(-) create mode 100644 tests/data/test2094 diff --git a/CMakeLists.txt b/CMakeLists.txt index 663ca5408157..dcb735d0ce66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1344,6 +1344,11 @@ if(CURL_USE_GSSAPI) elseif(GSS_VERSION) # MIT set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"") endif() + + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::gss) + check_function_exists("gss_set_neg_mechs" HAVE_GSS_SET_NEG_MECHS) + cmake_pop_check_state() else() message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.") endif() diff --git a/configure.ac b/configure.ac index 0d2ee2b3ff32..c40bfb2a1cac 100644 --- a/configure.ac +++ b/configure.ac @@ -2097,6 +2097,7 @@ if test "$want_gss" = "yes"; then AC_MSG_RESULT([no]) AC_MSG_ERROR([--with-gssapi was specified, but a GSS-API library was not found.]) ]) + AC_CHECK_FUNCS([gss_set_neg_mechs]) fi build_libstubgss=no diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index d241efb0077b..f5af80a418b2 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -517,6 +517,7 @@ the parent project, ideally in the "extra" find package redirect file: Available variables: - `HAVE_DES_ECB_ENCRYPT`: `DES_ecb_encrypt` present in OpenSSL (or fork). +- `HAVE_GSS_SET_NEG_MECHS`: `gss_set_neg_mechs` present in GSS-API library. - `HAVE_LDAP_INIT_FD`: `ldap_init_fd` present in LDAP library. - `HAVE_LDAP_URL_PARSE`: `ldap_url_parse` present in LDAP library. - `HAVE_MBEDTLS_DES_CRYPT_ECB`: `mbedtls_des_crypt_ecb` present in mbedTLS <4. diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index ffcce8273c8e..c992434530ee 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -330,6 +330,9 @@ /* if you have the GNU gssapi libraries */ #cmakedefine HAVE_GSSGNU 1 +/* if you have gss_set_neg_mechs */ +#cmakedefine HAVE_GSS_SET_NEG_MECHS 1 + /* MIT Kerberos version */ #cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION} diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index fbc3c262f166..ccc24e09b128 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -81,7 +81,6 @@ enum min_err_code { /* libcurl is also passing this struct to these functions, which are not yet * stubbed: - * gss_inquire_context() * gss_unwrap() * gss_wrap() */ @@ -93,6 +92,12 @@ struct stub_gss_ctx_id_t_desc { char creds[250]; }; +/* Stub credential: tracks which mechanisms are allowed */ +struct stub_gss_cred_id_t_desc { + int allow_krb5; + int allow_ntlm; +}; + static OM_uint32 stub_gss_init_sec_context( OM_uint32 *min, gss_cred_id_t initiator_cred_handle, @@ -116,7 +121,6 @@ static OM_uint32 stub_gss_init_sec_context( char *token = NULL; const char *creds = NULL; - (void)initiator_cred_handle; (void)mech_type; (void)time_req; (void)input_chan_bindings; @@ -214,6 +218,16 @@ static OM_uint32 stub_gss_init_sec_context( if(strstr(creds, "NTLM")) ctx->have_ntlm = 1; + /* If a credential restricts allowed mechs, honour it */ + if(initiator_cred_handle != GSS_C_NO_CREDENTIAL) { + struct stub_gss_cred_id_t_desc *cred = + (struct stub_gss_cred_id_t_desc *)initiator_cred_handle; + if(!cred->allow_krb5) + ctx->have_krb5 = 0; + if(!cred->allow_ntlm) + ctx->have_ntlm = 0; + } + if(ctx->have_krb5) ctx->sent = STUB_GSS_KRB5; else if(ctx->have_ntlm) @@ -307,6 +321,174 @@ static OM_uint32 stub_gss_delete_sec_context( return GSS_S_COMPLETE; } + +/* NTLMSSP OID: 1.3.6.1.4.1.311.2.2.10 */ +static gss_OID_desc stub_ntlmssp_oid = { + 10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a") +}; + +static OM_uint32 stub_gss_inquire_context( + OM_uint32 *min, + struct stub_gss_ctx_id_t_desc *context, + gss_name_t *src_name, + gss_name_t *targ_name, + OM_uint32 *lifetime_rec, + gss_OID *mech_type, + OM_uint32 *ctx_flags, + int *locally_initiated, + int *open_context) +{ + (void)src_name; + (void)targ_name; + (void)lifetime_rec; + (void)ctx_flags; + (void)locally_initiated; + (void)open_context; + + if(!min) + return GSS_S_FAILURE; + + if(!context) { + *min = STUB_GSS_INVALID_CTX; + return GSS_S_FAILURE; + } + + *min = 0; + if(mech_type) { + switch(context->sent) { + case STUB_GSS_NTLM1: + case STUB_GSS_NTLM3: + *mech_type = &stub_ntlmssp_oid; + break; + default: + *mech_type = (gss_OID)&Curl_krb5_mech_oid; + break; + } + } + + return GSS_S_COMPLETE; +} +static OM_uint32 stub_gss_acquire_cred( + OM_uint32 *min, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + (void)desired_name; + (void)time_req; + (void)desired_mechs; + (void)cred_usage; + (void)actual_mechs; + (void)time_rec; + + if(!min) + return GSS_S_FAILURE; + + *min = 0; + /* Allocate a stub credential that initially allows all mechanisms */ + if(output_cred_handle) { + struct stub_gss_cred_id_t_desc *cred = + curlx_calloc(1, sizeof(*cred)); + if(!cred) { + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + cred->allow_krb5 = 1; + cred->allow_ntlm = 1; + *output_cred_handle = (gss_cred_id_t)cred; + } + return GSS_S_COMPLETE; +} + +static OM_uint32 stub_gss_indicate_mechs( + OM_uint32 *min, + gss_OID_set *mech_set) +{ + const char *creds; + OM_uint32 major; + + if(!min) + return GSS_S_FAILURE; + + *min = 0; + creds = getenv("CURL_STUB_GSS_CREDS"); + if(!creds) { + *min = STUB_GSS_INVALID_CREDS; + return GSS_S_FAILURE; + } + + major = gss_create_empty_oid_set(min, mech_set); + if(GSS_ERROR(major)) + return major; + + /* Always include Kerberos */ + gss_add_oid_set_member(min, (gss_OID)&Curl_krb5_mech_oid, mech_set); + + /* Include NTLM if the stub creds contain NTLM */ + if(strstr(creds, "NTLM")) + gss_add_oid_set_member(min, &stub_ntlmssp_oid, mech_set); + + return GSS_S_COMPLETE; +} + +#ifdef HAVE_GSS_SET_NEG_MECHS +static OM_uint32 stub_gss_set_neg_mechs( + OM_uint32 *min, + gss_cred_id_t cred_handle, + const gss_OID_set mech_set) +{ + struct stub_gss_cred_id_t_desc *cred; + size_t i; + int found_krb5 = 0; + int found_ntlm = 0; + + if(!min) + return GSS_S_FAILURE; + + *min = 0; + if(cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_FAILURE; + + cred = (struct stub_gss_cred_id_t_desc *)cred_handle; + + /* Determine which mechs are in the allowed set */ + if(mech_set) { + for(i = 0; i < mech_set->count; i++) { + gss_OID oid = &mech_set->elements[i]; + if(oid->length == Curl_krb5_mech_oid.length && + !memcmp(oid->elements, Curl_krb5_mech_oid.elements, oid->length)) + found_krb5 = 1; + if(oid->length == stub_ntlmssp_oid.length && + !memcmp(oid->elements, stub_ntlmssp_oid.elements, oid->length)) + found_ntlm = 1; + } + } + + cred->allow_krb5 = found_krb5; + cred->allow_ntlm = found_ntlm; + return GSS_S_COMPLETE; +} +#endif /* HAVE_GSS_SET_NEG_MECHS */ + +static OM_uint32 stub_gss_release_cred( + OM_uint32 *min, + gss_cred_id_t *cred_handle) +{ + if(!min) + return GSS_S_FAILURE; + + *min = 0; + if(cred_handle && *cred_handle != GSS_C_NO_CREDENTIAL) { + curlx_free(*cred_handle); + *cred_handle = GSS_C_NO_CREDENTIAL; + } + return GSS_S_COMPLETE; +} + #endif /* CURL_GSS_STUB */ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, @@ -318,7 +500,8 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, gss_buffer_t input_token, gss_buffer_t output_token, const bool mutual_auth, - OM_uint32 *ret_flags) + OM_uint32 *ret_flags, + gss_cred_id_t cred_handle) { OM_uint32 req_flags = GSS_C_REPLAY_FLAG; @@ -340,7 +523,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, #ifdef CURL_GSS_STUB if(getenv("CURL_STUB_GSS_CREDS")) return stub_gss_init_sec_context(minor_status, - GSS_C_NO_CREDENTIAL, /* cred_handle */ + cred_handle, (struct stub_gss_ctx_id_t_desc **)context, target_name, mech_type, @@ -355,7 +538,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, #endif /* CURL_GSS_STUB */ return gss_init_sec_context(minor_status, - GSS_C_NO_CREDENTIAL, /* cred_handle */ + cred_handle, context, target_name, mech_type, @@ -383,6 +566,80 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, return gss_delete_sec_context(min, context, output_token); } +OM_uint32 Curl_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context, + gss_OID *mech_type) +{ +#ifdef CURL_GSS_STUB + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_inquire_context(minor_status, + (struct stub_gss_ctx_id_t_desc *)context, + NULL, NULL, NULL, mech_type, + NULL, NULL, NULL); +#endif /* CURL_GSS_STUB */ + + return gss_inquire_context(minor_status, context, + NULL, NULL, NULL, mech_type, + NULL, NULL, NULL); +} + +OM_uint32 Curl_gss_acquire_cred(OM_uint32 *minor_status, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ +#ifdef CURL_GSS_STUB + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_acquire_cred(minor_status, desired_name, time_req, + desired_mechs, cred_usage, + output_cred_handle, actual_mechs, time_rec); +#endif /* CURL_GSS_STUB */ + + return gss_acquire_cred(minor_status, desired_name, time_req, + desired_mechs, cred_usage, + output_cred_handle, actual_mechs, time_rec); +} + +OM_uint32 Curl_gss_indicate_mechs(OM_uint32 *minor_status, + gss_OID_set *mech_set) +{ +#ifdef CURL_GSS_STUB + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_indicate_mechs(minor_status, mech_set); +#endif /* CURL_GSS_STUB */ + + return gss_indicate_mechs(minor_status, mech_set); +} + +#ifdef HAVE_GSS_SET_NEG_MECHS +OM_uint32 Curl_gss_set_neg_mechs(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mech_set) +{ +#ifdef CURL_GSS_STUB + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_set_neg_mechs(minor_status, cred_handle, mech_set); +#endif /* CURL_GSS_STUB */ + + return gss_set_neg_mechs(minor_status, cred_handle, mech_set); +} +#endif /* HAVE_GSS_SET_NEG_MECHS */ + +OM_uint32 Curl_gss_release_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle) +{ +#ifdef CURL_GSS_STUB + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_release_cred(minor_status, cred_handle); +#endif /* CURL_GSS_STUB */ + + return gss_release_cred(minor_status, cred_handle); +} + #ifdef CURLVERBOSE #define GSS_LOG_BUFFER_LEN 1024 static size_t display_gss_error(OM_uint32 status, int type, diff --git a/lib/curl_gssapi.h b/lib/curl_gssapi.h index fc3759ebcbcf..b33df77c17ae 100644 --- a/lib/curl_gssapi.h +++ b/lib/curl_gssapi.h @@ -41,12 +41,38 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, gss_buffer_t input_token, gss_buffer_t output_token, const bool mutual_auth, - OM_uint32 *ret_flags); + OM_uint32 *ret_flags, + gss_cred_id_t cred_handle); OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, gss_ctx_id_t *context, gss_buffer_t output_token); +OM_uint32 Curl_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context, + gss_OID *mech_type); + +OM_uint32 Curl_gss_acquire_cred(OM_uint32 *minor_status, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 Curl_gss_indicate_mechs(OM_uint32 *minor_status, + gss_OID_set *mech_set); + +#ifdef HAVE_GSS_SET_NEG_MECHS +OM_uint32 Curl_gss_set_neg_mechs(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mech_set); +#endif + +OM_uint32 Curl_gss_release_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle); + #ifdef CURLVERBOSE /* Helper to log a GSS-API error status */ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index b7dcb7d55cc2..1429f7143a2e 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -93,7 +93,7 @@ void Curl_sspi_global_cleanup(void) * Returns CURLE_OK on success. */ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, - SEC_WINNT_AUTH_IDENTITY *identity) + SEC_WINNT_AUTH_IDENTITY_EX *identity) { xcharp_u useranddomain; xcharp_u user, dup_user; @@ -105,6 +105,8 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, /* Initialize the identity */ memset(identity, 0, sizeof(*identity)); + identity->Version = SEC_WINNT_AUTH_IDENTITY_VERSION; + identity->Length = sizeof(*identity); useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp); if(!useranddomain.tchar_ptr) @@ -195,7 +197,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, * * identity [in/out] - The identity structure. */ -void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity) +void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity) { if(identity) { curlx_safefree(identity->User); diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 2bd7eb4be88e..992e01a9f255 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -34,14 +34,14 @@ void Curl_sspi_global_cleanup(void); /* This is used to populate the domain in an SSPI identity structure */ CURLcode Curl_override_sspi_http_realm(const char *chlg, - SEC_WINNT_AUTH_IDENTITY *identity); + SEC_WINNT_AUTH_IDENTITY_EX *identity); /* This is used to generate an SSPI identity structure */ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, - SEC_WINNT_AUTH_IDENTITY *identity); + SEC_WINNT_AUTH_IDENTITY_EX *identity); /* This is used to free an SSPI identity structure */ -void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); +void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity); /* Forward-declaration of global variables defined in curl_sspi.c */ extern PSecurityFunctionTable Curl_pSecFn; diff --git a/lib/ldap.c b/lib/ldap.c index 63a40784a807..dac472d4c986 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -157,7 +157,7 @@ static ULONG ldap_win_bind_auth(LDAP *server, const char *user, const char *passwd, unsigned long authflags) { ULONG method = 0; - SEC_WINNT_AUTH_IDENTITY cred; + SEC_WINNT_AUTH_IDENTITY_EX cred; ULONG rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; memset(&cred, 0, sizeof(cred)); diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 9a5ad62f5c5e..c1cfba5b7189 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -176,7 +176,8 @@ static CURLcode socks5_gss_auth_loop(struct Curl_cfilter *cf, gss_token, &gss_send_token, TRUE, - gss_ret_flags); + gss_ret_flags, + GSS_C_NO_CREDENTIAL); if(gss_token != GSS_C_NO_BUFFER) { curlx_safefree(gss_recv_token.value); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 305e367e175b..c9b1432830a1 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -95,8 +95,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, CredHandle credentials; CtxtHandle context; PSecPkgInfo SecurityPackage; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; + SEC_WINNT_AUTH_IDENTITY_EX identity; + SEC_WINNT_AUTH_IDENTITY_EX *p_identity; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; @@ -243,7 +243,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_override_sspi_http_realm(const char *chlg, - SEC_WINNT_AUTH_IDENTITY *identity) + SEC_WINNT_AUTH_IDENTITY_EX *identity) { xcharp_u domain, dup_domain; @@ -465,8 +465,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(!digest->http_context) { CredHandle credentials; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; + SEC_WINNT_AUTH_IDENTITY_EX identity; + SEC_WINNT_AUTH_IDENTITY_EX *p_identity; SecBuffer resp_buf; SecBufferDesc resp_desc; unsigned long attrs; diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index 41c47c055d3b..6d9a12517743 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -136,7 +136,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, &input_token, &output_token, mutual_auth, - NULL); + NULL, + GSS_C_NO_CREDENTIAL); if(GSS_ERROR(major_status)) { if(output_token.value) diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 1483b1f1e5b6..b2bc28fb3fa7 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -158,6 +158,57 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } #endif +#ifdef HAVE_GSS_SET_NEG_MECHS + /* Acquire explicit credentials and restrict SPNEGO sub-mechanisms to + * exclude NTLM. We enumerate all available mechanisms and filter out + * the NTLMSSP OID, matching SSPI's "!ntlm". */ + if(nego->cred == GSS_C_NO_CREDENTIAL) { + /* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */ + static const gss_OID_desc ntlmssp_oid = { + 10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a") + }; + gss_OID_set available_mechs = GSS_C_NO_OID_SET; + gss_OID_set filtered_mechs = GSS_C_NO_OID_SET; + + /* Acquire default credentials for SPNEGO */ + major_status = Curl_gss_acquire_cred(&minor_status, GSS_C_NO_NAME, + GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_INITIATE, &nego->cred, NULL, NULL); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_acquire_cred() failed: ", + major_status, minor_status); + curlx_safefree(input_token.value); + return CURLE_AUTH_ERROR; + } + + /* Get all available mechanisms */ + major_status = Curl_gss_indicate_mechs(&minor_status, &available_mechs); + if(!GSS_ERROR(major_status)) { + /* Build a set excluding NTLMSSP */ + major_status = gss_create_empty_oid_set(&minor_status, &filtered_mechs); + if(!GSS_ERROR(major_status)) { + size_t i; + for(i = 0; i < available_mechs->count; i++) { + gss_OID oid = &available_mechs->elements[i]; + if(oid->length != ntlmssp_oid.length || + memcmp(oid->elements, ntlmssp_oid.elements, oid->length)) { + gss_add_oid_set_member(&minor_status, oid, &filtered_mechs); + } + } + /* Restrict SPNEGO to only use non-NTLM mechanisms */ + major_status = Curl_gss_set_neg_mechs(&minor_status, nego->cred, + filtered_mechs); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, "gss_set_neg_mechs() failed: ", + major_status, minor_status); + } + gss_release_oid_set(&minor_status, &filtered_mechs); + } + gss_release_oid_set(&minor_status, &available_mechs); + } + } +#endif /* HAVE_GSS_SET_NEG_MECHS */ + /* Generate our challenge-response message */ major_status = Curl_gss_init_sec_context(data, &minor_status, @@ -168,7 +219,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, &input_token, &output_token, TRUE, - NULL); + NULL, + nego->cred); /* Free the decoded challenge as it is not required anymore */ curlx_safefree(input_token.value); @@ -191,6 +243,29 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, return CURLE_AUTH_ERROR; } + /* Check if NTLM was selected and is disallowed */ + if(nego->context != GSS_C_NO_CONTEXT) { + /* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */ + static const gss_OID_desc ntlmssp_oid = { + 10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a") + }; + OM_uint32 inquire_major, inquire_minor; + gss_OID mech_type = GSS_C_NO_OID; + + inquire_major = Curl_gss_inquire_context(&inquire_minor, + nego->context, + &mech_type); + if(!GSS_ERROR(inquire_major) && mech_type && + mech_type->length == ntlmssp_oid.length && + !memcmp(mech_type->elements, ntlmssp_oid.elements, + ntlmssp_oid.length)) { + infof(data, "SPNEGO chose NTLM, but NTLM is not allowed"); + gss_release_buffer(&unused_status, &output_token); + Curl_auth_cleanup_spnego(nego); + return CURLE_AUTH_ERROR; + } + } + /* Free previous token */ if(nego->output_token.length && nego->output_token.value) gss_release_buffer(&unused_status, &nego->output_token); @@ -280,6 +355,12 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) nego->spn = GSS_C_NO_NAME; } + /* Free our credentials */ + if(nego->cred != GSS_C_NO_CREDENTIAL) { + Curl_gss_release_cred(&minor_status, &nego->cred); + nego->cred = GSS_C_NO_CREDENTIAL; + } + /* Reset any variables */ nego->status = 0; nego->noauthpersist = FALSE; diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index b7d82c04ddfb..da9d7b023b95 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -148,6 +148,32 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Use the current Windows user */ nego->p_identity = NULL; + /* Exclude NTLM from SPNEGO negotiation via the PackageList field */ + if(!nego->p_identity) { + memset(&nego->identity, 0, sizeof(nego->identity)); + nego->identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; + nego->identity.Length = sizeof(nego->identity); + nego->identity.Flags = +#ifdef UNICODE + SEC_WINNT_AUTH_IDENTITY_UNICODE; +#else + SEC_WINNT_AUTH_IDENTITY_ANSI; +#endif + nego->p_identity = &nego->identity; + } + + /* Use the special name "!ntlm" to prevent NTLM from being used: + * https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa + */ +#ifdef UNICODE + nego->identity.PackageList = + (unsigned short *)CURL_UNCONST(TEXT("!ntlm")); +#else + nego->identity.PackageList = + (unsigned char *)CURL_UNCONST(TEXT("!ntlm")); +#endif + nego->identity.PackageListLength = 5; + /* Allocate our credentials handle */ nego->credentials = curlx_calloc(1, sizeof(CredHandle)); if(!nego->credentials) diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 0f82f929453d..f8ec563e92bb 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -169,8 +169,8 @@ struct ntlmdata { #endif CredHandle *credentials; CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; + SEC_WINNT_AUTH_IDENTITY_EX identity; + SEC_WINNT_AUTH_IDENTITY_EX *p_identity; size_t token_max; BYTE *output_token; BYTE *input_token; @@ -236,8 +236,8 @@ struct kerberos5data { CredHandle *credentials; CtxtHandle *context; TCHAR *spn; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; + SEC_WINNT_AUTH_IDENTITY_EX identity; + SEC_WINNT_AUTH_IDENTITY_EX *p_identity; size_t token_max; BYTE *output_token; #else @@ -291,6 +291,7 @@ struct negotiatedata { OM_uint32 status; gss_ctx_id_t context; gss_name_t spn; + gss_cred_id_t cred; gss_buffer_desc output_token; #ifdef GSS_C_CHANNEL_BOUND_FLAG struct dynbuf channel_binding_data; @@ -303,8 +304,8 @@ struct negotiatedata { SECURITY_STATUS status; CredHandle *credentials; CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; + SEC_WINNT_AUTH_IDENTITY_EX identity; + SEC_WINNT_AUTH_IDENTITY_EX *p_identity; TCHAR *spn; size_t token_max; BYTE *output_token; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 728858dafa2d..76ac5be1bf26 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -252,7 +252,7 @@ test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ -test2088 test2089 test2090 test2091 test2092 test2093 \ +test2088 test2089 test2090 test2091 test2092 test2093 test2094 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \ \ diff --git a/tests/data/test2057 b/tests/data/test2057 index 18ce33d947a4..d2ca3b79682a 100644 --- a/tests/data/test2057 +++ b/tests/data/test2057 @@ -5,45 +5,17 @@ HTTP HTTP GET HTTP Negotiate auth (stub ntlm) +SPNEGO NTLM disallowed # Server-side - - -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate Qw== -Content-Length: 19 + +HTTP/1.1 200 OK swsclose +Content-Length: 23 -Still not yet sir! - - - -HTTP/1.1 200 Things are fine in server land -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate RA== -Content-Length: 15 - -Nice auth sir! - - -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate Qw== -Content-Length: 19 - -HTTP/1.1 200 Things are fine in server land -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate RA== -Content-Length: 15 - -Nice auth sir! - +This IS the real page! + # Client-side @@ -52,7 +24,7 @@ Nice auth sir! http -HTTP Negotiate authentication (stub NTLM) +HTTP Negotiate authentication blocked for stub NTLM credentials GSS-API @@ -68,16 +40,15 @@ CURL_STUB_GSS_CREDS="NTLM_Alice" # Verify data after the test has been "shot" + +0 + +# NTLM is blocked within SPNEGO, so when only NTLM credentials are +# available, negotiate auth silently fails and the request is sent +# without any Authorization header. GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT -Authorization: Negotiate %b64["NTLM_Alice":HTTP@127.0.0.1:2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64% -User-Agent: curl/%VERSION -Accept: */* - -GET /%TESTNUMBER HTTP/1.1 -Host: %HOSTIP:%HTTPPORT -Authorization: Negotiate %b64["NTLM_Alice":HTTP@127.0.0.1:3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64% User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test2094 b/tests/data/test2094 new file mode 100644 index 000000000000..57ded4456e7d --- /dev/null +++ b/tests/data/test2094 @@ -0,0 +1,68 @@ + + + + +HTTP +HTTP GET +HTTP Negotiate auth (stub krb5) +SPNEGO NTLM disallowed + + + +# Server-side + + +HTTP/1.1 200 Things are fine in server land +Server: Microsoft-IIS/7.0 +Content-Type: text/html; charset=iso-8859-1 +WWW-Authenticate: Negotiate RA== +Content-Length: 15 + +Nice auth sir! + + +HTTP/1.1 200 Things are fine in server land +Server: Microsoft-IIS/7.0 +Content-Type: text/html; charset=iso-8859-1 +WWW-Authenticate: Negotiate RA== +Content-Length: 15 + +Nice auth sir! + + + +# Client-side + + +http + + +SPNEGO with Kerberos still works when NTLM is blocked + + +GSS-API +Debug + + +CURL_STUB_GSS_CREDS="KRB5_Alice" + + +--negotiate http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +0 + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Negotiate %b64["KRB5_Alice":HTTP@127.0.0.1:1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64% +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/valgrind.supp b/tests/valgrind.supp index aad725524883..026723d5d19a 100644 --- a/tests/valgrind.supp +++ b/tests/valgrind.supp @@ -128,3 +128,14 @@ fun:inet_pton6 fun:ipv6_parse } + +{ + mit-krb5-gss_display_status-internal-leak + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + ... + fun:gss_display_status + fun:display_gss_error + fun:Curl_gss_log_error +} From 27a4557c9ea87f1961741fb728387261387b398c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 27 Jul 2026 23:56:44 +0200 Subject: [PATCH 0739/1062] EXPERIMENTAL.md: We do not accept vuln reports for experimental features Closes #22411 --- docs/EXPERIMENTAL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index ff37422172fa..c5083e9f6549 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -21,6 +21,8 @@ Experimental support in curl means: to our API/ABI rules as we do for regular features, as long as it is marked experimental. 5. Experimental features are clearly marked so in documentation. Beware. +6. Vulnerabilities in experimental features are not considered security + problems; please report them as regular bugs/feedback instead. ## Graduation From c7328740ecad6e5549c210366b8403eca29b4123 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 Jul 2026 11:53:42 +0200 Subject: [PATCH 0740/1062] lib2405: adjust for non-threaded builds - Attempt to fix the flakiness set in 9726fc8259ad7ebf1682 - Reduce macro use Closes #22414 --- tests/data/test2405 | 1 - tests/data/test2407 | 1 - tests/libtest/lib2405.c | 69 ++++++++++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/tests/data/test2405 b/tests/data/test2405 index 0d64ec458a5f..6ccb6ba394d4 100644 --- a/tests/data/test2405 +++ b/tests/data/test2405 @@ -4,7 +4,6 @@ multi HTTP -flaky diff --git a/tests/data/test2407 b/tests/data/test2407 index 33555a85028f..b9f17bb22fe1 100644 --- a/tests/data/test2407 +++ b/tests/data/test2407 @@ -5,7 +5,6 @@ multi HTTP HTTP/2 -flaky diff --git a/tests/libtest/lib2405.c b/tests/libtest/lib2405.c index 412faf45a01e..6d98183a83ba 100644 --- a/tests/libtest/lib2405.c +++ b/tests/libtest/lib2405.c @@ -41,24 +41,6 @@ /* ---------------------------------------------------------------- */ -#define test_check(expected_fds) \ - if(result != CURLE_OK) { \ - curl_mfprintf(stderr, "test failed with code: %d\n", (int)result); \ - goto test_cleanup; \ - } \ - else if(fd_count != (expected_fds)) { \ - curl_mfprintf(stderr, "Max number of waitfds: %u not as expected: %u\n", \ - fd_count, expected_fds); \ - result = TEST_ERR_FAILURE; \ - goto test_cleanup; \ - } - -#define test_run_check(option, expected_fds) \ - do { \ - result = test_run(URL, option, &fd_count); \ - test_check(expected_fds); \ - } while(0) - /* ---------------------------------------------------------------- */ enum { @@ -302,6 +284,22 @@ static CURLcode test_run(const char *URL, long option, return result; } +static CURLcode test_run_check(const char *URL, long option, + unsigned int expected_fds) +{ + unsigned int fd_count = 0; + CURLcode result = test_run(URL, option, &fd_count); + if(result) + curl_mfprintf(stderr, "test failed with code: %d\n", (int)result); + else if(fd_count != expected_fds) { + curl_mfprintf(stderr, + "Max number of waitfds: %u not as expected: %u\n", + fd_count, expected_fds); + result = TEST_ERR_FAILURE; + } + return result; +} + static CURLcode empty_multi_test(void) { CURLMcode mresult = CURLM_OK; @@ -360,29 +358,50 @@ static CURLcode empty_multi_test(void) return result; } +static unsigned int uses_threaded(void) +{ + curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); + const char * const *n = ver->feature_names; + int i; + unsigned int uses = 0; + /* the 'asyn-rr' feature tells us libcurl uses the threaded resolver */ + for(i = 0; n[i]; i++) { + if(!strcmp("asyn-rr", n[i])) + uses = 1; + } + /* if not using asyn-rr, check if doing asynch DNS without using c-ares */ + if(!uses && (ver->features & CURL_VERSION_ASYNCHDNS) && !ver->ares) + uses = 1; + return uses; +} + static CURLcode test_lib2405(const char *URL) { CURLcode result = CURLE_OK; - unsigned int fd_count = 0; + int uses_threaded_resolver; global_init(CURL_GLOBAL_ALL); + uses_threaded_resolver = uses_threaded(); + /* Testing curl_multi_waitfds on empty and not started handles */ result = empty_multi_test(); if(result != CURLE_OK) goto test_cleanup; if(testnum == 2405) { - /* HTTP1, expected 3 waitfds - one for each transfer + wakeup */ - test_run_check(TEST_USE_HTTP1, 3U); + /* HTTP1, one for each transfer + possible wakeup */ + result = test_run_check(URL, TEST_USE_HTTP1, 2 + uses_threaded_resolver); } #ifdef USE_HTTP2 else { /* 2407 */ - /* HTTP2, expected 3 waitfds - one for each transfer + wakeup */ - test_run_check(TEST_USE_HTTP2, 3U); + /* HTTP2, one for each transfer + possible wakeup */ + result = test_run_check(URL, TEST_USE_HTTP2, 2 + uses_threaded_resolver); - /* HTTP2 with multiplexing, expected 2 waitfds - transfers + wakeup */ - test_run_check(TEST_USE_HTTP2_MPLEX, 2U); + /* HTTP2 with multiplexing, expected one waitfds + possible wakeup */ + if(!result) + result = test_run_check(URL, TEST_USE_HTTP2_MPLEX, + 1 + uses_threaded_resolver); } #endif From 573a6ec16b5822ee209d46c490f7e4200036c7dd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 27 Jul 2026 16:05:38 +0200 Subject: [PATCH 0741/1062] urlapi: improved return codes - add CURLUE_BACKSLASH that can be returned when a backslash was used where a forward one probably was intended. - make CURLUE_NO_HOST higher priority than port number errors for URLs without hostname. Like in "http://::1" - shortened some URL parser error strings Extend test 1560 to verify. Reported-by: kit-ty-kate on github Fixes #22337 Closes #22408 --- docs/libcurl/libcurl-errors.md | 5 +++++ docs/libcurl/symbols-in-versions | 1 + include/curl/urlapi.h | 1 + lib/strerror.c | 23 +++++++++++++---------- lib/urlapi.c | 16 +++++++++++----- projects/OS400/curl.inc.in | 2 ++ tests/data/test1538 | 23 ++++++++++++----------- tests/libtest/lib1560.c | 10 +++++++++- 8 files changed, 54 insertions(+), 27 deletions(-) diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index c658361a7920..6f348eb5813f 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -724,6 +724,11 @@ libcurl lacks IDN support. A value or data field is larger than allowed. +## CURLUE_BACKSLASH (32) + +Found a backslash character where a forward slash was expected. URL separators +are forward slashes (`/`), not backslashes (`\`). + # CURLHcode The header interface returns a *CURLHcode* to indicate when an error has diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index a3d92a2637d8..65279d642659 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -1113,6 +1113,7 @@ CURLU_PUNY2IDN 8.3.0 CURLU_PUNYCODE 7.88.0 CURLU_URLDECODE 7.62.0 CURLU_URLENCODE 7.62.0 +CURLUE_BACKSLASH 8.22.0 CURLUE_BAD_FILE_URL 7.81.0 CURLUE_BAD_FRAGMENT 7.81.0 CURLUE_BAD_HANDLE 7.62.0 diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h index b1f3a2316b7a..02553dfebfcb 100644 --- a/include/curl/urlapi.h +++ b/include/curl/urlapi.h @@ -64,6 +64,7 @@ typedef enum { CURLUE_BAD_USER, /* 29 */ CURLUE_LACKS_IDN, /* 30 */ CURLUE_TOO_LARGE, /* 31 */ + CURLUE_BACKSLASH, /* 32 */ CURLUE_LAST } CURLUcode; diff --git a/lib/strerror.c b/lib/strerror.c index 7b6dc0212788..6fc801dc6128 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -452,34 +452,34 @@ const char *curl_url_strerror(CURLUcode error) return "An unknown part ID was passed to a URL API function"; case CURLUE_NO_SCHEME: - return "No scheme part in the URL"; + return "No scheme present"; case CURLUE_NO_USER: - return "No user part in the URL"; + return "No user present"; case CURLUE_NO_PASSWORD: - return "No password part in the URL"; + return "No password present"; case CURLUE_NO_OPTIONS: - return "No options part in the URL"; + return "No options present"; case CURLUE_NO_HOST: - return "No host part in the URL"; + return "No host present"; case CURLUE_NO_PORT: - return "No port part in the URL"; + return "No port number present"; case CURLUE_NO_QUERY: - return "No query part in the URL"; + return "No query present"; case CURLUE_NO_FRAGMENT: - return "No fragment part in the URL"; + return "No fragment present"; case CURLUE_NO_ZONEID: - return "No zoneid part in the URL"; + return "No zoneid present"; case CURLUE_BAD_LOGIN: - return "Bad login part"; + return "Bad login"; case CURLUE_BAD_IPV6: return "Bad IPv6 address"; @@ -517,6 +517,9 @@ const char *curl_url_strerror(CURLUcode error) case CURLUE_TOO_LARGE: return "A value or data field is larger than allowed"; + case CURLUE_BACKSLASH: + return "Found a backslash where a forward slash was expected"; + case CURLUE_LAST: break; } diff --git a/lib/urlapi.c b/lib/urlapi.c index cbb16a43c427..35251053adeb 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -382,6 +382,7 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, if(portptr) { curl_off_t port; size_t keep = portptr - hostname; + int rc; /* Browser behavior adaptation. If there is a colon with no digits after, cut off the name there which makes us ignore the colon and use the @@ -393,8 +394,14 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, portptr++; if(!*portptr) return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER; - - if(curlx_str_number(&portptr, &port, 0xffff) || *portptr) + if(*portptr == '\\') + return CURLUE_BACKSLASH; + rc = curlx_str_number(&portptr, &port, 0xffff); + if(rc) + return CURLUE_BAD_PORT_NUMBER; + else if(*portptr == '\\') + return CURLUE_BACKSLASH; + else if(*portptr) return CURLUE_BAD_PORT_NUMBER; u->portnum = (uint16_t)port; @@ -666,12 +673,11 @@ static CURLUcode parse_authority(struct Curl_URL *u, } uc = parse_port(u, host, has_scheme); - if(uc) - return uc; if(!curlx_dyn_len(host)) + /* this makes no-host errors override port number problems */ uc = CURLUE_NO_HOST; - else + if(!uc) uc = urldecode_host(host); if(uc) return uc; diff --git a/projects/OS400/curl.inc.in b/projects/OS400/curl.inc.in index f97f26e01282..d048663f7203 100644 --- a/projects/OS400/curl.inc.in +++ b/projects/OS400/curl.inc.in @@ -2340,6 +2340,8 @@ d c 30 d CURLUE_TOO_LARGE... d c 31 + d CURLUE_BACKSLASH... + d c 32 * d CURLUPart s 10i 0 based(######ptr######) Enum d CURLUPART_URL c 0 diff --git a/tests/data/test1538 b/tests/data/test1538 index 51580576ee2c..bdfe9478edd4 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -163,20 +163,20 @@ u6: URL decode error, most likely because of rubbish in the input u7: A memory function failed u8: Credentials was passed in the URL when prohibited u9: An unknown part ID was passed to a URL API function -u10: No scheme part in the URL -u11: No user part in the URL -u12: No password part in the URL -u13: No options part in the URL -u14: No host part in the URL -u15: No port part in the URL -u16: No query part in the URL -u17: No fragment part in the URL -u18: No zoneid part in the URL +u10: No scheme present +u11: No user present +u12: No password present +u13: No options present +u14: No host present +u15: No port number present +u16: No query present +u17: No fragment present +u18: No zoneid present u19: Bad file:// URL u20: Bad fragment u21: Bad hostname u22: Bad IPv6 address -u23: Bad login part +u23: Bad login u24: Bad password u25: Bad path u26: Bad query @@ -185,7 +185,8 @@ u28: Unsupported number of slashes following scheme u29: Bad user u30: libcurl lacks IDN support u31: A value or data field is larger than allowed -u32: CURLUcode unknown +u32: Found a backslash where a forward slash was expected +u33: CURLUcode unknown diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 0b20780b7e61..1c8aabfd5b2f 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -153,6 +153,14 @@ struct clearurlcase { }; static const struct testcase get_parts_list[] = { + /* backslash mistakes */ + {"http:\\\\hostname", "", + CURLU_GUESS_SCHEME, 0, CURLUE_BACKSLASH }, + {"http:\\\\hostname:1234", "", + CURLU_GUESS_SCHEME, 0, CURLUE_BACKSLASH }, + {"http://hostname:1111\\path", "", + 0, 0, CURLUE_BACKSLASH }, + /* non-supported URL without hostname */ {"weird:///path", "weird | [11] | [12] | [13] | | [15] | /path | [16] | [17]", @@ -753,7 +761,7 @@ static const struct urltestcase get_url_list[] = { /* malformed unbracketed IPv6 */ {"https://fe80:8080::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, - {"https://::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + {"https://::1/", "", 0, 0, CURLUE_NO_HOST}, /* Empty host with standard schemes */ {"http:///", "", 0, 0, CURLUE_NO_HOST}, From e1450d8fdaf05f27ec75eb15df303fe931755a59 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 23 Jul 2026 01:50:06 +0200 Subject: [PATCH 0742/1062] tidy-up: use more `static`, `sizeof()`, `char[]`, double-const - make `const` data `static`, where missing and possible. - replace `strlen()` on literal or const strings with `sizeof()`. While the latter is optimized by popular C compiler, e.g. MSVC only does it with `/O2`. - replace magic numbers with `sizeof()`, where missing. - introduce `CURL_CSTRLEN()` macro for `sizeof(char[]) - 1`. - use `CURL_CSTRLEN()` macro. - move `const` before integer types, where missing. - replace `char *var` with `var[]`, where missing and possible. - use double const, where missing. `static const char *` -> `static const char * const`. - lib1514: constify pointers. - unit3205: drop redundant cast, avoid another one. - unit1666: map `OID()` macro to identical `STRCONST()`. Closes #22406 --- CMake/CurlTests.c | 2 +- docs/examples/10-at-a-time.c | 2 +- docs/examples/crawler.c | 2 +- docs/examples/ephiperfifo.c | 6 ++- docs/examples/evhiperfifo.c | 6 ++- docs/examples/ghiper.c | 6 ++- docs/examples/hiperfifo.c | 6 ++- docs/examples/imap-append.c | 4 +- docs/examples/maxconnects.c | 2 +- docs/examples/postinmemory.c | 4 +- docs/examples/sendrecv.c | 4 +- docs/examples/sepheaders.c | 4 +- docs/examples/sftpuploadresume.c | 4 +- docs/examples/simplepost.c | 4 +- docs/examples/simplessl.c | 6 +-- docs/examples/smtp-authzid.c | 2 +- docs/examples/smtp-mail.c | 2 +- docs/examples/smtp-mime.c | 4 +- docs/examples/smtp-multi.c | 2 +- docs/examples/smtp-ssl.c | 2 +- docs/examples/smtp-tls.c | 2 +- docs/examples/synctime.c | 4 +- docs/examples/url2file.c | 2 +- docs/examples/websocket-updown.c | 4 +- lib/cf-h1-proxy.c | 4 +- lib/cf-h2-proxy.c | 2 +- lib/curl_setup.h | 6 ++- lib/dict.c | 12 +++--- lib/http.c | 2 +- lib/http2.c | 4 +- lib/http_aws_sigv4.c | 4 +- lib/http_digest.c | 2 +- lib/http_negotiate.c | 2 +- lib/http_ntlm.c | 2 +- lib/http_proxy.c | 6 +-- lib/imap.c | 2 +- lib/ldap.c | 4 +- lib/mqtt.c | 4 +- lib/progress.c | 2 +- lib/smb.c | 9 +++-- lib/tftp.c | 4 +- lib/vquic/cf-quiche.c | 7 ++-- lib/vssh/libssh2.c | 2 +- lib/vtls/cipher_suite.c | 2 +- lib/vtls/gtls.c | 6 +-- lib/vtls/keylog.h | 2 +- lib/vtls/openssl.c | 24 +++++------ lib/vtls/schannel.c | 12 +++--- lib/vtls/schannel_verify.c | 4 +- lib/vtls/vtls.c | 4 +- lib/vtls/wolfssl.c | 8 ++-- scripts/schemetable.c | 2 +- src/curlinfo.c | 2 +- src/tool_doswin.c | 6 +-- src/tool_easysrc.c | 2 +- src/tool_formparse.c | 8 ++-- src/tool_getparam.c | 2 +- src/tool_helpers.c | 4 +- src/tool_libinfo.c | 2 +- src/tool_paramhlp.c | 2 +- src/tool_progress.c | 2 +- src/var.c | 10 ++--- .../http/testenv/mod_curltest/mod_curltest.c | 2 +- tests/libtest/lib1514.c | 6 +-- tests/libtest/lib1517.c | 2 +- tests/libtest/lib1520.c | 2 +- tests/libtest/lib1525.c | 2 +- tests/libtest/lib1526.c | 2 +- tests/libtest/lib1527.c | 2 +- tests/libtest/lib1531.c | 4 +- tests/libtest/lib1537.c | 6 ++- tests/libtest/lib1554.c | 2 +- tests/libtest/lib1560.c | 4 +- tests/libtest/lib1576.c | 5 ++- tests/libtest/lib1591.c | 2 +- tests/libtest/lib1598.c | 4 +- tests/libtest/lib1662.c | 2 +- tests/libtest/lib1901.c | 2 +- tests/libtest/lib1940.c | 2 +- tests/libtest/lib1948.c | 6 +-- tests/libtest/lib1965.c | 2 +- tests/libtest/lib3102.c | 8 ++-- tests/libtest/lib505.c | 4 +- tests/libtest/lib508.c | 2 +- tests/libtest/lib536.c | 2 +- tests/libtest/lib544.c | 2 +- tests/libtest/lib547.c | 2 +- tests/libtest/lib554.c | 4 +- tests/libtest/lib555.c | 2 +- tests/libtest/lib571.c | 2 +- tests/libtest/lib643.c | 4 +- tests/libtest/lib654.c | 2 +- tests/libtest/lib655.c | 2 +- tests/libtest/lib667.c | 2 +- tests/libtest/lib668.c | 2 +- tests/libtest/lib677.c | 6 +-- tests/libtest/lib757.c | 2 +- tests/server/mqttd.c | 8 ++-- tests/server/rtspd.c | 38 +++++++++--------- tests/server/socksd.c | 4 +- tests/server/sws.c | 40 ++++++++++--------- tests/tunit/tool1720.c | 2 +- tests/unit/unit1303.c | 2 +- tests/unit/unit1395.c | 2 +- tests/unit/unit1396.c | 4 +- tests/unit/unit1398.c | 2 +- tests/unit/unit1625.c | 4 +- tests/unit/unit1627.c | 4 +- tests/unit/unit1650.c | 12 +++--- tests/unit/unit1655.c | 6 +-- tests/unit/unit1664.c | 24 +++++------ tests/unit/unit1666.c | 2 +- tests/unit/unit1675.c | 12 +++--- tests/unit/unit2603.c | 14 +++---- tests/unit/unit3200.c | 2 +- tests/unit/unit3205.c | 7 ++-- tests/unit/unit3400.c | 32 ++++++++------- 117 files changed, 311 insertions(+), 287 deletions(-) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index f43521bfd658..e8117410fd20 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -65,7 +65,7 @@ int main(void) #include int main(void) { - const char *address = "localhost"; + static const char address[] = "localhost"; struct hostent h; int rc = 0; #if defined(HAVE_GETHOSTBYNAME_R_3) || \ diff --git a/docs/examples/10-at-a-time.c b/docs/examples/10-at-a-time.c index d0982e0d4118..34572e438b3e 100644 --- a/docs/examples/10-at-a-time.c +++ b/docs/examples/10-at-a-time.c @@ -30,7 +30,7 @@ #include -static const char *urls[] = { +static const char * const urls[] = { "https://01.example/", "https://02.example/", "https://03.example/", diff --git a/docs/examples/crawler.c b/docs/examples/crawler.c index 21cdb5fcff4c..595375614eb2 100644 --- a/docs/examples/crawler.c +++ b/docs/examples/crawler.c @@ -47,7 +47,7 @@ static int max_total = 20000; static int max_requests = 500; static size_t max_link_per_page = 5; static int follow_relative_links = 0; -static const char *start_page = "https://www.reuters.com/"; +static const char start_page[] = "https://www.reuters.com/"; static int pending_interrupt = 0; static void sighandler(int dummy) diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c index 292bfc741ee5..de86690ef42f 100644 --- a/docs/examples/ephiperfifo.c +++ b/docs/examples/ephiperfifo.c @@ -303,7 +303,9 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { struct GlobalInfo *g = (struct GlobalInfo *)cbp; struct SockInfo *fdp = (struct SockInfo *)sockp; - const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; + static const char * const whatstr[] = { + "none", "IN", "OUT", "INOUT", "REMOVE" + }; fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { @@ -407,7 +409,7 @@ static void fifo_cb(struct GlobalInfo *g, int revents) static int init_fifo(struct GlobalInfo *g) { struct stat st; - static const char *fifo = "hiper.fifo"; + static const char fifo[] = "hiper.fifo"; curl_socket_t sockfd; struct epoll_event epev; diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index 079eab1e8abf..32f92f9415c5 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -267,7 +267,9 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { struct GlobalInfo *g = (struct GlobalInfo *)cbp; struct SockInfo *fdp = (struct SockInfo *)sockp; - const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; + static const char * const whatstr[] = { + "none", "IN", "OUT", "INOUT", "REMOVE" + }; printf("%s e %p s %d what %d cbp %p sockp %p\n", __PRETTY_FUNCTION__, e, s, what, cbp, sockp); @@ -378,7 +380,7 @@ static void fifo_cb(EV_P_ struct ev_io *w, int revents) static int init_fifo(struct GlobalInfo *g) { struct stat st; - static const char *fifo = "hiper.fifo"; + static const char fifo[] = "hiper.fifo"; curl_socket_t sockfd; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); diff --git a/docs/examples/ghiper.c b/docs/examples/ghiper.c index fd6041761e24..79516ccd82c5 100644 --- a/docs/examples/ghiper.c +++ b/docs/examples/ghiper.c @@ -258,7 +258,9 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { struct GlobalInfo *g = (struct GlobalInfo *)cbp; struct SockInfo *fdp = (struct SockInfo *)sockp; - static const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; + static const char * const whatstr[] = { + "none", "IN", "OUT", "INOUT", "REMOVE" + }; MSG_OUT("socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { @@ -395,8 +397,8 @@ static gboolean fifo_cb(GIOChannel *ch, GIOCondition condition, gpointer data) int init_fifo(void) { + static const char fifo[] = "hiper.fifo"; struct stat st; - const char *fifo = "hiper.fifo"; int socket; if(!lstat(fifo, &st)) { diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c index 96719530cf46..cc27799012cd 100644 --- a/docs/examples/hiperfifo.c +++ b/docs/examples/hiperfifo.c @@ -270,7 +270,9 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { struct GlobalInfo *g = (struct GlobalInfo *)cbp; struct SockInfo *fdp = (struct SockInfo *)sockp; - const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; + static const char * const whatstr[] = { + "none", "IN", "OUT", "INOUT", "REMOVE" + }; fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { @@ -376,7 +378,7 @@ static void fifo_cb(int fd, short event, void *arg) } /* Create a named pipe and tell libevent to monitor it */ -static const char *fifo = "hiper.fifo"; +static const char fifo[] = "hiper.fifo"; static int init_fifo(struct GlobalInfo *g) { struct stat st; diff --git a/docs/examples/imap-append.c b/docs/examples/imap-append.c index 77cf2bc02a39..c530bbb9c814 100644 --- a/docs/examples/imap-append.c +++ b/docs/examples/imap-append.c @@ -40,7 +40,7 @@ #define TO "" #define CC "" -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO "\r\n" "From: " FROM "(Example User)\r\n" @@ -111,7 +111,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - filesize = strlen(payload_text); + filesize = sizeof(payload_text) - 1; if(filesize <= LONG_MAX) infilesize = (long)filesize; curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize); diff --git a/docs/examples/maxconnects.c b/docs/examples/maxconnects.c index 7ef29828eab9..b52f1fcc3f54 100644 --- a/docs/examples/maxconnects.c +++ b/docs/examples/maxconnects.c @@ -39,7 +39,7 @@ int main(void) curl = curl_easy_init(); if(curl) { - const char *urls[] = { + static const char * const urls[] = { "https://example.com/", "https://curl.se/", "https://www.example/", diff --git a/docs/examples/postinmemory.c b/docs/examples/postinmemory.c index c4d9abcc1148..7f18e8e5f576 100644 --- a/docs/examples/postinmemory.c +++ b/docs/examples/postinmemory.c @@ -61,7 +61,7 @@ int main(void) CURL *curl; CURLcode result; struct MemoryStruct chunk; - static const char *postthis = "Field=1&Field=2&Field=3"; + static const char postthis[] = "Field=1&Field=2&Field=3"; result = curl_global_init(CURL_GLOBAL_ALL); if(result != CURLE_OK) @@ -87,7 +87,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postthis); /* if we do not provide POSTFIELDSIZE, libcurl calls strlen() by itself */ - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(postthis) - 1); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/docs/examples/sendrecv.c b/docs/examples/sendrecv.c index 2777ca0a6dda..a3539adb3ccc 100644 --- a/docs/examples/sendrecv.c +++ b/docs/examples/sendrecv.c @@ -77,8 +77,8 @@ int main(void) { CURL *curl; /* Minimalistic http request */ - const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"; - size_t request_len = strlen(request); + static const char request[] = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"; + static const size_t request_len = sizeof(request) - 1; CURLcode result = curl_global_init(CURL_GLOBAL_ALL); if(result != CURLE_OK) diff --git a/docs/examples/sepheaders.c b/docs/examples/sepheaders.c index c9fee6c2ba1d..011eae1aea94 100644 --- a/docs/examples/sepheaders.c +++ b/docs/examples/sepheaders.c @@ -53,9 +53,9 @@ int main(void) /* init the curl session */ curl = curl_easy_init(); if(curl) { - static const char *headerfilename = "head.out"; + static const char headerfilename[] = "head.out"; FILE *headerfile; - static const char *bodyfilename = "body.out"; + static const char bodyfilename[] = "body.out"; FILE *bodyfile; /* set URL to get */ diff --git a/docs/examples/sftpuploadresume.c b/docs/examples/sftpuploadresume.c index 153883640bea..e5d14a845945 100644 --- a/docs/examples/sftpuploadresume.c +++ b/docs/examples/sftpuploadresume.c @@ -134,8 +134,8 @@ int main(void) curl = curl_easy_init(); if(curl) { - const char *remote = "sftp://user:pass@example.com/path/filename"; - const char *filename = "filename"; + static const char remote[] = "sftp://user:pass@example.com/path/filename"; + static const char filename[] = "filename"; if(!sftpResumeUpload(curl, remote, filename)) { printf("resumed upload using curl %s failed\n", curl_version()); diff --git a/docs/examples/simplepost.c b/docs/examples/simplepost.c index d2540151ce63..fb85677c3128 100644 --- a/docs/examples/simplepost.c +++ b/docs/examples/simplepost.c @@ -32,7 +32,7 @@ int main(void) { - static const char *postthis = "moo mooo moo moo"; + static const char postthis[] = "moo mooo moo moo"; CURL *curl; @@ -46,7 +46,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postthis); /* if we do not provide POSTFIELDSIZE, libcurl calls strlen() by itself */ - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(postthis) - 1); /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); diff --git a/docs/examples/simplessl.c b/docs/examples/simplessl.c index 24e08c4c7144..45db65a05d01 100644 --- a/docs/examples/simplessl.c +++ b/docs/examples/simplessl.c @@ -55,9 +55,9 @@ int main(void) FILE *headerfile; const char *pPassphrase = NULL; - static const char *pCertFile = "testcert.pem"; - static const char *pCACertFile = "cacert.pem"; - static const char *pHeaderFile = "dumpit"; + static const char pCertFile[] = "testcert.pem"; + static const char pCACertFile[] = "cacert.pem"; + static const char pHeaderFile[] = "dumpit"; const char *pKeyName; const char *pKeyType; diff --git a/docs/examples/smtp-authzid.c b/docs/examples/smtp-authzid.c index fe91ba5e634a..0260452990c3 100644 --- a/docs/examples/smtp-authzid.c +++ b/docs/examples/smtp-authzid.c @@ -48,7 +48,7 @@ #define SENDER_MAIL "Kurt " SENDER_ADDR #define TO_MAIL "A Receiver " TO_ADDR -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO_MAIL "\r\n" "From: " FROM_MAIL "\r\n" diff --git a/docs/examples/smtp-mail.c b/docs/examples/smtp-mail.c index b1590fd3af75..618123ab5e2d 100644 --- a/docs/examples/smtp-mail.c +++ b/docs/examples/smtp-mail.c @@ -45,7 +45,7 @@ #define TO_MAIL "A Receiver " TO_ADDR #define CC_MAIL "John CC Smith " CC_ADDR -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO_MAIL "\r\n" "From: " FROM_MAIL "\r\n" diff --git a/docs/examples/smtp-mime.c b/docs/examples/smtp-mime.c index 0ddba4ebb736..4d54532559f6 100644 --- a/docs/examples/smtp-mime.c +++ b/docs/examples/smtp-mime.c @@ -41,7 +41,7 @@ #define TO "" #define CC "" -static const char *headers_text[] = { +static const char * const headers_text[] = { "Date: Tue, 22 Aug 2017 14:08:43 +0100", "To: " TO, "From: " FROM " (Example User)", @@ -82,7 +82,7 @@ int main(void) curl_mime *mime; curl_mime *alt; curl_mimepart *part; - const char **cpp; + const char * const *cpp; /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); diff --git a/docs/examples/smtp-multi.c b/docs/examples/smtp-multi.c index 86545a1ef3c6..9d76a57fe717 100644 --- a/docs/examples/smtp-multi.c +++ b/docs/examples/smtp-multi.c @@ -38,7 +38,7 @@ #define TO_MAIL "" #define CC_MAIL "" -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO_MAIL "\r\n" "From: " FROM_MAIL "\r\n" diff --git a/docs/examples/smtp-ssl.c b/docs/examples/smtp-ssl.c index 9cac9ecd5267..172f66b8dac7 100644 --- a/docs/examples/smtp-ssl.c +++ b/docs/examples/smtp-ssl.c @@ -42,7 +42,7 @@ #define TO_MAIL "" #define CC_MAIL "" -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO_MAIL "\r\n" "From: " FROM_MAIL "\r\n" diff --git a/docs/examples/smtp-tls.c b/docs/examples/smtp-tls.c index da452318a6e2..c3c992717be6 100644 --- a/docs/examples/smtp-tls.c +++ b/docs/examples/smtp-tls.c @@ -42,7 +42,7 @@ #define TO_MAIL "" #define CC_MAIL "" -static const char *payload_text = +static const char payload_text[] = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" "To: " TO_MAIL "\r\n" "From: " FROM_MAIL "\r\n" diff --git a/docs/examples/synctime.c b/docs/examples/synctime.c index 2c7501d4db56..8e9adf1f3f85 100644 --- a/docs/examples/synctime.c +++ b/docs/examples/synctime.c @@ -99,10 +99,10 @@ static int AutoSyncTime; static SYSTEMTIME SYSTime; static SYSTEMTIME LOCALTime; -static const char *DayStr[] = { +static const char * const DayStr[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -static const char *MthStr[] = { +static const char * const MthStr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; diff --git a/docs/examples/url2file.c b/docs/examples/url2file.c index ce8805a40e5f..1216575638c0 100644 --- a/docs/examples/url2file.c +++ b/docs/examples/url2file.c @@ -44,7 +44,7 @@ static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) int main(int argc, const char *argv[]) { - static const char *pagefilename = "page.out"; + static const char pagefilename[] = "page.out"; CURLcode result; CURL *curl; diff --git a/docs/examples/websocket-updown.c b/docs/examples/websocket-updown.c index 85a7e74ea397..495c4b71669c 100644 --- a/docs/examples/websocket-updown.c +++ b/docs/examples/websocket-updown.c @@ -88,7 +88,7 @@ int main(int argc, const char *argv[]) { CURL *curl; struct read_ctx rctx; - const char *payload = "Hello, friend!"; + static const char payload[] = "Hello, friend!"; CURLcode result = curl_global_init(CURL_GLOBAL_ALL); if(result != CURLE_OK) @@ -108,7 +108,7 @@ int main(int argc, const char *argv[]) curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* tell curl that we want to send the payload */ rctx.curl = curl; - rctx.blen = strlen(payload); + rctx.blen = sizeof(payload) - 1; memcpy(rctx.buf, payload, rctx.blen); curl_easy_setopt(curl, CURLOPT_READDATA, &rctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index f1e5c7a42ab2..b42524958864 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -329,7 +329,7 @@ static CURLcode on_resp_header_udp(struct Curl_cfilter *cf, k->httpcode); } else { - const char *p = header + strlen("Content-Length:"); + const char *p = header + CURL_CSTRLEN("Content-Length:"); if(curlx_str_numblanks(&p, &ts->cl)) { failf(data, "Unsupported Content-Length value"); return CURLE_WEIRD_SERVER_REPLY; @@ -419,7 +419,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, k->httpcode); } else { - const char *p = header + strlen("Content-Length:"); + const char *p = header + CURL_CSTRLEN("Content-Length:"); if(curlx_str_numblanks(&p, &ts->cl)) { failf(data, "Unsupported Content-Length value"); return CURLE_WEIRD_SERVER_REPLY; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index be303ffd3ea3..86e04fc0ec14 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -574,7 +574,7 @@ static int proxy_h2_on_header(nghttp2_session *session, return 0; } - if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && + if(namelen == CURL_CSTRLEN(HTTP_PSEUDO_STATUS) && !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { int http_status; struct http_resp *resp; diff --git a/lib/curl_setup.h b/lib/curl_setup.h index a91b912f3e22..e6d5b8f6946b 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1291,10 +1291,14 @@ typedef unsigned int curl_bit; #define CURLMAX(x, y) ((x) > (y) ? (x) : (y)) #define CURLMIN(x, y) ((x) < (y) ? (x) : (y)) +/* Convenience macro to provide the length of a string literal size without + the null-terminator. Equivalent to strlen() for constant strings. */ +#define CURL_CSTRLEN(x) (sizeof(x) - 1) + /* A convenience macro to provide both the string literal and the length of the string literal in one go, useful for functions that take "string,len" as their argument */ -#define STRCONST(x) x, sizeof(x) - 1 +#define STRCONST(x) x, CURL_CSTRLEN(x) #define CURL_ARRAYSIZE(A) (sizeof(A) / sizeof((A)[0])) diff --git a/lib/dict.c b/lib/dict.c index db25d5a7216c..2a0053e9343a 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -153,9 +153,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) if(result) return result; - if(curl_strnequal(path, DICT_MATCH, sizeof(DICT_MATCH) - 1) || - curl_strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2) - 1) || - curl_strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3) - 1)) { + if(curl_strnequal(path, DICT_MATCH, CURL_CSTRLEN(DICT_MATCH)) || + curl_strnequal(path, DICT_MATCH2, CURL_CSTRLEN(DICT_MATCH2)) || + curl_strnequal(path, DICT_MATCH3, CURL_CSTRLEN(DICT_MATCH3))) { word = strchr(path, ':'); if(word) { @@ -200,9 +200,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); } - else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE) - 1) || - curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2) - 1) || - curl_strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3) - 1)) { + else if(curl_strnequal(path, DICT_DEFINE, CURL_CSTRLEN(DICT_DEFINE)) || + curl_strnequal(path, DICT_DEFINE2, CURL_CSTRLEN(DICT_DEFINE2)) || + curl_strnequal(path, DICT_DEFINE3, CURL_CSTRLEN(DICT_DEFINE3))) { word = strchr(path, ':'); if(word) { diff --git a/lib/http.c b/lib/http.c index c8c008423366..4feb930069c4 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5026,7 +5026,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, if(e->namelen == 2 && curl_strequal("TE", e->name)) { if(http_TE_has_token(e->value, "trailers")) result = Curl_dynhds_add(h2_headers, e->name, e->namelen, - "trailers", sizeof("trailers") - 1); + "trailers", CURL_CSTRLEN("trailers")); } else if(h2_permissible_field(e)) { result = Curl_dynhds_add(h2_headers, e->name, e->namelen, diff --git a/lib/http2.c b/lib/http2.c index a63fc4a59c84..8d4a93cb58b0 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1427,7 +1427,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { char *h; - if((namelen == (sizeof(HTTP_PSEUDO_AUTHORITY) - 1)) && + if((namelen == CURL_CSTRLEN(HTTP_PSEUDO_AUTHORITY)) && !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { /* pseudo headers are lower case */ int rc = 0; @@ -1503,7 +1503,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, return 0; } - if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && + if(namelen == CURL_CSTRLEN(HTTP_PSEUDO_STATUS) && !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { /* nghttp2 guarantees :status is received first and only once. */ char buffer[32]; diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index d4ad7cc5f0c7..bff66a958227 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -623,7 +623,7 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, } else { /* Fall back to s3's UNSIGNED-PAYLOAD */ - size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1; + size_t len = CURL_CSTRLEN(S3_UNSIGNED_PAYLOAD); DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */ memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len); sha_hex[len] = 0; @@ -1143,7 +1143,7 @@ static CURLcode sign_and_set_auth_headers(struct Curl_easy *data, goto fail; /* provider 0 uppercase */ - Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], + Curl_strntoupper(&auth_headers[CURL_CSTRLEN("Authorization: ")], curlx_str(provider0), curlx_strlen(provider0)); curlx_free(data->req.hd_auth); diff --git a/lib/http_digest.c b/lib/http_digest.c index 31a9db35a827..90350a1d1e8d 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -54,7 +54,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data, if(!checkprefix("Digest", header) || !ISBLANK(header[6])) return CURLE_AUTH_ERROR; - header += strlen("Digest"); + header += CURL_CSTRLEN("Digest"); curlx_str_passblanks(&header); return Curl_auth_decode_digest_http_message(header, digest); diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 891369b5bc2b..7aaa6878d113 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -83,7 +83,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, return CURLE_OUT_OF_MEMORY; /* Obtain the input token, if any */ - header += strlen("Negotiate"); + header += CURL_CSTRLEN("Negotiate"); curlx_str_passblanks(&header); len = strlen(header); diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index dc9911fdacb2..b0a73a6c8a0e 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -65,7 +65,7 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, if(!ntlm) return CURLE_OUT_OF_MEMORY; - header += strlen("NTLM"); + header += CURL_CSTRLEN("NTLM"); curlx_str_passblanks(&header); if(*header) { unsigned char *hdr; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index aa20a52ebef2..c658b824781e 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -215,7 +215,7 @@ static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, + result = Curl_http_req_make(&req, "CONNECT", CURL_CSTRLEN("CONNECT"), NULL, 0, authority, strlen(authority), NULL, 0); if(result) @@ -340,7 +340,7 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, } if(ver == PROXY_HTTP_V1) { - result = Curl_http_req_make(&req, "GET", sizeof("GET")-1, + result = Curl_http_req_make(&req, "GET", CURL_CSTRLEN("GET"), proxy_scheme, strlen(proxy_scheme), authority, strlen(authority), path, strlen(path)); @@ -348,7 +348,7 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, goto out; } else if(ver == PROXY_HTTP_V2 || ver == PROXY_HTTP_V3) { - result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, + result = Curl_http_req_make(&req, "CONNECT", CURL_CSTRLEN("CONNECT"), proxy_scheme, strlen(proxy_scheme), authority, strlen(authority), path, strlen(path)); diff --git a/lib/imap.c b/lib/imap.c index 6a48f8515280..8f0c29771532 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1357,7 +1357,7 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, size_t len = curlx_dyn_len(&imapc->pp.recvbuf); if((len >= 18) && checkprefix("OK [UIDVALIDITY ", &line[2])) { curl_off_t value; - const char *p = &line[2] + strlen("OK [UIDVALIDITY "); + const char *p = &line[2] + CURL_CSTRLEN("OK [UIDVALIDITY "); if(!curlx_str_number(&p, &value, UINT_MAX)) { imapc->mb_uidvalidity = (unsigned int)value; imapc->mb_uidvalidity_set = TRUE; diff --git a/lib/ldap.c b/lib/ldap.c index dac472d4c986..a7c1fe8c8390 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -976,9 +976,9 @@ void Curl_ldap_version(char *buf, size_t bufsz) curl_msnprintf(buf, bufsz, "WinLDAP"); #else #ifdef LDAP_OPT_X_TLS_PASSPHRASE - static const char *flavor = "/Apple"; + static const char flavor[] = "/Apple"; #else - static const char *flavor = ""; + static const char flavor[] = ""; #endif LDAPAPIInfo api; api.ldapai_info_version = LDAP_API_INFO_VERSION; diff --git a/lib/mqtt.c b/lib/mqtt.c index a9cdf214984b..0858fd65f609 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -276,7 +276,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) size_t start_user = 0; size_t start_pwd = 0; char client_id[MQTT_CLIENTID_LEN + 1] = "curl"; - const size_t clen = strlen("curl"); + const size_t clen = CURL_CSTRLEN("curl"); char *packet = NULL; /* extracting username from request */ @@ -627,7 +627,7 @@ static bool mqtt_decode_len(size_t *lenp, const unsigned char *buf, } #if defined(DEBUGBUILD) && defined(CURLVERBOSE) -static const char *statenames[] = { +static const char * const statenames[] = { "MQTT_FIRST", "MQTT_REMAINING_LENGTH", "MQTT_CONNACK", diff --git a/lib/progress.c b/lib/progress.c index 3c0f3d57d7c0..b6322461d544 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -91,7 +91,7 @@ UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen) if(bytes < 100000) curl_msnprintf(max6, mlen, "%6" CURL_FORMAT_CURL_OFF_T, bytes); else { - const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 }; + static const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 }; int k = 0; curl_off_t nbytes; curl_off_t rest; diff --git a/lib/smb.c b/lib/smb.c index 2d1a859aa169..4299190ff577 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -654,9 +654,10 @@ static CURLcode smb_send_negotiate(struct Curl_easy *data, struct smb_conn *smbc, struct smb_request *req) { - const char *msg = "\x00\x0c\x00\x02NT LM 0.12"; + static const char msg[] = "\x00\x0c\x00\x02NT LM 0.12"; - return smb_send_message(data, smbc, req, SMB_COM_NEGOTIATE, msg, 15); + return smb_send_message(data, smbc, req, SMB_COM_NEGOTIATE, msg, + sizeof(msg)); } static CURLcode smb_send_setup(struct Curl_easy *data) @@ -678,7 +679,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) byte_count = sizeof(lm) + sizeof(nt) + strlen(smbc->user) + strlen(smbc->domain) + - strlen(CURL_OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ + CURL_CSTRLEN(CURL_OS) + CURL_CSTRLEN(CLIENTNAME) + 4; /* 4 null chars */ if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; @@ -724,7 +725,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, char *p = msg.bytes; const size_t byte_count = strlen(conn->origin->hostname) + strlen(smbc->share) + - strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ + CURL_CSTRLEN(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; diff --git a/lib/tftp.c b/lib/tftp.c index d5c85a3100af..c101edebedba 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -278,7 +278,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, infof(data, "got option=(%s) value=(%s)", option, value); - if((strlen(TFTP_OPTION_BLKSIZE) == olen) && + if((CURL_CSTRLEN(TFTP_OPTION_BLKSIZE) == olen) && checkprefix(TFTP_OPTION_BLKSIZE, option)) { curl_off_t blksize; if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) { @@ -308,7 +308,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } - else if((strlen(TFTP_OPTION_TSIZE) == olen) && + else if((CURL_CSTRLEN(TFTP_OPTION_TSIZE) == olen) && checkprefix(TFTP_OPTION_TSIZE, option)) { curl_off_t tsize = 0; /* tsize should be ignored on upload: Who cares about the size of the diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index c23b81025449..bfc68adf14ff 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -1298,9 +1298,8 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, 10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE); quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE); quiche_config_set_application_protos(ctx->cfg, - (uint8_t *)CURL_UNCONST(QUICHE_H3_APPLICATION_PROTOCOL), - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - - 1); + (uint8_t *)CURL_UNCONST(QUICHE_H3_APPLICATION_PROTOCOL), + CURL_CSTRLEN(QUICHE_H3_APPLICATION_PROTOCOL)); result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer, &ALPN_SPEC_H3, NULL, NULL, cf, NULL); @@ -1351,7 +1350,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, unsigned alpn_len, offset = 0; /* Replace each ALPN length prefix by a comma. */ - while(offset < sizeof(alpn_protocols) - 1) { + while(offset < CURL_CSTRLEN(alpn_protocols)) { alpn_len = alpn_protocols[offset]; alpn_protocols[offset] = ','; offset += 1 + alpn_len; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index effcc9beaf4f..940c67835805 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1768,7 +1768,7 @@ static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data, return CURLE_FAILED_INIT; rc = libssh2_sftp_symlink_ex(sshc->sftp_session, ".", - curlx_uztoui(strlen(".")), + curlx_uztoui(CURL_CSTRLEN(".")), sshp->readdir_filename, CURL_PATH_MAX, LIBSSH2_SFTP_REALPATH); if(rc == LIBSSH2_ERROR_EAGAIN) diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c index 197055fc0de5..2c6a0c02d021 100644 --- a/lib/vtls/cipher_suite.c +++ b/lib/vtls/cipher_suite.c @@ -48,7 +48,7 @@ /* Text for cipher suite parts (max 64 entries), keep indexes below in sync with this! */ -static const char *cs_txt = +static const char cs_txt[] = "\0" "TLS" "\0" "WITH" "\0" diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 423179172f1e..92607b1135cd 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -591,7 +591,7 @@ static struct gtls_shared_creds *gtls_get_cached_creds(struct Curl_cfilter *cf, if(data->multi) { shared_creds = Curl_hash_pick(&data->multi->proto_hash, CURL_UNCONST(MPROTO_GTLS_X509_KEY), - sizeof(MPROTO_GTLS_X509_KEY) - 1); + CURL_CSTRLEN(MPROTO_GTLS_X509_KEY)); if(shared_creds && shared_creds->creds && !gtls_shared_creds_expired(data, shared_creds) && !gtls_shared_creds_different(cf, shared_creds)) { @@ -604,7 +604,7 @@ static struct gtls_shared_creds *gtls_get_cached_creds(struct Curl_cfilter *cf, static void gtls_shared_creds_hash_free(void *key, size_t key_len, void *p) { struct gtls_shared_creds *sc = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_GTLS_X509_KEY) - 1)); + DEBUGASSERT(key_len == CURL_CSTRLEN(MPROTO_GTLS_X509_KEY)); DEBUGASSERT(!memcmp(MPROTO_GTLS_X509_KEY, key, key_len)); (void)key; (void)key_len; @@ -635,7 +635,7 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf, if(!Curl_hash_add2(&data->multi->proto_hash, CURL_UNCONST(MPROTO_GTLS_X509_KEY), - sizeof(MPROTO_GTLS_X509_KEY) - 1, + CURL_CSTRLEN(MPROTO_GTLS_X509_KEY), sc, gtls_shared_creds_hash_free)) { Curl_gtls_shared_creds_free(&sc); /* down reference again */ return; diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h index b09fcc6f4d4c..f95dfeae2d53 100644 --- a/lib/vtls/keylog.h +++ b/lib/vtls/keylog.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) +#define KEYLOG_LABEL_MAXLEN CURL_CSTRLEN("CLIENT_HANDSHAKE_TRAFFIC_SECRET") #define CLIENT_RANDOM_SIZE 32 diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 9f576a9d521f..12903038f176 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -248,7 +248,7 @@ static CURLcode X509V3_ext(struct Curl_easy *data, if(asn1_object_dump(obj, namebuf, sizeof(namebuf))) /* make sure the name is null-terminated */ - namebuf[sizeof(namebuf) - 1] = 0; + namebuf[CURL_CSTRLEN(namebuf)] = 0; if(!X509V3_EXT_print(bio_out, ext, 0, 0)) ASN1_STRING_print(bio_out, @@ -1176,7 +1176,7 @@ static int engineload(struct Curl_easy *data, } if(data->state.engine) { - const char *cmd_name = "LOAD_CERT_CTRL"; + static const char cmd_name[] = "LOAD_CERT_CTRL"; struct { const char *cert_id; X509 *cert; @@ -2965,7 +2965,7 @@ static CURLcode ossl_windows_load_anchors(struct Curl_cfilter *cf, https://stackoverflow.com/questions/9507184/ https://github.com/d3x0r/SACK/blob/ff15424d3c581b86d40f818532e5a400c516d39d/src/netlib/ssl_layer.c#L1410 https://datatracker.ietf.org/doc/html/rfc5280 */ - const char *win_stores[] = { + static const char * const win_stores[] = { "ROOT", /* Trusted Root Certification Authorities */ "CA" /* Intermediate Certification Authorities */ }; @@ -3180,7 +3180,7 @@ struct ossl_x509_share { static void oss_x509_share_free(void *key, size_t key_len, void *p) { struct ossl_x509_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY) - 1)); + DEBUGASSERT(key_len == CURL_CSTRLEN(MPROTO_OSSL_X509_KEY)); DEBUGASSERT(!memcmp(MPROTO_OSSL_X509_KEY, key, key_len)); (void)key; (void)key_len; @@ -3231,7 +3231,7 @@ static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf, *pempty = TRUE; share = multi ? Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY) - 1) : NULL; + CURL_CSTRLEN(MPROTO_OSSL_X509_KEY)) : NULL; if(share && share->store && !ossl_cached_x509_store_expired(data, share) && !ossl_cached_x509_store_different(cf, data, share)) { @@ -3256,7 +3256,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, return; share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY) - 1); + CURL_CSTRLEN(MPROTO_OSSL_X509_KEY)); if(!share) { share = curlx_calloc(1, sizeof(*share)); @@ -3264,7 +3264,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, return; if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY) - 1, + CURL_CSTRLEN(MPROTO_OSSL_X509_KEY), share, oss_x509_share_free)) { curlx_free(share); return; @@ -5283,7 +5283,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, unsigned int length; unsigned char buf[EVP_MAX_MD_SIZE]; - const char prefix[] = "tls-server-end-point:"; + static const char prefix[] = "tls-server-end-point:"; struct connectdata *conn = data->conn; struct Curl_cfilter *cf = conn->cfilter[sockindex]; struct ossl_ctx *octx = NULL; @@ -5405,7 +5405,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, } /* Append "tls-server-end-point:" */ - result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1); + result = curlx_dyn_addn(binding, prefix, CURL_CSTRLEN(prefix)); if(result) goto out; @@ -5423,9 +5423,9 @@ size_t Curl_ossl_version(char *buffer, size_t size) char *p; size_t count; const char *ver = OpenSSL_version(OPENSSL_VERSION); - const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ - if(curl_strnequal(ver, expected, sizeof(expected) - 1)) { - ver += sizeof(expected) - 1; + static const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ + if(curl_strnequal(ver, expected, CURL_CSTRLEN(expected))) { + ver += CURL_CSTRLEN(expected); } count = curl_msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); for(p = buffer; *p; ++p) { diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 17c1c8d195db..f55784c624c1 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -291,9 +291,9 @@ static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, if(alg) algIds[algCount++] = (ALG_ID)alg; else if(!strncmp(startCur, "USE_STRONG_CRYPTO", - sizeof("USE_STRONG_CRYPTO") - 1) || + CURL_CSTRLEN("USE_STRONG_CRYPTO")) || !strncmp(startCur, "SCH_USE_STRONG_CRYPTO", - sizeof("SCH_USE_STRONG_CRYPTO") - 1)) + CURL_CSTRLEN("SCH_USE_STRONG_CRYPTO"))) schannel_cred->dwFlags |= SCH_USE_STRONG_CRYPTO; else return CURLE_SSL_CIPHER; @@ -2736,7 +2736,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); + CURL_CSTRLEN(MPROTO_SCHANNEL_CERT_SHARE_KEY)); if(!share || !share->cert_store) { return NULL; } @@ -2785,7 +2785,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, static void schannel_cert_share_free(void *key, size_t key_len, void *p) { struct schannel_cert_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1)); + DEBUGASSERT(key_len == CURL_CSTRLEN(MPROTO_SCHANNEL_CERT_SHARE_KEY)); DEBUGASSERT(!memcmp(MPROTO_SCHANNEL_CERT_SHARE_KEY, key, key_len)); (void)key; (void)key_len; @@ -2828,7 +2828,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); + CURL_CSTRLEN(MPROTO_SCHANNEL_CERT_SHARE_KEY)); if(!share) { share = curlx_calloc(1, sizeof(*share)); if(!share) { @@ -2837,7 +2837,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, } if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1, + CURL_CSTRLEN(MPROTO_SCHANNEL_CERT_SHARE_KEY), share, schannel_cert_share_free)) { curlx_free(share); curlx_free(CAfile); diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index e5fe2249a66e..6e55c9b839ba 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -119,8 +119,8 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, const char *ca_file_text, struct Curl_easy *data) { - const size_t begin_cert_len = strlen(BEGIN_CERT); - const size_t end_cert_len = strlen(END_CERT); + const size_t begin_cert_len = CURL_CSTRLEN(BEGIN_CERT); + const size_t end_cert_len = CURL_CSTRLEN(END_CERT); CURLcode result = CURLE_OK; int num_certs = 0; bool more_certs = 1; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 2da004adba0b..952c5e5e7118 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -483,8 +483,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, pinned_hash = pinnedpubkey; while(pinned_hash && - !strncmp(pinned_hash, "sha256//", (sizeof("sha256//") - 1))) { - pinned_hash = pinned_hash + (sizeof("sha256//") - 1); + !strncmp(pinned_hash, "sha256//", CURL_CSTRLEN("sha256//"))) { + pinned_hash = pinned_hash + CURL_CSTRLEN("sha256//"); end_pos = strchr(pinned_hash, ';'); pinned_hash_len = end_pos ? (size_t)(end_pos - pinned_hash) : strlen(pinned_hash); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index e6ee960117cc..9fe0dc1c884a 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -686,7 +686,7 @@ struct wssl_x509_share { static void wssl_x509_share_free(void *key, size_t key_len, void *p) { struct wssl_x509_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY) - 1)); + DEBUGASSERT(key_len == CURL_CSTRLEN(MPROTO_WSSL_X509_KEY)); DEBUGASSERT(!memcmp(MPROTO_WSSL_X509_KEY, key, key_len)); (void)key; (void)key_len; @@ -730,7 +730,7 @@ static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf, DEBUGASSERT(multi); share = multi ? Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY) - 1) : NULL; + CURL_CSTRLEN(MPROTO_WSSL_X509_KEY)) : NULL; if(share && share->store && !wssl_cached_x509_store_expired(data, share) && !wssl_cached_x509_store_different(cf, share)) { @@ -753,7 +753,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, return; share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY) - 1); + CURL_CSTRLEN(MPROTO_WSSL_X509_KEY)); if(!share) { share = curlx_calloc(1, sizeof(*share)); @@ -761,7 +761,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, return; if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY) - 1, + CURL_CSTRLEN(MPROTO_WSSL_X509_KEY), share, wssl_x509_share_free)) { curlx_free(share); return; diff --git a/scripts/schemetable.c b/scripts/schemetable.c index 8127ecd2a16a..67f6b81b144e 100644 --- a/scripts/schemetable.c +++ b/scripts/schemetable.c @@ -29,7 +29,7 @@ * function in url.c. */ -static const char *scheme[] = { +static const char * const scheme[] = { "dict", "file", "ftp", diff --git a/src/curlinfo.c b/src/curlinfo.c index 36eabeb78fb4..8b4659200fb2 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -43,7 +43,7 @@ #include /* for OPENSSL_NO_OCSP */ #endif -static const char *disabled[] = { +static const char * const disabled[] = { "bindlocal: " #ifdef CURL_DISABLE_BINDLOCAL "OFF" diff --git a/src/tool_doswin.c b/src/tool_doswin.c index a64665bcd95e..52eb69bd695e 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -145,13 +145,13 @@ static SANITIZEcode msdosify(char ** const sanitized, const char *file_name, static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ "|<>/\\\":?*"; /* illegal in DOS & W95 */ - static const char *illegal_chars_w95 = &illegal_chars_dos[8]; + static const char * const illegal_chars_w95 = &illegal_chars_dos[8]; int idx, dot_idx; const char *s = file_name; char *d = dos_name; - const char * const dlimit = dos_name + sizeof(dos_name) - 1; + const char * const dlimit = dos_name + CURL_CSTRLEN(dos_name); const char *illegal_aliens = illegal_chars_dos; - size_t len = sizeof(illegal_chars_dos) - 1; + size_t len = CURL_CSTRLEN(illegal_chars_dos); if(!sanitized) return SANITIZE_ERR_BAD_ARGUMENT; diff --git a/src/tool_easysrc.c b/src/tool_easysrc.c index 5b3c6cb45b6f..c2a34c3b2b1e 100644 --- a/src/tool_easysrc.c +++ b/src/tool_easysrc.c @@ -64,7 +64,7 @@ static const char * const srchard[] = { "", NULL }; -static const char *const srcend[] = { +static const char * const srcend[] = { "", " return (int)result;", "}", diff --git a/src/tool_formparse.c b/src/tool_formparse.c index 6d41711a406e..450a2b3a994a 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -509,7 +509,7 @@ static void param_type(char **ptr, char **ptype, char **endct, char *sep) { char *p = *ptr; size_t tlen; - for(p += sizeof("type=") - 1; ISBLANK(*p); p++) + for(p += CURL_CSTRLEN("type="); ISBLANK(*p); p++) ; /* set type pointer */ *ptype = p; @@ -533,7 +533,7 @@ static void param_filename(char **ptr, char **endct, char **pfilename, **endct = '\0'; *endct = NULL; } - for(p += sizeof("filename=") - 1; ISBLANK(*p); p++) + for(p += CURL_CSTRLEN("filename="); ISBLANK(*p); p++) ; tp = p; *pfilename = get_param_word(&p, &endpos, endchar); @@ -557,7 +557,7 @@ static int param_headers(char **ptr, char **endct, **endct = '\0'; *endct = NULL; } - p += sizeof("headers=") - 1; + p += CURL_CSTRLEN("headers="); if(*p == '@' || *p == '<') { char *hdrfile; FILE *fp; @@ -623,7 +623,7 @@ static void param_encoder(char **ptr, char **endct, char **pencoder, **endct = '\0'; *endct = NULL; } - for(p += sizeof("encoder=") - 1; ISBLANK(*p); p++) + for(p += CURL_CSTRLEN("encoder="); ISBLANK(*p); p++) ; tp = p; *pencoder = get_param_word(&p, &endpos, endchar); diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 479168e0bdb1..73419ead097f 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -2488,7 +2488,7 @@ static ParameterError opt_string(struct OperationConfig *config, { ParameterError err = PARAM_OK; curl_off_t value; - static const char *redir_protos[] = { + static const char * const redir_protos[] = { "http", "https", "ftp", diff --git a/src/tool_helpers.c b/src/tool_helpers.c index b5e9b5412128..1b998d2ca610 100644 --- a/src/tool_helpers.c +++ b/src/tool_helpers.c @@ -77,7 +77,7 @@ const char *param2text(ParameterError error) int SetHTTPrequest(HttpReq req, HttpReq *store) { /* this mirrors the HttpReq enum in tool_sdecls.h */ - const char *reqname[] = { + static const char * const reqname[] = { "", /* unspec */ "GET (-G, --get)", "HEAD (-I, --head)", @@ -101,7 +101,7 @@ int SetHTTPrequest(HttpReq req, HttpReq *store) void customrequest_helper(HttpReq req, const char *method) { /* this mirrors the HttpReq enum in tool_sdecls.h */ - const char *dflt[] = { + static const char * const dflt[] = { "GET", "GET", "HEAD", diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index bce6bb7d6ee6..dfb23912f345 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -27,7 +27,7 @@ /* global variable definitions, for libcurl runtime info */ -static const char *no_protos = NULL; +static const char * const no_protos = NULL; curl_version_info_data *curlinfo = NULL; const char * const *built_in_protos = &no_protos; diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c index 57a49ef97f6a..a3be2afcf736 100644 --- a/src/tool_paramhlp.c +++ b/src/tool_paramhlp.c @@ -297,7 +297,7 @@ ParameterError secs2ms(long *val, const char *str) { curl_off_t secs; long ms = 0; - const unsigned int digs[] = { + static const unsigned int digs[] = { 1, 10, 100, diff --git a/src/tool_progress.c b/src/tool_progress.c index c30644c0e380..d3ee694fea32 100644 --- a/src/tool_progress.c +++ b/src/tool_progress.c @@ -35,7 +35,7 @@ UNITTEST char *max5data(curl_off_t bytes, char *max5, size_t mlen) { /* a signed 64-bit value is 8192 petabytes maximum */ - const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 }; + static const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 }; int k = 0; if(bytes < 100000) { curl_msnprintf(max5, mlen, "%5" CURL_FORMAT_CURL_OFF_T, bytes); diff --git a/src/var.c b/src/var.c index 8f9dbbb5b072..464b0c1c2cec 100644 --- a/src/var.c +++ b/src/var.c @@ -62,15 +62,15 @@ static const struct tool_var *varcontent(const char *name, size_t nlen) (!strncmp(ptr, name, len) && ENDOFFUNC((ptr)[len])) #define FUNC_TRIM "trim" -#define FUNC_TRIM_LEN (sizeof(FUNC_TRIM) - 1) +#define FUNC_TRIM_LEN CURL_CSTRLEN(FUNC_TRIM) #define FUNC_JSON "json" -#define FUNC_JSON_LEN (sizeof(FUNC_JSON) - 1) +#define FUNC_JSON_LEN CURL_CSTRLEN(FUNC_JSON) #define FUNC_URL "url" -#define FUNC_URL_LEN (sizeof(FUNC_URL) - 1) +#define FUNC_URL_LEN CURL_CSTRLEN(FUNC_URL) #define FUNC_B64 "b64" -#define FUNC_B64_LEN (sizeof(FUNC_B64) - 1) +#define FUNC_B64_LEN CURL_CSTRLEN(FUNC_B64) #define FUNC_64DEC "64dec" /* base64 decode */ -#define FUNC_64DEC_LEN (sizeof(FUNC_64DEC) - 1) +#define FUNC_64DEC_LEN CURL_CSTRLEN(FUNC_64DEC) static ParameterError varfunc(char *c, /* content */ size_t clen, /* content length */ diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 0bb0c78a363f..d783af1ab641 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -949,7 +949,7 @@ static int curltest_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { void *data = NULL; - const char *key = "mod_curltest_init_counter"; + static const char *key = "mod_curltest_init_counter"; (void)p; (void)plog; diff --git a/tests/libtest/lib1514.c b/tests/libtest/lib1514.c index a799e7668de4..528468eee026 100644 --- a/tests/libtest/lib1514.c +++ b/tests/libtest/lib1514.c @@ -29,7 +29,7 @@ #include "first.h" struct t1514_WriteThis { - char *readptr; + const char *readptr; size_t sizeleft; }; @@ -55,9 +55,9 @@ static CURLcode test_lib1514(const char *URL) CURL *curl; CURLcode result = CURLE_OK; - static char testdata[] = "dummy"; + static const char testdata[] = "dummy"; - struct t1514_WriteThis pooh = { testdata, sizeof(testdata) - 1 }; + struct t1514_WriteThis pooh = { testdata, CURL_CSTRLEN(testdata) }; global_init(CURL_GLOBAL_ALL); diff --git a/tests/libtest/lib1517.c b/tests/libtest/lib1517.c index 30e80a7bceb7..a88705ed6872 100644 --- a/tests/libtest/lib1517.c +++ b/tests/libtest/lib1517.c @@ -60,7 +60,7 @@ static CURLcode test_lib1517(const char *URL) struct t1517_WriteThis pooh; pooh.readptr = testdata; - pooh.sizeleft = sizeof(testdata) - 1; + pooh.sizeleft = CURL_CSTRLEN(testdata); if(curl_global_init(CURL_GLOBAL_ALL)) { curl_mfprintf(stderr, "curl_global_init() failed\n"); diff --git a/tests/libtest/lib1520.c b/tests/libtest/lib1520.c index 9ed257591ee0..b97fd94fc84e 100644 --- a/tests/libtest/lib1520.c +++ b/tests/libtest/lib1520.c @@ -29,7 +29,7 @@ struct upload_status { static size_t t1520_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { - static const char *payload_text[] = { + static const char * const payload_text[] = { "From: different\r\n", "To: another\r\n", "\r\n", diff --git a/tests/libtest/lib1525.c b/tests/libtest/lib1525.c index 3498941ea2ee..7465eb59dd3e 100644 --- a/tests/libtest/lib1525.c +++ b/tests/libtest/lib1525.c @@ -31,7 +31,7 @@ #include "first.h" static const char t1525_data[] = "Hello Cloud!\n"; -static size_t const t1525_datalen = sizeof(t1525_data) - 1; +static const size_t t1525_datalen = CURL_CSTRLEN(t1525_data); static size_t t1525_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { diff --git a/tests/libtest/lib1526.c b/tests/libtest/lib1526.c index 6e01804628cc..56b1b749b325 100644 --- a/tests/libtest/lib1526.c +++ b/tests/libtest/lib1526.c @@ -30,7 +30,7 @@ #include "first.h" static const char t1526_data[] = "Hello Cloud!\n"; -static size_t const t1526_datalen = sizeof(t1526_data) - 1; +static const size_t t1526_datalen = CURL_CSTRLEN(t1526_data); static size_t t1526_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { diff --git a/tests/libtest/lib1527.c b/tests/libtest/lib1527.c index ef438dc901ae..1caa37d8d3d5 100644 --- a/tests/libtest/lib1527.c +++ b/tests/libtest/lib1527.c @@ -30,7 +30,7 @@ #include "first.h" static const char t1527_data[] = "Hello Cloud!\n"; -static size_t const t1527_datalen = sizeof(t1527_data) - 1; +static const size_t t1527_datalen = CURL_CSTRLEN(t1527_data); static size_t t1527_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { diff --git a/tests/libtest/lib1531.c b/tests/libtest/lib1531.c index def737f9f10e..20ac3639ebfb 100644 --- a/tests/libtest/lib1531.c +++ b/tests/libtest/lib1531.c @@ -25,8 +25,8 @@ static CURLcode test_lib1531(const char *URL) { - static char const testdata[] = ".abc\0xyz"; - static curl_off_t const testdatalen = sizeof(testdata) - 1; + static const char testdata[] = ".abc\0xyz"; + static const curl_off_t testdatalen = CURL_CSTRLEN(testdata); CURL *curl; CURLM *multi; diff --git a/tests/libtest/lib1537.c b/tests/libtest/lib1537.c index 1ef0be9c7522..7fd91f9d4f43 100644 --- a/tests/libtest/lib1537.c +++ b/tests/libtest/lib1537.c @@ -25,8 +25,10 @@ static CURLcode test_lib1537(const char *URL) { - const unsigned char a[] = { 0x2f, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe6, 0xf7 }; + static const unsigned char a[] = { + 0x2f, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe6, 0xf7 + }; CURLcode result = CURLE_OK; char *ptr = NULL; int asize; diff --git a/tests/libtest/lib1554.c b/tests/libtest/lib1554.c index 8b37374c5aa3..c553095c61ff 100644 --- a/tests/libtest/lib1554.c +++ b/tests/libtest/lib1554.c @@ -23,7 +23,7 @@ ***************************************************************************/ #include "first.h" -static const char *ldata_names[] = { +static const char * const ldata_names[] = { "NONE", "SHARE", "COOKIE", diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 1c8aabfd5b2f..fafdd8de9917 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -2269,7 +2269,7 @@ static char bigpart[120000]; */ static int huge(void) { - static const char *smallpart = "c"; + static const char smallpart[] = "c"; int i; CURLU *urlp = curl_url(); CURLUcode rc; @@ -2323,7 +2323,7 @@ static int huge(void) static int urldup(void) { - static const char *url[] = { + static const char * const url[] = { "http://" "user:pwd@" "[2a04:4e42:e00::347%25eth0]" diff --git a/tests/libtest/lib1576.c b/tests/libtest/lib1576.c index 64aaa4c5ea57..a052519a257c 100644 --- a/tests/libtest/lib1576.c +++ b/tests/libtest/lib1576.c @@ -23,8 +23,9 @@ ***************************************************************************/ #include "first.h" -static char t1576_data[] = "request indicates that the client, which made"; -static size_t const t1576_datalen = sizeof(t1576_data) - 1; +static const char t1576_data[] = "request indicates that the client, " + "which made"; +static const size_t t1576_datalen = CURL_CSTRLEN(t1576_data); static size_t t1576_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { diff --git a/tests/libtest/lib1591.c b/tests/libtest/lib1591.c index 97b234234fa4..427f9665645c 100644 --- a/tests/libtest/lib1591.c +++ b/tests/libtest/lib1591.c @@ -34,7 +34,7 @@ static size_t consumed = 0; static size_t t1591_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { static const char testdata[] = "Hello Cloud!\r\n"; - static size_t const datalen = sizeof(testdata) - 1; + static const size_t datalen = CURL_CSTRLEN(testdata); size_t amount = nmemb * size; /* Total bytes curl wants */ diff --git a/tests/libtest/lib1598.c b/tests/libtest/lib1598.c index 961c424e680a..106a339c0702 100644 --- a/tests/libtest/lib1598.c +++ b/tests/libtest/lib1598.c @@ -52,7 +52,7 @@ static int t1598_trailers_callback(struct curl_slist **list, void *userdata) static CURLcode test_lib1598(const char *URL) { - static const char *post_data = "xxx=yyy&aaa=bbbbb"; + static const char post_data[] = "xxx=yyy&aaa=bbbbb"; CURL *curl = NULL; CURLcode result = CURLE_FAILED_INIT; @@ -84,7 +84,7 @@ static CURLcode test_lib1598(const char *URL) easy_setopt(curl, CURLOPT_URL, URL); easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); - easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)CURL_CSTRLEN(post_data)); easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); easy_setopt(curl, CURLOPT_TRAILERFUNCTION, t1598_trailers_callback); easy_setopt(curl, CURLOPT_TRAILERDATA, NULL); diff --git a/tests/libtest/lib1662.c b/tests/libtest/lib1662.c index 0e4942e9ad10..4c338d29216c 100644 --- a/tests/libtest/lib1662.c +++ b/tests/libtest/lib1662.c @@ -30,7 +30,7 @@ struct t1662_WriteThis { static size_t t1662_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { static const char testdata[] = "mooaaa"; - static size_t const testdatalen = sizeof(testdata) - 1; + static const size_t testdatalen = CURL_CSTRLEN(testdata); struct t1662_WriteThis *pooh = (struct t1662_WriteThis *)userp; diff --git a/tests/libtest/lib1901.c b/tests/libtest/lib1901.c index a6ccbdb0cb00..dae52a7f2c31 100644 --- a/tests/libtest/lib1901.c +++ b/tests/libtest/lib1901.c @@ -25,7 +25,7 @@ static size_t t1901_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { - static const char *chunks[] = { "one", "two", "three", "four", NULL }; + static const char * const chunks[] = { "one", "two", "three", "four", NULL }; static int ix = 0; (void)stream; if(chunks[ix]) { diff --git a/tests/libtest/lib1940.c b/tests/libtest/lib1940.c index 349d8131c67c..a0fea9264f5f 100644 --- a/tests/libtest/lib1940.c +++ b/tests/libtest/lib1940.c @@ -25,7 +25,7 @@ static void t1940_showem(CURL *curl, int header_request, unsigned int type) { - static const char *testdata[] = { + static const char * const testdata[] = { "daTE", "Server", "content-type", diff --git a/tests/libtest/lib1948.c b/tests/libtest/lib1948.c index 9b8217be8ff0..73bd5d7ba0f1 100644 --- a/tests/libtest/lib1948.c +++ b/tests/libtest/lib1948.c @@ -55,9 +55,9 @@ static CURLcode test_lib1948(const char *URL) easy_setopt(curl, CURLOPT_HEADER, 1L); easy_setopt(curl, CURLOPT_READFUNCTION, put_callback); pbuf.buf = testput; - pbuf.len = sizeof(testput) - 1; + pbuf.len = CURL_CSTRLEN(testput); easy_setopt(curl, CURLOPT_READDATA, &pbuf); - easy_setopt(curl, CURLOPT_INFILESIZE, (long)(sizeof(testput) - 1)); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)CURL_CSTRLEN(testput)); easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); if(result) @@ -66,7 +66,7 @@ static CURLcode test_lib1948(const char *URL) /* POST */ easy_setopt(curl, CURLOPT_POST, 1L); easy_setopt(curl, CURLOPT_POSTFIELDS, testput); - easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)(sizeof(testput) - 1)); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)CURL_CSTRLEN(testput)); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib1965.c b/tests/libtest/lib1965.c index e09ff349b3e7..683e5a716acf 100644 --- a/tests/libtest/lib1965.c +++ b/tests/libtest/lib1965.c @@ -27,7 +27,7 @@ static CURLcode test_lib1965(const char *URL) { CURLcode result = CURLE_OK; CURLUcode rc; - static const char *schemes[] = { + static const char * const schemes[] = { "bad!", "bad{", "bad/", "bad\\", "a!", "a+123", "http-2", "http.1", "a+-.123", "http-+++2", "http.1--", diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index 59db9e45f163..312d5e600e00 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -47,11 +47,11 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) static const char issuer_prefix[] = "Issuer:"; static const char subject_prefix[] = "Subject:"; - if(!strncmp(slist->data, issuer_prefix, sizeof(issuer_prefix) - 1)) { - issuer = slist->data + sizeof(issuer_prefix) - 1; + if(!strncmp(slist->data, issuer_prefix, CURL_CSTRLEN(issuer_prefix))) { + issuer = slist->data + CURL_CSTRLEN(issuer_prefix); } - if(!strncmp(slist->data, subject_prefix, sizeof(subject_prefix) - 1)) { - subject = slist->data + sizeof(subject_prefix) - 1; + if(!strncmp(slist->data, subject_prefix, CURL_CSTRLEN(subject_prefix))) { + subject = slist->data + CURL_CSTRLEN(subject_prefix); } } diff --git a/tests/libtest/lib505.c b/tests/libtest/lib505.c index 7abaacd14ea2..bf1310fcd2e6 100644 --- a/tests/libtest/lib505.c +++ b/tests/libtest/lib505.c @@ -41,8 +41,8 @@ static CURLcode test_lib505(const char *URL) struct curl_slist *headerlist; struct curl_slist *temp; - static const char *buf_1 = "RNFR 505"; - static const char *buf_2 = "RNTO 505-forreal"; + static const char buf_1[] = "RNFR 505"; + static const char buf_2[] = "RNTO 505-forreal"; if(!libtest_arg2) { curl_mfprintf(stderr, "Usage: \n"); diff --git a/tests/libtest/lib508.c b/tests/libtest/lib508.c index 7ecf2665c48d..20aa1ff86506 100644 --- a/tests/libtest/lib508.c +++ b/tests/libtest/lib508.c @@ -56,7 +56,7 @@ static CURLcode test_lib508(const char *URL) struct t508_WriteThis pooh; pooh.readptr = testdata; - pooh.sizeleft = sizeof(testdata) - 1; + pooh.sizeleft = CURL_CSTRLEN(testdata); if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { curl_mfprintf(stderr, "curl_global_init() failed\n"); diff --git a/tests/libtest/lib536.c b/tests/libtest/lib536.c index e25922062ad4..d6347545f572 100644 --- a/tests/libtest/lib536.c +++ b/tests/libtest/lib536.c @@ -37,7 +37,7 @@ static CURLcode test_lib536(const char *URL) CURL *curl; struct curl_slist *host = NULL; - static const char *url_with_proxy = "http://usingproxy.test/"; + static const char url_with_proxy[] = "http://usingproxy.test/"; const char *url_without_proxy = libtest_arg2; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { diff --git a/tests/libtest/lib544.c b/tests/libtest/lib544.c index 02dc11f39bb3..013c2ab32b30 100644 --- a/tests/libtest/lib544.c +++ b/tests/libtest/lib544.c @@ -63,7 +63,7 @@ static CURLcode test_lib544(const char *URL) easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Update the original data to detect non-copy. */ - curlx_strcopy(teststring, sizeof(teststring), "FAIL", strlen("FAIL")); + curlx_strcopy(teststring, sizeof(teststring), "FAIL", CURL_CSTRLEN("FAIL")); { CURL *curl2; diff --git a/tests/libtest/lib547.c b/tests/libtest/lib547.c index e44c16b55bb1..66ad32afbb32 100644 --- a/tests/libtest/lib547.c +++ b/tests/libtest/lib547.c @@ -29,7 +29,7 @@ #include "first.h" static const char t547_uploadthis[] = "this is the blurb we want to upload\n"; -static size_t const t547_datalen = sizeof(t547_uploadthis) - 1; +static const size_t t547_datalen = CURL_CSTRLEN(t547_uploadthis); static size_t t547_read_cb(char *ptr, size_t size, size_t nmemb, void *clientp) { diff --git a/tests/libtest/lib554.c b/tests/libtest/lib554.c index 98a7ba7fdcb1..ce4348a8d733 100644 --- a/tests/libtest/lib554.c +++ b/tests/libtest/lib554.c @@ -69,7 +69,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) struct t554_WriteThis pooh2; pooh.readptr = testdata; - pooh.sizeleft = sizeof(testdata) - 1; + pooh.sizeleft = CURL_CSTRLEN(testdata); /* Fill in the file upload field */ if(oldstyle) { @@ -99,7 +99,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) a file upload but still using the callback */ pooh2.readptr = testdata; - pooh2.sizeleft = sizeof(testdata) - 1; + pooh2.sizeleft = CURL_CSTRLEN(testdata); /* Fill in the file upload field */ formrc = curl_formadd(&formpost, diff --git a/tests/libtest/lib555.c b/tests/libtest/lib555.c index 9e11aae7d70c..e2cdfbc97128 100644 --- a/tests/libtest/lib555.c +++ b/tests/libtest/lib555.c @@ -33,7 +33,7 @@ #include "first.h" static const char t555_uploadthis[] = "this is the blurb we want to upload\n"; -static size_t const t555_datalen = sizeof(t555_uploadthis) - 1; +static const size_t t555_datalen = CURL_CSTRLEN(t555_uploadthis); static size_t t555_read_cb(char *ptr, size_t size, size_t nmemb, void *clientp) { diff --git a/tests/libtest/lib571.c b/tests/libtest/lib571.c index 37b93e539078..4f506bc3b9dc 100644 --- a/tests/libtest/lib571.c +++ b/tests/libtest/lib571.c @@ -44,7 +44,7 @@ static int rtp_packet_count = 0; static size_t rtp_write(char *data, size_t size, size_t nmemb, void *stream) { - static const char *RTP_DATA = "$_1234\n\0Rsdf"; + static const char RTP_DATA[] = "$_1234\n\0Rsdf"; int channel = RTP_PKT_CHANNEL(data); int message_size; diff --git a/tests/libtest/lib643.c b/tests/libtest/lib643.c index eaf75e8fe153..c2aed4f0d29c 100644 --- a/tests/libtest/lib643.c +++ b/tests/libtest/lib643.c @@ -69,7 +69,7 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) pooh.readptr = testdata; if(testnum == 643) - datasize = (curl_off_t)(sizeof(testdata) - 1); + datasize = (curl_off_t)CURL_CSTRLEN(testdata); pooh.sizeleft = datasize; curl = curl_easy_init(); @@ -123,7 +123,7 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) pooh2.readptr = testdata; if(testnum == 643) - datasize = (curl_off_t)(sizeof(testdata) - 1); + datasize = (curl_off_t)CURL_CSTRLEN(testdata); pooh2.sizeleft = datasize; part = curl_mime_addpart(mime); diff --git a/tests/libtest/lib654.c b/tests/libtest/lib654.c index 0494d580abfc..a10dc944788d 100644 --- a/tests/libtest/lib654.c +++ b/tests/libtest/lib654.c @@ -92,7 +92,7 @@ static CURLcode test_lib654(const char *URL) /* Prepare the callback structure. */ pooh.readptr = testdata; - pooh.sizeleft = (curl_off_t)(sizeof(testdata) - 1); + pooh.sizeleft = (curl_off_t)CURL_CSTRLEN(testdata); pooh.freecount = 0; /* Build the mime tree. */ diff --git a/tests/libtest/lib655.c b/tests/libtest/lib655.c index 3dd70dfa7351..5e1f0990e21a 100644 --- a/tests/libtest/lib655.c +++ b/tests/libtest/lib655.c @@ -25,7 +25,7 @@ #include "testtrace.h" -static const char *TEST_DATA_STRING = "Test data"; +static const char TEST_DATA_STRING[] = "Test data"; static int cb_count = 0; static int resolver_alloc_cb_fail(void *resolver_state, void *reserved, diff --git a/tests/libtest/lib667.c b/tests/libtest/lib667.c index 28a9cde6d2b3..38158dc2b48b 100644 --- a/tests/libtest/lib667.c +++ b/tests/libtest/lib667.c @@ -82,7 +82,7 @@ static CURLcode test_lib667(const char *URL) /* Prepare the callback structure. */ pooh.readptr = testdata; - pooh.sizeleft = (curl_off_t)(sizeof(testdata) - 1); + pooh.sizeleft = (curl_off_t)CURL_CSTRLEN(testdata); /* Build the mime tree. */ mime = curl_mime_init(curl); diff --git a/tests/libtest/lib668.c b/tests/libtest/lib668.c index cd9897f7e90c..fd59a0c3f92f 100644 --- a/tests/libtest/lib668.c +++ b/tests/libtest/lib668.c @@ -77,7 +77,7 @@ static CURLcode test_lib668(const char *URL) /* Prepare the callback structures. */ pooh1.readptr = testdata; - pooh1.sizeleft = sizeof(testdata) - 1; + pooh1.sizeleft = CURL_CSTRLEN(testdata); pooh2 = pooh1; /* Build the mime tree. */ diff --git a/tests/libtest/lib677.c b/tests/libtest/lib677.c index 106fbb26034f..6a6c5ef5a923 100644 --- a/tests/libtest/lib677.c +++ b/tests/libtest/lib677.c @@ -77,8 +77,8 @@ static CURLcode test_lib677(const char *URL) if(!state) { CURLcode ec; - ec = curl_easy_send(curl, testcmd + pos, - sizeof(testcmd) - 1 - pos, &len); + ec = curl_easy_send(curl, testcmd + pos, CURL_CSTRLEN(testcmd) - pos, + &len); if(ec == CURLE_AGAIN) { continue; } @@ -92,7 +92,7 @@ static CURLcode test_lib677(const char *URL) pos += len; else pos = 0; - if(pos == sizeof(testcmd) - 1) { + if(pos == CURL_CSTRLEN(testcmd)) { state++; pos = 0; } diff --git a/tests/libtest/lib757.c b/tests/libtest/lib757.c index 325f3b7b6b96..378c78cf8176 100644 --- a/tests/libtest/lib757.c +++ b/tests/libtest/lib757.c @@ -24,7 +24,7 @@ #include "first.h" static const char t757_data[] = "fun-times"; -static size_t const t757_datalen = sizeof(t757_data) - 1; +static const size_t t757_datalen = CURL_CSTRLEN(t757_data); static size_t read_757(char *buffer, size_t size, size_t nitems, void *arg) { diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 189ac41bec7b..732bcd2efb75 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -178,8 +178,8 @@ static int connack(FILE *dump, curl_socket_t fd) MQTT_MSG_CONNACK, 0x02, 0x00, 0x00 }; - ssize_t rc; const char *label = "CONNACK"; + ssize_t rc; if(m_config.pingresp_as_connack) { /* Send a PINGRESP (0xD0) with remaining_length=2 and payload @@ -260,8 +260,8 @@ static int disconnect(FILE *dump, curl_socket_t fd) MQTT_MSG_DISCONNECT, 0x00, 0x00, 0x00 /* extra bytes for malformed variant */ }; - size_t pktlen = 2; const char *label = "DISCONNECT"; + size_t pktlen = 2; ssize_t rc; if(m_config.disconnect_malformed) { @@ -638,8 +638,8 @@ static curl_socket_t mqttit(curl_socket_t fd) } } else { - const char *def = "this is random payload yes yes it is"; - publish(dump, fd, packet_id, topic, def, strlen(def)); + static const char def[] = "this is random payload yes yes it is"; + publish(dump, fd, packet_id, topic, def, CURL_CSTRLEN(def)); } disconnect(dump, fd); } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index fe2dded9fa8e..7e6c8ef43bd6 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -106,18 +106,18 @@ struct rtspd_httprequest { #define END_OF_HEADERS "\r\n\r\n" /* sent as reply to a QUIT */ -static const char *docquit_rtsp = "HTTP/1.1 200 Goodbye" END_OF_HEADERS; +static const char docquit_rtsp[] = "HTTP/1.1 200 Goodbye" END_OF_HEADERS; /* sent as reply to a CONNECT */ -static const char *docconnect = +static const char docconnect[] = "HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS; /* sent as reply to a "bad" CONNECT */ -static const char *docbadconnect = +static const char docbadconnect[] = "HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS; /* send back this on HTTP 404 file not found */ -static const char *doc404_HTTP = +static const char doc404_HTTP[] = "HTTP/1.1 404 Not Found\r\n" "Server: " RTSPDVERSION "\r\n" "Connection: close\r\n" @@ -133,13 +133,13 @@ static const char *doc404_HTTP = "\n"; /* send back this on RTSP 404 file not found */ -static const char *doc404_RTSP = "RTSP/1.0 404 Not Found\r\n" +static const char doc404_RTSP[] = "RTSP/1.0 404 Not Found\r\n" "Server: " RTSPDVERSION END_OF_HEADERS; /* Default size to send away fake RTP data */ #define RTP_DATA_SIZE 12 -static const char *RTP_DATA = "$_1234\n\0Rsdf"; +static const char RTP_DATA[] = "$_1234\n\0Rsdf"; static int rtspd_ProcessRequest(struct rtspd_httprequest *req) { @@ -267,16 +267,17 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) logmsg("Found a reply-servercmd section!"); do { rtp_size_err = 0; - if(!strncmp(CMD_AUTH_REQUIRED, ptr, strlen(CMD_AUTH_REQUIRED))) { + if(!strncmp(CMD_AUTH_REQUIRED, ptr, + CURL_CSTRLEN(CMD_AUTH_REQUIRED))) { logmsg("instructed to require authorization header"); req->auth_req = TRUE; } - else if(!strncmp(CMD_IDLE, ptr, strlen(CMD_IDLE))) { + else if(!strncmp(CMD_IDLE, ptr, CURL_CSTRLEN(CMD_IDLE))) { logmsg("instructed to idle"); req->rcmd = RCMD_IDLE; req->open = TRUE; } - else if(!strncmp(CMD_STREAM, ptr, strlen(CMD_STREAM))) { + else if(!strncmp(CMD_STREAM, ptr, CURL_CSTRLEN(CMD_STREAM))) { logmsg("instructed to stream"); req->rcmd = RCMD_STREAM; } @@ -404,7 +405,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) if(req->pipe) /* we do have a full set, advance the checkindex to after the end of the headers, for the pipelining case mostly */ - req->checkindex += (end - line) + strlen(END_OF_HEADERS); + req->checkindex += (end - line) + CURL_CSTRLEN(END_OF_HEADERS); /* **** Persistence **** * @@ -427,7 +428,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) ignore the content-length, we return as soon as all headers have been received */ curl_off_t clen; - const char *p = line + strlen("Content-Length:"); + const char *p = line + CURL_CSTRLEN("Content-Length:"); if(curlx_str_numblanks(&p, &clen)) { /* this assumes that a zero Content-Length is valid */ logmsg("Found invalid '%s' in the request", line); @@ -442,7 +443,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) break; } else if(!CURL_STRNICMP("Transfer-Encoding: chunked", line, - strlen("Transfer-Encoding: chunked"))) { + CURL_CSTRLEN("Transfer-Encoding: chunked"))) { /* chunked data coming in */ chunked = TRUE; } @@ -506,12 +507,12 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) if(!req->pipe && req->open && req->prot_version >= 11 && - req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) && - (!strncmp(req->reqbuf, "GET", strlen("GET")) || - !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) { + req->reqbuf + req->offset > end + CURL_CSTRLEN(END_OF_HEADERS) && + (!strncmp(req->reqbuf, "GET", CURL_CSTRLEN("GET")) || + !strncmp(req->reqbuf, "HEAD", CURL_CSTRLEN("HEAD")))) { /* If we have a persistent connection, HTTP version >= 1.1 and GET/HEAD request, enable pipelining. */ - req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS); + req->checkindex = (end - req->reqbuf) + CURL_CSTRLEN(END_OF_HEADERS); req->pipelining = TRUE; } @@ -523,7 +524,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) end = strstr(line, END_OF_HEADERS); if(!end) break; - req->checkindex += (end - line) + strlen(END_OF_HEADERS); + req->checkindex += (end - line) + CURL_CSTRLEN(END_OF_HEADERS); req->pipe--; } @@ -535,7 +536,8 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) return 1; /* done */ if(req->cl > 0) { - if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS)) + if(req->cl <= req->offset - (end - req->reqbuf) - + CURL_CSTRLEN(END_OF_HEADERS)) return 1; /* done */ else return 0; /* not complete yet */ diff --git a/tests/server/socksd.c b/tests/server/socksd.c index b0b5b6771c5c..4de563b06cab 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -102,9 +102,9 @@ static void socksd_resetdefaults(void) curlx_strcopy(s_config.addr, sizeof(s_config.addr), CONFIG_ADDR, strlen(CONFIG_ADDR)); curlx_strcopy(s_config.user, sizeof(s_config.user), - "user", strlen("user")); + "user", CURL_CSTRLEN("user")); curlx_strcopy(s_config.password, sizeof(s_config.password), - "password", strlen("password")); + "password", CURL_CSTRLEN("password")); } static void socksd_getconfig(void) diff --git a/tests/server/sws.c b/tests/server/sws.c index 8190002af2fd..e47ba7fc3d91 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -141,10 +141,10 @@ static const char *cmdfile = "log/server.cmd"; static const char *end_of_headers = END_OF_HEADERS; /* sent as reply to a QUIT */ -static const char *docquit_sws = "HTTP/1.1 200 Goodbye" END_OF_HEADERS; +static const char docquit_sws[] = "HTTP/1.1 200 Goodbye" END_OF_HEADERS; /* send back this on 404 file not found */ -static const char *doc404 = +static const char doc404[] = "HTTP/1.1 404 Not Found\r\n" "Server: " SWSVERSION "\r\n" "Connection: close\r\n" @@ -262,29 +262,29 @@ static int sws_parse_servercmd(struct sws_httprequest *req) while(cmd && cmdsize) { const char *check; - if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) { + if(!strncmp(CMD_AUTH_REQUIRED, cmd, CURL_CSTRLEN(CMD_AUTH_REQUIRED))) { logmsg("instructed to require authorization header"); req->auth_req = TRUE; } - else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) { + else if(!strncmp(CMD_IDLE, cmd, CURL_CSTRLEN(CMD_IDLE))) { logmsg("instructed to idle"); req->rcmd = RCMD_IDLE; req->open = TRUE; } - else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) { + else if(!strncmp(CMD_STREAM, cmd, CURL_CSTRLEN(CMD_STREAM))) { logmsg("instructed to stream"); req->rcmd = RCMD_STREAM; } else if(!strncmp(CMD_CONNECTIONMONITOR, cmd, - strlen(CMD_CONNECTIONMONITOR))) { + CURL_CSTRLEN(CMD_CONNECTIONMONITOR))) { logmsg("enabled connection monitoring"); req->connmon = TRUE; } - else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) { + else if(!strncmp(CMD_UPGRADE, cmd, CURL_CSTRLEN(CMD_UPGRADE))) { logmsg("enabled upgrade"); req->upgrade = TRUE; } - else if(!strncmp(CMD_SWSCLOSE, cmd, strlen(CMD_SWSCLOSE))) { + else if(!strncmp(CMD_SWSCLOSE, cmd, CURL_CSTRLEN(CMD_SWSCLOSE))) { logmsg("swsclose: close this connection after response"); req->close = TRUE; } @@ -292,7 +292,7 @@ static int sws_parse_servercmd(struct sws_httprequest *req) logmsg("instructed to skip this number of bytes %d", num); req->skip = num; } - else if(!strncmp(CMD_NOEXPECT, cmd, strlen(CMD_NOEXPECT))) { + else if(!strncmp(CMD_NOEXPECT, cmd, CURL_CSTRLEN(CMD_NOEXPECT))) { logmsg("instructed to reject Expect: 100-continue"); req->noexpect = TRUE; } @@ -591,7 +591,7 @@ static int sws_ProcessRequest(struct sws_httprequest *req) ignore the content-length, we return as soon as all headers have been received */ curl_off_t clen; - const char *p = line + strlen("Content-Length:"); + const char *p = line + CURL_CSTRLEN("Content-Length:"); if(curlx_str_numblanks(&p, &clen)) { /* this assumes that a zero Content-Length is valid */ logmsg("Found invalid '%s' in the request", line); @@ -608,12 +608,13 @@ static int sws_ProcessRequest(struct sws_httprequest *req) logmsg("... but going to abort after %zu bytes", req->cl); } else if(!CURL_STRNICMP("Transfer-Encoding: chunked", line, - strlen("Transfer-Encoding: chunked"))) { + CURL_CSTRLEN("Transfer-Encoding: chunked"))) { /* chunked data coming in */ chunked = TRUE; } - else if(req->noexpect && !CURL_STRNICMP("Expect: 100-continue", line, - strlen("Expect: 100-continue"))) { + else if(req->noexpect && + !CURL_STRNICMP("Expect: 100-continue", line, + CURL_CSTRLEN("Expect: 100-continue"))) { if(req->cl) req->cl = 0; req->skipall = TRUE; @@ -709,8 +710,8 @@ static int sws_ProcessRequest(struct sws_httprequest *req) req->prot_version >= 11 && req->reqbuf + req->offset > end + strlen(end_of_headers) && !req->cl && - (!strncmp(req->reqbuf, "GET", strlen("GET")) || - !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) { + (!strncmp(req->reqbuf, "GET", CURL_CSTRLEN("GET")) || + !strncmp(req->reqbuf, "HEAD", CURL_CSTRLEN("HEAD")))) { /* If we have a persistent connection, HTTP version >= 1.1 and GET/HEAD request, enable pipelining. */ req->checkindex = (end - req->reqbuf) + strlen(end_of_headers); @@ -771,10 +772,10 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) case RCMD_STREAM: { static const char streamthis[] = "a string to stream 01234567890\n"; for(;;) { - written = swrite(sock, streamthis, sizeof(streamthis) - 1); + written = swrite(sock, streamthis, CURL_CSTRLEN(streamthis)); if(got_exit_signal) return -1; - if(written != (ssize_t)(sizeof(streamthis) - 1)) { + if(written != (ssize_t)CURL_CSTRLEN(streamthis)) { logmsg("Stopped streaming"); break; } @@ -2325,8 +2326,9 @@ static int test_sws(int argc, const char *argv[]) logmsg("====> Client disconnect %d", req->connmon); if(req->connmon) { - const char *keepopen = "[DISCONNECT]\n"; - storerequest(keepopen, strlen(keepopen), REQUEST_DUMP_FILENAME); + static const char keepopen[] = "[DISCONNECT]\n"; + storerequest(keepopen, CURL_CSTRLEN(keepopen), + REQUEST_DUMP_FILENAME); } if(!req->open) diff --git a/tests/tunit/tool1720.c b/tests/tunit/tool1720.c index 80e3c157baa6..486d57e630be 100644 --- a/tests/tunit/tool1720.c +++ b/tests/tunit/tool1720.c @@ -28,7 +28,7 @@ static CURLcode test_tool1720(const char *arg) { UNITTEST_BEGIN_SIMPLE - static const char *check[] = { + static const char * const check[] = { "", "(null)", "filename", diff --git a/tests/unit/unit1303.c b/tests/unit/unit1303.c index 50cdaa12f126..52a7ea9c4331 100644 --- a/tests/unit/unit1303.c +++ b/tests/unit/unit1303.c @@ -85,7 +85,7 @@ static CURLcode test_unit1303(const char *arg) const char *comment; }; - const struct timetest run[] = { + static const struct timetest run[] = { /* both timeouts set, not connecting */ {BASE + 4, 0, 10000, 8000, FALSE, 6000, "6 seconds should be left"}, {BASE + 4, 990000, 10000, 8000, FALSE, 5010, "5010 ms should be left"}, diff --git a/tests/unit/unit1395.c b/tests/unit/unit1395.c index d25d0e3005d2..a28630397720 100644 --- a/tests/unit/unit1395.c +++ b/tests/unit/unit1395.c @@ -35,7 +35,7 @@ static CURLcode test_unit1395(const char *arg) const char *output; }; - const struct dotdot pairs[] = { + static const struct dotdot pairs[] = { { "/%2f%2e%2e%2f/../a", "/a" }, { "/%2f%2e%2e%2f/../", "/" }, { "/%2f%2e%2e%2f/.", "/%2f%2e%2e%2f/" }, diff --git a/tests/unit/unit1396.c b/tests/unit/unit1396.c index 5b03e092bb82..1dd3c171125b 100644 --- a/tests/unit/unit1396.c +++ b/tests/unit/unit1396.c @@ -51,7 +51,7 @@ static CURLcode test_unit1396(const char *arg) }; /* unescape, this => that */ - const struct test list1[] = { + static const struct test list1[] = { { "%61", 3, "a", 1 }, { "%61a", 4, "aa", 2 }, { "%61b", 4, "ab", 2 }, @@ -67,7 +67,7 @@ static CURLcode test_unit1396(const char *arg) { NULL, 0, NULL, 0 } /* end of list marker */ }; /* escape, this => that */ - const struct test list2[] = { + static const struct test list2[] = { { "a", 1, "a", 1 }, { "/", 1, "%2F", 3 }, { "a=b", 3, "a%3Db", 5 }, diff --git a/tests/unit/unit1398.c b/tests/unit/unit1398.c index 7f2267e4384c..2147a4a28304 100644 --- a/tests/unit/unit1398.c +++ b/tests/unit/unit1398.c @@ -34,7 +34,7 @@ static CURLcode test_unit1398(const char *arg) int rc; char buf[3] = { 'b', 'u', 'g' }; - static const char *str = "bug"; + static const char str[] = "bug"; int width = 3; char output[130]; diff --git a/tests/unit/unit1625.c b/tests/unit/unit1625.c index 9ca59b940288..21e37370c131 100644 --- a/tests/unit/unit1625.c +++ b/tests/unit/unit1625.c @@ -41,8 +41,8 @@ static CURLcode test_unit1625(const char *arg) char *large; size_t i; const size_t large_value_len = MAX_HTTP_RESP_HEADER_SIZE; - const size_t large_prefix_len = strlen("Encoding: "); - const size_t large_suffix_len = strlen(", chunked"); + const size_t large_prefix_len = CURL_CSTRLEN("Encoding: "); + const size_t large_suffix_len = CURL_CSTRLEN(", chunked"); static const struct check1625 list[] = { /* basic case */ { "Encoding: gzip, chunked", "Encoding:", "chunked", TRUE }, diff --git a/tests/unit/unit1627.c b/tests/unit/unit1627.c index 1d1f598358fc..06bd30cff5e8 100644 --- a/tests/unit/unit1627.c +++ b/tests/unit/unit1627.c @@ -32,7 +32,7 @@ static CURLcode test_unit1627(const char *arg) size_t i, j; /* existing schemes in different cases */ - static const char *okay[] = { + static const char * const okay[] = { /* all upper */ "DICT", "FILE", "FTP", "FTPS", "GOPHER", "GOPHERS", "HTTP", "HTTPS", "IMAP", "IMAPS", "LDAP", "LDAPS", "MQTT", "MQTTS", "POP3", "POP3S", @@ -50,7 +50,7 @@ static CURLcode test_unit1627(const char *arg) "TELNEt", "tFTP", "Ws", "wSS", }; /* non-existing schemes */ - static const char *notokay[] = { + static const char * const notokay[] = { "a", "A", "htt", "ttp", "httt", "http+", "HTTPP", "HTTPPS", "HTTSP", "GROPHER", "D1CT", "AbG", "zLQp", "mNrtW", "PkY", "bVcxZq", "LmO", "iUhyT", "rEwQA", "xSdfG", "nBvC", "pOiuY", "tRewQ", "aSdfG", "hJkl", diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index 4e307474fd5a..299a19bfd47b 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -54,8 +54,10 @@ static CURLcode test_unit1650(const char *arg) }; static const struct dohrequest req[] = { - {"test.host.name", CURL_DNS_TYPE_A, DNS_Q1, sizeof(DNS_Q1)-1, DOH_OK }, - {"test.host.name", CURL_DNS_TYPE_AAAA, DNS_Q2, sizeof(DNS_Q2)-1, DOH_OK }, + {"test.host.name", + CURL_DNS_TYPE_A, DNS_Q1, CURL_CSTRLEN(DNS_Q1), DOH_OK }, + {"test.host.name", + CURL_DNS_TYPE_AAAA, DNS_Q2, CURL_CSTRLEN(DNS_Q2), DOH_OK }, {"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" ".host.name", CURL_DNS_TYPE_AAAA, NULL, 0, DOH_DNS_BAD_LABEL } @@ -235,7 +237,7 @@ static CURLcode test_unit1650(const char *arg) } /* pass all sizes into the decoder until full */ - for(i = 0; i < sizeof(full49) - 1; i++) { + for(i = 0; i < CURL_CSTRLEN(full49); i++) { struct dohentry d; DOHcode rc; memset(&d, 0, sizeof(d)); @@ -267,8 +269,8 @@ static CURLcode test_unit1650(const char *arg) struct dohentry d; struct dohaddr *a; memset(&d, 0, sizeof(d)); - rc = doh_resp_decode((const unsigned char *)full49, - sizeof(full49) - 1, CURL_DNS_TYPE_A, &d); + rc = doh_resp_decode((const unsigned char *)full49, CURL_CSTRLEN(full49), + CURL_DNS_TYPE_A, &d); fail_if(d.numaddr != 1, "missing address"); a = &d.addr[0]; p = &a->ip.v4[0]; diff --git a/tests/unit/unit1655.c b/tests/unit/unit1655.c index ff3dc5348181..55e902098977 100644 --- a/tests/unit/unit1655.c +++ b/tests/unit/unit1655.c @@ -132,9 +132,9 @@ static CURLcode test_unit1655(const char *arg) const size_t buflen = sizeof(buffer); const size_t magic1 = 9765; size_t olen1 = magic1; - static const char *sunshine1 = "a.com"; - static const char *dotshine1 = "a.com."; - static const char *sunshine2 = "aa.com"; + static const char sunshine1[] = "a.com"; + static const char dotshine1[] = "a.com."; + static const char sunshine2[] = "aa.com"; size_t olen2; DOHcode ret2; size_t olen; diff --git a/tests/unit/unit1664.c b/tests/unit/unit1664.c index 17a04a37f244..f4574d485c67 100644 --- a/tests/unit/unit1664.c +++ b/tests/unit/unit1664.c @@ -41,7 +41,7 @@ static CURLcode test_unit1664(const char *arg) { UNITTEST_BEGIN(t1664_setup()) - static const char *wordparse[] = { + static const char * const wordparse[] = { "word", "word ", " word ", @@ -78,7 +78,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *qwords[] = { + static const char * const qwords[] = { "\"word\"", "\"word", "word\"", @@ -112,7 +112,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *single[] = { + static const char * const single[] = { "a", "aa", "A", @@ -133,7 +133,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *single[] = { + static const char * const single[] = { "a", "aa", "A", @@ -156,7 +156,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *single[] = { + static const char * const single[] = { "a", "aa", "A", @@ -177,7 +177,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *nums[] = { + static const char * const nums[] = { "1", "10000", "1234", @@ -313,7 +313,7 @@ static CURLcode test_unit1664(const char *arg) { /* CURL_OFF_T is typically 9223372036854775807 */ - static const char *nums[] = { + static const char * const nums[] = { "9223372036854775807", /* 2^63 -1 */ "9223372036854775808", /* 2^63 */ "18446744073709551615", /* 2^64 - 1 */ @@ -347,7 +347,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *newl[] = { + static const char * const newl[] = { "a", "aa", "A", @@ -372,7 +372,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *nums[] = { + static const char * const nums[] = { "1", "1000", "1234", @@ -399,7 +399,7 @@ static CURLcode test_unit1664(const char *arg) } { - static const char *nums[] = { + static const char * const nums[] = { "1", "1000", "1234", @@ -427,7 +427,7 @@ static CURLcode test_unit1664(const char *arg) { /* CURL_OFF_T is typically 2^63-1 */ - static const char *nums[] = { + static const char * const nums[] = { "777777777777777777777", /* 2^63 -1 */ "1000000000000000000000", /* 2^63 */ "111111111111111111111", @@ -451,7 +451,7 @@ static CURLcode test_unit1664(const char *arg) { /* CURL_OFF_T is typically 2^63-1 */ - static const char *nums[] = { + static const char * const nums[] = { "7FFFFFFFFFFFFFFF", /* 2^63 -1 */ "8000000000000000", /* 2^63 */ "1111111111111111", diff --git a/tests/unit/unit1666.c b/tests/unit/unit1666.c index c2f20c977a58..cf272da42fcc 100644 --- a/tests/unit/unit1666.c +++ b/tests/unit/unit1666.c @@ -36,7 +36,7 @@ struct test_1666 { }; /* the size of the object needs to deduct the null-terminator */ -#define OID(x) x, sizeof(x) - 1 +#define OID(x) STRCONST(x) static bool test1666(const struct test_1666 *spec, size_t i, struct dynbuf *dbuf) diff --git a/tests/unit/unit1675.c b/tests/unit/unit1675.c index 7e869651d006..1d7c44d46120 100644 --- a/tests/unit/unit1675.c +++ b/tests/unit/unit1675.c @@ -38,7 +38,7 @@ static CURLcode test_unit1675(const char *arg) const char *in; const char *out; }; - const struct ipv4_test tests[] = { + static const struct ipv4_test tests[] = { { "0x.0x.0x.0x", NULL }, /* invalid hex */ { "0x.0x.0x", NULL }, /* invalid hex */ { "0x.0x", NULL }, /* invalid hex */ @@ -146,7 +146,7 @@ static CURLcode test_unit1675(const char *arg) unsigned int query; const char *out; }; - const struct urlencode_test tests[] = { + static const struct urlencode_test tests[] = { { "http://leave\x01/hello\x01world", FALSE, QUERY_NO, "http://leave\x01/hello%01world" }, { "http://leave/hello\x01world", FALSE, QUERY_NO, @@ -204,7 +204,7 @@ static CURLcode test_unit1675(const char *arg) const char *out_host; const char *out_zone; }; - const struct ipv6_test tests[] = { + static const struct ipv6_test tests[] = { { "[::1]", "[::1]", NULL }, { "[fe80::1%eth0]", "[fe80::1]", "eth0" }, { "[fe80::1%25eth0]", "[fe80::1]", "eth0" }, @@ -260,7 +260,7 @@ static CURLcode test_unit1675(const char *arg) const char *out_path; bool fine; }; - const struct file_test tests[] = { + static const struct file_test tests[] = { { "file:///etc/hosts", "/etc/hosts", TRUE }, { "file://localhost/etc/hosts", "/etc/hosts", TRUE }, { "file://apple/etc/hosts", "/etc/hosts", FALSE }, @@ -317,7 +317,7 @@ static CURLcode test_unit1675(const char *arg) const char *path; bool expect_match; }; - const struct origin_test tests[] = { + static const struct origin_test tests[] = { { "http://host:123/x", "http", "host", "123", "/y", TRUE }, { "http://host:123/x", NULL, "host", "123", "/y", TRUE }, { "http://host:123/x", NULL, NULL, NULL, "/y", TRUE }, @@ -395,7 +395,7 @@ static CURLcode test_unit1675(const char *arg) const char *options; size_t offset; }; - const struct test tests[] = { + static const struct test tests[] = { { CURLUE_OK, "foo:bar@host", NULL, 0, "foo", "bar", "o", 8 }, { CURLUE_OK, "foo:bar;abc@host", "imap", 0, "foo", "bar", "abc", 12 }, { CURLUE_OK, "foo:bar;abc@host", NULL, 0, "foo", "bar;abc", "o", 12 }, diff --git a/tests/unit/unit2603.c b/tests/unit/unit2603.c index 0198ccf7f4c2..f2ec4f6b9036 100644 --- a/tests/unit/unit2603.c +++ b/tests/unit/unit2603.c @@ -48,7 +48,7 @@ static void check_eq(const char *s, const char *exp_s, const char *name) } struct tcase { - const char **input; + const char * const *input; const char *default_scheme; const char *custom_method; const char *method; @@ -117,7 +117,7 @@ static CURLcode test_unit2603(const char *arg) UNITTEST_BEGIN_SIMPLE #ifndef CURL_DISABLE_HTTP - static const char *T1_INPUT[] = { + static const char * const T1_INPUT[] = { "GET /path HTTP/1.1\r\nHost: test.curl.se\r\n\r\n", NULL, }; @@ -128,7 +128,7 @@ static CURLcode test_unit2603(const char *arg) T1_INPUT, "https", NULL, "GET", "https", NULL, "/path", 1, 0 }; - static const char *T2_INPUT[] = { + static const char * const T2_INPUT[] = { "GET /path HTT", "P/1.1\r\nHost: te", "st.curl.se\r\n\r", @@ -139,7 +139,7 @@ static CURLcode test_unit2603(const char *arg) T2_INPUT, NULL, NULL, "GET", NULL, NULL, "/path", 1, 8 }; - static const char *T3_INPUT[] = { + static const char * const T3_INPUT[] = { "GET ftp://ftp.curl.se/xxx?a=2 HTTP/1.1\r\nContent-Length: 0\r", "\nUser-Agent: xxx\r\n\r\n", NULL, @@ -148,7 +148,7 @@ static CURLcode test_unit2603(const char *arg) T3_INPUT, NULL, NULL, "GET", "ftp", "ftp.curl.se", "/xxx?a=2", 2, 0 }; - static const char *T4_INPUT[] = { + static const char * const T4_INPUT[] = { "CONNECT ftp.curl.se:123 HTTP/1.1\r\nContent-Length: 0\r\n", "User-Agent: xxx\r\n", "nothing: \r\n\r\n\n\n", @@ -158,7 +158,7 @@ static CURLcode test_unit2603(const char *arg) T4_INPUT, NULL, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2 }; - static const char *T6_INPUT[] = { + static const char * const T6_INPUT[] = { "PUT /path HTTP/1.1\nHost: test.curl.se\n\n123", NULL, }; @@ -167,7 +167,7 @@ static CURLcode test_unit2603(const char *arg) }; /* test a custom method with space, #19543 */ - static const char *T7_INPUT[] = { + static const char * const T7_INPUT[] = { "IN SANE /path HTTP/1.1\r\nContent-Length: 0\r\n\r\n", NULL, }; diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c index 3a7a37597c42..23e5d567135b 100644 --- a/tests/unit/unit3200.c +++ b/tests/unit/unit3200.c @@ -45,7 +45,7 @@ static CURLcode test_unit3200(const char *arg) #define C1024 C256 C256 C256 C256 #define C4096 C1024 C1024 C1024 C1024 - static const char *filecontents[] = { + static const char * const filecontents[] = { /* Both should be read */ "LINE1\n" "LINE2 NEWLINE\n", diff --git a/tests/unit/unit3205.c b/tests/unit/unit3205.c index 039b679d0ae9..234d8ae9335e 100644 --- a/tests/unit/unit3205.c +++ b/tests/unit/unit3205.c @@ -420,7 +420,7 @@ static CURLcode test_unit3205(const char *arg) #endif }; - static const char *cs_test_string = + static const char cs_test_string[] = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:" "TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:" @@ -561,11 +561,12 @@ static CURLcode test_unit3205(const char *arg) if(test->id >= 0x0011 && test->id < 0x0017) { if(expect && !memcmp(expect, "EDH-", 4)) { curlx_strcopy(alt, sizeof(alt), expect, strlen(expect)); - expect = (const char *)memcpy(alt, "DHE-", sizeof("DHE-") - 1); + expect = memcpy(alt, "DHE-", CURL_CSTRLEN("DHE-")); } if(expect && !memcmp(expect + 4, "EDH-", 4)) { curlx_strcopy(alt, sizeof(alt), expect, strlen(expect)); - expect = (const char *)memcpy(alt + 4, "DHE-", sizeof("DHE-") - 1) - 4; + expect = memcpy(alt + 4, "DHE-", CURL_CSTRLEN("DHE-")); + expect -= 4; } } diff --git a/tests/unit/unit3400.c b/tests/unit/unit3400.c index 060cc6179946..364a2b540f11 100644 --- a/tests/unit/unit3400.c +++ b/tests/unit/unit3400.c @@ -56,13 +56,13 @@ static void check_capsule_hdr(size_t payload_len, static void test_capsule_encap_udp_hdr_boundaries(void) { - const unsigned char p0[] = { 0x00, 0x01, 0x00 }; - const unsigned char p62[] = { 0x00, 0x3F, 0x00 }; - const unsigned char p63[] = { 0x00, 0x40, 0x40, 0x00 }; - const unsigned char p64[] = { 0x00, 0x40, 0x41, 0x00 }; - const unsigned char p16382[] = { 0x00, 0x7F, 0xFF, 0x00 }; - const unsigned char p16383[] = { 0x00, 0x80, 0x00, 0x40, 0x00, 0x00 }; - const unsigned char p16384[] = { 0x00, 0x80, 0x00, 0x40, 0x01, 0x00 }; + static const unsigned char p0[] = { 0x00, 0x01, 0x00 }; + static const unsigned char p62[] = { 0x00, 0x3F, 0x00 }; + static const unsigned char p63[] = { 0x00, 0x40, 0x40, 0x00 }; + static const unsigned char p64[] = { 0x00, 0x40, 0x41, 0x00 }; + static const unsigned char p16382[] = { 0x00, 0x7F, 0xFF, 0x00 }; + static const unsigned char p16383[] = { 0x00, 0x80, 0x00, 0x40, 0x00, 0x00 }; + static const unsigned char p16384[] = { 0x00, 0x80, 0x00, 0x40, 0x01, 0x00 }; check_capsule_hdr(0, p0, sizeof(p0)); check_capsule_hdr(62, p62, sizeof(p62)); @@ -139,7 +139,7 @@ static void test_capsule_sequential_decode(void) CURLcode err; size_t nread; /* Two back-to-back 3-byte UDP capsules */ - const unsigned char two_caps[] = { + static const unsigned char two_caps[] = { 0x00, 0x04, 0x00, 0x11, 0x22, 0x33, /* capsule 1: [0x11,0x22,0x33] */ 0x00, 0x04, 0x00, 0xAA, 0xBB, 0xCC /* capsule 2: [0xAA,0xBB,0xCC] */ }; @@ -186,13 +186,15 @@ static void test_capsule_decode_paths(void) unsigned char out[8]; CURLcode err = CURLE_OK; size_t nread; - const unsigned char invalid_type[] = { 0x01 }; - const unsigned char partial_len[] = { 0x00, 0x40 }; - const unsigned char invalid_context[] = { 0x00, 0x01, 0x01 }; - const unsigned char invalid_caps_len[] = { 0x00, 0x00, 0x00 }; - const unsigned char partial_payload[] = { 0x00, 0x04, 0x00, 0x11, 0x22 }; - const unsigned char payload_3b[] = { 0x00, 0x04, 0x00, 0x11, 0x22, 0x33 }; - const unsigned char payload_empty[] = { 0x00, 0x01, 0x00 }; + static const unsigned char invalid_type[] = { 0x01 }; + static const unsigned char partial_len[] = { 0x00, 0x40 }; + static const unsigned char invalid_context[] = { 0x00, 0x01, 0x01 }; + static const unsigned char invalid_caps_len[] = { 0x00, 0x00, 0x00 }; + static const unsigned char partial_payload[] = { 0x00, 0x04, 0x00, 0x11, + 0x22 }; + static const unsigned char payload_3b[] = { 0x00, 0x04, 0x00, + 0x11, 0x22, 0x33 }; + static const unsigned char payload_empty[] = { 0x00, 0x01, 0x00 }; Curl_bufq_init2(&q, 32, 4, BUFQ_OPT_NONE); From c39193a589164a5ef506385f352b9c904d7107f9 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 13:06:38 -0700 Subject: [PATCH 0743/1062] tests: improve exception handling in Python test code * Explicitly set "check" in subprocess.run() to raise an exception automatically, where it was done manually before * Use contextlib.suppress to ignore exceptions * Use custom exceptions for test errors for clarity and flexibility. * Replace IOError with OSError This fixes ruff rules BLE001, PLW1510, S110, TRY201, TRY203, TRY002, UP024. --- tests/dictserver.py | 4 +- tests/http/scorecard.py | 6 +-- tests/http/test_05_errors.py | 7 ++- tests/http/test_11_unix.py | 4 +- tests/http/testenv/caddy.py | 2 +- tests/http/testenv/certs.py | 12 ++--- tests/http/testenv/client.py | 2 +- tests/http/testenv/curl.py | 67 +++++++++++----------------- tests/http/testenv/env.py | 39 ++++++++-------- tests/http/testenv/httpd.py | 16 +++---- tests/http/testenv/ports.py | 11 ++--- tests/http/testenv/sshd.py | 18 +++----- tests/http/testenv/ws_echo_server.py | 7 ++- tests/negtelnetserver.py | 4 +- tests/util.py | 2 +- 15 files changed, 86 insertions(+), 115 deletions(-) diff --git a/tests/dictserver.py b/tests/dictserver.py index b4e1afd78b4b..4419e11582ee 100755 --- a/tests/dictserver.py +++ b/tests/dictserver.py @@ -101,8 +101,8 @@ def handle(self): log.debug("[DICT] Responding with %r", response) self.request.sendall(response.encode("utf-8")) - except IOError: - log.exception("[DICT] IOError hit during request") + except OSError: + log.exception("[DICT] OSError hit during request") def get_options(): diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 5bc24cf2c9c0..95e092672efd 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -148,7 +148,7 @@ def mk_reqs_cell(cls, samples, profiles, errors): def parse_size(cls, s): m = re.match(r'(\d+)(mb|kb|gb)?', s, re.IGNORECASE) if m is None: - raise Exception(f'unrecognized size: {s}') + raise ScoreCardError(f'unrecognized size: {s}') size = int(m.group(1)) if not m.group(2): pass @@ -263,7 +263,7 @@ def __init__(self, env: Env, if self._limit_rate: m = re.match(r'(\d+(\.\d+)?)([gmkb])?', self._limit_rate.lower()) if not m: - raise Exception(f'unrecognised limit-rate: {self._limit_rate}') + raise ScoreCardError(f'unrecognised limit-rate: {self._limit_rate}') self._limit_rate_num = float(m.group(1)) if m.group(3) == 'g': self._limit_rate_num *= 1024 * 1024 * 1024 @@ -274,7 +274,7 @@ def __init__(self, env: Env, elif m.group(3) == 'b': pass else: - raise Exception(f'unrecognised limit-rate: {self._limit_rate}') + raise ScoreCardError(f'unrecognised limit-rate: {self._limit_rate}') self.suppress_cl = suppress_cl def info(self, msg): diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index 76c12bc8e551..60a9aaa2e49b 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -24,6 +24,7 @@ # ########################################################################### # +import contextlib import logging import os import socket @@ -192,13 +193,11 @@ def test_05_09_handshake_eof(self, env: Env): # accept one connection and immediately close it def accept_and_close(): - try: + # ignore expected socket error + with contextlib.suppress(OSError): conn, _ = server.accept() conn.recv(1) # wait for ClientHello conn.close() - except Exception: - # ignore expected socket error - pass t = threading.Thread(target=accept_and_close) t.start() diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index 774a7737c91b..b4e5ac79742b 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -82,9 +82,7 @@ def _process(self): finally: c.close() - except ConnectionAbortedError: - self._done = True - except OSError: + except (ConnectionAbortedError, OSError): self._done = True diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index 1d554b1906f4..a6c8b58c5cbd 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -125,7 +125,7 @@ def stop(self, wait_dead=True): self._process.terminate() try: self._process.wait(timeout=1) - except Exception: + except subprocess.TimeoutExpired: self._process.kill() self._process = None self.close_log() diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 85012634a5a7..8c25155b24e7 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -57,6 +57,10 @@ ]]) +class CertError(Exception): + """Error in certificate handling.""" + + def _private_key(key_type): if isinstance(key_type, str): key_type = key_type.upper() @@ -150,7 +154,7 @@ def key_type(self): return f"rsa{self._pkey.key_size}" if isinstance(self._pkey, EllipticCurvePrivateKey): return f"{self._pkey.curve.name}" - raise Exception(f"unknown key type: {self._pkey}") + raise CertError(f"unknown key type: {self._pkey}") @property def private_key(self) -> Any: @@ -249,9 +253,7 @@ def create_hashdir(self, openssl): os.makedirs(self.hashdir, exist_ok=True) p = subprocess.run(args=[ openssl, 'x509', '-hash', '-noout', '-in', self.cert_file - ], capture_output=True, text=True) - if p.returncode != 0: - raise Exception(f'openssl failed to compute cert hash: {p}') + ], capture_output=True, text=True, check=True) cert_hname = f'{p.stdout.strip()}.0' shutil.copy(self.cert_file, os.path.join(self.hashdir, cert_hname)) @@ -401,7 +403,7 @@ def create_credentials(spec: CertificateSpec, issuer: Credentials, key_type: Any valid_from=valid_from, valid_to=valid_to, key_type=key_type) else: - raise Exception(f"unrecognized certificate specification: {spec}") + raise CertError(f"unrecognized certificate specification: {spec}") return creds @staticmethod diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 76c4822cbc8d..0df19e5b427a 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -98,7 +98,7 @@ def run(self, args): p = subprocess.run(myargs, stderr=cerr, stdout=cout, cwd=self._run_dir, shell=False, input=None, env=run_env, - timeout=self._timeout) + timeout=self._timeout, check=False) exitcode = p.returncode except subprocess.TimeoutExpired: log.warning(f'Timeout after {self._timeout}s: {args}') diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 2abbbbe49cb0..8e187039ab11 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -24,6 +24,7 @@ # ########################################################################### # +import contextlib import json import logging import os @@ -41,7 +42,7 @@ import psutil -from .env import Env +from .env import Env, EnvError log = logging.getLogger(__name__) @@ -133,12 +134,9 @@ def finish(self): self._proc.terminate() self._rc = self._proc.returncode with open(self._file, 'w') as cout: - p = subprocess.run([ + subprocess.run([ 'sudo', 'perf', 'script' - ], stdout=cout, cwd=self._run_dir, shell=False) - rc = p.returncode - if rc != 0: - raise Exception(f'perf returned error {rc}') + ], stdout=cout, cwd=self._run_dir, shell=False, check=True) @property def file(self): @@ -192,7 +190,7 @@ def get_rsts(self, ports: Optional[List[int]] = None, port_pairs: Optional[List[Tuple[int, int]]] = None ) -> Optional[List[str]]: if self._proc: - raise Exception('tcpdump still running') + raise EnvError('tcpdump still running') # a pair matches only a RST between exactly these two ports, while # a port in `ports` matches any RST it is involved in pairs = None @@ -222,7 +220,7 @@ def stats(self) -> Optional[List[str]]: @property def stderr(self) -> List[str]: if self._proc: - raise Exception('tcpdump still running') + raise EnvError('tcpdump still running') with open(self._stderrfile) as fd: return fd.readlines() @@ -232,7 +230,7 @@ def sample(self): try: tcpdump = self._env.tcpdump() if tcpdump is None: - raise Exception('tcpdump not available') + raise EnvError('tcpdump not available') # look with tcpdump for TCP RST packets which indicate # we did not shut down connections cleanly args = [] @@ -249,11 +247,9 @@ def sample(self): assert self._proc assert self._proc.returncode is None while self._proc: - try: + # timeout means tcpdump is still running + with contextlib.suppress(subprocess.TimeoutExpired): self._proc.wait(timeout=1) - except subprocess.TimeoutExpired: - # timeout means tcpdump is still running - pass except Exception: log.exception('Tcpdump') @@ -649,13 +645,13 @@ def __init__(self, env: Env, if 'FLAMEGRAPH' in os.environ: self._fg_dir = os.environ['FLAMEGRAPH'] if not os.path.exists(self._fg_dir): - raise Exception(f'FlameGraph checkout not found in {self._fg_dir}, set env variable FLAMEGRAPH') + raise EnvError(f'FlameGraph checkout not found in {self._fg_dir}, set env variable FLAMEGRAPH') if sys.platform.startswith('linux'): self._with_perf = True elif sys.platform.startswith('darwin'): self._with_dtrace = True else: - raise Exception(f'flame graphs unsupported on {sys.platform}') + raise EnvError(f'flame graphs unsupported on {sys.platform}') self._socks_args = socks_args self._silent = silent self._run_env = run_env @@ -919,7 +915,7 @@ def ftp_upload(self, urls: List[str], '--upload-file', '-' ]) else: - raise Exception('need either file or data to upload') + raise EnvError('need either file or data to upload') if with_stats: extra_args.extend([ '-w', '%{json}\\n' @@ -995,7 +991,7 @@ def ssh_upload(self, urls: List[str], '--upload-file', '-' ]) else: - raise Exception('need either file or data to upload') + raise EnvError('need either file or data to upload') if with_stats: extra_args.extend([ '-w', '%{json}\\n' @@ -1071,7 +1067,7 @@ def _run(self, args, intext='', with_stats: bool = False, cwd=self._run_dir, shell=False, input=intext.encode() if intext else None, timeout=self._timeout, - env=self._run_env) + env=self._run_env, check=False) exitcode = p.returncode except subprocess.TimeoutExpired: now = datetime.now() @@ -1160,7 +1156,7 @@ def _complete_args(self, urls, timeout=None, options=None, args.extend(options) if alpn_proto is not None: if alpn_proto not in self.ALPN_ARG: - raise Exception(f'unknown ALPN protocol: "{alpn_proto}"') + raise EnvError(f'unknown ALPN protocol: "{alpn_proto}"') args.append(self.ALPN_ARG[alpn_proto]) if u.scheme == 'http': @@ -1243,36 +1239,30 @@ def fin_response(resp): def _perf_collapse(self, perf: PerfProfile, file_err): if not os.path.exists(perf.file): - raise Exception(f'perf output file does not exist: {perf.file}') + raise EnvError(f'perf output file does not exist: {perf.file}') fg_collapse = os.path.join(self._fg_dir, 'stackcollapse-perf.pl') if not os.path.exists(fg_collapse): - raise Exception(f'FlameGraph script not found: {fg_collapse}') + raise EnvError(f'FlameGraph script not found: {fg_collapse}') stacks_collapsed = f'{perf.file}.collapsed' log.info(f'collapsing stacks into {stacks_collapsed}') with open(stacks_collapsed, 'w') as cout, open(file_err, 'w') as cerr: - p = subprocess.run([ + subprocess.run([ fg_collapse, perf.file - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False) - rc = p.returncode - if rc != 0: - raise Exception(f'{fg_collapse} returned error {rc}') + ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) return stacks_collapsed def _dtrace_collapse(self, dtrace: DTraceProfile, file_err): if not os.path.exists(dtrace.file): - raise Exception(f'dtrace output file does not exist: {dtrace.file}') + raise EnvError(f'dtrace output file does not exist: {dtrace.file}') fg_collapse = os.path.join(self._fg_dir, 'stackcollapse.pl') if not os.path.exists(fg_collapse): - raise Exception(f'FlameGraph script not found: {fg_collapse}') + raise EnvError(f'FlameGraph script not found: {fg_collapse}') stacks_collapsed = f'{dtrace.file}.collapsed' log.info(f'collapsing stacks into {stacks_collapsed}') with open(stacks_collapsed, 'w') as cout, open(file_err, 'a') as cerr: - p = subprocess.run([ + subprocess.run([ fg_collapse, dtrace.file - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False) - rc = p.returncode - if rc != 0: - raise Exception(f'{fg_collapse} returned error {rc}') + ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) return stacks_collapsed def _generate_flame(self, curl_args: List[str], @@ -1281,7 +1271,7 @@ def _generate_flame(self, curl_args: List[str], fg_gen_flame = os.path.join(self._fg_dir, 'flamegraph.pl') file_svg = os.path.join(self._run_dir, 'curl.flamegraph.svg') if not os.path.exists(fg_gen_flame): - raise Exception(f'FlameGraph script not found: {fg_gen_flame}') + raise EnvError(f'FlameGraph script not found: {fg_gen_flame}') log.info('waiting a sec for perf/dtrace to finish flushing') time.sleep(2) @@ -1292,7 +1282,7 @@ def _generate_flame(self, curl_args: List[str], elif dtrace: stacks_collapsed = self._dtrace_collapse(dtrace, file_err) else: - raise Exception('no stacks measure given') + raise EnvError('no stacks measure given') log.info(f'generating graph into {file_svg}') cmdline = ' '.join(curl_args) @@ -1303,14 +1293,11 @@ def _generate_flame(self, curl_args: List[str], title = cmdline subtitle = '' with open(file_svg, 'w') as cout, open(file_err, 'a') as cerr: - p = subprocess.run([ + subprocess.run([ fg_gen_flame, '--colors', 'green', '--title', title, '--subtitle', subtitle, stacks_collapsed - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False) - rc = p.returncode - if rc != 0: - raise Exception(f'{fg_gen_flame} returned error {rc}') + ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) def mk_altsvc_file(self, name, src_alpn, src_host, src_port, dest_alpn, dest_host, dest_port): diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 8f619271a279..5acb3ab67a58 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -43,6 +43,10 @@ log = logging.getLogger(__name__) +class EnvError(Exception): + """Exception from the test environment.""" + + def init_config_from(conf_path): if os.path.isfile(conf_path): config = ConfigParser(interpolation=ExtendedInterpolation()) @@ -58,7 +62,7 @@ def init_config_from(conf_path): if not os.path.exists(CONFIG_PATH): ALT_CONFIG_PATH = os.path.join(PROJ_PATH, "tests", "http", "config.ini") if not os.path.exists(ALT_CONFIG_PATH): - raise Exception( + raise EnvError( f"unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}" ) TOP_PATH = PROJ_PATH @@ -77,11 +81,7 @@ def version(cls, cmd): if cmd is None: return None if cls.VERSION_FULL is None or cmd != cls.CMD: - p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError( - f"{cmd} --version failed with exit code: {p.returncode}" - ) + p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True, check=True) cls.CMD = cmd for line in p.stdout.splitlines(keepends=False): if line.startswith("nghttpx "): @@ -131,9 +131,7 @@ def __init__( } self.curl_is_debug = False self.curl_protos = [] - p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError(f"{self.curl} -V failed with exit code: {p.returncode}") + p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True, check=True) if p.stderr.startswith("WARNING:"): self.curl_is_debug = True for line in p.stdout.splitlines(keepends=False): @@ -162,9 +160,7 @@ def __init__( prot.lower() for prot in line[11:].split(" ") } - p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError(f"{self.curlinfo} failed with exit code: {p.returncode}") + p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True, check=True) self.curl_is_verbose = 'verbose-strings: ON' in p.stdout self.curl_can_cert_status = 'cert-status: ON' in p.stdout self.curl_override_dns = 'override-dns: ON' in p.stdout @@ -226,7 +222,7 @@ def __init__( self.openssl = "openssl" p = subprocess.run( - args=[self.openssl, "version"], capture_output=True, text=True + args=[self.openssl, "version"], capture_output=True, text=True, check=False ) if p.returncode != 0: # no openssl in path @@ -256,7 +252,7 @@ def __init__( if self.h2o is not None: try: p = subprocess.run( - args=[self.h2o, "--version"], capture_output=True, text=True + args=[self.h2o, "--version"], capture_output=True, text=True, check=False ) if p.returncode != 0: # not a working h2o @@ -274,7 +270,7 @@ def __init__( if self.caddy is not None: p = subprocess.run( - args=[self.caddy, "version"], capture_output=True, text=True + args=[self.caddy, "version"], capture_output=True, text=True, check=False ) if p.returncode != 0: # not a working caddy @@ -294,7 +290,7 @@ def __init__( if self.vsftpd is not None: with tempfile.TemporaryFile("w+") as tmp: p = subprocess.run( - args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp + args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp, check=False ) if p.returncode != 0: # not a working vsftpd @@ -315,14 +311,14 @@ def __init__( # vsftp does not use stdout or stderr for printing its version... -.- self._vsftpd_version = "unknown" else: - raise Exception(f"Unable to determine VsFTPD version from: {p.stderr}") + raise EnvError(f"Unable to determine VsFTPD version from: {p.stderr}") self.danted = self.config["danted"]["danted"] if self.danted == "": self.danted = None self._danted_version = None if self.danted is not None: - p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True) + p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True, check=False) assert p.returncode == 0 if p.returncode != 0: # not a working vsftpd @@ -334,14 +330,14 @@ def __init__( self._danted_version = m.group(1) else: self.danted = None - raise Exception(f"Unable to determine danted version from: {p.stderr}") + raise EnvError(f"Unable to determine danted version from: {p.stderr}") self.sshd = self.config["sshd"]["sshd"] if self.sshd == "": self.sshd = None self._sshd_version = None if self.sshd is not None: - p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True) + p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True, check=False) assert p.returncode == 0 if p.returncode != 0: self.sshd = None @@ -352,7 +348,7 @@ def __init__( self._sshd_version = m.group(1) else: self.sshd = None - raise Exception( + raise EnvError( f"Unable to determine sshd version from: {p.stderr}" ) @@ -373,6 +369,7 @@ def httpd_version(self): args=[self.apxs, "-q", "HTTPD_VERSION"], capture_output=True, text=True, + check=False, ) if p.returncode != 0: log.error(f"{self.apxs} failed to query HTTPD_VERSION: {p}") diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index 64ca796d13b5..10ccce0c4a75 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -37,7 +37,7 @@ from typing import Dict, List, Optional, Union from .curl import CurlClient, ExecResult -from .env import Env +from .env import Env, EnvError from .ports import alloc_ports_and_do log = logging.getLogger(__name__) @@ -95,14 +95,12 @@ def __init__(self, env: Env): self._loaded_domain1_cred_name = None assert env.apxs p = subprocess.run(args=[env.apxs, '-q', 'libexecdir'], - capture_output=True, text=True) - if p.returncode != 0: - raise Exception(f'{env.apxs} failed to query libexecdir: {p}') + capture_output=True, text=True, check=True) self._mods_dir = p.stdout.strip() if self._mods_dir is None: - raise Exception('apache modules directory cannot be found') + raise EnvError('apache modules directory cannot be found') if not os.path.exists(self._mods_dir): - raise Exception(f'apache modules directory does not exist: {self._mods_dir}') + raise EnvError(f'apache modules directory does not exist: {self._mods_dir}') self._maybe_running = False self.ports = {} self._rmf(self._error_log) @@ -144,7 +142,7 @@ def _run(self, args, intext=''): p = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE, cwd=self.env.gen_dir, input=intext.encode() if intext else None, - env=env) + env=env, check=True) start = datetime.now() return ExecResult(args=args, exit_code=p.returncode, stdout=p.stdout.decode().splitlines(), @@ -587,9 +585,9 @@ def _init_curltest(self): shutil.copy(in_source, out_source) p = subprocess.run([ self.env.apxs, '-c', out_source - ], capture_output=True, cwd=out_dir) + ], capture_output=True, cwd=out_dir, check=False) rv = p.returncode if rv != 0: log.error(f"compiling mod_curltest failed: {p.stderr}") - raise Exception(f"compiling mod_curltest failed: {p.stderr}") + raise EnvError(f"compiling mod_curltest failed: {p.stderr}") Httpd.MOD_CURLTEST = os.path.join(out_dir, '.libs/mod_curltest.so') diff --git a/tests/http/testenv/ports.py b/tests/http/testenv/ports.py index f3d2348fa679..10accb2f3b73 100644 --- a/tests/http/testenv/ports.py +++ b/tests/http/testenv/ports.py @@ -39,13 +39,10 @@ def alloc_port_set(port_specs: Dict[str, int]) -> Dict[str, int]: socks = [] ports = {} for name, ptype in port_specs.items(): - try: - s = socket.socket(type=ptype) - s.bind(('127.0.0.1', 0)) - ports[name] = s.getsockname()[1] - socks.append(s) - except Exception as e: - raise e + s = socket.socket(type=ptype) + s.bind(('127.0.0.1', 0)) + ports[name] = s.getsockname()[1] + socks.append(s) for s in socks: s.close() return ports diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index de8078dd265c..49c39ce494df 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -125,11 +125,9 @@ def mk_host_keys(self): for alg in self._key_algs: key_file = os.path.join(self._sshd_dir, f'ssh_host_{alg}_key') if not os.path.exists(key_file): - p = subprocess.run(args=[ + subprocess.run(args=[ self._keygen, '-q', '-N', '', '-t', alg, '-f', key_file - ], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError(f'error generating host key {key_file}: {p.returncode}') + ], capture_output=True, text=True, check=True) self._host_key_files.append(key_file) pub_file = f'{key_file}.pub' self._host_pub_files.append(pub_file) @@ -139,11 +137,9 @@ def mk_host_keys(self): fd_known.write(f'[{self.env.domain1.lower()}]:{self.port} {pubkey}') fd_unknown.write(f'dummy.invalid {pubkey}') # hash the known_hosts file, libssh requires it - p = subprocess.run(args=[ + subprocess.run(args=[ self._keygen, '-H', '-f', self._known_hosts - ], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError(f'error hashing {self._known_hosts}: {p.returncode}') + ], capture_output=True, text=True, check=True) def mk_user_keys(self): self._user_key_files = [] @@ -152,11 +148,9 @@ def mk_user_keys(self): for user in self._users: key_file = os.path.join(self._sshd_dir, f'id_{user}_user_{alg}_key') if not os.path.exists(key_file): - p = subprocess.run(args=[ + subprocess.run(args=[ self._keygen, '-q', '-N', '', '-t', alg, '-f', key_file - ], capture_output=True, text=True) - if p.returncode != 0: - raise RuntimeError(f'error generating user key {key_file}: {p.returncode}') + ], capture_output=True, text=True, check=True) self._user_key_files.append(key_file) self._user_pub_files.append(f'{key_file}.pub') with open(self._auth_keys, 'w') as fd: diff --git a/tests/http/testenv/ws_echo_server.py b/tests/http/testenv/ws_echo_server.py index f3f659a27f78..fddd26f27195 100755 --- a/tests/http/testenv/ws_echo_server.py +++ b/tests/http/testenv/ws_echo_server.py @@ -26,6 +26,7 @@ # import argparse import asyncio +import contextlib import logging from websockets import server @@ -33,12 +34,10 @@ async def echo(websocket): - try: + # exception when websocket connection closed by client + with contextlib.suppress(ConnectionClosedError): async for message in websocket: await websocket.send(message) - except ConnectionClosedError: - # websocket connection closed by client - pass async def run_server(port): diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index a9a4880018f5..a65b156630c5 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -109,8 +109,8 @@ def handle(self): self.request.recv(4 * 1024) self.request.shutdown(socket.SHUT_RDWR) - except IOError: - log.exception("IOError hit during request") + except OSError: + log.exception("OSError hit during request") class Negotiator: diff --git a/tests/util.py b/tests/util.py index 7db47d0a006b..2921e93aa83b 100755 --- a/tests/util.py +++ b/tests/util.py @@ -77,7 +77,7 @@ def get_test_data(self, test_number): m = REPLY_DATA.search(contents) if not m: - raise Exception("Could not find a section") + raise RuntimeError("Could not find a section") # Left-strip the data so we do not get a newline before our data. return m.group(1).lstrip() From 4eb691e89c3e324d10d112510bb76f352d655b64 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 13:35:51 -0700 Subject: [PATCH 0744/1062] tests: change whitespace and comments in Python test code * remove an unneeded ruff warning disable * remove coding: utf-8 from Python code; PEP 3120 makes UTF-8 the default encoding * remove unusable shebang lines from Python code * remove empty print strings * disable warnings when file objects are stored; these instances can't be handled with context managers * use more consistent whitespace in Python code, fixing flake8 warnings * set the executable bit on scorecard.py, making it easier to run These fix ruff rules EXE001, FURB105, UP009, SIM115. --- tests/dictserver.py | 1 - tests/ech_combos.py | 9 ++-- tests/http/scorecard.py | 23 +++++----- tests/http/test_01_basic.py | 2 - tests/http/test_02_download.py | 10 ++-- tests/http/test_03_goaway.py | 2 - tests/http/test_04_stuttered.py | 2 - tests/http/test_05_errors.py | 2 - tests/http/test_06_eyeballs.py | 4 +- tests/http/test_07_upload.py | 12 ++--- tests/http/test_08_caddy.py | 2 - tests/http/test_09_push.py | 2 - tests/http/test_10_proxy.py | 2 - tests/http/test_11_unix.py | 2 - tests/http/test_12_reuse.py | 2 - tests/http/test_13_proxy_auth.py | 6 +-- tests/http/test_14_auth.py | 6 +-- tests/http/test_15_tracing.py | 10 ++-- tests/http/test_16_info.py | 2 - tests/http/test_17_ssl_use.py | 6 +-- tests/http/test_18_methods.py | 2 - tests/http/test_19_shutdown.py | 2 - tests/http/test_20_websockets.py | 6 +-- tests/http/test_21_resolve.py | 16 +++---- tests/http/test_22_httpsrr.py | 2 - tests/http/test_30_vsftpd.py | 2 - tests/http/test_31_vsftpds.py | 6 +-- tests/http/test_32_ftps_vsftpd.py | 6 +-- tests/http/test_40_socks.py | 2 - tests/http/test_50_scp.py | 2 - tests/http/test_51_sftp.py | 2 - tests/http/test_60_h3_proxy.py | 2 - tests/http/testenv/__init__.py | 4 +- tests/http/testenv/caddy.py | 4 +- tests/http/testenv/certs.py | 2 - tests/http/testenv/client.py | 4 +- tests/http/testenv/curl.py | 61 +++++++++++-------------- tests/http/testenv/dante.py | 8 ++-- tests/http/testenv/dnsd.py | 4 +- tests/http/testenv/env.py | 12 ++--- tests/http/testenv/h2o.py | 4 +- tests/http/testenv/httpd.py | 7 +-- tests/http/testenv/nghttpx.py | 6 +-- tests/http/testenv/ports.py | 2 - tests/http/testenv/sshd.py | 4 +- tests/http/testenv/vsftpd.py | 4 +- tests/http/testenv/ws_4frames_server.py | 1 - tests/http/testenv/ws_echo_server.py | 1 - tests/negtelnetserver.py | 1 - tests/smbserver.py | 17 +++---- tests/util.py | 1 - 51 files changed, 105 insertions(+), 199 deletions(-) mode change 100644 => 100755 tests/http/scorecard.py diff --git a/tests/dictserver.py b/tests/dictserver.py index 4419e11582ee..ea9f586c44d2 100755 --- a/tests/dictserver.py +++ b/tests/dictserver.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/ech_combos.py b/tests/ech_combos.py index 8b15eff41fe1..90db92ca91d0 100755 --- a/tests/ech_combos.py +++ b/tests/ech_combos.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -45,12 +44,12 @@ def CombinationRepetitionUtil(chosen, arr, badarr, index, if chosen[j] in badarr: res = 0 j = j - 1 - print("cli_test $turl 1", res, end = " ") + print("cli_test $turl 1", res, end=" ") # print combination but eliminating any runs of # two identical params for j in range(r): if j != 0 and chosen[j] != chosen[j-1]: - print(chosen[j], end = " ") + print(chosen[j], end=" ") print() return @@ -89,8 +88,8 @@ def CombinationRepetition(arr, badarr, n, r): # Driver code -badarr = [ '--ech grease', '--ech false', '--ech ecl:$badecl', '--ech pn:$badpn' ] -goodarr = [ '--ech hard', '--ech true', '--ech ecl:$goodecl', '--ech pn:$goodpn' ] +badarr = ['--ech grease', '--ech false', '--ech ecl:$badecl', '--ech pn:$badpn'] +goodarr = ['--ech hard', '--ech true', '--ech ecl:$goodecl', '--ech pn:$goodpn'] arr = badarr + goodarr r = 8 n = len(arr) - 1 diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py old mode 100644 new mode 100755 index 95e092672efd..1550ccb99e97 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -211,7 +210,7 @@ def print_score_table(cls, score): print(f' {col:>{colw[idx]}} {"[cpu/rss]":<{statw}}', end='') else: print(f' {col:>{colw[idx]}}', end='') - print('') + print() for row in rows: for idx, cell in enumerate(row): print(f' {cell["sval"]:>{colw[idx]}}', end='') @@ -224,7 +223,7 @@ def print_score_table(cls, score): print(f' {s:<{statw}}', end='') if 'errors' in cell: errors.extend(cell['errors']) - print('') + print() if len(errors): print(f'Errors: {errors}') @@ -647,13 +646,15 @@ def requests(self, count: int, meta: Dict[str, Any]) -> Dict[str, Any]: rows = [] mparallel = meta['request_parallels'] cols.extend([f'{mp} max' for mp in mparallel]) - row = [{ - 'val': fsize, - 'sval': Card.fmt_size(fsize) - },{ - 'val': count, - 'sval': f'{count}', - }] + row = [ + { + 'val': fsize, + 'sval': Card.fmt_size(fsize) + }, { + 'val': count, + 'sval': f'{count}', + } + ] self.info('requests, max parallel...') row.extend([self.do_requests(url=url, count=count, max_parallel=mp, nsamples=meta["samples"]) @@ -992,7 +993,7 @@ def main(): parser.add_argument("--remote", action='store', type=str, default=None, help="score against the remote server at :") parser.add_argument("--flame", action='store_true', - default = False, help="produce a flame graph on curl") + default=False, help="produce a flame graph on curl") parser.add_argument("--limit-rate", action='store', type=str, default=None, help="use curl's --limit-rate") parser.add_argument("--http-plain", action='store_true', diff --git a/tests/http/test_01_basic.py b/tests/http/test_01_basic.py index 86f7ad191f18..24e03d19b824 100644 --- a/tests/http/test_01_basic.py +++ b/tests/http/test_01_basic.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index 413d518a6ea4..437db80ae4a0 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -141,8 +139,8 @@ def test_02_07_download_reuse(self, env: Env, httpd, nghttpx, proto): urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' r = curl.http_download(urls=[urln], alpn_proto=proto, with_stats=True, extra_args=[ - '--parallel', '--parallel-max', '200' - ]) + '--parallel', '--parallel-max', '200' + ]) r.check_response(http_status=200, count=count) # should have used at most 2 connections only (test servers allow 100 req/conn) # it may be 1 on slow systems where request are answered faster than @@ -157,8 +155,8 @@ def test_02_07b_download_reuse(self, env: Env, httpd, nghttpx, proto): urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' r = curl.http_download(urls=[urln], alpn_proto=proto, with_stats=True, extra_args=[ - '--parallel' - ]) + '--parallel' + ]) r.check_response(count=count, http_status=200) # http/1.1 should have used count connections assert r.total_connects == count, "http/1.1 should use this many connections" diff --git a/tests/http/test_03_goaway.py b/tests/http/test_03_goaway.py index 4c57bf2715d0..36febdbd18a5 100644 --- a/tests/http/test_03_goaway.py +++ b/tests/http/test_03_goaway.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_04_stuttered.py b/tests/http/test_04_stuttered.py index 1c578e2e9e77..3ad25d93ae43 100644 --- a/tests/http/test_04_stuttered.py +++ b/tests/http/test_04_stuttered.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index 60a9aaa2e49b..768416eea331 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index 1dc3726f4887..b72a89d75e4c 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -129,7 +127,7 @@ def test_06_13_timers(self, env: Env): assert r.stats[0]['time_connect'] == 0 # no one connected # check that we indeed started attempts on all 3 addresses tcp_attempts = [line for line in r.trace_lines - if re.match(r'.*Trying \[100::[123]]:443', line)] + if re.match(r'.*Trying \[100::[123]]:443', line)] assert len(tcp_attempts) == 3, f'fond: {"".join(tcp_attempts)}\n{r.dump_logs()}' # if the 0100::/64 really goes into the void, we should see 2 HAPPY_EYEBALLS # timeouts being set here diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 6cb4d943e155..7ad396d73c38 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -549,7 +547,7 @@ def test_07_44_put_redir(self, env: Env, httpd, nghttpx, proto, httpcode): ]) r.check_exit_code(0) results = [int(m.group(1)) for line in r.trace_lines - if (m := re.match(r'.* FINISHED, result=(\d+), response=(\d+)', line))] + if (m := re.match(r'.* FINISHED, result=(\d+), response=(\d+)', line))] httpcodes = [int(m.group(2)) for line in r.trace_lines if (m := re.match(r'.* FINISHED, result=(\d+), response=(\d+)', line))] if httpcode == 308: @@ -568,8 +566,8 @@ def test_07_50_put_speed_limit(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-0]' r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto, with_headers=True, extra_args=[ - '--limit-rate', f'{speed_limit}' - ]) + '--limit-rate', f'{speed_limit}' + ]) r.check_response(count=count, http_status=200) assert r.responses[0]['header']['received-length'] == f'{up_len}', f'{r.responses[0]}' up_speed = r.stats[0]['speed_upload'] @@ -585,8 +583,8 @@ def test_07_51_echo_speed_limit(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, with_headers=True, extra_args=[ - '--limit-rate', f'{speed_limit}' - ]) + '--limit-rate', f'{speed_limit}' + ]) r.check_response(count=count, http_status=200) up_speed = r.stats[0]['speed_upload'] assert up_speed <= (speed_limit * 1.1), f'{r.stats[0]}' diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index 23cea4582ab8..70413a84d544 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_09_push.py b/tests/http/test_09_push.py index 386289b39e97..7fb37b679df3 100644 --- a/tests/http/test_09_push.py +++ b/tests/http/test_09_push.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index 7ef77465bc54..bd07841a68c0 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index b4e5ac79742b..6ba500e1ce52 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_12_reuse.py b/tests/http/test_12_reuse.py index d4e2f70861a2..b7a2513ef0fb 100644 --- a/tests/http/test_12_reuse.py +++ b/tests/http/test_12_reuse.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_13_proxy_auth.py b/tests/http/test_13_proxy_auth.py index 50e45960575d..4e5d45e91882 100644 --- a/tests/http/test_13_proxy_auth.py +++ b/tests/http/test_13_proxy_auth.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -175,9 +173,9 @@ def test_13_10_tunnels_mixed_auth(self, env: Env, httpd, configures_httpd): url2 = f'http://localhost:{env.http_port}/data.json?2' url3 = f'http://localhost:{env.http_port}/data.json?3' xargs1 = curl.get_proxy_args(proxys=False, tunnel=True) - xargs1.extend(['--proxy-user', 'proxy:proxy']) # good auth + xargs1.extend(['--proxy-user', 'proxy:proxy']) # good auth xargs2 = curl.get_proxy_args(proxys=False, tunnel=True) - xargs2.extend(['--proxy-user', 'ungood:ungood']) # bad auth + xargs2.extend(['--proxy-user', 'ungood:ungood']) # bad auth xargs3 = curl.get_proxy_args(proxys=False, tunnel=True) # no auth r = curl.http_download(urls=[url1, url2, url3], alpn_proto='http/1.1', with_stats=True, diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index ca808e007795..268ebae5141e 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -64,7 +62,7 @@ def test_14_02_digest_get_auth(self, env: Env, httpd, nghttpx, proto): def test_14_03_digest_put_auth(self, env: Env, httpd, nghttpx, proto): if not env.curl_has_feature('digest'): pytest.skip("curl built without digest") - data='0123456789' + data = '0123456789' curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[ @@ -77,7 +75,7 @@ def test_14_03_digest_put_auth(self, env: Env, httpd, nghttpx, proto): def test_14_04_digest_large_pw(self, env: Env, httpd, nghttpx, proto): if not env.curl_has_feature('digest'): pytest.skip("curl built without digest") - data='0123456789' + data = '0123456789' password = 'x' * 65535 curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json' diff --git a/tests/http/test_15_tracing.py b/tests/http/test_15_tracing.py index d1bcc2227316..d2e987f8679b 100644 --- a/tests/http/test_15_tracing.py +++ b/tests/http/test_15_tracing.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -55,7 +53,7 @@ def test_15_02_trace_ids(self, env: Env, httpd): '-v', '--trace-config', 'ids' ]) r.check_response(http_status=200) - for line in r.trace_lines: + for line in r.trace_lines: m = re.match(r'^\[0-[0x]] .+', line) if m is None: assert False, f'no match: {line}' @@ -68,7 +66,7 @@ def test_15_03_trace_ids_time(self, env: Env, httpd): '-v', '--trace-config', 'ids,time' ]) r.check_response(http_status=200) - for line in r.trace_lines: + for line in r.trace_lines: m = re.match(r'^([0-9:.]+) \[0-[0x]] .+', line) if m is None: assert False, f'no match: {line}' @@ -84,7 +82,7 @@ def test_15_04_trace_all(self, env: Env, httpd): ]) r.check_response(http_status=200) found_tcp = False - for line in r.trace_lines: + for line in r.trace_lines: m = re.match(r'^([0-9:.]+) \[0-[0x]] .+', line) if m is None: assert False, f'no match: {line}' @@ -102,7 +100,7 @@ def test_15_05_trace_all(self, env: Env, httpd): ]) r.check_response(http_status=200) found_tcp = False - for line in r.trace_lines: + for line in r.trace_lines: m = re.match(r'^\[0-[0x]] .+', line) if m is None: assert False, f'no match: {line}' diff --git a/tests/http/test_16_info.py b/tests/http/test_16_info.py index 977342eb55f8..0d184931e208 100644 --- a/tests/http/test_16_info.py +++ b/tests/http/test_16_info.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 4179925f42eb..c5b5e144f3ec 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -222,7 +220,7 @@ def gen_test_17_07_list(): ['AES256ish', ['ECDHE-ECDSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES256-GCM-SHA384'], False], ['CHACHA20ish', ['ECDHE-ECDSA-CHACHA20-POLY1305', 'ECDHE-RSA-CHACHA20-POLY1305'], True], ['AES256ish+CHACHA20ish', ['ECDHE-ECDSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES256-GCM-SHA384', - 'ECDHE-ECDSA-CHACHA20-POLY1305', 'ECDHE-RSA-CHACHA20-POLY1305'], True], + 'ECDHE-ECDSA-CHACHA20-POLY1305', 'ECDHE-RSA-CHACHA20-POLY1305'], True], ] ret = [] for tls_id, tls_proto in { @@ -521,7 +519,7 @@ def test_17_17_h1_ignore_ciphers13(self, env: Env, httpd): pytest.param("-MAC-ALL:+AEAD", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True, id='TLSv1.3-MAC-only-AEAD'), pytest.param("-GROUP-ALL:+GROUP-X25519", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True, id='TLSv1.3-group-only-X25519'), pytest.param("-GROUP-ALL:+GROUP-SECP192R1", "", [], False, id='group-only-SECP192R1'), - ]) + ]) def test_17_18_gnutls_priority(self, env: Env, httpd, configures_httpd, priority, tls_proto, ciphers, success): # to test setting cipher suites, the AES 256 ciphers are disabled in the test server httpd.set_extra_config('base', [ diff --git a/tests/http/test_18_methods.py b/tests/http/test_18_methods.py index faa31a638e7f..452b422c6b06 100644 --- a/tests/http/test_18_methods.py +++ b/tests/http/test_18_methods.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_19_shutdown.py b/tests/http/test_19_shutdown.py index 4ac447d75837..388d3955dabe 100644 --- a/tests/http/test_19_shutdown.py +++ b/tests/http/test_19_shutdown.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 5ee8b5e6a99e..0d1bdd9ed392 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -95,7 +93,7 @@ def startup(ports: Dict[str, int]) -> bool: self.wsproc = None return False - self.cerr = open(self.err_file, 'w') + self.cerr = open(self.err_file, 'w') # noqa: SIM115 port_spec = { self.name: socket.SOCK_STREAM } @@ -248,7 +246,7 @@ def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, extra_args=xargs) # The CONNECT through the proxy fails as it does not allow it - r.check_exit_code(7) # CURLE_COULDNT_CONNECT + r.check_exit_code(7) # CURLE_COULDNT_CONNECT assert r.stats[0]['http_connect'] == 403, f'{r}' def test_20_11_crazy_pings(self, env: Env): diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index 940952400a7f..a287cfdf8f9e 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -70,7 +68,7 @@ def test_21_02_resolv_invalid_serial(self, env: Env, delay_ms, httpd, nghttpx): run_env = os.environ.copy() run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = f'{delay_ms}' curl = CurlClient(env=env, run_env=run_env, force_resolv=False) - urls = [ f'https://test-{i}.http.curl.invalid/' for i in range(count)] + urls = [f'https://test-{i}.http.curl.invalid/' for i in range(count)] r = curl.http_download(urls=urls, with_stats=True) r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) @@ -82,7 +80,7 @@ def test_21_03_resolv_invalid_parallel(self, env: Env, delay_ms, httpd, nghttpx) run_env = os.environ.copy() run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = f'{delay_ms}' curl = CurlClient(env=env, run_env=run_env, force_resolv=False) - urls = [ f'https://test-{i}.http.curl.invalid/' for i in range(count)] + urls = [f'https://test-{i}.http.curl.invalid/' for i in range(count)] r = curl.http_download(urls=urls, with_stats=True, extra_args=[ '--parallel' ]) @@ -118,7 +116,7 @@ def test_21_05_resolv_single_thread(self, env: Env, httpd, nghttpx): run_env['CURL_DBG_RESOLV_FAIL_DELAY'] = f'{delay_ms}' run_env['CURL_DBG_RESOLV_MAX_THREADS'] = '1' curl = CurlClient(env=env, run_env=run_env, force_resolv=False) - urls = [ f'https://test-{i}.http.curl.invalid/' for i in range(count)] + urls = [f'https://test-{i}.http.curl.invalid/' for i in range(count)] r = curl.http_download(urls=urls, with_stats=True, extra_args=[ '--parallel', '-6' ]) @@ -301,11 +299,13 @@ def test_21_16_dnsd_link_local(self, env: Env, httpd, dnsd): ]) # should fail with CURLE_OPERATION_TIMEOUT or COULDNT_CONNECT assert r.exit_code in (7, 28), f'{r.dump_logs()}' - af_unspec_resolves = [line for line in r.trace_lines if - re.match(r'.* \[DNS] re-queueing query .+ for AF_UNSPEC resolve', line)] + af_unspec_resolves = [ + line for line in r.trace_lines + if re.match(r'.* \[DNS] re-queueing query .+ for AF_UNSPEC resolve', line) + ] assert len(af_unspec_resolves) == 1, f'{r.dump_logs()}' aaaa_resolves = [line for line in r.trace_lines if - re.match(r'.* \* IPv6: fe80::1', line)] + re.match(r'.* \* IPv6: fe80::1', line)] assert len(aaaa_resolves) == 1, f'{r.dump_logs()}' def _clean_files(self, files): diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 0b95472aa725..6ae5d0c72825 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index 6627be2613b7..860c38d5d3f4 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 1d8a897fbdfd..debb8c9b5bc1 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -109,7 +107,7 @@ def test_31_02_download_1(self, env: Env, vsftpds: VsFTPD, docname): ['data-1k', 10, True], ['data-1m', 5, True], ['data-1m', 5, False], - ['data-10m', 2,True] + ['data-10m', 2, True] ]) def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) @@ -128,7 +126,7 @@ def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, coun ['data-1k', 10, True], ['data-1m', 5, True], ['data-1m', 5, False], - ['data-10m', 2,True] + ['data-10m', 2, True] ]) def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index 1e2e9652927e..6db7fb1eaa0f 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -109,7 +107,7 @@ def test_32_02_download_1(self, env: Env, vsftpds: VsFTPD, docname): ['data-1k', 10, True], ['data-1m', 5, True], ['data-1m', 5, False], - ['data-10m', 2,True] + ['data-10m', 2, True] ]) def test_32_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure): curl = CurlClient(env=env) @@ -131,7 +129,7 @@ def test_32_03b_ftp_compat_ftps(self, env: Env, vsftpds: VsFTPD): docname = 'data-1k' count = 2 url1 = f'ftps://{env.ftp_domain}:{vsftpds.port}/{docname}' - url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}' + url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}' r = curl.ftp_get(urls=[url1, url2], with_stats=True) r.check_stats(count=count, http_status=226) assert r.total_connects == count + 1, 'should reuse the control conn' diff --git a/tests/http/test_40_socks.py b/tests/http/test_40_socks.py index f4ae322f7321..186b75e43072 100644 --- a/tests/http/test_40_socks.py +++ b/tests/http/test_40_socks.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_50_scp.py b/tests/http/test_50_scp.py index 831ee5b3f80e..13c36acd6571 100644 --- a/tests/http/test_50_scp.py +++ b/tests/http/test_50_scp.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_51_sftp.py b/tests/http/test_51_sftp.py index acf76ef68356..6caaad9d25ba 100644 --- a/tests/http/test_51_sftp.py +++ b/tests/http/test_51_sftp.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 29630e5b07fc..650644eb0d5a 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/testenv/__init__.py b/tests/http/testenv/__init__.py index d2eea486faa7..834230a853bd 100644 --- a/tests/http/testenv/__init__.py +++ b/tests/http/testenv/__init__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -23,7 +21,7 @@ # SPDX-License-Identifier: curl # ########################################################################### -# ruff: noqa: F401, E402 +# ruff: noqa: F401 import pytest pytest.register_assert_rewrite("testenv.env", "testenv.curl", "testenv.caddy", diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index a6c8b58c5cbd..399bead7a56c 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -113,7 +111,7 @@ def start(self, wait_live=True): args = [ self._caddy, 'run' ] - self._error_fd = open(self._error_log, 'a') + self._error_fd = open(self._error_log, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, cwd=self._caddy_dir, stderr=self._error_fd) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 8c25155b24e7..3102e853992a 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 0df19e5b427a..39fd61054dd3 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -41,7 +39,7 @@ class LocalClient: def __init__(self, name: str, env: Env, run_dir: Optional[str] = None, timeout: Optional[float] = None, - run_env: Optional[Dict[str,str]] = None): + run_env: Optional[Dict[str, str]] = None): self.name = name self.path = os.path.join(env.build_dir, 'tests/libtest/libtests') self.env = env diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 8e187039ab11..f37928471fed 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -74,7 +72,7 @@ def duration(self) -> timedelta: return self._duration @property - def stats(self) -> Optional[Dict[str,Any]]: + def stats(self) -> Optional[Dict[str, Any]]: return self._stats def sample(self): @@ -134,9 +132,8 @@ def finish(self): self._proc.terminate() self._rc = self._proc.returncode with open(self._file, 'w') as cout: - subprocess.run([ - 'sudo', 'perf', 'script' - ], stdout=cout, cwd=self._run_dir, shell=False, check=True) + subprocess.run(['sudo', 'perf', 'script'], + stdout=cout, cwd=self._run_dir, shell=False, check=True) @property def file(self): @@ -496,14 +493,14 @@ def check_stats(self, count: int, http_status: Optional[int] = None, for idx, x in enumerate(self.stats): assert 'remote_port' in x, f'remote_port missing\n{self.dump_stat(x)}' assert x['remote_port'] == remote_port, \ - f'status #{idx} remote_port: expected {remote_port}, '\ - f'got {x["remote_port"]}\n{self.dump_stat(x)}' + f'status #{idx} remote_port: expected {remote_port}, '\ + f'got {x["remote_port"]}\n{self.dump_stat(x)}' if remote_ip is not None: for idx, x in enumerate(self.stats): assert 'remote_ip' in x, f'remote_ip missing\n{self.dump_stat(x)}' assert x['remote_ip'] == remote_ip, \ - f'status #{idx} remote_ip: expected {remote_ip}, '\ - f'got {x["remote_ip"]}\n{self.dump_stat(x)}' + f'status #{idx} remote_ip: expected {remote_ip}, '\ + f'got {x["remote_ip"]}\n{self.dump_stat(x)}' def check_stat_positive(self, s, idx, key): assert key in s, f'stat #{idx} "{key}" missing: {s}' @@ -602,8 +599,8 @@ def dump_stat(self, x): return ''.join(lines) def xfer_trace_for(self, xfer_id) -> List[str]: - pat = re.compile(f'^[^[]* \\[{xfer_id}-.*$') - return [line for line in self._stderr if pat.match(line)] + pat = re.compile(f'^[^[]* \\[{xfer_id}-.*$') + return [line for line in self._stderr if pat.match(line)] class CurlClient: @@ -853,11 +850,11 @@ def http_form(self, urls: List[str], form: Dict[str, str], with_headers=with_headers) def ftp_get(self, urls: List[str], - with_stats: bool = True, - with_profile: bool = False, - with_tcpdump: bool = False, - no_save: bool = False, - extra_args: Optional[List[str]] = None): + with_stats: bool = True, + with_profile: bool = False, + with_tcpdump: bool = False, + no_save: bool = False, + extra_args: Optional[List[str]] = None): if extra_args is None: extra_args = [] if no_save: @@ -882,11 +879,11 @@ def ftp_get(self, urls: List[str], with_tcpdump=with_tcpdump) def ftp_ssl_get(self, urls: List[str], - with_stats: bool = True, - with_profile: bool = False, - with_tcpdump: bool = False, - no_save: bool = False, - extra_args: Optional[List[str]] = None): + with_stats: bool = True, + with_profile: bool = False, + with_tcpdump: bool = False, + no_save: bool = False, + extra_args: Optional[List[str]] = None): if extra_args is None: extra_args = [] extra_args.extend([ @@ -1164,7 +1161,7 @@ def _complete_args(self, urls, timeout=None, options=None, elif insecure: args.append('--insecure') elif active_options and ("--cacert" in active_options or - "--capath" in active_options): + "--capath" in active_options): pass elif u.hostname: args.extend(["--cacert", self.env.ca.cert_file]) @@ -1246,9 +1243,8 @@ def _perf_collapse(self, perf: PerfProfile, file_err): stacks_collapsed = f'{perf.file}.collapsed' log.info(f'collapsing stacks into {stacks_collapsed}') with open(stacks_collapsed, 'w') as cout, open(file_err, 'w') as cerr: - subprocess.run([ - fg_collapse, perf.file - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) + subprocess.run([fg_collapse, perf.file], + stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) return stacks_collapsed def _dtrace_collapse(self, dtrace: DTraceProfile, file_err): @@ -1260,9 +1256,8 @@ def _dtrace_collapse(self, dtrace: DTraceProfile, file_err): stacks_collapsed = f'{dtrace.file}.collapsed' log.info(f'collapsing stacks into {stacks_collapsed}') with open(stacks_collapsed, 'w') as cout, open(file_err, 'a') as cerr: - subprocess.run([ - fg_collapse, dtrace.file - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) + subprocess.run([fg_collapse, dtrace.file], + stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) return stacks_collapsed def _generate_flame(self, curl_args: List[str], @@ -1293,11 +1288,9 @@ def _generate_flame(self, curl_args: List[str], title = cmdline subtitle = '' with open(file_svg, 'w') as cout, open(file_err, 'a') as cerr: - subprocess.run([ - fg_gen_flame, '--colors', 'green', - '--title', title, '--subtitle', subtitle, - stacks_collapsed - ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) + subprocess.run([fg_gen_flame, '--colors', 'green', '--title', title, '--subtitle', + subtitle, stacks_collapsed], + stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True) def mk_altsvc_file(self, name, src_alpn, src_host, src_port, dest_alpn, dest_host, dest_port): diff --git a/tests/http/testenv/dante.py b/tests/http/testenv/dante.py index b2ea4dd0cfe4..43b058e196e7 100644 --- a/tests/http/testenv/dante.py +++ b/tests/http/testenv/dante.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -128,7 +126,7 @@ def start(self, wait_live=True): '-p', f'{self._pid_file}', '-d', '0', ] - self._error_fd = open(self._error_log, 'a') + self._error_fd = open(self._error_log, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False @@ -137,8 +135,8 @@ def start(self, wait_live=True): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir, timeout=timeout.total_seconds(), socks_args=[ - '--socks5', f'127.0.0.1:{self._port}' - ]) + '--socks5', f'127.0.0.1:{self._port}' + ]) try_until = datetime.now() + timeout while datetime.now() < try_until: r = curl.http_get(url=f'http://{self.env.domain1}:{self.env.http_port}/') diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index 8f6bb3edc801..18cfafcc3831 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -128,7 +126,7 @@ def start(self, wait_live=True): '--logfile', f'{self._log_file}', '--pidfile', f'{self._pid_file}', ] - self._error_fd = open(self._error_log, 'a') + self._error_fd = open(self._error_log, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 5acb3ab67a58..24c2276c1ef7 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -513,8 +511,8 @@ def curl_version_string() -> str: def curl_version_at_least(min_version) -> bool: version = Env.curl_version() return Env.CONFIG.versiontuple(min_version) <= Env.CONFIG.versiontuple( - version - ) + version + ) @staticmethod def curl_features_string() -> str: @@ -537,7 +535,7 @@ def curl_lib_version(libname: str) -> str: prefix = f"{libname.lower()}/" for lversion in Env.CONFIG.curl_props["lib_versions"]: if lversion.startswith(prefix): - return lversion[len(prefix) :] + return lversion[len(prefix):] return "unknown" @staticmethod @@ -914,14 +912,14 @@ def make_data_file( fpath = os.path.join(indir, fname) s10 = "0123456789" s = round((line_length / 10) + 1) * s10 - s = s[0 : line_length - 11] + s = s[0:line_length - 11] with open(fpath, "w") as fd: for i in range(int(fsize / line_length)): fd.write(f"{i:09d}-{s}\n") remain = int(fsize % line_length) if remain != 0: i = int(fsize / line_length) + 1 - fd.write(f"{i:09d}-{s}"[0 : remain - 1] + "\n") + fd.write(f"{i:09d}-{s}"[0:remain - 1] + "\n") return fpath def make_data_gzipbomb(self, indir: str, fname: str, fsize: int) -> str: diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index deb3608d3a8c..e6241c2b4bfe 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- # *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -133,7 +131,7 @@ def start(self, wait_live=True): self._loaded_cred_name = self._cred_name self.write_config() args = [self._cmd, "-c", self._conf_file] - self._error_fd = open(self._stderr, "a") + self._error_fd = open(self._stderr, "a") # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index 10ccce0c4a75..c2a0a22b8fa3 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -583,9 +581,8 @@ def _init_curltest(self): if not os.path.exists(out_source) or \ os.stat(in_source).st_mtime > os.stat(out_source).st_mtime: shutil.copy(in_source, out_source) - p = subprocess.run([ - self.env.apxs, '-c', out_source - ], capture_output=True, cwd=out_dir, check=False) + p = subprocess.run([self.env.apxs, '-c', out_source], + capture_output=True, cwd=out_dir, check=False) rv = p.returncode if rv != 0: log.error(f"compiling mod_curltest failed: {p.stderr}") diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index 37c72ad9451f..3caa99dcca1f 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -270,7 +268,7 @@ def start(self, wait_live=True): '--frontend-http3-max-connection-window-size=100M', # f'--frontend-quic-debug-log', ]) - self._error_fd = open(self._stderr, 'a') + self._error_fd = open(self._stderr, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False @@ -320,7 +318,7 @@ def start(self, wait_live=True): creds.pkey_file, creds.cert_file, ] - self._error_fd = open(self._stderr, 'a') + self._error_fd = open(self._stderr, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/ports.py b/tests/http/testenv/ports.py index 10accb2f3b73..2436d4076776 100644 --- a/tests/http/testenv/ports.py +++ b/tests/http/testenv/ports.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index 49c39ce494df..57c7d1e34a05 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -235,7 +233,7 @@ def start(self, wait_live=True): run_env = os.environ.copy() # does not have any effect, sadly # run_env['HOME'] = f'{self._home_dir}' - self._error_fd = open(self._sshd_log, 'a') + self._error_fd = open(self._sshd_log, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd, env=run_env) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index e2c33edee33b..7cd9596b1640 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -146,7 +144,7 @@ def start(self, wait_live=True): self._cmd, f'{self._conf_file}', ] - self._error_fd = open(self._error_log, 'a') + self._error_fd = open(self._error_log, 'a') # noqa: SIM115 self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False diff --git a/tests/http/testenv/ws_4frames_server.py b/tests/http/testenv/ws_4frames_server.py index 8d5655aa5cbd..42d6e9dc2995 100755 --- a/tests/http/testenv/ws_4frames_server.py +++ b/tests/http/testenv/ws_4frames_server.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/http/testenv/ws_echo_server.py b/tests/http/testenv/ws_echo_server.py index fddd26f27195..a063b030799c 100755 --- a/tests/http/testenv/ws_echo_server.py +++ b/tests/http/testenv/ws_echo_server.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index a65b156630c5..4e65b62ec461 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Project ___| | | | _ \| | # / __| | | | |_) | | diff --git a/tests/smbserver.py b/tests/smbserver.py index 44daefc15a11..6623aac708df 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Project ___| | | | _ \| | # / __| | | | |_) | | @@ -153,8 +152,9 @@ def smbserver(options): class TestSmbServer(imp_smbserver.SMBSERVER): """ - Test server for SMB which subclasses the impacket SMBSERVER and provides - test functionality. + Test server for SMB. + + It subclasses the impacket SMBSERVER and provides test functionality. """ def __init__(self, @@ -176,9 +176,10 @@ def __init__(self, def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): """ - Our version of smbComNtCreateAndX looks for special test files and - fools the rest of the framework into opening them as if they were - normal files. + Our version of smbComNtCreateAndX. + + It looks for special test files and fools the rest of the framework + into opening them as if they were normal files. """ conn_data = smb_server.getConnectionData(conn_id) @@ -372,9 +373,9 @@ def get_options(): parser = argparse.ArgumentParser() parser.add_argument("--port", action="store", default=9017, - type=int, help="port to listen on") + type=int, help="port to listen on") parser.add_argument("--host", action="store", default="127.0.0.1", - help="host to listen on") + help="host to listen on") parser.add_argument("--verbose", action="store", type=int, default=0, help="verbose output") parser.add_argument("--pidfile", action="store", diff --git a/tests/util.py b/tests/util.py index 2921e93aa83b..ef212da073c1 100755 --- a/tests/util.py +++ b/tests/util.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Project ___| | | | _ \| | # / __| | | | |_) | | From 3901c16933c2ab327bf9030e0d49f1161d603fbb Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 13:49:29 -0700 Subject: [PATCH 0745/1062] tests: simplify by removing unneeded Python code * combine separate if statements * remove an unneeded encode() call * remove unneeded returns * simplify code when returning early This fixes ruff rules SIM102, SIM114, UP012, PLR1711. --- tests/http/scorecard.py | 2 +- tests/http/test_02_download.py | 4 ---- tests/http/test_11_unix.py | 4 ++-- tests/http/test_17_ssl_use.py | 24 +++++++++++------------- tests/http/testenv/certs.py | 26 ++++++++++++-------------- tests/http/testenv/h2o.py | 2 -- tests/smbserver.py | 16 +++++++--------- tests/util.py | 18 +++++++++--------- 8 files changed, 42 insertions(+), 54 deletions(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 1550ccb99e97..41682ae13298 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -795,7 +795,7 @@ def run_score(args, protocol): socks_args = None if args.socks4 and args.socks5: raise ScoreCardError('unable to run --socks4 and --socks5 together') - elif args.socks4 or args.socks5: + if args.socks4 or args.socks5: sockd = Dante(env=env) if sockd: assert sockd.initial_start() diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index 437db80ae4a0..940245920d47 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -701,10 +701,6 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): if url_junk <= 1024: r.check_exit_code(0) r.check_response(http_status=200) - elif url_junk <= 16 * 1024: - r.check_exit_code(0) - # server replies with 414, Request URL too long - r.check_response(http_status=414) elif url_junk <= 32 * 1024: r.check_exit_code(0) # server replies with 414, Request URL too long diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index 6ba500e1ce52..115c74f2972d 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -71,12 +71,12 @@ def _process(self): c, client_address = self._socket.accept() try: c.recv(16) - c.sendall("""HTTP/1.1 200 Ok + c.sendall(b"""HTTP/1.1 200 Ok Server: UdsFaker Content-Type: application/json Content-Length: 19 -{ "host": "faked" }""".encode()) +{ "host": "faked" }""") finally: c.close() diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index c5b5e144f3ec..54f72cf27e77 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -80,9 +80,8 @@ def test_17_02_sslinfo_reconnect(self, env: Env, tls_max, httpd): count = 3 exp_resumed = 'Resumed' xargs = ['--sessionid', '--tls-max', tls_max, f'--tlsv{tls_max}'] - if env.curl_uses_lib('libressl'): - if tls_max == '1.3': - exp_resumed = 'Initial' # 1.2 works in LibreSSL, but 1.3 does not, TODO + if env.curl_uses_lib('libressl') and tls_max == '1.3': + exp_resumed = 'Initial' # 1.2 works in LibreSSL, but 1.3 does not, TODO if env.curl_uses_lib('rustls-ffi'): exp_resumed = 'Initial' # Rustls does not support sessions, TODO if env.curl_uses_lib('mbedtls') and tls_max == '1.3' and \ @@ -265,9 +264,9 @@ def test_17_07_ssl_ciphers(self, env: Env, httpd, configures_httpd, elif env.curl_uses_lib('schannel'): # not in CI, so untested if ciphers12 is not None: pytest.skip('Schannel does not support setting TLSv1.2 ciphers by name') - elif env.curl_uses_lib('mbedtls') and not env.curl_lib_version_at_least('mbedtls', '3.6.0'): - if tls_proto == 'TLSv1.3': - pytest.skip('mbedTLS < 3.6.0 does not support TLSv1.3') + elif (env.curl_uses_lib('mbedtls') and not env.curl_lib_version_at_least('mbedtls', '3.6.0') + and tls_proto == 'TLSv1.3'): + pytest.skip('mbedTLS < 3.6.0 does not support TLSv1.3') # test extra_args = ['--tls13-ciphers', ':'.join(ciphers13)] if ciphers13 else [] extra_args += ['--ciphers', ':'.join(ciphers12)] if ciphers12 else [] @@ -317,13 +316,12 @@ def test_17_09_ssl_min_max(self, env: Env, httpd, configures_httpd, server_tls, ]) httpd.reload_if_config_changed() # curl's TLS backend supported version - if env.curl_uses_lib('gnutls') or \ - env.curl_uses_lib('quiche') or \ - env.curl_uses_lib('aws-lc') or \ - env.curl_uses_lib('boringssl'): - curl_supported = [0x301, 0x302, 0x303, 0x304] - elif env.curl_uses_lib('openssl') and \ - env.curl_lib_version_before('openssl', '3.0.0'): + if (env.curl_uses_lib('gnutls') or + env.curl_uses_lib('quiche') or + env.curl_uses_lib('aws-lc') or + env.curl_uses_lib('boringssl') or + (env.curl_uses_lib('openssl') and + env.curl_lib_version_before('openssl', '3.0.0'))): curl_supported = [0x301, 0x302, 0x303, 0x304] else: # most SSL backends dropped support for TLSv1.0, TLSv1.1 curl_supported = [0x303, 0x304] diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 3102e853992a..81eb09059d53 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -389,20 +389,18 @@ def create_credentials(spec: CertificateSpec, issuer: Credentials, key_type: Any :returns: the certificate and private key PEM file paths """ if spec.domains and len(spec.domains): - creds = TestCA._make_server_credentials(name=spec.name, domains=spec.domains, - issuer=issuer, valid_from=valid_from, - valid_to=valid_to, key_type=key_type) - elif spec.client: - creds = TestCA._make_client_credentials(name=spec.name, issuer=issuer, - email=spec.email, valid_from=valid_from, - valid_to=valid_to, key_type=key_type) - elif spec.name: - creds = TestCA._make_ca_credentials(name=spec.name, issuer=issuer, - valid_from=valid_from, valid_to=valid_to, - key_type=key_type) - else: - raise CertError(f"unrecognized certificate specification: {spec}") - return creds + return TestCA._make_server_credentials(name=spec.name, domains=spec.domains, + issuer=issuer, valid_from=valid_from, + valid_to=valid_to, key_type=key_type) + if spec.client: + return TestCA._make_client_credentials(name=spec.name, issuer=issuer, + email=spec.email, valid_from=valid_from, + valid_to=valid_to, key_type=key_type) + if spec.name: + return TestCA._make_ca_credentials(name=spec.name, issuer=issuer, + valid_from=valid_from, valid_to=valid_to, + key_type=key_type) + raise CertError(f"unrecognized certificate specification: {spec}") @staticmethod def _make_x509_name(org_name: Optional[str] = None, common_name: Optional[str] = None, parent: x509.Name = None) -> x509.Name: diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index e6241c2b4bfe..f36e6975cec1 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -95,7 +95,6 @@ def dump_logs(self): def _rmf(self, path): if os.path.isfile(path): os.remove(path) - return def _dump_file(self, path, lines): if os.path.isfile(path): @@ -106,7 +105,6 @@ def _dump_file(self, path, lines): def _mkpath(self, path): if not os.path.exists(path): os.makedirs(path) - return def _log(self, level, msg): getattr(log, level)(f"[{self._name}] {msg}") diff --git a/tests/smbserver.py b/tests/smbserver.py index 6623aac708df..67ca6b296293 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -293,15 +293,13 @@ def get_share_path(self, conn_data, root_fid, tid): # If we have a rootFid, the path is relative to that fid path = conn_data["OpenedFiles"][root_fid]["FileName"] log.debug(f'RootFid present {path}!') - else: - if "path" in conn_shares[tid]: - path = conn_shares[tid]["path"] - else: - raise SmbError(STATUS_ACCESS_DENIED, "Connection share had no path") - else: - raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, "TID was invalid") - - return path + return path + + if "path" in conn_shares[tid]: + return conn_shares[tid]["path"] + + raise SmbError(STATUS_ACCESS_DENIED, "Connection share had no path") + raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, "TID was invalid") def get_server_path(self, requested_filename): log.debug("[SMB] Get server path '%s'", requested_filename) diff --git a/tests/util.py b/tests/util.py index ef212da073c1..bc94f41f7e8d 100755 --- a/tests/util.py +++ b/tests/util.py @@ -48,15 +48,15 @@ def setStream(self, stream): if callable(setStream): return setStream(stream) if stream is self.stream: - result = None - else: - result = self.stream - self.acquire() - try: - self.flush() - self.stream = stream - finally: - self.release() + return None + + result = self.stream + self.acquire() + try: + self.flush() + self.stream = stream + finally: + self.release() return result From b151a0bb901b8740c0025483c7fe375e65466dae Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 14:05:01 -0700 Subject: [PATCH 0746/1062] tests: use simpler constructions in Python code * call super() without arguments * mark an unused variable as such * simplify by using dict getter for default values * use writelines() when possible * use dedent to simplify some text formatting * avoid items() on dict in a loop when unnecessary * replace most Python format() calls with f-strings * use capture_output in subprocess.run This fixes ruff rules FLY002, FURB122, PERF102, RUF059, SIM401, UP008, UP022, UP030. --- tests/dictserver.py | 2 +- tests/http/test_11_unix.py | 2 +- tests/http/testenv/caddy.py | 2 +- tests/http/testenv/certs.py | 15 ++++++--------- tests/http/testenv/client.py | 2 +- tests/http/testenv/curl.py | 2 +- tests/http/testenv/env.py | 3 +-- tests/http/testenv/httpd.py | 21 ++++++++++----------- tests/http/testenv/nghttpx.py | 9 +++++---- tests/negtelnetserver.py | 4 +--- tests/smbserver.py | 4 ++-- tests/util.py | 9 ++++----- 12 files changed, 34 insertions(+), 41 deletions(-) diff --git a/tests/dictserver.py b/tests/dictserver.py index ea9f586c44d2..87a00d61d037 100755 --- a/tests/dictserver.py +++ b/tests/dictserver.py @@ -96,7 +96,7 @@ def handle(self): response_data = "No matches" # Send back a failure to find. - response = "552 {0}\n".format(response_data) + response = f"552 {response_data}\n" log.debug("[DICT] Responding with %r", response) self.request.sendall(response.encode("utf-8")) diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index 115c74f2972d..9321d7141402 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -68,7 +68,7 @@ def stop(self): def _process(self): while self._done is False: try: - c, client_address = self._socket.accept() + c, _client_address = self._socket.accept() try: c.recv(16) c.sendall(b"""HTTP/1.1 200 Ok diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index 399bead7a56c..6d79559b7e24 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -47,7 +47,7 @@ class Caddy: def __init__(self, env: Env): self.env = env - self._caddy = os.environ['CADDY'] if 'CADDY' in os.environ else env.caddy + self._caddy = os.environ.get('CADDY', env.caddy) self._caddy_dir = os.path.join(env.gen_dir, 'caddy') self._docs_dir = os.path.join(self._caddy_dir, 'docs') self._conf_file = os.path.join(self._caddy_dir, 'Caddyfile') diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 81eb09059d53..6376a3d445e4 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -280,8 +280,7 @@ def save(self, creds: Credentials, name: Optional[str] = None, with open(cert_file, "wb") as fd: fd.write(creds.cert_pem) if chain: - for c in chain: - fd.write(c.cert_pem) + fd.writelines(c.cert_pem for c in chain) if pkey_file is None: fd.write(creds.pkey_pem) if pkey_file is not None: @@ -290,8 +289,7 @@ def save(self, creds: Credentials, name: Optional[str] = None, with open(comb_file, "wb") as fd: fd.write(creds.cert_pem) if chain: - for c in chain: - fd.write(c.cert_pem) + fd.writelines(c.cert_pem for c in chain) fd.write(creds.pkey_pem) creds.set_files(cert_file, pkey_file, comb_file) self._add_credentials(name, creds) @@ -306,8 +304,7 @@ def save_chain(self, creds: Credentials, infix: str, with_root=False): chain = chain[:-1] chain_file = os.path.join(self._store_dir, f'{name}-{infix}.pem') with open(chain_file, "wb") as fd: - for c in chain: - fd.write(c.cert_pem) + fd.writelines(c.cert_pem for c in chain) def _add_credentials(self, name: str, creds: Credentials): if name not in self._creds_by_name: @@ -315,14 +312,14 @@ def _add_credentials(self, name: str, creds: Credentials): self._creds_by_name[name].append(creds) def get_credentials_for_name(self, name) -> List[Credentials]: - return self._creds_by_name[name] if name in self._creds_by_name else [] + return self._creds_by_name.get(name, []) def get_cert_file(self, name: str, key_type=None) -> str: - key_infix = ".{0}".format(key_type) if key_type is not None else "" + key_infix = f".{key_type}" if key_type is not None else "" return os.path.join(self._store_dir, f'{name}{key_infix}.cert.pem') def get_pkey_file(self, name: str, key_type=None) -> str: - key_infix = ".{0}".format(key_type) if key_type is not None else "" + key_infix = f".{key_type}" if key_type is not None else "" return os.path.join(self._store_dir, f'{name}{key_infix}.pkey.pem') def get_combined_file(self, name: str, key_type=None) -> str: diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 39fd61054dd3..a2e72237f4fe 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -45,7 +45,7 @@ def __init__(self, name: str, env: Env, run_dir: Optional[str] = None, self.env = env self._run_env = run_env self._timeout = timeout if timeout else env.test_timeout - self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl + self._curl = os.environ.get('CURL', env.curl) self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, name) self._stdoutfile = f'{self._run_dir}/stdout' self._stderrfile = f'{self._run_dir}/stderr' diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index f37928471fed..7b026e325fb0 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -627,7 +627,7 @@ def __init__(self, env: Env, socks_args: Optional[List[str]] = None): self.env = env self._timeout = timeout if timeout else env.test_timeout - self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl + self._curl = os.environ.get('CURL', env.curl) self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, 'curl') self._stdoutfile = f'{self._run_dir}/curl.stdout' self._stderrfile = f'{self._run_dir}/curl.stderr' diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 24c2276c1ef7..0c19d6bd1619 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -914,8 +914,7 @@ def make_data_file( s = round((line_length / 10) + 1) * s10 s = s[0:line_length - 11] with open(fpath, "w") as fd: - for i in range(int(fsize / line_length)): - fd.write(f"{i:09d}-{s}\n") + fd.writelines(f"{i:09d}-{s}\n" for i in range(int(fsize / line_length))) remain = int(fsize % line_length) if remain != 0: i = int(fsize / line_length) + 1 diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index c2a0a22b8fa3..c389a9a56cbd 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -29,6 +29,7 @@ import shutil import socket import subprocess +import textwrap import time from datetime import datetime, timedelta from json import JSONEncoder @@ -137,8 +138,7 @@ def _run(self, args, intext=''): env['APACHE_RUN_USER'] = os.environ['USER'] env['APACHE_LOCK_DIR'] = self._lock_dir env['APACHE_CONFDIR'] = self._apache_dir - p = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - cwd=self.env.gen_dir, + p = subprocess.run(args, capture_output=True, cwd=self.env.gen_dir, input=intext.encode() if intext else None, env=env, check=True) start = datetime.now() @@ -170,7 +170,7 @@ def startup(ports: Dict[str, int]) -> bool: def start(self): # assure ports are allocated - for key, _ in Httpd.PORT_SPECS.items(): + for key in Httpd.PORT_SPECS: assert self.ports[key] is not None if self._maybe_running: self.stop() @@ -479,14 +479,13 @@ def _write_config(self): fd.write("\n".join(conf)) with open(os.path.join(self._conf_dir, 'mime.types'), 'w') as fd: - fd.write("\n".join([ - 'text/plain txt', - 'text/html html', - 'application/json json', - 'application/x-gzip gzip', - 'application/x-gzip gz', - '' - ])) + fd.write(textwrap.dedent("""\ + text/plain txt + text/html html + application/json json + application/x-gzip gzip + application/x-gzip gz + """)) def _get_proxy_conf(self): if self._proxy_auth_basic: diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index 3caa99dcca1f..50dd5e21525f 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -27,6 +27,7 @@ import signal import socket import subprocess +import textwrap import time from datetime import datetime, timedelta from typing import Dict, Optional @@ -203,10 +204,10 @@ def _mkpath(self, path): def _write_config(self): with open(self._conf_file, 'w') as fd: - fd.write('# nghttpx test config') - fd.write("\n".join([ - '# do we need something here?' - ])) + fd.write(textwrap.dedent("""\ + # nghttpx test config + # do we need something here? + """)) class NghttpxQuic(Nghttpx): diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index 4e65b62ec461..ad7cb53e9867 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -310,9 +310,7 @@ def setup_logging(options): root_logger = logging.getLogger() add_stdout = False - formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s " - "[{ident}] %(message)s" - .format(ident=IDENT)) + formatter = logging.Formatter(f"%(asctime)s %(levelname)-5.5s [{IDENT}] %(message)s") # Write out to a logfile if options.logfile: diff --git a/tests/smbserver.py b/tests/smbserver.py index 67ca6b296293..1072fd239d50 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -64,7 +64,7 @@ class ShutdownHandler(threading.Thread): """ def __init__(self, server): - super(ShutdownHandler, self).__init__() + super().__init__() self.server = server self.shutdown_event = threading.Event() @@ -351,7 +351,7 @@ def get_test_path(self, requested_filename): class SmbError(Exception): def __init__(self, error_code, error_message): - super(SmbError, self).__init__(error_message) + super().__init__(error_message) self.error_code = error_code diff --git a/tests/util.py b/tests/util.py index bc94f41f7e8d..7c0ad4b499b5 100755 --- a/tests/util.py +++ b/tests/util.py @@ -33,18 +33,18 @@ class ClosingFileHandler(logging.StreamHandler): def __init__(self, filename): - super(ClosingFileHandler, self).__init__() + super().__init__() self.filename = os.path.abspath(filename) self.setStream(None) def emit(self, record): with open(self.filename, "a") as fp: self.setStream(fp) - super(ClosingFileHandler, self).emit(record) + super().emit(record) self.setStream(None) def setStream(self, stream): - setStream = getattr(super(ClosingFileHandler, self), 'setStream', None) + setStream = getattr(super(), 'setStream', None) if callable(setStream): return setStream(stream) if stream is self.stream: @@ -66,8 +66,7 @@ def __init__(self, data_folder): def get_test_data(self, test_number): # Create the test filename - filename = os.path.join(self.data_folder, - "test{0}".format(test_number)) + filename = os.path.join(self.data_folder, f"test{test_number}") log.debug("Parsing file %s", filename) From e13362c20accd210de60035825f7195f9679570b Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 13:23:21 -0700 Subject: [PATCH 0747/1062] tests: address mutable class vars and naive datetime in Python code Mark Python mutable class variables with ClassVar, to denote that the danger this can cause has been considered. Since any change made to these in any object affects all other objects, this can cause locality errors. However, as used in the test suite, they are are never modified and so they are annotated as being intended. Always set a timezone in datetime objects, as mixing naive and timezone-aware object can cause errors. These fix ruff rules DTZ005, RUF012. --- tests/http/test_17_ssl_use.py | 11 +++++++---- tests/http/test_20_websockets.py | 6 +++--- tests/http/testenv/caddy.py | 14 +++++++------- tests/http/testenv/certs.py | 6 +++--- tests/http/testenv/client.py | 6 +++--- tests/http/testenv/curl.py | 18 +++++++++--------- tests/http/testenv/dante.py | 6 +++--- tests/http/testenv/dnsd.py | 6 +++--- tests/http/testenv/h2o.py | 20 ++++++++++---------- tests/http/testenv/httpd.py | 22 +++++++++++----------- tests/http/testenv/nghttpx.py | 28 ++++++++++++++-------------- tests/http/testenv/sshd.py | 6 +++--- tests/http/testenv/vsftpd.py | 10 +++++----- 13 files changed, 81 insertions(+), 78 deletions(-) diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 54f72cf27e77..5d59765b6970 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -26,6 +26,8 @@ import logging import os import re +from dataclasses import dataclass +from typing import ClassVar, Dict, List import pytest from testenv import CurlClient, Env, LocalClient @@ -33,15 +35,16 @@ log = logging.getLogger(__name__) +@dataclass(frozen=True) class TLSDefs: - TLS_VERSIONS = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'] - TLS_VERSION_IDS = { + TLS_VERSIONS: ClassVar[List[str]] = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'] + TLS_VERSION_IDS: ClassVar[Dict[str, int]] = { 'TLSv1': 0x301, 'TLSv1.1': 0x302, 'TLSv1.2': 0x303, 'TLSv1.3': 0x304 } - CURL_ARG_MIN_VERSION_ID = { + CURL_ARG_MIN_VERSION_ID: ClassVar[Dict[str, int]] = { 'none': 0x0, 'tlsv1': 0x301, 'tlsv1.0': 0x301, @@ -49,7 +52,7 @@ class TLSDefs: 'tlsv1.2': 0x303, 'tlsv1.3': 0x304, } - CURL_ARG_MAX_VERSION_ID = { + CURL_ARG_MAX_VERSION_ID: ClassVar[Dict[str, int]] = { 'none': 0x0, '1.0': 0x301, '1.1': 0x302, diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 0d1bdd9ed392..b559383c937a 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -32,7 +32,7 @@ import subprocess import threading import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict import pytest @@ -59,8 +59,8 @@ def __init__(self, name, env, cmd): def check_alive(self, env, port, timeout=Env.SERVER_TIMEOUT): curl = CurlClient(env=env) url = f'http://localhost:{port}/' - end = datetime.now() + timedelta(seconds=timeout) - while datetime.now() < end: + end = datetime.now(timezone.utc) + timedelta(seconds=timeout) + while datetime.now(timezone.utc) < end: r = curl.http_download(urls=[url]) if r.exit_code == 0: return True diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index 6d79559b7e24..14f71f5bfd07 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -27,9 +27,9 @@ import socket import subprocess import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from json import JSONEncoder -from typing import Dict +from typing import ClassVar, Dict from .curl import CurlClient from .env import Env @@ -40,7 +40,7 @@ class Caddy: - PORT_SPECS = { + PORT_SPECS: ClassVar[Dict[str, int]] = { 'caddy': socket.SOCK_STREAM, 'caddys': socket.SOCK_STREAM, } @@ -137,8 +137,8 @@ def restart(self): def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'https://{self.env.domain1}:{self.port}/' r = curl.http_get(url=check_url) if r.exit_code != 0: @@ -150,8 +150,8 @@ def wait_dead(self, timeout: timedelta): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'https://{self.env.domain1}:{self.port}/' r = curl.http_get(url=check_url) if r.exit_code == 0: diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 6376a3d445e4..ab87bfa49e47 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -350,7 +350,7 @@ def load_credentials(self, name: str, key_type=None, (cert.not_valid_before_utc > now)): return None except AttributeError: # older python - now = datetime.now() + now = datetime.now(timezone.utc) if check_valid and \ ((cert.not_valid_after < now) or (cert.not_valid_before > now)): @@ -422,10 +422,10 @@ def _make_csr( pubkey = pkey.public_key() issuer_subject = issuer_subject if issuer_subject is not None else subject - valid_from = datetime.now() + valid_from = datetime.now(timezone.utc) if valid_until_delta is not None: valid_from += valid_from_delta - valid_until = datetime.now() + valid_until = datetime.now(timezone.utc) if valid_until_delta is not None: valid_until += valid_until_delta diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index a2e72237f4fe..c851246fe813 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -26,7 +26,7 @@ import os import shutil import subprocess -from datetime import datetime +from datetime import datetime, timezone from typing import Dict, Optional from . import ExecResult @@ -81,7 +81,7 @@ def _mkpath(self, path): def run(self, args): self._rmf(self._stdoutfile) self._rmf(self._stderrfile) - start = datetime.now() + start = datetime.now(timezone.utc) exception = None myargs = [self.path, self.name] myargs.extend(args) @@ -107,7 +107,7 @@ def run(self, args): cerrput = ferr.readlines() return ExecResult(args=myargs, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, - duration=datetime.now() - start) + duration=datetime.now(timezone.utc) - start) def dump_logs(self): lines = [] diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 7b026e325fb0..64b089ba819f 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -35,7 +35,7 @@ from functools import cmp_to_key from statistics import fmean, mean from threading import Thread -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union from urllib.parse import urlparse import psutil @@ -47,7 +47,7 @@ class RunProfile: - STAT_KEYS = ['cpu', 'rss', 'vsz'] + STAT_KEYS: ClassVar[List[str]] = ['cpu', 'rss', 'vsz'] @classmethod def AverageStats(cls, profiles: List['RunProfile']): @@ -76,7 +76,7 @@ def stats(self) -> Optional[Dict[str, Any]]: return self._stats def sample(self): - elapsed = datetime.now() - self._started_at + elapsed = datetime.now(timezone.utc) - self._started_at try: if self._psu is None: self._psu = psutil.Process(pid=self._pid) @@ -92,7 +92,7 @@ def sample(self): pass def finish(self): - self._duration = datetime.now() - self._started_at + self._duration = datetime.now(timezone.utc) - self._started_at if len(self._samples) > 0: weights = [s['time'].total_seconds() for s in self._samples] self._stats = {} @@ -605,7 +605,7 @@ def xfer_trace_for(self, xfer_id) -> List[str]: class CurlClient: - ALPN_ARG = { + ALPN_ARG: ClassVar[Dict[str, str]] = { 'http/0.9': '--http0.9', 'http/1.0': '--http1.0', 'http/1.1': '--http1.1', @@ -1028,7 +1028,7 @@ def _run(self, args, intext='', with_stats: bool = False, if with_tcpdump: tcpdump = RunTcpDump(self.env, self._run_dir) tcpdump.start() - started_at = datetime.now() + started_at = datetime.now(timezone.utc) try: with open(self._stdoutfile, 'w') as cout, open(self._stderrfile, 'w') as cerr: if with_profile: @@ -1051,7 +1051,7 @@ def _run(self, args, intext='', with_stats: bool = False, p.wait(timeout=ptimeout) break except subprocess.TimeoutExpired as e: - if end_at and datetime.now() >= end_at: + if end_at and datetime.now(timezone.utc) >= end_at: p.kill() raise subprocess.TimeoutExpired(cmd=args, timeout=self._timeout) from e profile.sample() @@ -1067,13 +1067,13 @@ def _run(self, args, intext='', with_stats: bool = False, env=self._run_env, check=False) exitcode = p.returncode except subprocess.TimeoutExpired: - now = datetime.now() + now = datetime.now(timezone.utc) duration = now - started_at log.warning(f'Timeout at {now} after {duration.total_seconds()}s ' f'(configured {self._timeout}s): {args}') exitcode = -1 exception = 'TimeoutExpired' - ended_at = datetime.now() + ended_at = datetime.now(timezone.utc) if tcpdump: tcpdump.finish() if perf: diff --git a/tests/http/testenv/dante.py b/tests/http/testenv/dante.py index 43b058e196e7..f42e3f875534 100644 --- a/tests/http/testenv/dante.py +++ b/tests/http/testenv/dante.py @@ -27,7 +27,7 @@ import socket import subprocess import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict from . import CurlClient @@ -137,8 +137,8 @@ def wait_live(self, timeout: timedelta): timeout=timeout.total_seconds(), socks_args=[ '--socks5', f'127.0.0.1:{self._port}' ]) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: r = curl.http_get(url=f'http://{self.env.domain1}:{self.env.http_port}/') if r.exit_code == 0: return True diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index 18cfafcc3831..d80405e9e835 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -27,7 +27,7 @@ import socket import subprocess import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict, List, Optional from .env import Env @@ -133,8 +133,8 @@ def start(self, wait_live=True): return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) def wait_live(self, timeout: timedelta): - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: if os.path.exists(self._log_file): return True time.sleep(.1) diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index f36e6975cec1..b7008b3c3cb9 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -28,8 +28,8 @@ import socket import subprocess import time -from datetime import datetime, timedelta -from typing import Dict, Optional +from datetime import datetime, timedelta, timezone +from typing import ClassVar, Dict, Optional from .curl import CurlClient from .env import Env @@ -181,12 +181,12 @@ def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): running = self._process self._process = None os.kill(running.pid, signal.SIGQUIT) - end_wait = datetime.now() + timedelta(seconds=5) + end_wait = datetime.now(timezone.utc) + timedelta(seconds=5) exited = False if not self.start(wait_live=False): self._process = running return False - while datetime.now() < end_wait: + while datetime.now(timezone.utc) < end_wait: try: self._log("debug", f"waiting for h2o({running.pid}) to exit.") running.wait(1) @@ -199,7 +199,7 @@ def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): except subprocess.TimeoutExpired: self._log("warning", f"h2o({running.pid}), not shut down yet.") os.kill(running.pid, signal.SIGQUIT) - if not exited and datetime.now() >= end_wait: + if not exited and datetime.now(timezone.utc) >= end_wait: self._log("error", f"h2o({running.pid}), terminate forcefully.") os.kill(running.pid, signal.SIGKILL) running.terminate() @@ -215,10 +215,10 @@ def wait_for_state( log_prefix: str = "h2o", ): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout + try_until = datetime.now(timezone.utc) + timeout if url is None: url = f"https://{self._domain}:{self._port}/" - while datetime.now() < try_until: + while datetime.now(timezone.utc) < try_until: if live: r = curl.http_get( url=url, extra_args=["--trace", "curl.trace", "--trace-time"] @@ -245,7 +245,7 @@ def write_config(self): class H2oServer(H2o): """h2o HTTP/3 server for testing.""" - PORT_SPECS = { + PORT_SPECS: ClassVar[Dict[str, int]] = { "h2o_https": socket.SOCK_STREAM, } @@ -422,10 +422,10 @@ def wait_for_state( log_prefix: str = "h2o", ): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout + try_until = datetime.now(timezone.utc) + timeout if url is None: url = f"https://{self.env.proxy_domain}:{self._port}/" - while datetime.now() < try_until: + while datetime.now(timezone.utc) < try_until: if live: r = curl.http_get( url=url, extra_args=["--trace", "curl.trace", "--trace-time"] diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index c389a9a56cbd..8ef53395ff84 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -31,9 +31,9 @@ import subprocess import textwrap import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from json import JSONEncoder -from typing import Dict, List, Optional, Union +from typing import ClassVar, Dict, List, Optional, Union from .curl import CurlClient, ExecResult from .env import Env, EnvError @@ -44,7 +44,7 @@ class Httpd: - MODULES = [ + MODULES: ClassVar[List[str]] = [ 'log_config', 'logio', 'unixd', 'version', 'watchdog', 'authn_core', 'authn_file', 'authz_user', 'authz_core', 'authz_host', @@ -55,14 +55,14 @@ class Httpd: 'brotli', 'mpm_event', ] - COMMON_MODULES_DIRS = [ + COMMON_MODULES_DIRS: ClassVar[List[str]] = [ '/usr/lib/apache2/modules', # debian '/usr/libexec/apache2/', # macos ] MOD_CURLTEST = None - PORT_SPECS = { + PORT_SPECS: ClassVar[Dict[str, int]] = { 'http': socket.SOCK_STREAM, 'https': socket.SOCK_STREAM, 'https-tcp-only': socket.SOCK_STREAM, @@ -141,11 +141,11 @@ def _run(self, args, intext=''): p = subprocess.run(args, capture_output=True, cwd=self.env.gen_dir, input=intext.encode() if intext else None, env=env, check=True) - start = datetime.now() + start = datetime.now(timezone.utc) return ExecResult(args=args, exit_code=p.returncode, stdout=p.stdout.decode().splitlines(), stderr=p.stderr.decode().splitlines(), - duration=datetime.now() - start) + duration=datetime.now(timezone.utc) - start) def _cmd_httpd(self, cmd: str): args = [self.env.httpd, @@ -221,8 +221,8 @@ def reload_if_config_changed(self): def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: r = curl.http_get(url=f'http://{self.env.domain1}:{self.ports["http"]}/') if r.exit_code != 0: self._maybe_running = False @@ -234,8 +234,8 @@ def wait_dead(self, timeout: timedelta): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir, timeout=timeout.total_seconds()) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: r = curl.http_get(url=f'http://{self.env.domain1}:{self.ports["http"]}/') if r.exit_code == 0: self._maybe_running = True diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index 50dd5e21525f..ba0cbe1a798e 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -29,8 +29,8 @@ import subprocess import textwrap import time -from datetime import datetime, timedelta -from typing import Dict, Optional +from datetime import datetime, timedelta, timezone +from typing import ClassVar, Dict, Optional from .curl import CurlClient from .env import Env, NghttpxUtil @@ -135,11 +135,11 @@ def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): running = self._process self._process = None os.kill(running.pid, signal.SIGQUIT) - end_wait = datetime.now() + timedelta(seconds=5) + end_wait = datetime.now(timezone.utc) + timedelta(seconds=5) if not self.start(wait_live=False): self._process = running return False - while datetime.now() < end_wait: + while datetime.now(timezone.utc) < end_wait: try: log.debug(f'waiting for nghttpx({running.pid}) to exit.') running.wait(1) @@ -149,7 +149,7 @@ def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): except subprocess.TimeoutExpired: log.warning(f'nghttpx({running.pid}), not shut down yet.') os.kill(running.pid, signal.SIGQUIT) - if running and datetime.now() >= end_wait: + if running and datetime.now(timezone.utc) >= end_wait: log.error(f'nghttpx({running.pid}), terminate forcefully.') os.kill(running.pid, signal.SIGKILL) running.terminate() @@ -159,8 +159,8 @@ def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: xargs = [ '--trace', 'curl.trace', '--trace-time', '--connect-timeout', '1' @@ -178,8 +178,8 @@ def wait_dead(self, timeout: timedelta): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: xargs = [ '--trace', 'curl.trace', '--trace-time', '--connect-timeout', '1' @@ -212,7 +212,7 @@ def _write_config(self): class NghttpxQuic(Nghttpx): - PORT_SPECS = { + PORT_SPECS: ClassVar[Dict[str, int]] = { 'nghttpx_https': socket.SOCK_STREAM, } @@ -327,8 +327,8 @@ def start(self, wait_live=True): def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'https://{self.env.proxy_domain}:{self._port}/' r = curl.http_get(url=check_url) if r.exit_code != 0: @@ -340,8 +340,8 @@ def wait_dead(self, timeout: timedelta): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'https://{self.env.proxy_domain}:{self._port}/' r = curl.http_get(url=check_url, extra_args=[ '--trace', 'curl.trace', '--trace-time' diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index 57c7d1e34a05..7753e855c9f1 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -28,7 +28,7 @@ import stat import subprocess import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict from . import CurlClient @@ -242,8 +242,8 @@ def start(self, wait_live=True): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir, timeout=timeout.total_seconds()) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: r = curl.http_get(url=f'scp://{self.env.domain1}:{self._port}/{self.home_dir}/data', extra_args=[ '--insecure', diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index 7cd9596b1640..cda57b182208 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -28,7 +28,7 @@ import socket import subprocess import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict, List, Tuple from .curl import CurlClient, ExecResult @@ -152,8 +152,8 @@ def start(self, wait_live=True): def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'{self._scheme}://{self.domain}:{self.port}/' r = curl.ftp_get(urls=[check_url], extra_args=['-v']) if r.exit_code != 0: @@ -165,8 +165,8 @@ def wait_dead(self, timeout: timedelta): def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) - try_until = datetime.now() + timeout - while datetime.now() < try_until: + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: check_url = f'{self._scheme}://{self.domain}:{self.port}/' r = curl.ftp_get(urls=[check_url], extra_args=[ '--trace', 'curl-start.trace', '--trace-time' From 3de2777421b25f8bc1dcd115143c3e1b5ae9f15a Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 13:34:38 -0700 Subject: [PATCH 0748/1062] tests: target Python 3.8 as the minimum Python version This version is already two releases out of support, but is "only" 7 years old so is probably still being used in the real world. Document this version along with some other testing dependencies. Remove code support for earlier versions. Disable ruff checks that need a newer version. --- docs/INTERNALS.md | 13 +++++++++++++ scripts/pythonlint.sh | 6 ++++-- tests/dictserver.py | 2 -- tests/http/testenv/ports.py | 3 +-- tests/negtelnetserver.py | 9 +-------- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index 244d7c46c330..77d17e83a042 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -64,6 +64,19 @@ these or later versions: - perl 5.8 (2002-07-19), on Windows: 5.22 (2015-06-01) - Visual Studio 2010 10.0 (2010-04-12 - 2020-07-14) +## Testing + +Certain tests require the following packages to operate. In some cases, tests +that do not find the necessary requirements are automatically skipped. + +- OpenSSL (see above) +- nghttp2 (see above) +- OpenSSH +- perl (see above) +- pytest +- Python 3.8 (2019-10-14) +- stunnel + ## Library Symbols All symbols used internally in libcurl must use a `Curl_` prefix if they are diff --git a/scripts/pythonlint.sh b/scripts/pythonlint.sh index a6d6aa2dd4ce..138553306448 100755 --- a/scripts/pythonlint.sh +++ b/scripts/pythonlint.sh @@ -29,9 +29,11 @@ set -eu -ruff check --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ +ruff check --target-version=py38 \ +--extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,\ SIM117,SIM118,TRY400,TRY401,RET503,RET504,UP004,B018,B904,RSE102,RET505,I001 \ +--ignore=FA100 \ "$@" # Checks that are in preview only, since --preview otherwise turns them all on -ruff check --preview --select=E301,E302,E303,E304,E305,E306,E502 "$@" +ruff check --target-version=py38 --preview --select=E301,E302,E303,E304,E305,E306,E502 "$@" diff --git a/tests/dictserver.py b/tests/dictserver.py index 87a00d61d037..bff6e80157e4 100755 --- a/tests/dictserver.py +++ b/tests/dictserver.py @@ -25,8 +25,6 @@ # """DICT server.""" -from __future__ import absolute_import, division, print_function, unicode_literals - import argparse import logging import os diff --git a/tests/http/testenv/ports.py b/tests/http/testenv/ports.py index 2436d4076776..8f25daedcaf1 100644 --- a/tests/http/testenv/ports.py +++ b/tests/http/testenv/ports.py @@ -25,8 +25,7 @@ import logging import os import socket -from collections.abc import Callable -from typing import Dict +from typing import Callable, Dict from filelock import FileLock diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index ad7cb53e9867..8d074f5046e6 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -22,22 +22,15 @@ # """A telnet server which negotiates.""" -from __future__ import absolute_import, division, print_function, unicode_literals - import argparse import logging import os import socket +import socketserver import sys from util import ClosingFileHandler -if sys.version_info.major >= 3: - import socketserver -else: - import SocketServer as socketserver - - log = logging.getLogger(__name__) HOST = "localhost" IDENT = "NTEL" From 5e75aedab2214c6114f1ad52b1bce6a6cd302480 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sat, 25 Jul 2026 16:41:05 -0700 Subject: [PATCH 0749/1062] tests: bump ruff version to 0.60.0 This version enables many more warnings by default. Closes #22396 --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 8d1a74af049a..d068818e57d3 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -5,4 +5,4 @@ cmakelang==0.6.13 codespell==2.4.3 reuse==6.2.0 -ruff==0.15.16 +ruff==0.16.0 From 75404bda1a4fe062fdb5c98b31c0be2e1558674e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 28 Jul 2026 17:08:02 +0200 Subject: [PATCH 0750/1062] curl_gssapi: document/update feature availability - update `GSS_C_DELEG_POLICY_FLAG` comment to include Apple GSS, add date, and amend MIT Kerberos version to 1.7+ (was: 1.8+) Ref: https://github.com/krb5/krb5/commit/45875a4d7bbd6bb8a943572d84fef5ca2bb18291 Ref: https://github.com/apple-oss-distributions/Heimdal/commit/1635de38a813f6e1dd3f8fd270683187ad4f02be - document `HAVE_GSS_SET_NEG_MECHS`/`gss_set_neg_mechs()`. Ref: https://github.com/krb5/krb5/commit/079eed2cf749702f75ddc385cf943fbab931f9d8 It's also committed to Heimdal, but not present in a release as of 7.8.0 (current latest). Ref: https://github.com/heimdal/heimdal/commit/735039dbdc3aa58d06afdefd214efe3f5e421244 Follow-up to a8881e5e1d2e22d4b085d45ab6c8ce1df251d602 #21315 #22410 Follow-up to d169ad68faa5ed5ba2375e7502307a6262466d88 #22052 Closes #22419 --- lib/curl_gssapi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index ccc24e09b128..354fa00deab0 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -435,7 +435,8 @@ static OM_uint32 stub_gss_indicate_mechs( return GSS_S_COMPLETE; } -#ifdef HAVE_GSS_SET_NEG_MECHS +#ifdef HAVE_GSS_SET_NEG_MECHS /* MIT Kerberos 1.8+ (2010-03-02), + missing from Apple GSS, GNU GSS */ static OM_uint32 stub_gss_set_neg_mechs( OM_uint32 *min, gss_cred_id_t cred_handle, @@ -509,7 +510,8 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, req_flags |= GSS_C_MUTUAL_FLAG; if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { -#ifdef GSS_C_DELEG_POLICY_FLAG /* MIT Kerberos 1.8+, missing from GNU GSS */ +#ifdef GSS_C_DELEG_POLICY_FLAG /* MIT Kerberos 1.7+ (2009-06-02), Apple GSS, + missing from GNU GSS */ req_flags |= GSS_C_DELEG_POLICY_FLAG; #else infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " From 1386135d1c65a3d32c2887a60e66b41afef341ec Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 15 Jul 2026 18:04:59 +0200 Subject: [PATCH 0751/1062] schannel: fix error check logic in `get_client_cert()` file reader Reported by GitHub Code Quality Follow-up to 0fdf96512613574591f501d63fe49495ba40e1d5 #5193 Closes #22415 --- lib/vtls/schannel.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index f55784c624c1..583c87222da0 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -441,12 +441,13 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, if(fInCert) { long cert_tell = 0; bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; - if(continue_reading) + if(continue_reading) { cert_tell = ftell(fInCert); - if(cert_tell < 0) - continue_reading = FALSE; - else - certsize = (size_t)cert_tell; + if(cert_tell < 0) + continue_reading = FALSE; + else + certsize = (size_t)cert_tell; + } if(continue_reading) continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; if(continue_reading && (certsize < CURL_MAX_INPUT_LENGTH)) From e2fa29c402b0662ff483a0ccec6b45eab5f7f7b0 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Tue, 28 Jul 2026 13:19:40 -0700 Subject: [PATCH 0752/1062] CI: improve labeler matches --- .github/labeler.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index f7adaba865e6..b298918ec86c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -39,9 +39,11 @@ authentication: - any-glob-to-all-files: "{\ CMake/FindGSS.cmake,\ CMake/FindLibgsasl.cmake,\ + docs/cmdline-opts/httpsig*,\ docs/internals/CREDENTIALS.md,\ docs/libcurl/opts/CURLINFO_HTTPAUTH*,\ docs/libcurl/opts/CURLINFO_PROXYAUTH*,\ + docs/libcurl/opts/CURLOPT_HTTPSIG*,\ docs/libcurl/opts/CURLOPT_KRB*,\ docs/libcurl/opts/CURLOPT_SASL*,\ docs/libcurl/opts/CURLOPT_SERVICE_NAME*,\ @@ -55,6 +57,7 @@ authentication: lib/curl_sasl.*,\ lib/http_aws*,\ lib/http_digest.*,\ + lib/http_httpsig.*,\ lib/http_negotiate.*,\ lib/http_ntlm.*,\ lib/vauth/**\ @@ -102,6 +105,7 @@ cmake: CMake/**,\ docs/INSTALL-CMAKE.md,\ lib/curl_config-cmake.h.in,\ + scripts/cmakeopts.sh,\ tests/cmake/**\ }" @@ -144,6 +148,7 @@ connecting & proxies: lib/if2ip.*,\ lib/proxy.*,\ lib/socks.*,\ + lib/vquic/*-proxy.*,\ src/tool_cb_soc.*,\ tests/http/*proxy*,\ tests/http/*socks*,\ @@ -172,6 +177,7 @@ cryptography: docs/CIPHERS.md,\ docs/RUSTLS.md,\ docs/libcurl/opts/CURLOPT_EGDSOCKET*,\ + lib/*ed25519*,\ lib/*sha256*,\ lib/*sha512*,\ lib/curl_hmac.*,\ @@ -218,6 +224,8 @@ documentation: # negative matches - '!**/CMakeLists.txt' - '!**/Makefile.am' + - '!tests/**/data*.txt' + - '!tests/**/requirements*.txt' FTP: - all: @@ -305,7 +313,6 @@ HTTP/3: docs/cmdline-opts/proxy-http3.md,\ docs/HTTP3.md,\ docs/examples/http3*,\ - lib/cf-h3-proxy.*,\ lib/vquic/**,\ tests/http3-server.pl,\ tests/nghttpx.conf,\ From 387b4c5e4c3fe654187c49db743efd0be41febaf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 15 Jul 2026 02:28:09 +0200 Subject: [PATCH 0753/1062] dnsd: fix bounds check in `read_https_alpn_part()` "The check `i > 256` permits `i == 256` to pass through. When `i` is then cast to `uint8_t` in `blob_add(b, (uint8_t)i)`, the value wraps to 0, silently encoding a zero-length ALPN entry instead of rejecting it. The condition should be `i > 255` (or equivalently `i >= 256`) to correctly reject any length that does not fit in a single byte." Reported by GitHub Code Quality Follow-up to 86f1e5b3f6c90e453368fdf23c366c1d4a8c953b #21299 Closes #22420 --- tests/server/dnsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index a9b869d7715e..9d460d9452e1 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -570,7 +570,7 @@ static int read_https_alpn_part(struct blob *b, struct Curl_str *str) if(str->str[i] == ',') break; } - if(i > 256) + if(i >= 256) return 1; if(blob_add(b, (uint8_t)i) || blob_addchars(b, str->str, i)) return 1; From ca96fd5190c3910f633ad8fe9f66380c28cd91ad Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 15 Jul 2026 02:18:30 +0200 Subject: [PATCH 0754/1062] scorecard: fix `max_upload` init value in `ul_parallel()` "In `ul_parallel`, `max_parallel` is computed using `self._download_parallel` instead of `self._upload_parallel`. This causes the upload parallelism to incorrectly follow the download parallel setting. It should use `self._upload_parallel` to be consistent with how `uploads()` computes `max_parallel`." Reported by GitHub Code Quality Follow-up to 30ef79ed937ca0fc7592ff73d162398773c6a5aa #17295 Closes #22421 --- tests/http/scorecard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 41682ae13298..d6d89a375355 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -542,7 +542,7 @@ def ul_parallel(self, url: str, fpath: str, count: int, nsamples: int = 1): samples = [] errors = [] profiles = [] - max_parallel = self._download_parallel if self._download_parallel > 0 else count + max_parallel = self._upload_parallel if self._upload_parallel > 0 else count url = f'{url}?id=[0-{count - 1}]' self.info('parallel...') for _ in range(nsamples): From cccd76f2aa02b78af0b305a3f548edafb12966e9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 Jul 2026 14:47:44 +0200 Subject: [PATCH 0755/1062] docs/INTERNALS.md: move a column one byte To make the minimum gap two spaces, so that we can separate the columns programmatically --- docs/INTERNALS.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index 77d17e83a042..581f0d45b513 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -26,27 +26,27 @@ want it to remain functional and buildable with these and later versions We aim to support these or later versions: -- brotli 1.0.0 (2017-09-21) -- c-ares 1.16.0 (2020-03-13) -- GnuTLS 3.6.5 (2018-12-01) -- libidn2 2.0.0 (2017-03-29) -- libgsasl 1.6.0 (2010-12-14) -- libpsl 0.16.0 (2016-12-10) -- LibreSSL 2.9.1 (2019-04-21) -- libssh 0.9.0 (2019-06-28) -- libssh2 1.9.0 (2019-06-20) -- mbedTLS 3.2.0 (2022-07-11) -- MIT Kerberos 1.3 (2003-07-31) -- nghttp2 1.15.0 (2016-09-25) -- nghttp3 1.0.0 (2023-10-15) -- ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) -- OpenLDAP 2.0 (2000-08-01) -- OpenSSL 3.0.0 (2021-09-07) -- quiche 0.20.0 (2023-12-12) -- Windows Vista 6.0 (2006-11-08 - 2012-04-10) -- wolfSSL 5.0.0 (2021-11-01) -- zlib 1.2.5.2 (2011-12-11) -- zstd 1.0 (2016-08-31) +- brotli 1.0.0 (2017-09-21) +- c-ares 1.16.0 (2020-03-13) +- GnuTLS 3.6.5 (2018-12-01) +- libidn2 2.0.0 (2017-03-29) +- libgsasl 1.6.0 (2010-12-14) +- libpsl 0.16.0 (2016-12-10) +- LibreSSL 2.9.1 (2019-04-21) +- libssh 0.9.0 (2019-06-28) +- libssh2 1.9.0 (2019-06-20) +- mbedTLS 3.2.0 (2022-07-11) +- MIT Kerberos 1.3 (2003-07-31) +- nghttp2 1.15.0 (2016-09-25) +- nghttp3 1.0.0 (2023-10-15) +- ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) +- OpenLDAP 2.0 (2000-08-01) +- OpenSSL 3.0.0 (2021-09-07) +- quiche 0.20.0 (2023-12-12) +- Windows Vista 6.0 (2006-11-08 - 2012-04-10) +- wolfSSL 5.0.0 (2021-11-01) +- zlib 1.2.5.2 (2011-12-11) +- zstd 1.0 (2016-08-31) ## Build tools From 8186edf85dd35b27fbb2865176b6ac2e8947b49c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 Jul 2026 22:44:50 +0200 Subject: [PATCH 0756/1062] scripts/badwords.txt: do not recommend using 'will' in rewrites Instead suggest "avoid contraction and rewrite to present tense" Reported-by: claudex on github Fixes #22422 Closes #22423 --- scripts/badwords.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 2651a4859874..69bc87a08c85 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -45,12 +45,12 @@ doesn't:does not don't:do not haven't:have not i'd:I would -i'll:I will +i'll:avoid contraction and rewrite to present tense i'm:I am i've:I have isn't:is not it'd:it would -it'll:it will +it'll:avoid contraction and rewrite to present tense might've:might have needn't:need not should've:should have @@ -58,21 +58,21 @@ shouldn't:should not that's:that is there's:there is they'd:They would -they'll:They will +they'll:avoid contraction and rewrite to present tense they're:They are they've:They have -this'll:this will +this'll:avoid contraction and rewrite to present tense wasn't:was not we'd:we would -we'll:we will +we'll:avoid contraction and rewrite to present tense we're:we are we've:we have weren't:were not -won't:will not +won't:avoid contraction and rewrite to present tense would've:would have wouldn't:would not you'd:you would -you'll:you will +you'll:avoid contraction and rewrite to present tense you're:you are you've:you have a html:an html From 59dc2bbe07c3b5889e0b380e5fa384d338d9d24e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 28 Jul 2026 22:36:11 +0200 Subject: [PATCH 0757/1062] tidy-up: use `CURL_CSTRLEN()` macro on more static strings Follow-up to e1450d8fdaf05f27ec75eb15df303fe931755a59 #22406 Closes #22424 --- lib/file.c | 2 +- lib/mprintf.c | 6 +++--- lib/rand.c | 2 +- lib/vtls/mbedtls.c | 2 +- tests/server/rtspd.c | 4 ++-- tests/server/util.c | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/file.c b/lib/file.c index 979204bf44e0..2ebfd0c67bcc 100644 --- a/lib/file.c +++ b/lib/file.c @@ -433,7 +433,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) return result; result = Curl_client_write(data, CLIENTWRITE_HEADER, - accept_ranges, sizeof(accept_ranges) - 1); + accept_ranges, CURL_CSTRLEN(accept_ranges)); if(result != CURLE_OK) return result; } diff --git a/lib/mprintf.c b/lib/mprintf.c index 230b554191e4..7eb25c2e6847 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -878,9 +878,9 @@ static bool out_string(void *userp, if(!str) { /* Write null string if there is space. */ - if(prec == -1 || prec >= (int)sizeof(nilstr) - 1) { + if(prec == -1 || prec >= (int)CURL_CSTRLEN(nilstr)) { str = nilstr; - len = sizeof(nilstr) - 1; + len = CURL_CSTRLEN(nilstr); /* Disable quotes around (nil) */ flags &= ~(unsigned int)FLAGS_ALT; } @@ -939,7 +939,7 @@ static bool out_pointer(void *userp, int width = p->width; int flags = p->flags; - width -= (int)(sizeof(nilstr) - 1); + width -= (int)CURL_CSTRLEN(nilstr); if(flags & FLAGS_LEFT) while(width-- > 0) OUTCHAR(' '); diff --git a/lib/rand.c b/lib/rand.c index 71f629e72349..d0527aa3c032 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -208,7 +208,7 @@ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num) { CURLcode result = CURLE_OK; - const unsigned int alnumspace = sizeof(alnum) - 1; + const unsigned int alnumspace = CURL_CSTRLEN(alnum); unsigned int r; DEBUGASSERT(num > 1); diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index b2ab84425075..f0d2f802307b 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -268,7 +268,7 @@ static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) static const char ecjpake_suite[] = "TLS_ECJPAKE_WITH_AES_128_CCM_8"; if(!id) { - if((len == sizeof(ecjpake_suite) - 1) && + if((len == CURL_CSTRLEN(ecjpake_suite)) && curl_strnequal(ecjpake_suite, *str, len)) id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 7e6c8ef43bd6..c7c7ed75e522 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -690,10 +690,10 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) case RCMD_STREAM: { static const char streamthis[] = "a string to stream 01234567890\n"; for(;;) { - written = swrite(sock, streamthis, sizeof(streamthis) - 1); + written = swrite(sock, streamthis, CURL_CSTRLEN(streamthis)); if(got_exit_signal) return -1; - if(written != (ssize_t)(sizeof(streamthis) - 1)) { + if(written != (ssize_t)CURL_CSTRLEN(streamthis)) { logmsg("Stopped streaming"); break; } diff --git a/tests/server/util.c b/tests/server/util.c index 6041bc0c6d26..805f615f4cb8 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -380,7 +380,7 @@ static void exit_signal_handler(int signum) int old_errno = errno; if(!serverlogfile) { static const char msg[] = "exit_signal_handler: serverlogfile not set\n"; - (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); } else { int fd = -1; @@ -394,12 +394,12 @@ static void exit_signal_handler(int signum) if(fd != -1) { #endif static const char msg[] = "exit_signal_handler: called\n"; - (void)write(fd, msg, sizeof(msg) - 1); + (void)write(fd, msg, CURL_CSTRLEN(msg)); curlx_close(fd); } else { static const char msg[] = "exit_signal_handler: failed opening "; - (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); (void)write(STDERR_FILENO, serverlogfile, strlen(serverlogfile)); (void)write(STDERR_FILENO, "\n", 1); } From 14cd071484d8abdf41b72d5d862d404381e880b9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jul 2026 09:04:46 +0200 Subject: [PATCH 0758/1062] HISTORY: add when c-ares support was introduced (2004) --- docs/HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 3723052f9e95..7e38f4ec15ad 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -173,6 +173,8 @@ December: full-fledged SSL for FTP is supported. January: curl 7.11.0 introduced large file support. +March: added asynch name resolves using the c-ares library + June: curl 7.12.0 introduced IDN support. 10 official web mirrors. This release bumped the major SONAME to 3 due to the removal of the From c7245578c389fffe11d9bf23b5af150817d3e43d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 22:21:01 +0000 Subject: [PATCH 0759/1062] GHA: update dependency google/boringssl to v0.20260728.0 Closes #22425 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 157bf6758394..6d4856054ea8 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260713.0 + BORINGSSL_VERSION: 0.20260728.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 47c887651c95..389fd5775a83 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -37,7 +37,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260713.0 + BORINGSSL_VERSION: 0.20260728.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.681 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com From c7fb986b72e14d702172a789111502e34123b305 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Jul 2026 11:25:07 +0200 Subject: [PATCH 0760/1062] GHA/configure-vs-cmake: dump `./configure --help` For visual review when necessary. For the three major platforms for symmetry and good measure. Closes #22427 --- .github/workflows/configure-vs-cmake.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 940456e7c9cf..334c5d52d0a3 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -51,6 +51,7 @@ jobs: - name: 'run configure --with-openssl' run: | autoreconf -fi + echo '::group::configure --help output'; ./configure --help; echo '::endgroup::' export PKG_CONFIG_DEBUG_SPEW=1 mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-openssl --without-libpsl @@ -109,6 +110,7 @@ jobs: - name: 'run configure --with-openssl' run: | autoreconf -fi + echo '::group::configure --help output'; ./configure --help; echo '::endgroup::' export PKG_CONFIG_DEBUG_SPEW=1 mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-openssl --without-libpsl --disable-ldap --with-brotli --with-zstd --with-apple-sectrust @@ -161,6 +163,7 @@ jobs: - name: 'run configure --with-schannel' run: | autoreconf -fi + echo '::group::configure --help output'; ./configure --help; echo '::endgroup::' export PKG_CONFIG_DEBUG_SPEW=1 mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-schannel --without-libpsl --host="${TRIPLET}" From 079a11bcba5858ffab2cf6e810e9d98024458487 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 28 Jul 2026 14:22:43 +0200 Subject: [PATCH 0761/1062] vquic: add option to use Apple fast UDP Using Apple's secret `SYS_recvmsg_x` and `SYS_sendmsg_x` syscalls for receiving/sending batches of UDP packets. Since it uses undocumented calls, it's experimental and disabled by default. To enable: - autotools: `--enable-apple-fast-udp` - cmake: `-DCURL_ENABLE_APPLE_FAST_UDP=ON` Also: - enable in two H3 CI jobs with both build tools, pytest and clang-tidy. Refs: https://max-inden.de/post/fast-udp-io-in-firefox/ https://www.macsyscalls.com/en/syscall/480-recvmsg-x https://www.macsyscalls.com/en/syscall/481-sendmsg-x C-code-authored-by: Stefan Eissing Build-code-authored-by: Viktor Szakats Closes #22341 Closes #22417 --- .github/workflows/macos.yml | 7 +- CMakeLists.txt | 8 ++ configure.ac | 22 ++++ docs/INSTALL-CMAKE.md | 1 + lib/curl_config-cmake.h.in | 3 + lib/vquic/vquic.c | 233 ++++++++++++++++++++++++++++++++++-- 6 files changed, 259 insertions(+), 15 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 21e2617c9369..14a276384c66 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -37,6 +37,7 @@ permissions: {} # # - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) # - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) +# - 10.10 Yosemite (2014) - SYS_recvmsg_x, SYS_sendmsg_x (runtime) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) # - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime), GSS Framework @@ -281,7 +282,9 @@ jobs: compiler: clang install: libnghttp3 libngtcp2 install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi + configure: >- + --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi + --enable-apple-fast-udp - name: 'OpenSSL event-based' compiler: clang @@ -326,7 +329,7 @@ jobs: -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy - -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON + -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON -DCURL_ENABLE_APPLE_FAST_UDP=ON - name: 'LibreSSL openldap AppleGSS c-ares +examples' compiler: clang diff --git a/CMakeLists.txt b/CMakeLists.txt index dcb735d0ce66..1cf5d8dce410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -805,6 +805,14 @@ else() set(USE_APPLE_SECTRUST OFF) endif() +set(USE_APPLE_FAST_UDP 0) +if(APPLE) + option(CURL_ENABLE_APPLE_FAST_UDP "Use Apple fast UDP (experimental)" OFF) + if(CURL_ENABLE_APPLE_FAST_UDP) + set(USE_APPLE_FAST_UDP 1) + endif() +endif() + if(_use_core_foundation_and_core_services) find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation") mark_as_advanced(COREFOUNDATION_FRAMEWORK) diff --git a/configure.ac b/configure.ac index c40bfb2a1cac..5c83942fbcae 100644 --- a/configure.ac +++ b/configure.ac @@ -2852,6 +2852,28 @@ AC_SUBST([CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME], ["4"]) dnl Keep in sync with AM_CONDITIONAL([CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS], [test "$versioned_symbols" = "yes"]) +dnl --------------------------- +dnl check Apple fast UDP option +dnl --------------------------- + +if test "$curl_cv_apple" = "yes"; then + AC_MSG_CHECKING([whether to use Apple fast UDP]) + AC_ARG_ENABLE(apple-fast-udp, +AS_HELP_STRING([--enable-apple-fast-udp],[Enable using Apple fast UDP (experimental)]) +AS_HELP_STRING([--disable-apple-fast-udp],[Disable using Apple fast UDP (experimental) (default)]), + [ case "$enableval" in + yes) + AC_MSG_RESULT([yes]) + AC_DEFINE(USE_APPLE_FAST_UDP, 1, [to use Apple fast UDP (SYS_recvmsg_x, SYS_sendmsg_x)]) + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac ], + AC_MSG_RESULT([no]) + ) +fi + dnl ---------------------------- dnl check Windows Unicode option dnl ---------------------------- diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index f5af80a418b2..1e7b1639e951 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -242,6 +242,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) ## Enabling features +- `CURL_ENABLE_APPLE_FAST_UDP`: Use Apple fast UDP (experimental). Default: `OFF` - `CURL_ENABLE_NTLM`: Enable NTLM support. Default: `OFF` - `CURL_ENABLE_SMB`: Enable SMB. Default: `OFF` - `CURL_ENABLE_SSL`: Enable SSL support. Default: `ON` diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index c992434530ee..26379e88a992 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -774,6 +774,9 @@ ${SIZEOF_TIME_T_CODE} /* to enable Apple OS-native certificate verification */ #cmakedefine USE_APPLE_SECTRUST 1 +/* to use Apple fast UDP (SYS_recvmsg_x, SYS_sendmsg_x) */ +#cmakedefine USE_APPLE_FAST_UDP + /* Define to 1 to query for HTTPSRR when using DoH */ #cmakedefine USE_HTTPSRR 1 diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 1f5e5bc6b8b0..384935f87a78 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -37,6 +37,13 @@ #include #endif +#if defined(USE_APPLE_FAST_UDP) && defined(__APPLE__) +#include +#if defined(SYS_recvmsg_x) && defined(SYS_sendmsg_x) +#define HAVE_APPLE_MSG_X +#endif +#endif + #include "bufq.h" #include "curlx/dynbuf.h" #include "curlx/fopen.h" @@ -57,6 +64,29 @@ #define NW_CHUNK_SIZE (64 * 1024) #define NW_SEND_CHUNKS 1 +#ifdef HAVE_APPLE_MSG_X +struct msghdr_x { + void *msg_name; /* optional address */ + socklen_t msg_namelen; /* size of address */ + struct iovec *msg_iov; /* scatter/gather array */ + int msg_iovlen; /* # elements in msg_iov */ + void *msg_control; /* ancillary data, see below */ + socklen_t msg_controllen; /* ancillary data buffer len */ + int msg_flags; /* flags on received message */ + size_t msg_datalen; /* byte length of buffer in msg_iov */ +}; +#endif + +#ifdef CURLVERBOSE +#ifdef HAVE_APPLE_MSG_X +#define VQUIC_SEND_METHOD "sendmsg_x" +#elif defined(HAVE_SENDMSG) +#define VQUIC_SEND_METHOD "sendmsg" +#else +#define VQUIC_SEND_METHOD "send" +#endif +#endif + int Curl_vquic_init(void) { #if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2) @@ -91,6 +121,8 @@ CURLcode Curl_vquic_ctx_init(struct Curl_easy *data, BUFQ_OPT_SOFT_LIMIT); #if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) qctx->no_gso = FALSE; +#elif defined(HAVE_APPLE_MSG_X) + qctx->no_gso = FALSE; #else qctx->no_gso = TRUE; #endif @@ -126,6 +158,79 @@ void Curl_vquic_ctx_update_time(struct cf_quic_ctx *qctx, qctx->last_op = *pnow; } +#ifdef HAVE_APPLE_MSG_X +static CURLcode do_sendmsg(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen, + size_t *psent) +{ +#define MSG_X_SNUM 64 + struct iovec msg_iov[MSG_X_SNUM]; + struct msghdr_x mmsg[MSG_X_SNUM]; + char errstr[STRERROR_LEN]; + size_t n, i = 0, sent = 0; + int rc; + CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); + + *psent = 0; + if(!pktlen) + return CURLE_OK; + if(!gsolen || (pktlen < gsolen)) + gsolen = pktlen; + n = pktlen / gsolen; + while(i < n) { + size_t j, batch = CURLMIN(n - i, MSG_X_SNUM); + + for(j = 0; j < batch; ++j) { + msg_iov[j].iov_base = CURL_UNCONST(&pkt[(i + j) * gsolen]); + msg_iov[j].iov_len = gsolen; + memset(&mmsg[j], 0, sizeof(mmsg[j])); + mmsg[j].msg_iov = &msg_iov[j]; + mmsg[j].msg_iovlen = 1; + } + +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 && + (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) + ; +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + + if(rc == -1) { + if(SOCK_EAGAIN(SOCKERRNO)) { + CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN"); + result = sent ? CURLE_OK : CURLE_AGAIN; + goto out; + } + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: sendmsg_x() unexpectedly returned %d (errno=%d; %s)", + rc, SOCKERRNO, errstr); + result = CURLE_RECV_ERROR; + goto out; + } + VERBOSE(++calls); + i += batch; + sent += (batch * gsolen); + } + +out: + *psent = sent; + if(sent || result) + CURL_TRC_CF(data, cf, + "vquic_sendmsg_x(len=%zu, gso=%zu, packets=%zu, " + "calls=%zu) -> %d", + sent, gsolen, n, calls, (int)result); + return result; +} + +#else /* HAVE_APPLE_MSG_X */ + static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -231,16 +336,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, (void)cf; out: + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent); return result; } -#ifdef CURLVERBOSE -#ifdef HAVE_SENDMSG -#define VQUIC_SEND_METHOD "sendmsg" -#else -#define VQUIC_SEND_METHOD "send" -#endif -#endif +#endif /* !HAVE_APPLE_MSG_X */ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -333,9 +435,6 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } else { result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); - CURL_TRC_CF(data, cf, - "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", - VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent); } if(!result) qctx->last_io = qctx->last_op; @@ -405,13 +504,13 @@ CURLcode Curl_vquic_send_tail_split(struct Curl_cfilter *cf, qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; qctx->split_gsolen = gsolen; qctx->gsolen = tail_gsolen; - CURL_TRC_CF(data, cf, "Curl_vquic_send_tail_split: " - "[%zu gso=%zu][%zu gso=%zu]", + CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", qctx->split_len, qctx->split_gsolen, tail_len, qctx->gsolen); return Curl_vquic_flush(cf, data, qctx); } -#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) +#if (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ + !defined(HAVE_APPLE_MSG_X) static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) { int gso_size = 0; @@ -547,6 +646,112 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, return result; } +#elif defined(HAVE_APPLE_MSG_X) + +static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + Curl_vquic_recv_pkts_cb *recv_cb, + void *userp) +{ +#define MSG_X_NUM 64 +#define MSG_BUF_SIZE (2048) + struct iovec msg_iov[MSG_X_NUM]; + struct msghdr_x mmsg[MSG_X_NUM]; + uint8_t msg_ctrl[MSG_X_NUM * CMSG_SPACE(sizeof(int))]; + struct sockaddr_storage remote_addr[MSG_X_NUM]; + size_t total_nread = 0, pkts = 0; +#ifdef CURLVERBOSE + size_t calls = 0; +#endif + int mcount, i; + char errstr[STRERROR_LEN]; + CURLcode result = CURLE_OK; + size_t gso_size; + char *sockbuf = NULL; + uint8_t (*bufs)[MSG_BUF_SIZE] = NULL; + + DEBUGASSERT(max_pkts > 0); + result = Curl_multi_xfer_sockbuf_borrow(data, MSG_X_NUM * MSG_BUF_SIZE, + &sockbuf); + if(result) + goto out; + bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf; + + total_nread = 0; + while(pkts < max_pkts) { + int n = (int)CURLMIN(CURLMIN(MSG_X_NUM, IOV_MAX), max_pkts); + memset(&mmsg, 0, sizeof(mmsg)); + for(i = 0; i < n; ++i) { + msg_iov[i].iov_base = bufs[i]; + msg_iov[i].iov_len = sizeof(bufs[i]); + mmsg[i].msg_iov = &msg_iov[i]; + mmsg[i].msg_iovlen = 1; + mmsg[i].msg_name = &remote_addr[i]; + mmsg[i].msg_namelen = sizeof(remote_addr[i]); + mmsg[i].msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))]; + mmsg[i].msg_controllen = CMSG_SPACE(sizeof(int)); + } + +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + while((mcount = syscall(SYS_recvmsg_x, qctx->sockfd, mmsg, n, 0)) == -1 && + (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) + ; +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + if(mcount == -1) { + if(SOCK_EAGAIN(SOCKERRNO)) { + CURL_TRC_CF(data, cf, "ingress, recvmsg_x -> EAGAIN"); + goto out; + } + if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { + struct ip_quadruple ip; + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg_x() unexpectedly returned %d (errno=%d; %s)", + mcount, SOCKERRNO, errstr); + result = CURLE_RECV_ERROR; + goto out; + } + + VERBOSE(++calls); + for(i = 0; i < mcount; ++i) { + /* A zero-length UDP packet is no QUIC packet. Ignore. */ + if(!mmsg[i].msg_datalen) { + ++pkts; + continue; + } + total_nread += mmsg[i].msg_datalen; + gso_size = mmsg[i].msg_datalen; + + result = recv_cb(bufs[i], mmsg[i].msg_datalen, gso_size, + mmsg[i].msg_name, + mmsg[i].msg_namelen, 0, userp); + if(result) + goto out; + pkts += (mmsg[i].msg_datalen + gso_size - 1) / gso_size; + } + } + +out: + if(total_nread || result) + CURL_TRC_CF(data, cf, + "vquic_recvmsg_x(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); + Curl_multi_xfer_sockbuf_release(data, sockbuf); + return result; +} + #elif defined(HAVE_SENDMSG) static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -705,6 +910,8 @@ CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf, CURLcode result; #ifdef HAVE_SENDMMSG result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#elif defined(HAVE_APPLE_MSG_X) + result = recvmsg_x_packets(cf, data, qctx, max_pkts, recv_cb, userp); #elif defined(HAVE_SENDMSG) result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #else From 53565fbfd60909697ca27a41abc9995c45a9c673 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 28 Jul 2026 10:43:30 +0200 Subject: [PATCH 0762/1062] conncache: connection healthiness fix Update the `lastchecked` timestamp on connection health checks when successful to prevent repeated recalcs for a second. Rename `seems_dead` to `seems_healthy` because the world is already depressing enough. Consider pending input on connection only unhealthy when the connection has no transfers and is not multiplexed. Closes #22412 --- lib/conncache.c | 69 +++++++++++++++++++++---------------------------- lib/conncache.h | 11 ++++---- lib/http2.c | 4 +-- lib/url.c | 11 ++++---- 4 files changed, 42 insertions(+), 53 deletions(-) diff --git a/lib/conncache.c b/lib/conncache.c index 897b41cc4251..11ef03343557 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -702,7 +702,6 @@ void Curl_conn_terminate(struct Curl_easy *data, } struct cpool_reaper_ctx { - size_t checked; size_t reaped; struct curltime now; }; @@ -711,17 +710,15 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, struct connectdata *conn, void *param) { struct cpool_reaper_ctx *reaper = param; - bool terminate = !CONN_INUSE(conn) && conn->bits.no_reuse; - if(!terminate) { - reaper->checked++; - terminate = Curl_cpool_conn_seems_dead(conn, data, &reaper->now); - } - if(terminate) { - /* stop the iteration here, pass back the connection that was pruned */ - reaper->reaped++; - Curl_conn_terminate(data, conn, FALSE); - return 1; + if(!CONN_INUSE(conn)) { + if(conn->bits.no_reuse || conn->bits.close || + !Curl_cpool_conn_seems_healthy(conn, data, &reaper->now)) { + /* terminate conn and stop the iteration */ + reaper->reaped++; + Curl_conn_terminate(data, conn, FALSE); + return 1; + } } return 0; /* continue iteration */ } @@ -906,49 +903,41 @@ static bool cpool_conn_maxage(struct Curl_easy *data, return FALSE; } -/* - * Return TRUE iff the given connection is considered dead. - */ -bool Curl_cpool_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - const struct curltime *pnow) +bool Curl_cpool_conn_seems_healthy(struct connectdata *conn, + struct Curl_easy *data, + const struct curltime *pnow) { - bool input_pending = FALSE; - bool dead = FALSE; + bool healthy = TRUE; DEBUGASSERT(!data->conn); - /* The check only makes sense only if the connection is not in use */ - if(CONN_INUSE(conn)) + if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */ return FALSE; - else if(cpool_conn_maxage(data, conn, pnow)) /* too old? */ - return TRUE; else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) - return FALSE; + return TRUE; else if(conn->scheme->run->connection_is_dead) { Curl_attach_connection(data, conn); - dead = conn->scheme->run->connection_is_dead(data, conn); + healthy = !conn->scheme->run->connection_is_dead(data, conn); Curl_detach_connection(data); } else { + bool input_pending = FALSE; + Curl_attach_connection(data, conn); - dead = !Curl_conn_is_alive(data, conn, &input_pending); + healthy = Curl_conn_is_alive(data, conn, &input_pending); Curl_detach_connection(data); + if(healthy && input_pending && + !CONN_INUSE(conn) && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) { + /* Non-multiplexed connections without attached transfers should + * not have input pending. The input might be a TLS Notify Close, + * for all we know. */ + DEBUGF(infof(data, "connection has no transfer but input, not healthy")); + healthy = FALSE; + } } - if(input_pending) { - /* For reuse, we want a "clean" connection state. This includes - * that we expect - in general - no waiting input data. Input - * waiting might be a TLS Notify Close, for example. We reject - * that. - * For protocols where data from other end may arrive at - * any time (HTTP/2 PING for example), the protocol handler needs - * to install its own `connection_check` callback. - */ - DEBUGF(infof(data, "connection has input pending, not reusable")); - dead = TRUE; - } - - return dead; + if(healthy) + conn->lastchecked = *pnow; + return healthy; } #if 0 diff --git a/lib/conncache.h b/lib/conncache.h index 6798a3b8b3e7..4924c038b36f 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -156,11 +156,10 @@ void Curl_cpool_do_locked(struct Curl_easy *data, /* Close all unused connections, prevent reuse of existing ones. */ void Curl_cpool_nw_changed(struct Curl_easy *data); -/** - * Return TRUE iff the given connection is considered dead. - */ -bool Curl_cpool_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - const struct curltime *pnow); +/* Return TRUE iff the given connection is considered healthy, e.g. + * usable for more transfers. */ +bool Curl_cpool_conn_seems_healthy(struct connectdata *conn, + struct Curl_easy *data, + const struct curltime *pnow); #endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/lib/http2.c b/lib/http2.c index 8d4a93cb58b0..b8733d8ec127 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -545,9 +545,9 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, *input_pending = FALSE; result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); + CURL_TRC_CF(data, cf, "connisalive, recv pending input -> %d, %zu", + (int)result, nread); if(!result) { - CURL_TRC_CF(data, cf, "%zu bytes stray data read before trying " - "h2 connection", nread); result = h2_process_pending_input(cf, data); if(result) /* immediate error, considered dead */ diff --git a/lib/url.c b/lib/url.c index 4cb25350338c..5cc5ab7ce962 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1063,8 +1063,10 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_limits(conn, m)) return FALSE; - if(Curl_cpool_conn_seems_dead(conn, m->data, &m->now)) { - /* remove and disconnect. */ + /* If we are going to pick an idle connection, do an extra + * health check before we reuse it. */ + if(!CONN_INUSE(conn) && + !Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) { infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating", conn->connection_id); Curl_conn_terminate(m->data, conn, FALSE); @@ -1117,6 +1119,8 @@ static bool url_attach_existing(struct Curl_easy *data, bool success; DEBUGASSERT(!data->conn); + Curl_cpool_prune_dead(data); + memset(&match, 0, sizeof(match)); match.data = data; match.needle = needle; @@ -2283,9 +2287,6 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) if(result) goto out; - /* Get rid of any dead connections so limit are easier kept. */ - Curl_cpool_prune_dead(data); - /************************************************************* * Reuse of existing connection is not allowed when * - connect_only is set or From 3974491c97cbff6ca39dd249c02b24e30171f265 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 28 Jul 2026 14:27:13 +0200 Subject: [PATCH 0763/1062] websocket: pause writing and meta data fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When writing a decoded chunk of websocket data, always flush the writer chain so that buffered data gets delivered before the ws meta data gets updated. Add client writer flags CURL_CW_FLAG_BLOWUP for writer types that may significantly enlarge write sizes. This flag causes the pause writer to be added and shrinks the write chunk sizes. We do not want that for content decoders like WS that do not change the size. Add test_20_13 to check that large frames are paused/unpaused correctly with the matching meta data. Fixes #22413 Reported-by: Hendrik Hübner Closes #22416 --- lib/content_encoding.c | 6 ++++++ lib/cw-out.c | 28 ++++++++++++------------- lib/cw-pause.c | 1 + lib/ftp.c | 1 + lib/headers.c | 1 + lib/http_chunks.c | 1 + lib/sendf.c | 8 ++++--- lib/sendf.h | 4 ++++ lib/ws.c | 8 ++++++- tests/http/test_20_websockets.py | 16 +++++++++++--- tests/http/testenv/ws_4frames_server.py | 13 ++++++++++-- 11 files changed, 64 insertions(+), 23 deletions(-) diff --git a/lib/content_encoding.c b/lib/content_encoding.c index a3724d319d69..e23850783619 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -297,6 +297,7 @@ static void deflate_do_close(struct Curl_easy *data, static const struct Curl_cwtype deflate_encoding = { "deflate", NULL, + CURL_CW_FLAG_BLOWUP, deflate_do_init, deflate_do_write, Curl_cwriter_def_flush, @@ -359,6 +360,7 @@ static void gzip_do_close(struct Curl_easy *data, static const struct Curl_cwtype gzip_encoding = { "gzip", "x-gzip", + CURL_CW_FLAG_BLOWUP, gzip_do_init, gzip_do_write, Curl_cwriter_def_flush, @@ -493,6 +495,7 @@ static void brotli_do_close(struct Curl_easy *data, static const struct Curl_cwtype brotli_encoding = { "br", NULL, + CURL_CW_FLAG_BLOWUP, brotli_do_init, brotli_do_write, Curl_cwriter_def_flush, @@ -607,6 +610,7 @@ static void zstd_do_close(struct Curl_easy *data, static const struct Curl_cwtype zstd_encoding = { "zstd", NULL, + CURL_CW_FLAG_BLOWUP, zstd_do_init, zstd_do_write, Curl_cwriter_def_flush, @@ -619,6 +623,7 @@ static const struct Curl_cwtype zstd_encoding = { static const struct Curl_cwtype identity_encoding = { "identity", "none", + 0, Curl_cwriter_def_init, Curl_cwriter_def_write, Curl_cwriter_def_flush, @@ -707,6 +712,7 @@ static void error_do_close(struct Curl_easy *data, static const struct Curl_cwtype error_writer = { "ce-error", NULL, + 0, error_do_init, error_do_write, Curl_cwriter_def_flush, diff --git a/lib/cw-out.c b/lib/cw-out.c index 5ff4a695a1c1..b47587781682 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -190,9 +190,9 @@ static CURLcode cw_out_cb_write(struct Curl_easy *data, nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data); CURL_CBAPI_END(&guard); } - CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu", + CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes, type=%x -> %zu", blen, (otype == CW_OUT_HDS) ? "header" : "body", - nwritten); + (unsigned int)otype, nwritten); if(nwritten == CURL_WRITEFUNC_PAUSE) { if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is @@ -429,8 +429,6 @@ static CURLcode cw_out_write(struct Curl_easy *data, CURLcode result; bool flush_all = !!(type & CLIENTWRITE_EOS); - CURL_TRC_WRITE(data, "[OUT] write %zu bytes, type=%x", - blen, (unsigned int)type); if((type & CLIENTWRITE_BODY) || ((type & CLIENTWRITE_HEADER) && data->set.include_header)) { cw_out_type otype = (!blen && (type & CLIENTWRITE_0LEN)) ? @@ -454,32 +452,34 @@ static CURLcode cw_out_do_flush(struct Curl_easy *data, bool flush_all) { struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; - CURLcode result = CURLE_OK; if(ctx->errored) return CURLE_WRITE_ERROR; - if(data->req.writer.paused) - return CURLE_OK; /* not doing it */ - result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); - if(result) { - ctx->errored = TRUE; - cw_out_bufs_free(ctx); - return result; + if(!data->req.writer.paused && ctx->buf) { + CURLcode result; + + CURL_TRC_WRITE(data, "[OUT] flush"); + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) { + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + return result; + } } - return result; + return CURLE_OK; } static CURLcode cw_out_flush(struct Curl_easy *data, struct Curl_cwriter *writer) { - CURL_TRC_WRITE(data, "[OUT] flush"); return cw_out_do_flush(data, writer, FALSE); } const struct Curl_cwtype Curl_cwt_out = { "cw-out", NULL, + 0, cw_out_init, cw_out_write, cw_out_flush, diff --git a/lib/cw-pause.c b/lib/cw-pause.c index ccdc7657b187..a5b9ba148e08 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -207,6 +207,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, const struct Curl_cwtype Curl_cwt_pause = { "cw-pause", NULL, + 0, cw_pause_init, cw_pause_write, cw_pause_flush, diff --git a/lib/ftp.c b/lib/ftp.c index ff9fed98afc5..abcc9d25a967 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -441,6 +441,7 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data, static const struct Curl_cwtype ftp_cw_lc = { "ftp-lineconv", NULL, + 0, Curl_cwriter_def_init, ftp_cw_lc_write, Curl_cwriter_def_flush, diff --git a/lib/headers.c b/lib/headers.c index 5d2101ee881a..3bc9c3aceba0 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -315,6 +315,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data, static const struct Curl_cwtype hds_cw_collect = { "hds-collect", NULL, + 0, Curl_cwriter_def_init, hds_cw_collect_write, Curl_cwriter_def_flush, diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 85f453eb59fc..f67459bf55ba 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -465,6 +465,7 @@ static CURLcode cw_chunked_write(struct Curl_easy *data, const struct Curl_cwtype Curl_httpchunk_unencoder = { "chunked", NULL, + 0, cw_chunked_init, cw_chunked_write, Curl_cwriter_def_flush, diff --git a/lib/sendf.c b/lib/sendf.c index 27f305521477..a5aad668f5e0 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -293,6 +293,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, static const struct Curl_cwtype cw_download = { "protocol", NULL, + 0, Curl_cwriter_def_init, cw_download_write, Curl_cwriter_def_flush, @@ -315,6 +316,7 @@ static CURLcode cw_raw_write(struct Curl_easy *data, static const struct Curl_cwtype cw_raw = { "raw", NULL, + 0, Curl_cwriter_def_init, cw_raw_write, Curl_cwriter_def_flush, @@ -484,9 +486,9 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data, return result; } - if(writer->phase == CURL_CW_CONTENT_DECODE) { - /* On adding a content decoder, add the pause writer. Do this - * BEFORE the given writer as any failure will make the + if(writer->cwt->flags & CURL_CW_FLAG_BLOWUP) { + /* On adding a writer that may blow up write sizes, e.g. zip bombs, + * add the pause writer. Do this first as any failure will make the * caller destroy the writer again. */ result = cwriter_ensure_pause_writer(data); if(result) diff --git a/lib/sendf.h b/lib/sendf.h index 7f31b246622b..9863c5b0dcaa 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -110,10 +110,14 @@ typedef enum { CURL_CW_CLIENT /* data written to client */ } Curl_cwriter_phase; +/* writer may blow up size of write data, e.g. zip bombs */ +#define CURL_CW_FLAG_BLOWUP (1U << 0) + /* Client Writer Type, provides the implementation */ struct Curl_cwtype { const char *name; /* writer name. */ const char *alias; /* writer name alias, maybe NULL. */ + uint8_t flags; /* flags for writer behaviour */ CURLcode (*do_init)(struct Curl_easy *data, struct Curl_cwriter *writer); CURLcode (*do_write)(struct Curl_easy *data, diff --git a/lib/ws.c b/lib/ws.c index cdaec3ba9356..46f4505a6a11 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -695,6 +695,7 @@ static CURLcode ws_cw_dec_next(const uint8_t *buf, size_t buflen, update_meta(ws, frame_age, frame_flags, payload_offset, payload_len, buflen); + CURL_TRC_WRITE(data, "[WS] pass %zu decoded bytes", buflen); result = Curl_cwriter_write(data, ctx->next_writer, (ctx->cw_type | CLIENTWRITE_0LEN), (const char *)buf, buflen); @@ -713,7 +714,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, struct websocket *ws; CURLcode result = CURLE_OK; - CURL_TRC_WRITE(data, "ws_cw_write(len=%zu, type=%d)", nbytes, type); + CURL_TRC_WRITE(data, "[WS] write(len=%zu, type=%d)", nbytes, type); if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -733,6 +734,10 @@ static CURLcode ws_cw_write(struct Curl_easy *data, } } + result = Curl_cwriter_flush(data, writer->next); + if(result) + goto out; + while(!Curl_bufq_is_empty(&ctx->buf) && !Curl_cwriter_is_paused(data)) { struct ws_cw_dec_ctx pass_ctx; pass_ctx.data = data; @@ -822,6 +827,7 @@ static CURLcode ws_cw_flush(struct Curl_easy *data, static const struct Curl_cwtype ws_cw_decode = { "ws-decode", NULL, + 0, ws_cw_init, ws_cw_write, ws_cw_flush, diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index b559383c937a..70c00797194a 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -325,12 +325,22 @@ def srv(): rss2 = r.profile.stats['rss'] / (1024 * 1024) assert (rss1 * 1.1) >= rss2, 'bad memory increase' - # test frame delivery when pausing - def test_20_12_pause_frames(self, env: Env, ws_4frames): + # test small frames delivery when pausing + def test_20_12_pause_frames_small(self, env: Env, ws_4frames): payload = 127 * "x" client = LocalClient(env=env, name='cli_ws_pause') if not client.exists(): pytest.skip(f'example client not built: {client.name}') - url = f'ws://localhost:{ws_4frames.port}/' + url = f'ws://localhost:{ws_4frames.port}/small' + r = client.run(args=[url, payload]) + r.check_exit_code(0) + + # test small frames delivery when pausing + def test_20_13_pause_frames_large(self, env: Env, ws_4frames): + payload = 127 * "x" + client = LocalClient(env=env, name='cli_ws_pause') + if not client.exists(): + pytest.skip(f'example client not built: {client.name}') + url = f'ws://localhost:{ws_4frames.port}/large' r = client.run(args=[url, payload]) r.check_exit_code(0) diff --git a/tests/http/testenv/ws_4frames_server.py b/tests/http/testenv/ws_4frames_server.py index 42d6e9dc2995..488fb5a9cfc8 100755 --- a/tests/http/testenv/ws_4frames_server.py +++ b/tests/http/testenv/ws_4frames_server.py @@ -29,21 +29,30 @@ import websockets -MESSAGES = [ +MESSAGES_SMALL = [ "Hello 1", "Hello 2", "Hello 3", "Hello 4", ] +MESSAGES_LARGE = [ + b"x" * 65536, + b"x" * 65536, + b"x" * 65536, + b"x" * 65536, +] + async def handler(websocket): peer = websocket.remote_address print(f"client from {peer[0]}:{peer[1]}", flush=True) print("handshake complete", flush=True) + msgs = MESSAGES_LARGE if websocket.request.path == '/large' else MESSAGES_SMALL + await asyncio.sleep(0.1) - for index, payload in enumerate(MESSAGES, start=1): + for index, payload in enumerate(msgs, start=1): await websocket.send(payload) print(f"sent frame {index}: {payload!r}", flush=True) # await asyncio.sleep(0.2) From ef15676abb39704129cb547f07d5f044837868b3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jul 2026 13:12:46 +0200 Subject: [PATCH 0764/1062] EXPERIMENTAL: Apple fast UDP Follow-up to 079a11bcba5858ffab2cf6e810e9d980 Closes #22428 --- docs/EXPERIMENTAL.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index c5083e9f6549..f2cd6416b711 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -126,3 +126,20 @@ Graduation requirements: - no test cases are disabled for the feature - feedback from users saying the API works for their use cases + +## Apple fast UDP + +There are undocumented system calls available in Apple operating systems that +when used allow for faster sending and receiving of UDP messages. + +The undocumented and thus unsupported-by-Apple nature of these functions +brings a risk that they are removed in a future OS update, or perhaps worse: +marginally modified to instead cause subtle and hard-to-spot bugs. + +Graduation requirements: + +- testimonials from users that these work reliably + +- measurements that show they make a measurable performance impact + +- an easy way to (re-)build curl to not use these functions From f76f79696e12f0334275f394f24344f69b6739f7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jul 2026 13:21:52 +0200 Subject: [PATCH 0765/1062] EXPERIMENTAL: cleanups, unify on titles, merge quiche into a single segment Closes #22428 --- docs/EXPERIMENTAL.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index f2cd6416b711..51407743ee39 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -34,18 +34,17 @@ Experimental support in curl means: provided by the experiment and then the disabling should be managed inside each affected test case. -## Experimental features right now - -### HTTP/3 support (non-ngtcp2 backends) +## The quiche QUIC and HTTP/3 backend Graduation requirements: -- The used libraries should be considered out-of-beta with a reasonable - expectation of a stable API going forward. +- The library should be considered out-of-beta + +- a reasonable expectation of a stable API going forward -- Using HTTP/3 with the given build should perform without risking busy-loops +- HTTP/3 with the given build should perform without risking busy-loops -### HTTP/3 proxy and CONNECT-UDP support +## HTTP/3 proxy and CONNECT-UDP support Support for HTTP/3 proxy and CONNECT-UDP tunneling is experimental and requires an explicit build-time opt-in (`--enable-proxy-http3` for @@ -55,14 +54,7 @@ Graduation requirements: - implementation stability over time with no known severe regressions -### The quiche backend - -Graduation requirements: - -- the library needs to consider itself non-beta. -- a reasonable expectation of a stable API going forward. - -### The Rustls backend +## The Rustls TLS backend Graduation requirements: From c7b1e10dc1dcacae17fc75fdb6572846605d2ffe Mon Sep 17 00:00:00 2001 From: CatboxParadox Date: Tue, 28 Jul 2026 14:12:09 +0200 Subject: [PATCH 0766/1062] h2: bootstrap max streams from multi handle if in use Closes #22418 --- lib/http2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/http2.c b/lib/http2.c index b8733d8ec127..08dfb6d91973 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2412,7 +2412,9 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, failf(data, "Could not initialize nghttp2"); goto out; } - ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; + ctx->max_concurrent_streams = data->multi ? + Curl_multi_max_concurrent_streams(data->multi) : + DEFAULT_MAX_CONCURRENT_STREAMS; if(ctx->via_h1_upgrade) { /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted From 2a3804936d8399916a95c103bf31623fccb123a8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 16 May 2026 12:08:00 +0200 Subject: [PATCH 0767/1062] src: improve `curlx_memzero()` internal APIs - delete zero-and-free wrapper macros. (not yet used) To keep it simple. - do NULL-check in `curlx_memzero()`. To avoid noise at call sites. - add `curlx_strzero()` for null-terminated strings, also with NULL-check. Ref: #21637 Follow-upt o 066478f6346a2d987a9ecc3bd3bf45764d69c1c4 #21598 Closes #21645 --- lib/curl_setup.h | 34 ++++++++++------------------------ lib/curlx/strdup.c | 12 +++++------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/lib/curl_setup.h b/lib/curl_setup.h index e6d5b8f6946b..9d18875b9d4f 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1338,20 +1338,6 @@ extern curl_calloc_callback Curl_ccalloc; (ptr) = NULL; \ } while(0) -/* Same as curlx_safefree() but zeroes memory before freeing */ -#define curlx_safefreezero(ptr, size) \ - do { \ - curlx_freezero(ptr, size); \ - (ptr) = NULL; \ - } while(0) - -/* Same as curlx_safefreezero() but determines length with strlen() */ -#define curlx_safefreezeroz(ptr) \ - do { \ - curlx_freezeroz(ptr); \ - (ptr) = NULL; \ - } while(0) - #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ #ifdef DEBUGBUILD @@ -1642,14 +1628,14 @@ typedef struct sockaddr_un { #if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ (NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */ #pragma comment(lib, "volatileaccessu.lib") -#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size) +#define curlx_memzero_low(buf, size) SecureZeroMemory2(buf, size) #else -#define curlx_memzero(buf, size) SecureZeroMemory(buf, size) +#define curlx_memzero_low(buf, size) SecureZeroMemory(buf, size) #endif #elif defined(HAVE_MEMSET_S) -#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size) +#define curlx_memzero_low(buf, size) (void)memset_s(buf, size, 0, size) #elif defined(HAVE_MEMSET_EXPLICIT) -#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size) +#define curlx_memzero_low(buf, size) (void)memset_explicit(buf, 0, size) #elif defined(__CYGWIN__) || \ (defined(__NEWLIB__) && !defined(__CLIB2__)) || \ (defined(__GLIBC__) && \ @@ -1657,17 +1643,17 @@ typedef struct sockaddr_un { (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) -#define curlx_memzero(buf, size) explicit_bzero(buf, size) +#define curlx_memzero_low(buf, size) explicit_bzero(buf, size) #elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ -#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size) +#define curlx_memzero_low(buf, size) (void)explicit_memset(buf, 0, size) #endif #endif /* !_CURL_LOCAL_MEMZERO */ -#ifndef curlx_memzero +#ifndef curlx_memzero_low #define USE_CURLX_MEMZERO -void curlx_memzero(void *buf, size_t size); +void curlx_memzero_low(void *buf, size_t size); #endif -void curlx_freezero(void *buf, size_t size); -void curlx_freezeroz(void *buf); +void curlx_memzero(void *buf, size_t size); +void curlx_strzero(void *buf); #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curlx/strdup.c b/lib/curlx/strdup.c index 8e788ea34c01..6c967ff296f0 100644 --- a/lib/curlx/strdup.c +++ b/lib/curlx/strdup.c @@ -101,7 +101,7 @@ static void *(* const volatile p_curlx_memset)(void *buf, int val, /* Local fallback in case there is no system function to securely zero a memory buffer. */ -void curlx_memzero(void *buf, size_t size) +void curlx_memzero_low(void *buf, size_t size) { if(buf) p_curlx_memset(buf, 0, size); @@ -109,17 +109,15 @@ void curlx_memzero(void *buf, size_t size) #endif /* Free 'buf' after zeroing its content. */ -void curlx_freezero(void *buf, size_t size) +void curlx_memzero(void *buf, size_t size) { if(buf) - curlx_memzero(buf, size); - curlx_free(buf); + curlx_memzero_low(buf, size); } /* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ -void curlx_freezeroz(void *buf) +void curlx_strzero(void *buf) { if(buf) - curlx_memzero(buf, strlen(buf)); - curlx_free(buf); + curlx_memzero_low(buf, strlen(buf)); } From 172c16b58ccf8bff2122dcde7ea48967198308f9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jul 2026 13:59:31 +0200 Subject: [PATCH 0768/1062] docs/INTERNALS.md -> docs/DEPENDENCIES.md With some minor cleanups Closes #22430 --- .github/workflows/linux-old.yml | 2 +- GIT-INFO.md | 4 ++-- docs/{INTERNALS.md => DEPENDENCIES.md} | 14 ++------------ docs/Makefile.am | 8 ++++---- 4 files changed, 9 insertions(+), 19 deletions(-) rename docs/{INTERNALS.md => DEPENDENCIES.md} (80%) diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 2f4d8ee81e21..dcffea33086a 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -7,7 +7,7 @@ # outdated systems. # # Debian stretch is chosen as it closely matches some of the oldest major -# versions we support (especially cmake); see docs/INTERNALS.md and it +# versions we support (especially cmake); see docs/DEPENDENCIES.md and it # is still supported (as of this writing). # stretch has ELTS support from Freexian until 2027-06-30 # For ELTS info see https://www.freexian.com/lts/extended/docs/how-to-use-extended-lts/ diff --git a/GIT-INFO.md b/GIT-INFO.md index ee912560fd5a..3c2e9efc4f66 100644 --- a/GIT-INFO.md +++ b/GIT-INFO.md @@ -28,6 +28,6 @@ Daniel uses a configure line similar to this for easier development: ## REQUIREMENTS -See [docs/INTERNALS.md][0] for requirement details. +See [docs/DEPENDENCIES.md][0] for requirement details. -[0]: docs/INTERNALS.md +[0]: docs/DEPENDENCIES.md diff --git a/docs/INTERNALS.md b/docs/DEPENDENCIES.md similarity index 80% rename from docs/INTERNALS.md rename to docs/DEPENDENCIES.md index 581f0d45b513..a6c97c29472e 100644 --- a/docs/INTERNALS.md +++ b/docs/DEPENDENCIES.md @@ -4,11 +4,9 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -# curl internals +# curl dependencies -The canonical libcurl internals documentation is now in the [everything -curl](https://everything.curl.dev/internals) book. This file lists supported -versions of libs and build tools. +Supported minimum versions of libs and build tools. ## Portability @@ -76,11 +74,3 @@ that do not find the necessary requirements are automatically skipped. - pytest - Python 3.8 (2019-10-14) - stunnel - -## Library Symbols - -All symbols used internally in libcurl must use a `Curl_` prefix if they are -used in more than a single file. Single-file symbols must be made static. -Public ("exported") symbols must use a `curl_` prefix. Public API functions -are marked with `CURL_EXTERN` in the public header files so that all others -can be hidden on platforms where this is possible. diff --git a/docs/Makefile.am b/docs/Makefile.am index fdc4511b5cab..d99dffe126d5 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -83,14 +83,15 @@ EXTRA_DIST = \ BINDINGS.md \ BUG-BOUNTY.md \ BUGS.md \ - CIPHERS.md \ CIPHERS-TLS12.md \ + CIPHERS.md \ CMakeLists.txt \ CODE_OF_CONDUCT.md \ CODE_REVIEW.md \ CONTRIBUTE.md \ CURL-DISABLE.md \ CURLDOWN.md \ + DEPENDENCIES.md \ DEPRECATE.md \ DISTROS.md \ EARLY-RELEASE.md \ @@ -109,7 +110,6 @@ EXTRA_DIST = \ INSTALL \ INSTALL-CMAKE.md \ INSTALL.md \ - INTERNALS.md \ IPFS.md \ KNOWN_BUGS.md \ KNOWN_RISKS.md \ @@ -118,15 +118,15 @@ EXTRA_DIST = \ options-in-versions \ README.md \ RELEASE-PROCEDURE.md \ - RUSTLS.md \ ROADMAP.md \ + RUSTLS.md \ SECURITY-ADVISORY.md \ SPONSORS.md \ SSL-PROBLEMS.md \ SSLCERTS.md \ THANKS \ - TODO.md \ TheArtOfHttpScripting.md \ + TODO.md \ URL-SYNTAX.md \ VERIFY.md \ VERSIONS.md \ From 112a8b5adf36c17e7816a8db701b3e7a22958e52 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 14 May 2026 01:36:22 +0200 Subject: [PATCH 0769/1062] src: safely clear certain buffers That may hold credentials or other sensitive data, or where we want to ensure the zeroing is not optimized out by the compiler. Credits-to: Daniel Gustafsson Ref: #13589 (original attempt) Ref: #21588 Follow-up to #21645 Follow-up to 066478f6346a2d987a9ecc3bd3bf45764d69c1c4 #21598 Closes #21637 --- lib/creds.c | 7 +++++-- lib/creds.h | 1 + lib/curl_share.c | 5 ++++- lib/curl_sspi.c | 8 +++++++- lib/imap.c | 1 + lib/mqtt.c | 4 +++- lib/multi.c | 1 + lib/setopt.c | 2 ++ lib/url.c | 10 ++++++++++ lib/urlapi.c | 6 ++++++ src/tool_cfgable.c | 3 +++ 11 files changed, 43 insertions(+), 5 deletions(-) diff --git a/lib/creds.c b/lib/creds.c index d22c166a4275..5507ae86f6ac 100644 --- a/lib/creds.c +++ b/lib/creds.c @@ -46,6 +46,7 @@ CURLcode Curl_creds_create(const char *user, size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; size_t sslen = sasl_service ? strlen(sasl_service) : 0; char *s, *buf; + size_t bufsize; CURLcode result = CURLE_OK; Curl_creds_unlink(pcreds); @@ -64,13 +65,14 @@ CURLcode Curl_creds_create(const char *user, } /* null-terminator for user already part of struct */ - creds = curlx_calloc(1, sizeof(*creds) + - ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1); + bufsize = ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1; + creds = curlx_calloc(1, sizeof(*creds) + bufsize); if(!creds) { result = CURLE_OUT_OF_MEMORY; goto out; } + creds->bufsize = bufsize; creds->refcount = 1; creds->source = source; /* Some compilers try to be too smart about our dynamic struct size */ @@ -147,6 +149,7 @@ void Curl_creds_unlink(struct Curl_creds **pcreds) if(creds->refcount) creds->refcount--; if(!creds->refcount) { + curlx_memzero(creds, sizeof(*creds) + creds->bufsize); curlx_free(creds); } } diff --git a/lib/creds.h b/lib/creds.h index 4a469d69f6aa..5db3f8f22e12 100644 --- a/lib/creds.h +++ b/lib/creds.h @@ -39,6 +39,7 @@ struct Curl_creds { const char *sasl_service; /* non-NULL, maybe empty string */ uint32_t refcount; uint8_t source; /* CREDS_* value */ + size_t bufsize; /* extra bytes added to sizeof(struct Curl_creds) */ char buf[1]; }; diff --git a/lib/curl_share.c b/lib/curl_share.c index 94469bc3ef6e..b7c331adf7e9 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -34,6 +34,9 @@ static void share_destroy(struct Curl_share *share) { + if(!share) + return; + if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { Curl_cpool_destroy(&share->cpool); } @@ -61,7 +64,7 @@ static void share_destroy(struct Curl_share *share) #ifdef USE_MUTEX Curl_mutex_destroy(&share->lock); #endif - share->magic = 0; + curlx_memzero(share, sizeof(*share)); curlx_free(share); } diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index 1429f7143a2e..5b4e6e41fdea 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -100,6 +100,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, xcharp_u domain, dup_domain; xcharp_u passwd, dup_passwd; size_t domlen = 0; + size_t pwlen; domain.const_tchar_ptr = _TEXT(""); @@ -157,17 +158,20 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, curlx_free(dup_domain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } + pwlen = _tcslen(passwd.tchar_ptr); dup_passwd.tchar_ptr = curlx_tcsdup(passwd.tchar_ptr); if(!dup_passwd.tchar_ptr) { curlx_free(dup_user.tchar_ptr); curlx_free(dup_domain.tchar_ptr); + curlx_memzero(passwd.tchar_ptr, pwlen * sizeof(*passwd.tchar_ptr)); curlx_free(passwd.tchar_ptr); return CURLE_OUT_OF_MEMORY; } identity->Password = dup_passwd.tbyte_ptr; - identity->PasswordLength = curlx_uztoul(_tcslen(dup_passwd.tchar_ptr)); + identity->PasswordLength = curlx_uztoul(pwlen); dup_passwd.tchar_ptr = NULL; + curlx_memzero(passwd.tchar_ptr, pwlen * sizeof(*passwd.tchar_ptr)); curlx_free(passwd.tchar_ptr); identity->User = dup_user.tbyte_ptr; @@ -201,6 +205,8 @@ void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity) { if(identity) { curlx_safefree(identity->User); + curlx_memzero(identity->Password, + identity->PasswordLength * sizeof(*identity->Password)); curlx_safefree(identity->Password); curlx_safefree(identity->Domain); } diff --git a/lib/imap.c b/lib/imap.c index 8f0c29771532..cb8f7e8cca55 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -613,6 +613,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, passwd ? passwd : ""); curlx_free(user); + curlx_strzero(passwd); curlx_free(passwd); if(!result) diff --git a/lib/mqtt.c b/lib/mqtt.c index 0858fd65f609..29e92ce5c133 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -347,8 +347,10 @@ static CURLcode mqtt_connect(struct Curl_easy *data) result = mqtt_send(data, packet, packetlen); end: - if(packet) + if(packet) { + curlx_memzero(packet, packetlen); curlx_free(packet); + } Curl_creds_unlink(&data->state.creds); return result; } diff --git a/lib/multi.c b/lib/multi.c index f68124436f09..e402be099cf2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3060,6 +3060,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) Curl_uint32_bset_destroy(&multi->pending); Curl_uint32_bset_destroy(&multi->msgsent); Curl_uint32_tbl_destroy(&multi->xfers); + curlx_memzero(multi, sizeof(*multi)); curlx_free(multi); mresult = CURLM_OK; diff --git a/lib/setopt.c b/lib/setopt.c index a030b5489e95..427cce07af39 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -154,6 +154,7 @@ static CURLcode setstropt_userpwd(const char *option, char **userp, curlx_free(*userp); *userp = user; + curlx_strzero(*passwdp); curlx_free(*passwdp); *passwdp = passwd; @@ -1668,6 +1669,7 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, REJECT_ZERO); curlx_free(u); + curlx_strzero(p); curlx_free(p); break; } diff --git a/lib/url.c b/lib/url.c index 5cc5ab7ce962..6dff5102ccbe 100644 --- a/lib/url.c +++ b/lib/url.c @@ -153,6 +153,15 @@ void Curl_freeset(struct Curl_easy *data) enum dupblob j; for(i = (enum dupstring)0; i < STRING_LAST; i++) { + if(i == STRING_PASSWORD || + i == STRING_KEY_PASSWD || +#ifndef CURL_DISABLE_PROXY + i == STRING_PROXYPASSWORD || + i == STRING_KEY_PASSWD_PROXY || +#endif + i == STRING_BEARER) { + curlx_strzero(data->set.str[i]); + } curlx_safefree(data->set.str[i]); } @@ -301,6 +310,7 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_PROXY Curl_ssl_config_cleanup(&data->set.proxy_ssl.primary); #endif + curlx_memzero(data, sizeof(*data)); curlx_free(data); return CURLE_OK; } diff --git a/lib/urlapi.c b/lib/urlapi.c index 35251053adeb..b4dfdda678a3 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -73,6 +73,7 @@ static void free_urlhandle(struct Curl_URL *u) { curlx_free(u->scheme); curlx_free(u->user); + curlx_strzero(u->password); curlx_free(u->password); curlx_free(u->options); curlx_free(u->host); @@ -325,6 +326,7 @@ UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, } if(passwdp) { + curlx_strzero(u->password); curlx_free(u->password); u->password = passwdp; } @@ -341,6 +343,7 @@ UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, out: curlx_free(userp); + curlx_strzero(passwdp); curlx_free(passwdp); curlx_free(optionsp); curlx_safefree(u->user); @@ -1808,6 +1811,7 @@ static CURLUcode urlset_clear(CURLU *u, CURLUPart what) curlx_safefree(u->user); break; case CURLUPART_PASSWORD: + curlx_strzero(u->password); curlx_safefree(u->password); break; case CURLUPART_OPTIONS: @@ -2084,6 +2088,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(status) return status; + if(what == CURLUPART_PASSWORD) + curlx_strzero(*storep); curlx_free(*storep); *storep = (char *)CURL_UNCONST(newp); } diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 5423944a2990..1ece58359516 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -139,7 +139,9 @@ static void free_config_fields(struct OperationConfig *config) curlx_safefree(config->proxy_key); curlx_safefree(config->key_type); curlx_safefree(config->proxy_key_type); + curlx_strzero(config->key_passwd); curlx_safefree(config->key_passwd); + curlx_strzero(config->proxy_key_passwd); curlx_safefree(config->proxy_key_passwd); curlx_safefree(config->pubkey); curlx_safefree(config->hostpubmd5); @@ -152,6 +154,7 @@ static void free_config_fields(struct OperationConfig *config) curlx_safefree(config->request_target); curlx_safefree(config->customrequest); curlx_safefree(config->krblevel); + curlx_strzero(config->oauth_bearer); curlx_safefree(config->oauth_bearer); curlx_safefree(config->sasl_authzid); curlx_safefree(config->unix_socket_path); From 64f746ee1e71f1079424cc173b7ec9d93adbb169 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Jul 2026 17:09:06 +0200 Subject: [PATCH 0770/1062] urlapi: clear password buffer on error path Reported by Copilot Bug: https://github.com/curl/curl/pull/21637#pullrequestreview-4809702512 Follow-up to 112a8b5adf36c17e7816a8db701b3e7a22958e52 #21637 Follow-up to 7c34365ccea19949317878c7fcd5f7376e2e09f1 #21879 Closes #22432 --- lib/urlapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/urlapi.c b/lib/urlapi.c index b4dfdda678a3..1e61fbcae57d 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -347,6 +347,7 @@ UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, curlx_free(passwdp); curlx_free(optionsp); curlx_safefree(u->user); + curlx_strzero(u->password); curlx_safefree(u->password); curlx_safefree(u->options); From a7f82509e44b22f08c21f189ca468bc39155d62d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 17:14:11 +0000 Subject: [PATCH 0771/1062] GHA: update dependency nghttp2/nghttp2 to v1.70.0 Closes #22434 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 6d4856054ea8..11e8a79111f2 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -60,7 +60,7 @@ env: # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com NGTCP2_VERSION: 1.25.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com - NGHTTP2_VERSION: 1.69.0 + NGHTTP2_VERSION: 1.70.0 # no tagged releases H2O_VERSION: 11b0cfa2771e3ccad4a852e72473e4e278ab1de7 # 2026-05-28 H2O_SHA256: 5ae1bd7b09970d7d49c41fa68193e24da04c2a7ac5581fbe2affc79200b0721f diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 389fd5775a83..e1add7cbe004 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -48,7 +48,7 @@ env: MBEDTLS_PREV_VERSION: 3.6.7 MBEDTLS_PREV_SHA256: a7e8bcbec0e6f761b4af24f25677626b35f762f68eef79c08677a363212d11f6 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com - NGHTTP2_VERSION: 1.69.0 + NGHTTP2_VERSION: 1.70.0 # handled in renovate.json OPENLDAP_VERSION: 2.6.10 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com From c4fe0c7b9ffbb8884ca8383230006b03c00d5028 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Fri, 26 Jun 2026 14:12:43 -0400 Subject: [PATCH 0772/1062] ldap: support empty username and password Prior to this change an empty username or password was passed to the LDAP bind function as NULL instead of an empty string. Regression since 8f71d0fd. Reported-by: Yoshiro Yoneya Fixes https://github.com/curl/curl/issues/22162 Closes https://github.com/curl/curl/pull/22196 --- lib/ldap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/ldap.c b/lib/ldap.c index a7c1fe8c8390..c8830b3edcd5 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -282,10 +282,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *host = NULL; #endif - const char *user = Curl_creds_has_user(data->state.creds) ? - data->state.creds->user : NULL; - const char *passwd = Curl_creds_has_passwd(data->state.creds) ? - data->state.creds->passwd : NULL; + const char *user = data->state.creds ? data->state.creds->user : NULL; + const char *passwd = data->state.creds ? data->state.creds->passwd : NULL; struct ip_quadruple ipquad; bool is_ipv6; BerElement *ber = NULL; From 9743720ab93d6f90ef359b3e1c8e55c1cbdbb729 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Tue, 28 Jul 2026 16:27:38 -0700 Subject: [PATCH 0773/1062] tests: fix cert comparison with old cryptography The fallback path for cryptography < 42 was broken by commit e13362c2 that caused a comparison between offset-naive and offset-aware datetimes. Use the positional form of tz in datetime.now() everywhere. Ref: #22396 Pointed out by Codex Security Closes #22426 --- tests/http/scorecard.py | 2 +- tests/http/testenv/certs.py | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index d6d89a375355..08986ba1d628 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -693,7 +693,7 @@ def score(self, 'os': self.env.curl_os(), 'server': self.server_descr, 'samples': nsamples, - 'date': f'{datetime.datetime.now(tz=datetime.timezone.utc).isoformat()}', + 'date': f'{datetime.datetime.now(datetime.timezone.utc).isoformat()}', } } if self._limit_rate: diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index ab87bfa49e47..444f26011696 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -344,17 +344,15 @@ def load_credentials(self, name: str, key_type=None, cert = self.load_pem_cert(cert_file) pkey = self.load_pem_pkey(pkey_file) try: - now = datetime.now(tz=timezone.utc) - if check_valid and \ - ((cert.not_valid_after_utc < now) or - (cert.not_valid_before_utc > now)): - return None - except AttributeError: # older python - now = datetime.now(timezone.utc) - if check_valid and \ - ((cert.not_valid_after < now) or - (cert.not_valid_before > now)): - return None + before = cert.not_valid_before_utc + after = cert.not_valid_after_utc + except AttributeError: # cryptography < 42.0.0 + # the timestamps are already returned in UTC, just missing the time zone + before = cert.not_valid_before.replace(tzinfo=timezone.utc) + after = cert.not_valid_after.replace(tzinfo=timezone.utc) + now = datetime.now(timezone.utc) + if check_valid and ((after < now) or (before > now)): + return None creds = Credentials(name=name, cert=cert, pkey=pkey, issuer=issuer) creds.set_store(self) creds.set_files(cert_file, pkey_file, comb_file) From 9951a2247e5b18a3c961c25ba15cb39f1be11522 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 29 Jul 2026 17:58:08 +0200 Subject: [PATCH 0774/1062] tool: remove duplicate setopts CURLOPT_RESOLVE and CURLOPT_CONNECT_TO were set twice. A happy little accident? Closes #22433 --- src/config2setopts.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/config2setopts.c b/src/config2setopts.c index 47b67aed0b7a..d7963f6716d6 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -1120,8 +1120,10 @@ static CURLcode misc_setopts(struct OperationConfig *config, CURL *curl) CURLcode result = CURLE_OK; my_setopt_ptr(curl, CURLOPT_STDERR, tool_stderr); - my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); - my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); + if(config->resolve) + my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); + if(config->connect_to) + my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); if(config->gssapi_delegation) my_setopt_long(curl, CURLOPT_GSSAPI_DELEGATION, config->gssapi_delegation); @@ -1131,9 +1133,6 @@ static CURLcode misc_setopts(struct OperationConfig *config, CURL *curl) if(config->proto_redir_present) MY_SETOPT_STR(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); - my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); - my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); - if(config->gssapi_delegation) my_setopt_long(curl, CURLOPT_GSSAPI_DELEGATION, config->gssapi_delegation); From 57cae40ae5cfbdc928953c074345ef747401be70 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jul 2026 16:32:40 +0200 Subject: [PATCH 0775/1062] sws: log the exact closing reason better, to help debugging tests Closes #22431 --- tests/server/sws.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/server/sws.c b/tests/server/sws.c index e47ba7fc3d91..d68f60b5b803 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -887,10 +887,18 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) /* If the word 'swsclose' is present anywhere in the reply chunk, the connection is closed after the data has been sent to the requesting client... */ - if(strstr(buffer, "swsclose") || !count || req->close) { + if(strstr(buffer, "swsclose")) { persistent = FALSE; logmsg("connection close instruction \"swsclose\" found in response"); } + else if(!count) { + persistent = FALSE; + logmsg("connection closed because of empty response"); + } + else if(req->close) { + persistent = FALSE; + logmsg("connection closed because of close instruction in servercmd"); + } if(strstr(buffer, "swsbounce")) { sws_prevbounce = TRUE; logmsg("enable \"swsbounce\" in the next request"); From c155cbc6380467487fcbca126366638149f04cf0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Jul 2026 01:46:15 +0200 Subject: [PATCH 0776/1062] GHA: work around Homebrew `ca-certificates` install error Working around: ``` ==> Installing libngtcp2 dependency: ca-certificates ==> Pouring ca-certificates--2026-07-16.all.bottle.1.tar.gz Warning: The post-install step did not complete successfully [...] Error: Process completed with exit code 1. ``` Refs: https://github.com/Homebrew/homebrew-core/pull/295934 https://github.com/Homebrew/brew/pull/23357 Bug: https://github.com/curl/curl/pull/22437#issuecomment-5124151490 Closes #22438 --- .github/workflows/codeql.yml | 1 + .github/workflows/linux.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c1fb8f6b92fc..16f30b44cca0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -80,6 +80,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 install \ libpsl-dev libbrotli-dev libidn2-dev libssh2-1-dev libssh-dev \ libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev libwolfssl-dev + /home/linuxbrew/.linuxbrew/bin/brew install ca-certificates || true /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e1add7cbe004..8622caa2d0a7 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -481,6 +481,7 @@ jobs: ${INSTALL_PACKAGES} \ ${MATRIX_INSTALL_PACKAGES} if [ -n "${INSTALL_PACKAGES_BREW}" ]; then + /home/linuxbrew/.linuxbrew/bin/brew install ca-certificates || true /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} fi From 7d5398f764ebed25da3bf75afc5384b2a7893399 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Jul 2026 02:24:58 +0200 Subject: [PATCH 0777/1062] build: fix HTTPSIG option for unsupported TLS backends Show a warning and force-disable HTTPSIG when the TLS backend is not OpenSSL or wolfSSL. Before this patch this resulted in a mismatched feature list in configure and `curl -V`. Also enable HTTPSIG in more CI jobs to cover unsupported ones, Windows compilers, clang-tidy, cmake. Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 Cherry-picked from #22437 Closes #22439 --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 4 ++-- .github/workflows/windows.yml | 3 ++- CMakeLists.txt | 5 +++++ appveyor.sh | 1 + configure.ac | 10 ++++++++-- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8622caa2d0a7..3c8b3832052f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -316,7 +316,7 @@ jobs: - name: '!ssl !http !smtp !imap' image: ubuntu-26.04-arm tflags: '--min=500' - configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity + configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity --enable-httpsig - name: 'clang-tidy' image: ubuntu-26.04 @@ -341,7 +341,7 @@ jobs: -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl@4 -DCURL_USE_WOLFSSL=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_RUSTLS=ON -DCURL_USE_GSASL=ON - -DUSE_ECH=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON + -DUSE_ECH=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON -DCURL_DISABLE_HTTPSIG=OFF -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22 - name: 'clang-tidy H3 c-ares !examples' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 14a276384c66..61ded654957c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -235,7 +235,7 @@ jobs: - name: '!ssl libssh2 AppleIDN' compiler: clang - generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=ON -DUSE_APPLE_IDN=ON -DCURL_ENABLE_SSL=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=ON -DUSE_APPLE_IDN=ON -DCURL_ENABLE_SSL=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_DISABLE_HTTPSIG=OFF tflags: '--min=1630' - name: 'OpenSSL libssh c-ares' @@ -345,7 +345,7 @@ jobs: compiler: clang install: brotli wolfssl zstd install_steps: pytest - generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bcee2e7330e2..da7dde328bc1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -313,7 +313,7 @@ jobs: # install: 'mingw-w64-ucrt-x86_64-libssh2' } - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_WOLFSSL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_WOLFSSL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DCURL_DISABLE_HTTPSIG=OFF', install: 'mingw-w64-x86_64-libssh2 mingw-w64-x86_64-wolfssl' } - { name: 'MultiSSL R', type: 'Release', build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun', @@ -686,6 +686,7 @@ jobs: -DCURL_DROP_UNUSED=ON \ -DCURL_WERROR=ON \ -DUSE_LIBIDN2=OFF \ + -DCURL_DISABLE_HTTPSIG=OFF \ ${MATRIX_CONFIG} done if [ -d bld_chkprefill ] && ! diff -u bld/lib/curl_config.h bld_chkprefill/lib/curl_config.h; then diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cf5d8dce410..ec6d1dc36736 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1106,6 +1106,11 @@ if(USE_ECH) endif() endif() +if(NOT CURL_DISABLE_HTTPSIG AND (NOT USE_OPENSSL AND NOT USE_WOLFSSL)) + message(WARNING "HTTPSIG requires OpenSSL or wolfSSL. HTTPSIG support disabled.") + set(CURL_DISABLE_HTTPSIG ON) +endif() + option(USE_SSLS_EXPORT "Enable SSL session import/export (experimental)" OFF) if(USE_SSLS_EXPORT) if(_ssl_enabled) diff --git a/appveyor.sh b/appveyor.sh index e9900191c5d3..0a40dd281236 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -79,6 +79,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then -DCURL_STATIC_CRT=ON \ -DCURL_DROP_UNUSED=ON \ -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBPSL=OFF \ + -DCURL_DISABLE_HTTPSIG=OFF \ ${CMAKE_GENERATE:-} \ ${options} \ || { cat _bld/CMakeFiles/CMake* 2>/dev/null; false; } diff --git a/configure.ac b/configure.ac index 5c83942fbcae..434534d2fb1b 100644 --- a/configure.ac +++ b/configure.ac @@ -4599,7 +4599,6 @@ AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (exp [ case "$enableval" in no) AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_HTTPSIG, 1, [to disable HTTP Message Signatures support]) want_httpsig="no" ;; *) @@ -4608,10 +4607,17 @@ AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (exp ;; esac ], AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_HTTPSIG, 1, [to disable HTTP Message Signatures support]) want_httpsig="no" ) +if test "$want_httpsig" = "yes" && test "$OPENSSL_ENABLED" != "1" && test "$WOLFSSL_ENABLED" != "1"; then + AC_MSG_WARN([HTTPSIG requires OpenSSL or wolfSSL. HTTPSIG support disabled.]) + want_httpsig="no" +fi +if test "$want_httpsig" != "yes"; then + AC_DEFINE(CURL_DISABLE_HTTPSIG, 1, [to disable HTTP Message Signatures support]) +fi + dnl ************************************************************ dnl disable NTLM support dnl From 33a967318c74c681fbb0ceee7f079a5aa2df0b97 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 30 Jul 2026 09:12:28 +0200 Subject: [PATCH 0778/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 69 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index b750535fa1d2..8c709a35d39a 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,15 +4,17 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1500 - Contributors: 3753 + Authors: 1502 + Contributors: 3756 This release includes the following changes: o gssapi: add support for Apple GSS Framework [72] o hardening: add API guards [64] o RFC 9421 HTTP Message Signatures support [108] + o spnego: block NTLM fallback in SPNEGO negotiation [151] o TLS: drop support for TLS-SRP [71] + o vquic: add option to use Apple fast UDP [137] This release includes the following bugfixes: @@ -23,6 +25,7 @@ This release includes the following bugfixes: o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] o cd2nroff: fix backslashes for 4-space indent lines [104] o cd2nroff: stricter checks for asterisks for italics [73] + o cf-ngtcp2-cmn: de-duplicate `ngtcp2_conn_client_new()` call code [156] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] o cfilters: fix event-based connection shutdown [91] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] @@ -38,19 +41,25 @@ This release includes the following bugfixes: o configure: remove double check for GnuTLS [21] o configure: set ldap lib to no by default for non-finds [18] o conncache: apply multi limits to transfers using a shared pool [41] + o conncache: conn upkeep/alive: move and enhance [152] o conncache: connection alive checks intervals [20] o connect: connection close tweaks [112] o content_encoding: give a clear error on multi-member gzip [46] o CREDENTIALS.md: remove comment about empty user/pass [50] o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] + o curl_gssapi: document/update feature availability [145] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] + o dnsd: fix bounds check in `read_https_alpn_part()` [143] + o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gopher: reject CR and LF in the selector [1] + o h2: bootstrap max streams from multi handle if in use [132] + o HISTORY: add when c-ares support was introduced (2004) o hostip: only cache negative resolves for authoritative answers [16] o http: avoid length underflow in Curl_compareheader [78] o http: fix non-tunneling proxy hostname use [116] @@ -60,8 +69,10 @@ This release includes the following bugfixes: o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] + o ldap: support empty username and password [106] o ldap: support insecure mode for Windows native LDAP [3] o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] + o lib2405: adjust for non-threaded builds [149] o lib: add "Curl_" prefix to two global functions [84] o lib: add multi_wakeup_internal [86] o lib: fix 'ns' -> 'us' in trace messages [57] @@ -81,30 +92,42 @@ This release includes the following bugfixes: o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] + o pytest: update two H3 tests for nghttp3 1.18.0+ [158] o quiche: set the max field section size [100] o runtests: allow comments in `setenv` section, merge sections in test433 [89] o runtests: fix `mode="warn"` tests passing unconditionally, fix test 1752 [66] o runtests: flush cached test parts when (re)loading a file [95] o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] + o schannel: fix error check logic in `get_client_cert()` file reader [144] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] + o scorecard: fix `max_upload` init value in `ul_parallel()` [142] + o scripts/badwords.txt: do not recommend using 'will' in rewrites [141] o scripts: replace/extend `--` with `--end-of-options` in git commands [128] o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] o setopt: error for CURLOPT_SHARE when easy handle is used [68] o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] o spacecheck: cap number of lines per file [111] + o src: safely clear certain buffers [125] o ssls: fix potential memory leak on import [96] o sws: allow connection-monitor to log all disconnects [2] + o sws: log the exact closing reason better, to help debugging tests [85] o terminal: Enhance terminal size detection for multiple outputs [115] o test 1560: test RFC4291 style IPv6 IPv4-mapped addresses [40] o test1560: allow to build and run without LDAP support [109] o test798: force IPv4 to avoid cross-runner port aliasing [97] o test: adjust test_06_13 for 0100::/64 being blackholed [13] + o tests: address mutable class vars and naive datetime in Python code + o tests: change whitespace and comments in Python test code o tests: fix the FTP check for unexpected RST [117] o tests: fix type promotion on 32-bit arches in http test code [88] + o tests: improve exception handling in Python test code o tests: remove test1701 [58] + o tests: simplify by removing unneeded Python code o tests: skip test 311 for wolfSSL 5.9.2 [63] + o tests: target Python 3.8 as the minimum Python version + o tests: use simpler constructions in Python code o thrdpool: retry failed thread starts while items wait [62] o tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) [102] o tidy-up: drop redundant includes [110] @@ -114,22 +137,29 @@ This release includes the following bugfixes: o tool: do not flush on out-null [38] o tool: fix memory use in parallel mode [59] o tool: init progress bar on demand [39] + o tool: remove duplicate setopts [94] o tool_cb_hdr: de-duplicate filename setter [24] o tool_cb_prg: avoid integer overflows [93] o tool_doswin: add stdin relay auth [130] o tool_operate: remove call to abort() [23] o tool_xattr: add support for Windows alternate data stream [129] + o typecheck-gcc: allow passing `char[]` as callback data [153] o uint-spbset: reused empty chunks [67] o unit3214: fix to pass on systems with >=128-bit pointers [107] o url: reject control codes in credentials set via CURLOPT [70] o urlapi: allow URLs to not have userauth (hostname) [92] + o urlapi: clear password buffer on error path [121] o urlapi: do not keep an internal port string [31] + o urlapi: improved return codes [148] o urlapi: preserve empty markers in relative URLs [61] o vms: fix symbol typo and missing closing quotes in `config_h.com` [124] o vquic: add Curl_ prefix to some global functions [76] o vquic: initialize new callback slot for nghttp3 v1.18.0+ [87] + o vquic: silence `-Wmissing-field-initializers` for nghttp3/ngtcp2 callback tables [159] + o vquic: use ngtcp2 v1.25.0 new close2 callback [154] o vssh: keyfile use cleanups [83] o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] + o websocket: pause writing and meta data fix [135] o wolfssl: fix build for wolfssl without bio chain support [75] o ws: pause/unpause write handling [55] @@ -154,16 +184,18 @@ advice from friends like these: 11soda11, AlanKingPL, Alb3e3, Alhuda Khan, Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, - Carlos Henrique Lima Melara, Christian Ullrich, Christoph Reiter, - Collin Funk, Dan Fandrich, Daniel Stenberg, dependabot[bot], + Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, + Christoph Reiter, claudex on github, Collin Funk, Dan Fandrich, + Daniel Gustafsson, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, - Keng-Yu Lin, Laurent Sabourin, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, + Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, Laurent Sabourin, + Matthew John Cheetham, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, Ray Satiro, renovate[bot], RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, stze on hackerone, - Viktor Szakats, xmoezzz on github - (44 contributors) + Viktor Szakats, xmoezzz on github, Yoshiro Yoneya + (51 contributors) References to bug reports and discussions on issues: @@ -249,6 +281,7 @@ References to bug reports and discussions on issues: [82] = https://curl.se/bug/?i=22248 [83] = https://curl.se/bug/?i=22243 [84] = https://curl.se/bug/?i=22245 + [85] = https://curl.se/bug/?i=22431 [86] = https://curl.se/bug/?i=22272 [87] = https://curl.se/bug/?i=22399 [88] = https://curl.se/bug/?i=22210 @@ -257,6 +290,7 @@ References to bug reports and discussions on issues: [91] = https://curl.se/bug/?i=22282 [92] = https://curl.se/bug/?i=22279 [93] = https://curl.se/bug/?i=22316 + [94] = https://curl.se/bug/?i=22433 [95] = https://curl.se/bug/?i=22319 [96] = https://curl.se/bug/?i=22323 [97] = https://curl.se/bug/?i=22318 @@ -267,6 +301,7 @@ References to bug reports and discussions on issues: [103] = https://curl.se/bug/?i=22326 [104] = https://curl.se/bug/?i=22393 [105] = https://curl.se/bug/?i=22320 + [106] = https://curl.se/bug/?i=22162 [107] = https://curl.se/bug/?i=22299 [108] = https://curl.se/bug/?i=22386 [109] = https://curl.se/bug/?i=22312 @@ -280,10 +315,30 @@ References to bug reports and discussions on issues: [118] = https://curl.se/bug/?i=22330 [119] = https://curl.se/bug/?i=22371 [120] = https://curl.se/bug/?i=22380 + [121] = https://curl.se/bug/?i=21637 [122] = https://curl.se/bug/?i=22377 [123] = https://curl.se/bug/?i=22376 [124] = https://curl.se/bug/?i=22375 + [125] = https://curl.se/bug/?i=21637 [126] = https://curl.se/bug/?i=22363 + [127] = https://curl.se/bug/?i=22430 [128] = https://curl.se/bug/?i=22369 [129] = https://curl.se/bug/?i=22354 [130] = https://curl.se/bug/?i=21467 + [132] = https://curl.se/bug/?i=22418 + [135] = https://curl.se/bug/?i=22413 + [137] = https://curl.se/bug/?i=22341 + [141] = https://curl.se/bug/?i=22422 + [142] = https://curl.se/bug/?i=22421 + [143] = https://curl.se/bug/?i=22420 + [144] = https://curl.se/bug/?i=22415 + [145] = https://curl.se/bug/?i=22419 + [148] = https://curl.se/bug/?i=22337 + [149] = https://curl.se/bug/?i=22414 + [151] = https://curl.se/bug/?i=21315 + [152] = https://curl.se/bug/?i=21806 + [153] = https://curl.se/bug/?i=22409 + [154] = https://curl.se/bug/?i=22270 + [156] = https://curl.se/bug/?i=22401 + [158] = https://curl.se/bug/?i=22397 + [159] = https://curl.se/bug/?i=22400 From d3e74f127864777de76016a76d547592e756d479 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Jul 2026 00:52:10 +0200 Subject: [PATCH 0779/1062] GHA: set `HOMEBREW_NO_INSTALL_CLEANUP=1` where brew is used To save work unnecessary in CI context, and to reduce log noise. Cherry-picked from #22437 Closes #22440 --- .github/workflows/checksrc.yml | 1 + .github/workflows/codeql.yml | 1 + .github/workflows/configure-vs-cmake.yml | 1 + .github/workflows/distcheck.yml | 1 + .github/workflows/linux.yml | 1 + .github/workflows/macos.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 8baeac288749..a43d2bca6498 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -31,6 +31,7 @@ permissions: {} env: DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' jobs: checksrc: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 16f30b44cca0..42ccc91e4874 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,6 +35,7 @@ permissions: {} env: DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' jobs: gha_python: diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 334c5d52d0a3..f7bc89b3bcb0 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -38,6 +38,7 @@ permissions: {} env: DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' jobs: check-linux: diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index 7235b0a0b39c..1ab2e2d53e7b 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -22,6 +22,7 @@ permissions: {} env: CURL_TEST_MIN: 1500 DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' MAKEFLAGS: -j 5 jobs: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3c8b3832052f..be7ab8a09bab 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,6 +34,7 @@ env: CURL_CI: github CURL_TEST_MIN: 1660 DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 61ded654957c..ac5de2626997 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -46,6 +46,7 @@ env: CURL_CI: github CURL_TEST_MIN: 1750 DO_NOT_TRACK: '1' + HOMEBREW_NO_INSTALL_CLEANUP: '1' MAKEFLAGS: -j 4 LDFLAGS: -w # suppress 'object file was built for newer macOS version than being linked' warnings From ddb22d12c2a3336b33b3e2c9b7fc0da5b893de49 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Jul 2026 23:30:51 +0200 Subject: [PATCH 0780/1062] lib5004: fix memleak on OOM, check all slist append results (httpsig) Detected by torture tests: ``` test 5004...[HTTP RFC 9421 B.2.6: Ed25519 POST with headers (RFC test vector)] 105 functions found, but only fail 25 (23.81%) ** MEMORY FAILURE Leak detected: memory still allocated: 99 bytes At 6000022c9408, there is 36 bytes. allocated by /Users/runner/work/curl/curl/lib/slist.c:87 At 6000039c8e78, there is 31 bytes. allocated by /Users/runner/work/curl/curl/lib/slist.c:87 At 6000037dd688, there is 16 bytes. allocated by /Users/runner/work/curl/curl/lib/slist.c:62 At 6000037dd628, there is 16 bytes. allocated by /Users/runner/work/curl/curl/lib/slist.c:62 LIMIT /Users/runner/work/curl/curl/lib/slist.c:62 malloc reached memlimit 5004: torture FAILED: function number 10 in test. ``` Ref: https://github.com/curl/curl/actions/runs/30497660391/job/90730128599?pr=22437#step:16:2331 Also: - enable HTTPSIG in torture tests. - NULL check all `curl_slist_append()` results. - apply a NULL check to sibling test 5000 also. Co-authored-by: Daniel Stenberg Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 Closes #22437 --- .github/workflows/linux.yml | 4 ++-- tests/libtest/lib5000.c | 4 +++- tests/libtest/lib5004.c | 25 ++++++++++++++++++------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index be7ab8a09bab..fb3a2eae9ad6 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -296,13 +296,13 @@ jobs: install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev tflags: '-t --shallow=25 --min=960 1 to 1000' torture: true - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'openssl torture 2' install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev tflags: '-t --shallow=25 --min=915 1001 to 9999' torture: true - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'openssl i686' install_packages: gcc-14-i686-linux-gnu libssl-dev:i386 libssh2-1-dev:i386 libidn2-dev:i386 libc-ares-dev:i386 zlib1g-dev:i386 diff --git a/tests/libtest/lib5000.c b/tests/libtest/lib5000.c index a0eb92f0aebb..05c5b99ac4fa 100644 --- a/tests/libtest/lib5000.c +++ b/tests/libtest/lib5000.c @@ -50,7 +50,9 @@ static CURLcode test_lib5000(const char *URL) easy_setopt(curl, CURLOPT_HEADER, 0L); easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { - connect_to = curl_slist_append(connect_to, libtest_arg2); + connect_to = curl_slist_append(NULL, libtest_arg2); + if(!connect_to) + goto test_cleanup; } easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); diff --git a/tests/libtest/lib5004.c b/tests/libtest/lib5004.c index 1f6fb1a22399..767bec410a63 100644 --- a/tests/libtest/lib5004.c +++ b/tests/libtest/lib5004.c @@ -34,6 +34,7 @@ static CURLcode test_lib5004(const char *URL) CURLcode result = TEST_ERR_MAJOR_BAD; struct curl_slist *connect_to = NULL; struct curl_slist *headers = NULL; + struct curl_slist *nheaders; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { curl_mfprintf(stderr, "curl_global_init() failed\n"); @@ -57,18 +58,28 @@ static CURLcode test_lib5004(const char *URL) "date: method path authority content-type: content-length:"); easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"hello\": \"world\"}"); - headers = curl_slist_append(headers, - "Date: Tue, 20 Apr 2021 02:07:55 GMT"); - headers = curl_slist_append(headers, - "Content-Type: application/json"); - headers = curl_slist_append(headers, - "Content-Length: 18"); + headers = curl_slist_append(NULL, "Date: Tue, 20 Apr 2021 02:07:55 GMT"); + if(!headers) + goto test_cleanup; + + nheaders = curl_slist_append(headers, "Content-Type: application/json"); + if(!nheaders) + goto test_cleanup; + headers = nheaders; + + nheaders = curl_slist_append(headers, "Content-Length: 18"); + if(!nheaders) + goto test_cleanup; + headers = nheaders; + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); easy_setopt(curl, CURLOPT_HEADER, 0L); easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { - connect_to = curl_slist_append(connect_to, libtest_arg2); + connect_to = curl_slist_append(NULL, libtest_arg2); + if(!connect_to) + goto test_cleanup; } easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); From 83216878e4fdb7748e13d571068b08412c132d66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:37:30 +0000 Subject: [PATCH 0781/1062] GHA: update dependency google/boringssl to v0.20260730.0 Closes #22442 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 11e8a79111f2..bcda476bd5e6 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -39,7 +39,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260728.0 + BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com NETTLE_VERSION: 3.10.2 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fb3a2eae9ad6..86d8a2a93238 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -38,7 +38,7 @@ env: # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com AWSLC_VERSION: 5.1.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260728.0 + BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com FIL_C_VERSION: 0.681 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com From c4013cdb85fcb8fa3f0613b5263a09af21832aef Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 24 Jul 2026 02:44:11 +0200 Subject: [PATCH 0782/1062] tidy-up: comments, messages, formatting - 'null-terminate', sync casing. - add an `#endif` comment. - avoid a few instances of 'will'. - configure: 'aws' -> 'aws-sigv4', where missing. - unfold/fold lines. - update memzero/strzero comments. - uppercase 'CRLF'. Closes #22443 --- README | 2 +- configure.ac | 12 ++++++------ docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md | 3 +-- docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md | 3 +-- docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md | 3 +-- docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md | 3 +-- lib/curl_ed25519.c | 16 ++++++++++++---- lib/curl_gssapi.c | 7 +++---- lib/curlx/base64.c | 4 ++-- lib/curlx/strdup.c | 4 ++-- lib/escape.c | 2 +- lib/ftp.c | 2 +- lib/pop3.c | 2 +- lib/sendf.c | 2 +- lib/smtp.c | 2 +- lib/vauth/ntlm.c | 8 ++++---- projects/OS400/os400sys.c | 2 +- projects/vms/curl_crtl_init.c | 2 +- src/tool_doswin.c | 4 ++-- tests/ftpserver.pl | 6 +++--- tests/negtelnetserver.py | 2 +- tests/server/tftpd.c | 4 ++-- 22 files changed, 49 insertions(+), 46 deletions(-) diff --git a/README b/README index 4ee7e43a2c84..bc04a79dbc5e 100644 --- a/README +++ b/README @@ -41,7 +41,7 @@ GIT git clone https://github.com/curl/curl - (you will get a directory named curl created, filled with the source code) + (you get a directory named curl, filled with the source code) SECURITY PROBLEMS diff --git a/configure.ac b/configure.ac index 434534d2fb1b..5e9f1ac2e47d 100644 --- a/configure.ac +++ b/configure.ac @@ -2153,7 +2153,7 @@ fi case "x$SSL_DISABLED$OPENSSL_ENABLED$GNUTLS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENABLED$SCHANNEL_ENABLED$RUSTLS_ENABLED" in x) - AC_MSG_ERROR([TLS not detected, you will not be able to use HTTPS, FTPS, NTLM and more. + AC_MSG_ERROR([TLS not detected, cannot build with HTTPS, FTPS, NTLM and more. Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl or --with-rustls to address this.]) ;; x1) @@ -4571,16 +4571,16 @@ AS_HELP_STRING([--disable-negotiate-auth],[Disable negotiate authentication]), ) dnl ************************************************************ -dnl disable aws +dnl disable aws-sigv4 dnl -AC_MSG_CHECKING([whether to enable aws sig methods]) +AC_MSG_CHECKING([whether to enable aws-sigv4 methods]) AC_ARG_ENABLE(aws, -AS_HELP_STRING([--enable-aws],[Enable AWS sig support (default)]) -AS_HELP_STRING([--disable-aws],[Disable AWS sig support]), +AS_HELP_STRING([--enable-aws],[Enable aws-sigv4 support (default)]) +AS_HELP_STRING([--disable-aws],[Disable aws-sigv4 support]), [ case "$enableval" in no) AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_AWS, 1, [to disable AWS sig support]) + AC_DEFINE(CURL_DISABLE_AWS, 1, [to disable aws-sigv4 support]) ;; *) AC_MSG_RESULT(yes) diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md index 2501ec33eb01..3fa4da378bd0 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_ALGORITHM.md @@ -68,8 +68,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); - curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, - CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md index 2e2cc4f77ea4..ef56d2f9bb65 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_HEADERS.md @@ -84,8 +84,7 @@ int main(void) headers = curl_slist_append(headers, "User-Agent: MyApp/1.0"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); - curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, - CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md index e81378f9cc88..1f03de2c26e5 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEY.md @@ -69,8 +69,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); - curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, - CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md index d921db89a4ba..43e0c087ea23 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md +++ b/docs/libcurl/opts/CURLOPT_HTTPSIG_KEYID.md @@ -55,8 +55,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/api"); - curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, - CURLHTTPSIG_ED25519); + curl_easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); curl_easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/lib/curl_ed25519.c b/lib/curl_ed25519.c index 318c70ed2110..5c664d95c6da 100644 --- a/lib/curl_ed25519.c +++ b/lib/curl_ed25519.c @@ -147,8 +147,12 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, const unsigned char *msg, size_t msglen, unsigned char *sig, size_t *siglen) { - (void)key; (void)keylen; (void)msg; (void)msglen; - (void)sig; (void)siglen; + (void)key; + (void)keylen; + (void)msg; + (void)msglen; + (void)sig; + (void)siglen; return CURLE_NOT_BUILT_IN; } @@ -160,8 +164,12 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, const unsigned char *msg, size_t msglen, unsigned char *sig, size_t *siglen) { - (void)key; (void)keylen; (void)msg; (void)msglen; - (void)sig; (void)siglen; + (void)key; + (void)keylen; + (void)msg; + (void)msglen; + (void)sig; + (void)siglen; return CURLE_NOT_BUILT_IN; } diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 354fa00deab0..437f76d04a46 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -437,10 +437,9 @@ static OM_uint32 stub_gss_indicate_mechs( #ifdef HAVE_GSS_SET_NEG_MECHS /* MIT Kerberos 1.8+ (2010-03-02), missing from Apple GSS, GNU GSS */ -static OM_uint32 stub_gss_set_neg_mechs( - OM_uint32 *min, - gss_cred_id_t cred_handle, - const gss_OID_set mech_set) +static OM_uint32 stub_gss_set_neg_mechs(OM_uint32 *min, + gss_cred_id_t cred_handle, + const gss_OID_set mech_set) { struct stub_gss_cred_id_t_desc *cred; size_t i; diff --git a/lib/curlx/base64.c b/lib/curlx/base64.c index 7f51576f5098..519b045cfe30 100644 --- a/lib/curlx/base64.c +++ b/lib/curlx/base64.c @@ -149,7 +149,7 @@ CURLcode curlx_base64_decode(const char *src, pos += 3 - padding; } - /* Null-terminate */ + /* null-terminate */ *pos = '\0'; /* Return the decoded data */ @@ -213,7 +213,7 @@ static CURLcode base64_encode(const char *table64, } } - /* Null-terminate */ + /* null-terminate */ *output = '\0'; /* Return the pointer to the new data (allocated memory) */ diff --git a/lib/curlx/strdup.c b/lib/curlx/strdup.c index 6c967ff296f0..ebc58c23634b 100644 --- a/lib/curlx/strdup.c +++ b/lib/curlx/strdup.c @@ -108,14 +108,14 @@ void curlx_memzero_low(void *buf, size_t size) } #endif -/* Free 'buf' after zeroing its content. */ +/* Fill 'buf' with zeroes. */ void curlx_memzero(void *buf, size_t size) { if(buf) curlx_memzero_low(buf, size); } -/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ +/* Fill 'buf' with zeroes, where 'buf' is null-terminated. */ void curlx_strzero(void *buf) { if(buf) diff --git a/lib/escape.c b/lib/escape.c index 4aff583de1b2..a1a714f8f592 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -196,7 +196,7 @@ void curl_free(void *p) * Curl_hexencode() * * Converts binary input to lowercase hex-encoded ASCII output. - * Null-terminated. + * null-terminated. */ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ unsigned char *out, size_t olen) /* output buffer size */ diff --git a/lib/ftp.c b/lib/ftp.c index abcc9d25a967..811e58508b0a 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -3794,7 +3794,7 @@ static CURLcode ftp_done_check_partial(struct Curl_easy *data, (data->state.infilesize != -1) && /* upload with known size */ ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */ (data->state.infilesize != data->req.writebytecount)) || - ((data->set.crlf || data->state.prefer_ascii) && /* maybe crlf conv */ + ((data->set.crlf || data->state.prefer_ascii) && /* maybe CRLF conv */ (data->state.infilesize > data->req.writebytecount)) )) { failf(data, "Uploaded unaligned file size (%" FMT_OFF_T diff --git a/lib/pop3.c b/lib/pop3.c index 071872687cd8..9ebe7ce4bffd 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -1407,7 +1407,7 @@ static const struct SASLproto saslpop3 = { pop3_continue_auth, /* Send authentication continuation */ pop3_cancel_auth, /* Send authentication cancellation */ pop3_get_message, /* Get SASL response message */ - 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - CRLF */ '*', /* Code received when continuation is expected */ '+', /* Code to receive upon authentication success */ SASL_AUTH_DEFAULT, /* Default mechanisms */ diff --git a/lib/sendf.c b/lib/sendf.c index a5aad668f5e0..78cda23cfec6 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -1128,7 +1128,7 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data, data->req.reader.stack = r; clen = r->crt->total_length(data, r); - /* if we do not have 0 length init, and crlf conversion is wanted, + /* if we do not have 0 length init, and CRLF conversion is wanted, * add the reader for it */ if(clen && (data->set.crlf #ifdef CURL_PREFER_LF_LINEENDS diff --git a/lib/smtp.c b/lib/smtp.c index a37a5733811e..616fa8c00f05 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -1657,7 +1657,7 @@ static const struct SASLproto saslsmtp = { smtp_continue_auth, /* Send authentication continuation */ smtp_cancel_auth, /* Cancel authentication */ smtp_get_message, /* Get SASL response message */ - 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - CRLF */ 334, /* Code received when continuation is expected */ 235, /* Code to receive upon authentication success */ SASL_AUTH_DEFAULT, /* Default mechanisms */ diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 0be26b3cac27..3f1ffb50ce7a 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -341,7 +341,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, /* NTLM type-2 message structure: Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + 0 NTLMSSP Signature null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x02000000) 12 Target Name security buffer @@ -430,7 +430,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* NTLM type-1 message structure: Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + 0 NTLMSSP Signature null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x01000000) 12 Flags long @@ -549,13 +549,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* NTLM type-3 message structure: Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" + 0 NTLMSSP Signature null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x03000000) 12 LM/LMv2 Response security buffer 20 NTLM/NTLMv2 Response security buffer 28 Target Name security buffer - 36 username security buffer + 36 Username security buffer 44 Workstation Name security buffer (52) Session Key security buffer (*) (60) Flags long (*) diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index 207eb297bb79..41f0f670e70b 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -1035,4 +1035,4 @@ int Curl_os400_inflateEnd(z_streamp strm) return ret; } -#endif +#endif /* HAVE_LIBZ */ diff --git a/projects/vms/curl_crtl_init.c b/projects/vms/curl_crtl_init.c index 9c8e46761ef9..09c46dd03c38 100644 --- a/projects/vms/curl_crtl_init.c +++ b/projects/vms/curl_crtl_init.c @@ -126,7 +126,7 @@ static int sys_trnlnm(const char *logname, char *value, int value_len) if($VMS_STATUS_SUCCESS(status)) { - /* Null-terminate and return the string */ + /* null-terminate and return the string */ /*--------------------------------------*/ value[result] = '\0'; } diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 52eb69bd695e..aacab5f75d1d 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -710,8 +710,8 @@ struct win_thread_data { /* This is the listen socket for the thread. It is closed after the first connection. */ curl_socket_t socket_l; - /* This is the random number which the background thread will use to verify - * the peer. */ + /* This is the random number which the background thread uses to verify + the peer. */ uint64_t expected_auth_val; }; diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 958160646f29..23278e6de665 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -2574,8 +2574,8 @@ sub PASV_ftp { sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\r\n", $pasvport); } - logmsg "Client has been notified that DATA conn ". - "will be accepted on port $pasvport\n"; + logmsg "Client has been notified that upcoming DATA connection ". + "is awaited on port $pasvport\n"; if($nodataconn) { my $str = nodataconn_str(); @@ -2765,7 +2765,7 @@ sub datasockf_state { } elsif($state eq 'PASSIVE_NODATACONN') { # Data sockfilter bound port without listening, - # client will not be able to establish data connection. + # client is unable to establish data connection. $datasockf_state = $state; $datasockf_mode = 'passive'; $datasockf_runs = 'yes'; diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index 8d074f5046e6..44ae2eeb75c8 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -126,7 +126,7 @@ def recv(self, bytes): """ buffer = bytearray() - # If we keep receiving negotiation sequences, we will not fill the buffer. + # If we keep receiving negotiation sequences, we do not fill the buffer. # Keep looping while we can, and until we have something to give back # to the caller. while len(buffer) == 0: diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 8f2ab642cdff..8144f81be887 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -186,7 +186,7 @@ static struct bf bfs[2]; static int nextone; /* index of next buffer to use */ static int current; /* index of buffer in use */ - /* control flags for crlf conversions */ + /* control flags for CRLF conversions */ static int newline = 0; /* fillbuf: in middle of newline expansion */ static int prevchar = -1; /* putbuf: previous char (cr check) */ @@ -296,7 +296,7 @@ static void nak(int error) */ static struct tftphdr *rw_init(int x) { - newline = 0; /* init crlf flag */ + newline = 0; /* init CRLF flag */ prevchar = -1; bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ current = 0; From b84838073c8d3beb0e870fb4e5e346186a2ecdc0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Jul 2026 00:11:21 +0200 Subject: [PATCH 0783/1062] tidy-up: minor code fixes and improvements - schannel: drop redundant parentheses. - os400sys: drop redundant includes. Follow-up to ebc5212dacd3db8f5315b5a2d676415848e936d5 #22374 - pytest: replace `()` with `[]` to match rest of tests. - libtests: constify some local pointers. - libtests: drop redundant `(long)` casts. - lib650: use `CURL_CSTRLEN()`. Follow-up to 59dc2bbe07c3b5889e0b380e5fa384d338d9d24e #22424 Closes #22444 --- lib/vtls/schannel.c | 4 ++-- projects/OS400/os400sys.c | 2 -- tests/http/test_02_download.py | 2 +- tests/http/test_21_resolve.py | 2 +- tests/libtest/lib1549.c | 2 +- tests/libtest/lib1582.c | 2 +- tests/libtest/lib3102.c | 2 +- tests/libtest/lib3207.c | 2 +- tests/libtest/lib5000.c | 2 +- tests/libtest/lib5004.c | 2 +- tests/libtest/lib650.c | 2 +- 11 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 583c87222da0..88d7ccec88f0 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -921,7 +921,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* The first four bytes is an unsigned int indicating number of bytes of data in the rest of the buffer. */ - extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); + extension_len = (unsigned int *)(void *)&alpn_buffer[cur]; cur += (int)sizeof(unsigned int); /* The next four bytes are an indicator that this buffer contains @@ -932,7 +932,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* The next two bytes is an unsigned short indicating the number of bytes used to list the preferred protocols. */ - list_len = (unsigned short *)(void *)(&alpn_buffer[cur]); + list_len = (unsigned short *)(void *)&alpn_buffer[cur]; cur += (int)sizeof(unsigned short); list_start_index = cur; diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index 41f0f670e70b..bea84500629d 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -28,8 +28,6 @@ #include #include "config-os400.h" /* Not curl_setup.h: we only need some defines. */ -#include -#include #include #include diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index 940245920d47..e556c014b821 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -718,7 +718,7 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): # or destroys the connection with internal error # ERR_QPACK_HEADER_TOO_LARGE, # depending on nghttp3 version and payload size - assert r.exit_code in (0, 56), f'expected exit code 0 or 56, '\ + assert r.exit_code in [0, 56], f'expected exit code 0 or 56, '\ f'got {r.exit_code}\n{r.dump_logs()}' if r.exit_code == 0: r.check_response(http_status=431) diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index a287cfdf8f9e..ec15de7a9c19 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -298,7 +298,7 @@ def test_21_16_dnsd_link_local(self, env: Env, httpd, dnsd): '--connect-timeout', '1' ]) # should fail with CURLE_OPERATION_TIMEOUT or COULDNT_CONNECT - assert r.exit_code in (7, 28), f'{r.dump_logs()}' + assert r.exit_code in [7, 28], f'{r.dump_logs()}' af_unspec_resolves = [ line for line in r.trace_lines if re.match(r'.* \[DNS] re-queueing query .+ for AF_UNSPEC resolve', line) diff --git a/tests/libtest/lib1549.c b/tests/libtest/lib1549.c index e004546f58fe..04510526bb5c 100644 --- a/tests/libtest/lib1549.c +++ b/tests/libtest/lib1549.c @@ -55,7 +55,7 @@ static CURLcode test_lib1549(const char *URL) result = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); if(!result && cookies) { /* a linked list of cookies in cookie file format */ - struct curl_slist *each = cookies; + const struct curl_slist *each = cookies; while(each) { curl_mprintf("%s\n", each->data); each = each->next; diff --git a/tests/libtest/lib1582.c b/tests/libtest/lib1582.c index ec9624be940b..81f8664ef308 100644 --- a/tests/libtest/lib1582.c +++ b/tests/libtest/lib1582.c @@ -43,7 +43,7 @@ static CURLcode test_lib1582(const char *URL) easy_setopt(curl, CURLOPT_HEADER, 1L); easy_setopt(curl, CURLOPT_VERBOSE, 1L); easy_setopt(curl, CURLOPT_URL, URL); - easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_NEGOTIATE); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE); easy_setopt(curl, CURLOPT_USERPWD, ":"); easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index 312d5e600e00..b3af3981a168 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -38,7 +38,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) /* Enumerate each certificate in the chain */ for(cert = 0; cert < cert_info->num_of_certs; cert++) { - struct curl_slist *slist = cert_info->certinfo[cert]; + const struct curl_slist *slist = cert_info->certinfo[cert]; const char *issuer = NULL; const char *subject = NULL; diff --git a/tests/libtest/lib3207.c b/tests/libtest/lib3207.c index 77df591710c8..f28085c8652d 100644 --- a/tests/libtest/lib3207.c +++ b/tests/libtest/lib3207.c @@ -197,7 +197,7 @@ static CURLcode test_lib3207(const char *URL) result = ctx[i].result; } else { - struct curl_slist *item = ctx[i].contents; + const struct curl_slist *item = ctx[i].contents; while(item) { curl_mprintf("%s", item->data); item = item->next; diff --git a/tests/libtest/lib5000.c b/tests/libtest/lib5000.c index 05c5b99ac4fa..511b26c8e481 100644 --- a/tests/libtest/lib5000.c +++ b/tests/libtest/lib5000.c @@ -42,7 +42,7 @@ static CURLcode test_lib5000(const char *URL) } easy_setopt(curl, CURLOPT_VERBOSE, 1L); - easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, (long)CURLHTTPSIG_ED25519); + easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/tests/libtest/lib5004.c b/tests/libtest/lib5004.c index 767bec410a63..08ab3b6f384b 100644 --- a/tests/libtest/lib5004.c +++ b/tests/libtest/lib5004.c @@ -49,7 +49,7 @@ static CURLcode test_lib5004(const char *URL) } easy_setopt(curl, CURLOPT_VERBOSE, 1L); - easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, (long)CURLHTTPSIG_ED25519); + easy_setopt(curl, CURLOPT_HTTPSIG_ALGORITHM, CURLHTTPSIG_ED25519); easy_setopt(curl, CURLOPT_HTTPSIG_KEY, "9f8362f87a484a954e6e740c5b4c0e84" "229139a20aa8ab56ff66586f6a7d29c5"); diff --git a/tests/libtest/lib650.c b/tests/libtest/lib650.c index ac56c4f35bc4..04b288725fc4 100644 --- a/tests/libtest/lib650.c +++ b/tests/libtest/lib650.c @@ -92,7 +92,7 @@ static CURLcode test_lib650(const char *URL) formrc = curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, testname, - CURLFORM_NAMELENGTH, (long)(sizeof(testname) - 2), + CURLFORM_NAMELENGTH, (long)CURL_CSTRLEN(testname) - 1, CURLFORM_ARRAY, formarray, CURLFORM_FILENAME, "remotefile.txt", CURLFORM_END); From 1d7b8e6c29e22c88750fb53a01e30be03104a3fd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 25 Jul 2026 16:50:51 +0200 Subject: [PATCH 0784/1062] gitignore: maintenance updates - docs/cmdline-opts/.gitignore: also ignore `manpage.tmp.*`. Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 - ./.gitignore: drop obsolete entries. Follow-up to 4f38db1d28a971f938400f558e968fdffb9233a0 #1923 Closes #22445 --- .gitignore | 3 --- docs/cmdline-opts/.gitignore | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0ac100295697..84d2f3b4fcdf 100644 --- a/.gitignore +++ b/.gitignore @@ -66,9 +66,6 @@ test-driver stamp-h* scripts/_curl scripts/curl.fish -curl_fuzzer -curl_fuzzer_seed_corpus.zip -libstandaloneengine.a tests/string tests/config tests/ech-log/ diff --git a/docs/cmdline-opts/.gitignore b/docs/cmdline-opts/.gitignore index b903c3bf7781..4af021b28799 100644 --- a/docs/cmdline-opts/.gitignore +++ b/docs/cmdline-opts/.gitignore @@ -4,3 +4,4 @@ curl.txt asciipage.tmp.* +manpage.tmp.* From 5eb2f0757ebe9fa0328acc2bdb4df155a40942cf Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 30 Jul 2026 13:47:03 +0200 Subject: [PATCH 0785/1062] curl: help category cleanups - add 'mqtt' as a category - add more protocol categories to several options - make --data worded better to also cover MQTT Closes #22447 --- docs/cmdline-opts/abstract-unix-socket.md | 2 +- docs/cmdline-opts/basic.md | 2 +- docs/cmdline-opts/compressed-ssh.md | 2 +- docs/cmdline-opts/data.md | 17 +++++--- docs/cmdline-opts/form-escape.md | 2 +- docs/cmdline-opts/key.md | 2 +- docs/cmdline-opts/knownhosts.md | 2 +- docs/cmdline-opts/max-filesize.md | 2 +- docs/cmdline-opts/oauth2-bearer.md | 2 +- docs/cmdline-opts/pass.md | 2 +- docs/cmdline-opts/proxy-header.md | 2 +- docs/cmdline-opts/remote-header-name.md | 2 +- docs/cmdline-opts/sasl-authzid.md | 2 +- docs/cmdline-opts/sasl-ir.md | 2 +- docs/cmdline-opts/show-headers.md | 2 +- docs/cmdline-opts/ssl-reqd.md | 2 +- docs/cmdline-opts/ssl.md | 2 +- docs/cmdline-opts/unix-socket.md | 2 +- docs/cmdline-opts/upload-file.md | 2 +- docs/cmdline-opts/upload-flags.md | 2 +- scripts/managen | 2 +- src/tool_help.c | 1 + src/tool_help.h | 1 + src/tool_listhelp.c | 48 +++++++++++++---------- tests/data/data1461.txt | 6 +-- tests/data/test1462 | 1 + 26 files changed, 64 insertions(+), 50 deletions(-) diff --git a/docs/cmdline-opts/abstract-unix-socket.md b/docs/cmdline-opts/abstract-unix-socket.md index b1b6100e1611..40ecce5cf699 100644 --- a/docs/cmdline-opts/abstract-unix-socket.md +++ b/docs/cmdline-opts/abstract-unix-socket.md @@ -6,7 +6,7 @@ Arg: Help: Connect via abstract Unix domain socket Added: 7.53.0 Protocols: HTTP -Category: connection +Category: connection http Multi: single See-also: - unix-socket diff --git a/docs/cmdline-opts/basic.md b/docs/cmdline-opts/basic.md index 1c372fbc7138..4285e88a42b9 100644 --- a/docs/cmdline-opts/basic.md +++ b/docs/cmdline-opts/basic.md @@ -4,7 +4,7 @@ SPDX-License-Identifier: curl Long: basic Help: HTTP Basic Authentication Protocols: HTTP -Category: auth +Category: auth http Added: 7.10.6 Multi: boolean See-also: diff --git a/docs/cmdline-opts/compressed-ssh.md b/docs/cmdline-opts/compressed-ssh.md index 07d3981b484e..2c1fda9bdc4e 100644 --- a/docs/cmdline-opts/compressed-ssh.md +++ b/docs/cmdline-opts/compressed-ssh.md @@ -5,7 +5,7 @@ Long: compressed-ssh Help: Enable SSH compression Protocols: SCP SFTP Added: 7.56.0 -Category: scp ssh +Category: scp sftp ssh Multi: boolean See-also: - compressed diff --git a/docs/cmdline-opts/data.md b/docs/cmdline-opts/data.md index d95b02395369..6faaad628dde 100644 --- a/docs/cmdline-opts/data.md +++ b/docs/cmdline-opts/data.md @@ -4,16 +4,17 @@ SPDX-License-Identifier: curl Long: data Short: d Arg: -Help: HTTP POST data +Help: Post data Protocols: HTTP MQTT Mutexed: form head upload-file -Category: important http post upload +Category: important http post upload mqtt Added: 4.0 Multi: append See-also: - data-binary - data-urlencode - data-raw + - form Example: - -d "name=curl" $URL - -d "name=curl" -d "tool=cmdline" $URL @@ -22,10 +23,14 @@ Example: # `--data` -Send the specified data in a POST request to the HTTP server, in the same way -that a browser does when a user has filled in an HTML form and presses the -submit button. This option makes curl pass the data to the server using the -content-type application/x-www-form-urlencoded. Compared to --form. +Send the specified data to a server. + +For HTTP(S), this is done with the POST method in the same way that a browser +does when a user has filled in an HTML form and presses the submit button. +This option makes curl pass the data to the server using the content-type +application/x-www-form-urlencoded. + +For MQTT, the data is sent as a PUBLISH. --data-raw is almost the same but does not have a special interpretation of the @ character. To post data purely binary, you should instead use the diff --git a/docs/cmdline-opts/form-escape.md b/docs/cmdline-opts/form-escape.md index 7cf1cb7403db..d9f4ad4aa16e 100644 --- a/docs/cmdline-opts/form-escape.md +++ b/docs/cmdline-opts/form-escape.md @@ -5,7 +5,7 @@ Long: form-escape Help: Escape form fields using backslash Protocols: HTTP IMAP SMTP Added: 7.81.0 -Category: http upload post +Category: http upload post imap smtp Multi: single See-also: - form diff --git a/docs/cmdline-opts/key.md b/docs/cmdline-opts/key.md index cc4bc73fa5b3..c0d6f804c412 100644 --- a/docs/cmdline-opts/key.md +++ b/docs/cmdline-opts/key.md @@ -5,7 +5,7 @@ Long: key Arg: Protocols: TLS SCP SFTP Help: Private key filename -Category: tls ssh +Category: tls sftp scp ssh Added: 7.9.3 Multi: single See-also: diff --git a/docs/cmdline-opts/knownhosts.md b/docs/cmdline-opts/knownhosts.md index 4b6386dd24ec..cb99fb910ab4 100644 --- a/docs/cmdline-opts/knownhosts.md +++ b/docs/cmdline-opts/knownhosts.md @@ -5,7 +5,7 @@ Long: knownhosts Arg: Protocols: SCP SFTP Help: Specify knownhosts path -Category: ssh +Category: sftp scp ssh Added: 8.17.0 Multi: single See-also: diff --git a/docs/cmdline-opts/max-filesize.md b/docs/cmdline-opts/max-filesize.md index 02b2293c56b2..a3adcdebf474 100644 --- a/docs/cmdline-opts/max-filesize.md +++ b/docs/cmdline-opts/max-filesize.md @@ -5,7 +5,7 @@ Long: max-filesize Arg: Help: Maximum file size to download Protocols: FTP HTTP MQTT -Category: connection +Category: connection ftp http mqtt Added: 7.10.8 Multi: single See-also: diff --git a/docs/cmdline-opts/oauth2-bearer.md b/docs/cmdline-opts/oauth2-bearer.md index b66477fc70c3..cf260e1b51c7 100644 --- a/docs/cmdline-opts/oauth2-bearer.md +++ b/docs/cmdline-opts/oauth2-bearer.md @@ -5,7 +5,7 @@ Long: oauth2-bearer Help: OAuth 2 Bearer Token Arg: Protocols: IMAP LDAP POP3 SMTP HTTP -Category: auth imap pop3 smtp ldap +Category: auth imap pop3 smtp ldap http Added: 7.33.0 Multi: single See-also: diff --git a/docs/cmdline-opts/pass.md b/docs/cmdline-opts/pass.md index 79c2f8738a13..39b60ebfd93c 100644 --- a/docs/cmdline-opts/pass.md +++ b/docs/cmdline-opts/pass.md @@ -5,7 +5,7 @@ Long: pass Arg: Help: Passphrase for the private key Protocols: TLS SCP SFTP -Category: ssh tls auth +Category: tls sftp scp ssh auth Added: 7.9.3 Multi: single See-also: diff --git a/docs/cmdline-opts/proxy-header.md b/docs/cmdline-opts/proxy-header.md index 459eb462f683..3cc179657b04 100644 --- a/docs/cmdline-opts/proxy-header.md +++ b/docs/cmdline-opts/proxy-header.md @@ -6,7 +6,7 @@ Arg:
Help: Pass custom header(s) to proxy Protocols: HTTP Added: 7.37.0 -Category: proxy +Category: proxy http Multi: append See-also: - proxy diff --git a/docs/cmdline-opts/remote-header-name.md b/docs/cmdline-opts/remote-header-name.md index 52ae98b01cff..5faddaa79ad6 100644 --- a/docs/cmdline-opts/remote-header-name.md +++ b/docs/cmdline-opts/remote-header-name.md @@ -5,7 +5,7 @@ Long: remote-header-name Short: J Protocols: HTTP Help: Use the header-provided filename -Category: output +Category: output http Added: 7.20.0 Multi: boolean See-also: diff --git a/docs/cmdline-opts/sasl-authzid.md b/docs/cmdline-opts/sasl-authzid.md index 4e92a2054152..4b1338866b4f 100644 --- a/docs/cmdline-opts/sasl-authzid.md +++ b/docs/cmdline-opts/sasl-authzid.md @@ -6,7 +6,7 @@ Arg: Help: Identity for SASL PLAIN authentication Protocols: LDAP IMAP POP3 SMTP Added: 7.66.0 -Category: auth +Category: auth imap smtp pop3 ldap Multi: single See-also: - login-options diff --git a/docs/cmdline-opts/sasl-ir.md b/docs/cmdline-opts/sasl-ir.md index 206bf29317a8..49f21c1b5b24 100644 --- a/docs/cmdline-opts/sasl-ir.md +++ b/docs/cmdline-opts/sasl-ir.md @@ -5,7 +5,7 @@ Long: sasl-ir Help: Initial response in SASL authentication Protocols: LDAP IMAP POP3 SMTP Added: 7.31.0 -Category: auth +Category: auth imap pop3 ldap smtp Multi: boolean See-also: - sasl-authzid diff --git a/docs/cmdline-opts/show-headers.md b/docs/cmdline-opts/show-headers.md index 40c5ae790bec..7e181246be86 100644 --- a/docs/cmdline-opts/show-headers.md +++ b/docs/cmdline-opts/show-headers.md @@ -5,7 +5,7 @@ Long: show-headers Short: i Help: Show response headers in output Protocols: HTTP FTP -Category: important verbose output +Category: important verbose output http ftp Added: 4.8 Multi: boolean See-also: diff --git a/docs/cmdline-opts/ssl-reqd.md b/docs/cmdline-opts/ssl-reqd.md index f21c145f5993..c48209676c7d 100644 --- a/docs/cmdline-opts/ssl-reqd.md +++ b/docs/cmdline-opts/ssl-reqd.md @@ -5,7 +5,7 @@ Long: ssl-reqd Help: Require SSL/TLS Protocols: FTP IMAP POP3 SMTP LDAP Added: 7.20.0 -Category: tls imap pop3 smtp ldap +Category: tls imap pop3 smtp ldap ftp Multi: boolean See-also: - ssl diff --git a/docs/cmdline-opts/ssl.md b/docs/cmdline-opts/ssl.md index b2345858091d..9461cda4c72d 100644 --- a/docs/cmdline-opts/ssl.md +++ b/docs/cmdline-opts/ssl.md @@ -5,7 +5,7 @@ Long: ssl Help: Try enabling TLS Protocols: FTP IMAP POP3 SMTP LDAP Added: 7.20.0 -Category: tls imap pop3 smtp ldap +Category: tls imap pop3 smtp ldap ftp Multi: boolean See-also: - ssl-reqd diff --git a/docs/cmdline-opts/unix-socket.md b/docs/cmdline-opts/unix-socket.md index 34cc714f79df..d448ad07721b 100644 --- a/docs/cmdline-opts/unix-socket.md +++ b/docs/cmdline-opts/unix-socket.md @@ -6,7 +6,7 @@ Arg: Help: Connect through this Unix domain socket Added: 7.40.0 Protocols: HTTP -Category: connection +Category: connection http Multi: single See-also: - abstract-unix-socket diff --git a/docs/cmdline-opts/upload-file.md b/docs/cmdline-opts/upload-file.md index 1988d8afb728..030fe96771c9 100644 --- a/docs/cmdline-opts/upload-file.md +++ b/docs/cmdline-opts/upload-file.md @@ -5,7 +5,7 @@ Long: upload-file Short: T Arg: Help: Transfer local FILE to destination -Category: important upload +Category: important upload imap Added: 4.0 Multi: per-URL See-also: diff --git a/docs/cmdline-opts/upload-flags.md b/docs/cmdline-opts/upload-flags.md index 6c014d0895f3..c4321880224e 100644 --- a/docs/cmdline-opts/upload-flags.md +++ b/docs/cmdline-opts/upload-flags.md @@ -5,7 +5,7 @@ Long: upload-flags Arg: Help: IMAP upload behavior Protocols: IMAP -Category: curl output +Category: curl output imap upload Added: 8.13.0 Multi: single See-also: diff --git a/scripts/managen b/scripts/managen index 4e21474ea021..9eececee9aa2 100755 --- a/scripts/managen +++ b/scripts/managen @@ -1127,7 +1127,7 @@ HEAD $bitmask .= ' | '; } } - $bitmask =~ s/(?=.{76}).{1,76}\|/$&\n /g; + $bitmask =~ s/(?=.{75}).{1,75}\|/$&\n /g; my $arg = $arglong{$long}; if($arg) { $opt .= " $arg"; diff --git a/src/tool_help.c b/src/tool_help.c index c9d76d5d2072..597a6a554d3f 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -51,6 +51,7 @@ static const struct category_descriptors categories[] = { { "http", "HTTP and HTTPS protocol", CURLHELP_HTTP }, { "imap", "IMAP protocol", CURLHELP_IMAP }, { "ldap", "LDAP protocol", CURLHELP_LDAP }, + { "mqtt", "MQTT protocol", CURLHELP_MQTT }, { "output", "File system output", CURLHELP_OUTPUT }, { "pop3", "POP3 protocol", CURLHELP_POP3 }, { "post", "HTTP POST specific", CURLHELP_POST }, diff --git a/src/tool_help.h b/src/tool_help.h index 6a2ecdb0622e..d8562255d3c7 100644 --- a/src/tool_help.h +++ b/src/tool_help.h @@ -85,6 +85,7 @@ struct helptxt { #define CURLHELP_TLS (1 << 23) #define CURLHELP_UPLOAD (1 << 24) #define CURLHELP_VERBOSE (1 << 25) +#define CURLHELP_MQTT (1 << 26) #define CURLHELP_ALL 0xfffffffU diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 6232fa8b5270..b6d3ddfd1de9 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -36,7 +36,7 @@ const struct helptxt helptext[] = { { " --abstract-unix-socket ", "Connect via abstract Unix domain socket", - CURLHELP_CONNECTION }, + CURLHELP_CONNECTION | CURLHELP_HTTP }, { " --alt-svc ", "Enable alt-svc with this cache file", CURLHELP_HTTP }, @@ -51,7 +51,7 @@ const struct helptxt helptext[] = { CURLHELP_AUTH | CURLHELP_HTTP }, { " --basic", "HTTP Basic Authentication", - CURLHELP_AUTH }, + CURLHELP_AUTH | CURLHELP_HTTP }, { " --ca-native", "Load CA certs from the OS", CURLHELP_TLS }, @@ -78,7 +78,7 @@ const struct helptxt helptext[] = { CURLHELP_HTTP }, { " --compressed-ssh", "Enable SSH compression", - CURLHELP_SCP | CURLHELP_SSH }, + CURLHELP_SCP | CURLHELP_SFTP | CURLHELP_SSH }, { "-K, --config ", "Read config from a file", CURLHELP_CURL }, @@ -113,8 +113,9 @@ const struct helptxt helptext[] = { "(EC) TLS key exchange algorithms to request", CURLHELP_TLS }, { "-d, --data ", - "HTTP POST data", - CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD }, + "Post data", + CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD | + CURLHELP_MQTT }, { " --data-ascii ", "HTTP POST ASCII data", CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD }, @@ -211,7 +212,8 @@ const struct helptxt helptext[] = { CURLHELP_SMTP }, { " --form-escape", "Escape form fields using backslash", - CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST }, + CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_IMAP | + CURLHELP_SMTP }, { " --form-string ", "Specify multipart MIME data", CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_SMTP | @@ -360,13 +362,13 @@ const struct helptxt helptext[] = { CURLHELP_CONNECTION | CURLHELP_TIMEOUT }, { " --key ", "Private key filename", - CURLHELP_TLS | CURLHELP_SSH }, + CURLHELP_TLS | CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH }, { " --key-type ", "Private key file type (DER/PEM/ENG)", CURLHELP_TLS }, { " --knownhosts ", "Specify knownhosts path", - CURLHELP_SSH }, + CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH }, { " --krb ", "Enable Kerberos with security ", CURLHELP_DEPRECATED }, @@ -409,7 +411,7 @@ const struct helptxt helptext[] = { CURLHELP_CURL }, { " --max-filesize ", "Maximum file size to download", - CURLHELP_CONNECTION }, + CURLHELP_CONNECTION | CURLHELP_FTP | CURLHELP_HTTP | CURLHELP_MQTT }, { " --max-redirs ", "Maximum number of redirects allowed", CURLHELP_HTTP }, @@ -470,7 +472,7 @@ const struct helptxt helptext[] = { { " --oauth2-bearer ", "OAuth 2 Bearer Token", CURLHELP_AUTH | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP }, + CURLHELP_LDAP | CURLHELP_HTTP }, { " --out-null", "Discard response data into the void", CURLHELP_OUTPUT }, @@ -494,7 +496,8 @@ const struct helptxt helptext[] = { CURLHELP_CONNECTION | CURLHELP_CURL | CURLHELP_GLOBAL }, { " --pass ", "Passphrase for the private key", - CURLHELP_SSH | CURLHELP_TLS | CURLHELP_AUTH }, + CURLHELP_TLS | CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH | + CURLHELP_AUTH }, { " --path-as-is", "Do not squash .. sequences in URL path", CURLHELP_CURL }, @@ -560,7 +563,7 @@ const struct helptxt helptext[] = { CURLHELP_PROXY | CURLHELP_TLS }, { " --proxy-header
", "Pass custom header(s) to proxy", - CURLHELP_PROXY }, + CURLHELP_PROXY | CURLHELP_HTTP }, { " --proxy-http2", "Use HTTP/2 with HTTPS proxy", CURLHELP_HTTP | CURLHELP_PROXY }, @@ -644,7 +647,7 @@ const struct helptxt helptext[] = { CURLHELP_HTTP }, { "-J, --remote-header-name", "Use the header-provided filename", - CURLHELP_OUTPUT }, + CURLHELP_OUTPUT | CURLHELP_HTTP }, { "-O, --remote-name", "Write output to file named as remote file", CURLHELP_IMPORTANT | CURLHELP_OUTPUT }, @@ -684,10 +687,12 @@ const struct helptxt helptext[] = { CURLHELP_CURL | CURLHELP_TIMEOUT }, { " --sasl-authzid ", "Identity for SASL PLAIN authentication", - CURLHELP_AUTH }, + CURLHELP_AUTH | CURLHELP_IMAP | CURLHELP_SMTP | CURLHELP_POP3 | + CURLHELP_LDAP }, { " --sasl-ir", "Initial response in SASL authentication", - CURLHELP_AUTH }, + CURLHELP_AUTH | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_LDAP | + CURLHELP_SMTP }, { " --service-name ", "SPNEGO service name", CURLHELP_AUTH }, @@ -696,7 +701,8 @@ const struct helptxt helptext[] = { CURLHELP_CURL | CURLHELP_GLOBAL }, { "-i, --show-headers", "Show response headers in output", - CURLHELP_IMPORTANT | CURLHELP_VERBOSE | CURLHELP_OUTPUT }, + CURLHELP_IMPORTANT | CURLHELP_VERBOSE | CURLHELP_OUTPUT | CURLHELP_HTTP | + CURLHELP_FTP }, { " --sigalgs ", "TLS signature algorithms to use", CURLHELP_TLS }, @@ -739,7 +745,7 @@ const struct helptxt helptext[] = { { " --ssl", "Try enabling TLS", CURLHELP_TLS | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP }, + CURLHELP_LDAP | CURLHELP_FTP }, { " --ssl-allow-beast", "Allow security flaw to improve interop", CURLHELP_TLS }, @@ -752,7 +758,7 @@ const struct helptxt helptext[] = { { " --ssl-reqd", "Require SSL/TLS", CURLHELP_TLS | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP }, + CURLHELP_LDAP | CURLHELP_FTP }, { " --ssl-revoke-best-effort", "Ignore missing cert CRL dist points", CURLHELP_TLS }, @@ -845,13 +851,13 @@ const struct helptxt helptext[] = { CURLHELP_VERBOSE | CURLHELP_GLOBAL }, { " --unix-socket ", "Connect through this Unix domain socket", - CURLHELP_CONNECTION }, + CURLHELP_CONNECTION | CURLHELP_HTTP }, { "-T, --upload-file ", "Transfer local FILE to destination", - CURLHELP_IMPORTANT | CURLHELP_UPLOAD }, + CURLHELP_IMPORTANT | CURLHELP_UPLOAD | CURLHELP_IMAP }, { " --upload-flags ", "IMAP upload behavior", - CURLHELP_CURL | CURLHELP_OUTPUT }, + CURLHELP_CURL | CURLHELP_OUTPUT | CURLHELP_IMAP | CURLHELP_UPLOAD }, { " --url ", "URL(s) to work with", CURLHELP_CURL }, diff --git a/tests/data/data1461.txt b/tests/data/data1461.txt index 286be7c34434..5ddb4e1edda0 100644 --- a/tests/data/data1461.txt +++ b/tests/data/data1461.txt @@ -1,5 +1,5 @@ Usage: curl [options...] - -d, --data HTTP POST data + -d, --data Post data -f, --fail Fail fast with no output on HTTP errors -I, --head Show document info only -H, --header
Pass custom header(s) to server @@ -17,7 +17,7 @@ Usage: curl [options...] This is not the full help; this menu is split into categories. Use "--help category" to get an overview of all categories, which are: auth, connection, curl, deprecated, dns, file, ftp, global, http, imap, ldap,%SP -output, pop3, post, proxy, scp, sftp, smtp, ssh, telnet, tftp, timeout, tls,%SP -upload, verbose. +mqtt, output, pop3, post, proxy, scp, sftp, smtp, ssh, telnet, tftp, timeout,%SP +tls, upload, verbose. Use "--help all" to list all options Use "--help [option]" to view documentation for a given option diff --git a/tests/data/test1462 b/tests/data/test1462 index 9e68c6b447f1..240eca4ab2a6 100644 --- a/tests/data/test1462 +++ b/tests/data/test1462 @@ -39,6 +39,7 @@ Unknown category provided, here is a list of all categories: http HTTP and HTTPS protocol imap IMAP protocol ldap LDAP protocol + mqtt MQTT protocol output File system output pop3 POP3 protocol post HTTP POST specific From 6e130eb484bf3d974fbcbc704db051c868e35d7f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 29 Jul 2026 13:11:20 +0200 Subject: [PATCH 0786/1062] apple-fast-udp: fix sendmsg_x partial results When sending with sendmsg_x(), fix handling of last gso chunk being smaller. Handle partial results correctly. Ignore SOCKEMSGSIZE by reporting success which drops PMTUD probes into the void. Closes #22429 --- lib/vquic/vquic.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 384935f87a78..3844f4e88a59 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -179,16 +179,18 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, return CURLE_OK; if(!gsolen || (pktlen < gsolen)) gsolen = pktlen; - n = pktlen / gsolen; + n = (pktlen + gsolen - 1) / gsolen; while(i < n) { - size_t j, batch = CURLMIN(n - i, MSG_X_SNUM); + size_t j, batch = CURLMIN(n - i, MSG_X_SNUM), pkts_sent = 0; for(j = 0; j < batch; ++j) { - msg_iov[j].iov_base = CURL_UNCONST(&pkt[(i + j) * gsolen]); - msg_iov[j].iov_len = gsolen; + const size_t offset = (i + j) * gsolen; + msg_iov[j].iov_base = CURL_UNCONST(pkt + offset); + msg_iov[j].iov_len = CURLMIN(gsolen, pktlen - offset); memset(&mmsg[j], 0, sizeof(mmsg[j])); mmsg[j].msg_iov = &msg_iov[j]; mmsg[j].msg_iovlen = 1; + mmsg[j].msg_datalen = msg_iov[j].iov_len; } #if defined(CURL_HAVE_DIAG) && defined(__APPLE__) @@ -196,27 +198,43 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 && - (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) + (SOCKERRNO == SOCKEINTR)) ; #if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif - if(rc == -1) { + if(rc < 0) { if(SOCK_EAGAIN(SOCKERRNO)) { CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN"); result = sent ? CURLE_OK : CURLE_AGAIN; goto out; } - curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); - failf(data, "QUIC: sendmsg_x() unexpectedly returned %d (errno=%d; %s)", - rc, SOCKERRNO, errstr); - result = CURLE_RECV_ERROR; - goto out; + if(SOCKERRNO != SOCKEMSGSIZE) { + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: sendmsg_x() returned %d (errno=%d; %s)", + rc, SOCKERRNO, errstr); + result = CURLE_SEND_ERROR; + goto out; + } + /* Error was SOCKEMSGSIZE. Network stack does not accept the packet + * length(s). This might be a PMTUD. Just drop it into the void + * and fall through to the success handling. */ + pkts_sent = batch; } + else + pkts_sent = CURLMIN((size_t)rc, batch); + VERBOSE(++calls); - i += batch; - sent += (batch * gsolen); + if(!pkts_sent) { /* no packets of the current batch were sent */ + result = sent ? CURLE_OK : CURLE_AGAIN; + goto out; + } + i += pkts_sent; /* Some have been sent */ + for(j = 0; j < pkts_sent; ++j) + sent += msg_iov[j].iov_len; + if(pkts_sent < batch) + goto out; /* but not all of them */ } out: From decc6090851236ec6b09ce79433283867d4d9746 Mon Sep 17 00:00:00 2001 From: Ramesh Adhikari Date: Thu, 30 Jul 2026 21:59:16 +0530 Subject: [PATCH 0787/1062] h3-proxy: fix NULL deref when non-:status header arrives before :status Closes #22449 --- lib/vquic/cf-ngtcp2-proxy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index ec6de93368af..f5c967c5b1e4 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -431,6 +431,9 @@ static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t stream_id, pctx->tunnel.resp = resp; } else { + if(!pctx->tunnel.resp) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } /* store as an HTTP1-style header */ CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); From cb21a37a68d347e79e06bec648cfa1be44e3944d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Jul 2026 14:20:59 +0200 Subject: [PATCH 0788/1062] build: assume POSIX `select()` is available This change effectively replaces an explicit compile-time #error with a missing prototype error in environments not offering `select()`, and saves curl-compatible systems from performing an explicit feature check. Refs: https://pubs.opengroup.org/onlinepubs/009695399/functions/pselect.html https://linux.die.net/man/2/select Closes #22448 --- CMake/unix-cache.cmake | 1 - CMake/win32-cache.cmake | 1 - CMakeLists.txt | 1 - acinclude.m4 | 53 -------------------------------------- configure.ac | 2 -- lib/config-mac.h | 1 - lib/config-os400.h | 3 --- lib/config-riscos.h | 3 --- lib/config-win32.h | 3 --- lib/curl_config-cmake.h.in | 3 --- lib/curlx/wait.c | 4 --- lib/select.c | 6 +---- 12 files changed, 1 insertion(+), 80 deletions(-) diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 287c3fdf9ef6..4ee28f7eb7ac 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -226,7 +226,6 @@ set(HAVE_REALPATH 1) set(HAVE_RECV 1) set(HAVE_SA_FAMILY_T 1) set(HAVE_SCHED_YIELD 1) -set(HAVE_SELECT 1) set(HAVE_SEND 1) if(APPLE OR CYGWIN OR diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index a684b459b085..87e0879ab2b7 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -128,7 +128,6 @@ set(HAVE_POLL_H 0) set(HAVE_POSIX_STRERROR_R 0) set(HAVE_PWD_H 0) set(HAVE_RECV 1) -set(HAVE_SELECT 1) set(HAVE_SEND 1) set(HAVE_SENDMMSG 0) set(HAVE_SENDMSG 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6d1dc36736..ae1152c9b3f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1640,7 +1640,6 @@ check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) # proto/bsd check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) # proto/bsdsocket.h sys/types.h sys/socket.h check_function_exists("sendmsg" HAVE_SENDMSG) check_function_exists("sendmmsg" HAVE_SENDMMSG) -check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) # proto/bsdsocket.h sys/select.h sys/socket.h check_symbol_exists("memrchr" "string.h" HAVE_MEMRCHR) check_symbol_exists("alarm" "unistd.h" HAVE_ALARM) check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) diff --git a/acinclude.m4 b/acinclude.m4 index 3528b8507e1d..310d680600c3 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -972,59 +972,6 @@ AC_DEFUN([CURL_CHECK_LIBS_CONNECT], [ ]) -dnl CURL_CHECK_FUNC_SELECT -dnl ------------------------------------------------- -dnl Test if the socket select() function is available. - -AC_DEFUN([CURL_CHECK_FUNC_SELECT], [ - AC_REQUIRE([CURL_CHECK_STRUCT_TIMEVAL]) - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) - AC_CHECK_HEADERS(sys/select.h) - - AC_MSG_CHECKING([for select]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #undef inline - #ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #else - #include - #include - #endif - #ifdef HAVE_SYS_TYPES_H - #include - #endif - #include - #ifndef _WIN32 - #ifdef HAVE_SYS_SELECT_H - #include - #elif defined(HAVE_UNISTD_H) - #include - #endif - $curl_includes_bsdsocket - #endif - ]],[[ - select(0, 0, 0, 0, 0); - ]]) - ],[ - AC_MSG_RESULT([yes]) - curl_cv_select="yes" - ],[ - AC_MSG_RESULT([no]) - curl_cv_select="no" - ]) - - if test "$curl_cv_select" = "yes"; then - AC_DEFINE_UNQUOTED(HAVE_SELECT, 1, - [Define to 1 if you have the select function.]) - curl_cv_func_select="yes" - fi -]) - - dnl CURL_VERIFY_RUNTIMELIBS dnl ------------------------------------------------- dnl Verify that the shared libs found so far can be used when running diff --git a/configure.ac b/configure.ac index 5e9f1ac2e47d..a00a2f8aa65c 100644 --- a/configure.ac +++ b/configure.ac @@ -4178,8 +4178,6 @@ esac TYPE_SOCKADDR_STORAGE -CURL_CHECK_FUNC_SELECT - CURL_CHECK_FUNC_RECV CURL_CHECK_FUNC_SEND diff --git a/lib/config-mac.h b/lib/config-mac.h index fd9d3e7b6032..9037dbf5c77f 100644 --- a/lib/config-mac.h +++ b/lib/config-mac.h @@ -52,7 +52,6 @@ #define HAVE_SYS_IOCTL_H 1 #define HAVE_ALARM 1 #define HAVE_UTIME 1 -#define HAVE_SELECT 1 #define HAVE_SOCKET 1 #define HAVE_STRUCT_TIMEVAL 1 diff --git a/lib/config-os400.h b/lib/config-os400.h index 050fd9d7dac6..8ff560c92044 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -142,9 +142,6 @@ /* Define to 1 if symbol `sa_family_t' exists */ #define HAVE_SA_FAMILY_T 1 -/* Define to 1 if you have the select function. */ -#define HAVE_SELECT 1 - /* Define to 1 if you have the send function. */ #define HAVE_SEND 1 diff --git a/lib/config-riscos.h b/lib/config-riscos.h index afff218a76c7..358544fb3cb9 100644 --- a/lib/config-riscos.h +++ b/lib/config-riscos.h @@ -69,9 +69,6 @@ /* Define if you have the header file. */ #define HAVE_NET_IF_H -/* Define if you have the `select' function. */ -#define HAVE_SELECT - /* Define if you have the `signal' function. */ #define HAVE_SIGNAL diff --git a/lib/config-win32.h b/lib/config-win32.h index 1f4f12f433a2..81f1e51ceed7 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -103,9 +103,6 @@ /* Define if you have a working ioctlsocket FIONBIO function. */ #define HAVE_IOCTLSOCKET_FIONBIO 1 -/* Define if you have the select function. */ -#define HAVE_SELECT 1 - /* Define if you have the setlocale function. */ #define HAVE_SETLOCALE 1 diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 26379e88a992..7b0dd9a28236 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -460,9 +460,6 @@ /* Define to 1 if you have the recv function. */ #cmakedefine HAVE_RECV 1 -/* Define to 1 if you have the select function. */ -#cmakedefine HAVE_SELECT 1 - /* Define to 1 if you have the sched_yield function. */ #cmakedefine HAVE_SCHED_YIELD 1 diff --git a/lib/curlx/wait.c b/lib/curlx/wait.c index e50a0f0af697..7610a4ba367c 100644 --- a/lib/curlx/wait.c +++ b/lib/curlx/wait.c @@ -23,10 +23,6 @@ ***************************************************************************/ #include "curl_setup.h" -#ifndef HAVE_SELECT -#error "We cannot compile without select() support." -#endif - #ifdef HAVE_SYS_SELECT_H #include #elif defined(HAVE_UNISTD_H) diff --git a/lib/select.c b/lib/select.c index 281917bd70c4..76b56bc0426b 100644 --- a/lib/select.c +++ b/lib/select.c @@ -23,10 +23,6 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(HAVE_SELECT) && !defined(HAVE_POLL) -#error "We cannot compile without select() or poll() support." -#endif - #ifdef HAVE_SYS_SELECT_H #include #elif defined(HAVE_UNISTD_H) @@ -40,7 +36,7 @@ #include "curlx/timediff.h" #include "curlx/wait.h" -#ifndef HAVE_POLL +#ifndef HAVE_POLL /* use select() */ /* * This is a wrapper around select() to aid in Windows compatibility. A * negative timeout value makes this function wait indefinitely, unless no From ca48bd076dfe108b22137340a9d050bb1c09ec24 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 31 Jul 2026 00:26:37 +0200 Subject: [PATCH 0789/1062] curl_ed25519: tidy-up backend fallback Sync fallback logic with other crypto algos to: - allow falling back to the next backend candidate when wolfSSL does not have ed25519 built in. - de-duplicate fallback code. Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 Closes #22450 --- lib/curl_ed25519.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/curl_ed25519.c b/lib/curl_ed25519.c index 5c664d95c6da..c01be70e74c3 100644 --- a/lib/curl_ed25519.c +++ b/lib/curl_ed25519.c @@ -35,6 +35,11 @@ #include "curl_ed25519.h" +#ifdef USE_WOLFSSL +#include +#include +#endif + #ifdef USE_OPENSSL #include @@ -80,11 +85,9 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, return CURLE_OK; } -#elif defined(USE_WOLFSSL) -#include -#include -#if (defined(HAVE_ED25519) || defined(WOLFSSL_CURVE25519_USE_ED25519)) && \ - defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_SIGN) +#elif defined(USE_WOLFSSL) && \ + (defined(HAVE_ED25519) || defined(WOLFSSL_CURVE25519_USE_ED25519)) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_SIGN) #include #include #include @@ -141,23 +144,6 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, return CURLE_AUTH_ERROR; } -#else /* USE_WOLFSSL but no Ed25519 sign/import in this wolfSSL build */ - -CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, - const unsigned char *msg, size_t msglen, - unsigned char *sig, size_t *siglen) -{ - (void)key; - (void)keylen; - (void)msg; - (void)msglen; - (void)sig; - (void)siglen; - return CURLE_NOT_BUILT_IN; -} - -#endif /* wolfSSL Ed25519 */ - #else /* no Ed25519-capable backend */ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, From e9f66196948e9e29e7cfd1d66357072c8ab1da11 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 31 Jul 2026 01:35:56 +0200 Subject: [PATCH 0790/1062] curl_ed25519: drop unused wolfSSL random generator Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 Closes #22451 --- lib/curl_ed25519.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/curl_ed25519.c b/lib/curl_ed25519.c index c01be70e74c3..cc95a7d526cc 100644 --- a/lib/curl_ed25519.c +++ b/lib/curl_ed25519.c @@ -97,7 +97,6 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, unsigned char *sig, size_t *siglen) { int ret; - WC_RNG rng; ed25519_key edkey; word32 outlen; unsigned char pubkey[ED25519_PUB_KEY_SIZE]; @@ -105,15 +104,9 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, if(keylen != ED25519_KEY_SIZE) return CURLE_BAD_FUNCTION_ARGUMENT; - ret = wc_InitRng(&rng); - if(ret) - return CURLE_AUTH_ERROR; - ret = wc_ed25519_init(&edkey); - if(ret) { - wc_FreeRng(&rng); + if(ret) return CURLE_AUTH_ERROR; - } ret = wc_ed25519_import_private_only(key, ED25519_KEY_SIZE, &edkey); if(ret) @@ -135,12 +128,10 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, *siglen = (size_t)outlen; wc_ed25519_free(&edkey); - wc_FreeRng(&rng); return CURLE_OK; fail: wc_ed25519_free(&edkey); - wc_FreeRng(&rng); return CURLE_AUTH_ERROR; } From 527573490eb2564b3d7c9dd51d8bff963b5d6303 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 31 Jul 2026 12:51:25 +0200 Subject: [PATCH 0791/1062] GHA/http3-linux: enable HTTPSIG in jobs running tests To test HTTPSIG with all supported OpenSSL forks. Follow-up to a55731050e8c3dbea0b96205cf916443118f6acb #22386 #21239 Closes #22453 --- .github/workflows/http3-linux.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index bcda476bd5e6..683c2fdb2dd4 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -448,7 +448,7 @@ jobs: PKG_CONFIG_PATH: /home/runner/awslc/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- -DOPENSSL_ROOT_DIR=/home/runner/awslc/build -DUSE_NGTCP2=ON -DBUILD_SHARED_LIBS=OFF - -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON + -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'boringssl' install_steps: skipall @@ -465,7 +465,7 @@ jobs: /home/runner/nghttp2/build/lib/pkgconfig" generate: >- -DOPENSSL_ROOT_DIR=/home/runner/boringssl/build -DUSE_NGTCP2=ON -DBUILD_SHARED_LIBS=OFF - -DCMAKE_UNITY_BUILD=ON + -DCMAKE_UNITY_BUILD=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'gnutls' install_packages: libp11-kit-dev libssh-dev @@ -500,14 +500,14 @@ jobs: - name: 'libressl' PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'openssl' tflags: '--min=1700' LDFLAGS: -Wl,-rpath,/home/runner/openssl/build/lib PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export --enable-proxy-http3 + --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export --enable-proxy-http3 --enable-httpsig - name: 'openssl' install_steps: skipall @@ -537,7 +537,7 @@ jobs: /home/runner/nghttp2/build/lib/pkgconfig" generate: >- -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DUSE_NGTCP2=ON - -DCURL_DISABLE_LDAP=ON + -DCURL_DISABLE_LDAP=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'quiche' install_steps: skipall @@ -554,7 +554,7 @@ jobs: generate: >- -DOPENSSL_ROOT_DIR=/home/runner/quiche/boringssl -DUSE_QUICHE=ON - -DCURL_CA_FALLBACK=ON + -DCURL_CA_FALLBACK=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'wolfssl' install_packages: libssh2-1-dev @@ -571,7 +571,7 @@ jobs: PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON - -DUSE_ECH=ON + -DUSE_ECH=ON -DCURL_DISABLE_HTTPSIG=OFF steps: - name: 'install prereqs' From d2ea63b17c279b1b70d6310f21b23552248c462d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 14:56:08 +0000 Subject: [PATCH 0792/1062] GHA: bump GitHub Actions and pips - update `actions/checkout` from 7.0.0 to 7.0.1 - update `actions/labeler` from 6.1.0 to 7.0.0 - update `github/codeql-action/analyze` from 4.36.2 to 4.37.3 - update `github/codeql-action/init` from 4.36.2 to 4.37.3 - update `cryptography` from 48.0.1 to 49.0.0 - update `filelock` from 3.29.0 to 3.32.0 - update `impacket` from 0.13.0 to 0.13.1 - update `pytest` from 9.0.3 to 9.1.1 - update `websockets` from 16.0 to 16.1.1 Closes #22458 Closes #22459 --- .github/workflows/checkdocs.yml | 6 +++--- .github/workflows/checksrc.yml | 12 ++++++------ .github/workflows/checkurls.yml | 2 +- .github/workflows/codeql.yml | 12 ++++++------ .github/workflows/configure-vs-cmake.yml | 6 +++--- .github/workflows/curl-for-win.yml | 12 ++++++------ .github/workflows/distcheck.yml | 8 ++++---- .github/workflows/http3-linux.yml | 2 +- .github/workflows/label.yml | 2 +- .github/workflows/linux-old.yml | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/macos.yml | 6 +++--- .github/workflows/non-native.yml | 8 ++++---- .github/workflows/windows.yml | 10 +++++----- tests/http/requirements.txt | 8 ++++---- tests/requirements.txt | 2 +- 16 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 1b1c01915d7c..2222dd9027fb 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -43,7 +43,7 @@ jobs: name: 'proselint' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: name: 'pyspelling' runs-on: ubuntu-26.04 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -124,7 +124,7 @@ jobs: name: 'synopsis, man-examples' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index a43d2bca6498..a5d18356db82 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -38,7 +38,7 @@ jobs: name: 'checksrc' runs-on: ubuntu-slim steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -49,7 +49,7 @@ jobs: name: 'spellcheck, linters, REUSE' runs-on: ubuntu-26.04-arm steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -100,7 +100,7 @@ jobs: name: 'pytype' runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -131,7 +131,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install pmccabe - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -154,7 +154,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install libxml2-utils - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -170,7 +170,7 @@ jobs: timeout-minutes: 2 run: /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/checkurls.yml b/.github/workflows/checkurls.yml index 466d645a7fee..67b81e03ded4 100644 --- a/.github/workflows/checkurls.yml +++ b/.github/workflows/checkurls.yml @@ -30,7 +30,7 @@ jobs: name: 'linkcheck' runs-on: ubuntu-slim steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 42ccc91e4874..330e64404c37 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -45,18 +45,18 @@ jobs: permissions: security-events: write # To create/update security events steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -84,7 +84,7 @@ jobs: /home/linuxbrew/.linuxbrew/bin/brew install ca-certificates || true /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: languages: cpp build-mode: manual @@ -141,4 +141,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index f7bc89b3bcb0..9f296c1c9f18 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -45,7 +45,7 @@ jobs: name: 'Linux' runs-on: ubuntu-latest steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -104,7 +104,7 @@ jobs: - name: 'toolchain versions' run: echo '::group::brew packages installed'; ls -l /opt/homebrew/opt; echo '::endgroup::' - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -157,7 +157,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 6c65e8ca8746..aeb95618edbe 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 5 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' @@ -100,7 +100,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' @@ -131,7 +131,7 @@ jobs: env: CW_JOBS: '4' steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' @@ -152,7 +152,7 @@ jobs: CW_PKG_NODELETE: '1' CW_PKG_FLATTEN: '1' steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' @@ -185,7 +185,7 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 10 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false path: 'curl' diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index 1ab2e2d53e7b..bfa3d15f2534 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -215,7 +215,7 @@ jobs: timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -231,7 +231,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -308,7 +308,7 @@ jobs: printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-macos-universal/CMake.app/Contents/bin/cmake > ~/old-cmake-path.txt fi - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 683c2fdb2dd4..6d107895c1e1 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -770,7 +770,7 @@ jobs: # lib dir # /home/runner/quiche/boringssl/lib - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 3922b8b7dfc0..fa06c826ac5a 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -30,6 +30,6 @@ jobs: pull-requests: write # To edit labels on PRs steps: - - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 + - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index dcffea33086a..ffe97cf532ea 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -90,7 +90,7 @@ jobs: sha256sum "${fn}".tar*.gz | tee /dev/stderr | grep -qwF -- "${CMAKE_SHA256}" && tar -xf "${fn}".tar*.gz && rm -f "${fn}".tar*.gz mv "cmake-${CMAKE_VERSION}-Linux-x86_64" cmake - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 86d8a2a93238..ce194f016dc9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -844,7 +844,7 @@ jobs: source /opt/intel/oneapi/setvars.sh printenv >> "$GITHUB_ENV" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ac5de2626997..bd7ca4a2661f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -141,7 +141,7 @@ jobs: cmake --build . cmake --install . --verbose - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -442,7 +442,7 @@ jobs: echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages installed'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -701,7 +701,7 @@ jobs: echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 102cf87366e0..1ab4a8804420 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -112,7 +112,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -268,7 +268,7 @@ jobs: --location --proto-redir =https "https://github.com/jens-maus/amissl/releases/download/${AMISSL_VERSION}/AmiSSL-${AMISSL_VERSION}-SDK.lha" --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${AMISSL_SHA256}" && 7z x -bd -y pkg.bin && rm -f pkg.bin - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -371,7 +371,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -493,7 +493,7 @@ jobs: https://www.delorie.com/pub/djgpp/current/v2tk/zlb13b.zip --output pkg.bin sha256sum pkg.bin | tee /dev/stderr | grep -qwF f3d2fa8129e7591c7e79074306d8ab91a70ec172cc01baedeae74992285dd3a3 && unzip -q pkg.bin && rm -f pkg.bin - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index da7dde328bc1..828a04c29eb9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -114,7 +114,7 @@ jobs: libnghttp2-devel ${{ matrix.install }} - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -349,7 +349,7 @@ jobs: mingw-w64-${{ matrix.env }}-libpsl ${{ matrix.install }} - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -662,7 +662,7 @@ jobs: pwd sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${MATRIX_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin && ls -l - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -796,7 +796,7 @@ jobs: sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -997,7 +997,7 @@ jobs: timeout-minutes: 45 run: vcpkg x-set-installed ${MATRIX_INSTALL_VCPKG} --triplet="${MATRIX_ARCH}-${MATRIX_PLAT}" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/tests/http/requirements.txt b/tests/http/requirements.txt index a31374e06446..0d7569c0afa9 100644 --- a/tests/http/requirements.txt +++ b/tests/http/requirements.txt @@ -2,9 +2,9 @@ # # SPDX-License-Identifier: curl -cryptography==48.0.1 -filelock==3.29.0 +cryptography==49.0.0 +filelock==3.32.0 psutil==7.2.2 -pytest==9.0.3 +pytest==9.1.1 pytest-xdist==3.8.0 -websockets==16.0 +websockets==16.1.1 diff --git a/tests/requirements.txt b/tests/requirements.txt index cb30e3e29827..abf6918bb334 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: curl -impacket==0.13.0 +impacket==0.13.1 From 0043b3fb8cf930825c3043bd72af2f57ab5d6719 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 1 Aug 2026 13:08:30 +0200 Subject: [PATCH 0793/1062] DEPENDENCIES.md: document minimum nettle version: 3.4.1 (2018-12-04) It comes as a transitive requirement by the minimum GnuTLS version. Because libcurl uses nettle directly (in GnuTLS builds), I figure it is useful to document explicitly. Refs: https://github.com/gnutls/gnutls/commit/4353ea025ae032887f3e8cf5aadace25662c6b35 https://github.com/curl/curl/pull/22456#discussion_r3695417678 https://github.com/gnutls/nettle/releases/tag/nettle_3.4.1_release_20181204 Closes #22457 --- docs/DEPENDENCIES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index a6c97c29472e..efa659cb9350 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -35,6 +35,7 @@ We aim to support these or later versions: - libssh2 1.9.0 (2019-06-20) - mbedTLS 3.2.0 (2022-07-11) - MIT Kerberos 1.3 (2003-07-31) +- nettle 3.4.1 (2018-12-04) - nghttp2 1.15.0 (2016-09-25) - nghttp3 1.0.0 (2023-10-15) - ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) From c59b06c99ce1663560caf0147a11eb05c4b30689 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 2 Aug 2026 11:09:40 +0200 Subject: [PATCH 0794/1062] sshserver.pl: bump an sshd config to use its modern name Ref: https://github.com/openssh/openssh-portable/commit/ee9c0da8035b3168e8e57c1dedc2d1b0daf00eec Closes #22460 --- tests/sshserver.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sshserver.pl b/tests/sshserver.pl index 27a7fa81db6a..1aacbb53b766 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -612,7 +612,7 @@ sub logmsg { } if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880) && ($keyalgo eq 'rsa')) { push @cfgarr, 'HostKeyAlgorithms +ssh-rsa'; - push @cfgarr, 'PubkeyAcceptedKeyTypes +ssh-rsa'; + push @cfgarr, 'PubkeyAcceptedAlgorithms +ssh-rsa'; # named PubkeyAcceptedKeyTypes in OpenSSH <8.5 } push @cfgarr, '#'; push @cfgarr, "Port $port"; From 54371bca75051f6a0af4b5edcb2f1fe59af1198e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 00:53:00 +0200 Subject: [PATCH 0795/1062] lib: update mentions of the legacy "sessionhandle" It is now "Curl_easy" Follow-up to 434f8d0389 (June 2016) Closes #22463 --- lib/multi.c | 2 +- lib/url.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index e402be099cf2..c2595b990a7d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3130,7 +3130,7 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) * timeout to use (skip the already expired ones) and add this node back to * the splay tree again. * - * The splay tree only has each sessionhandle as a single node and the nearest + * The splay tree only has each Curl_easy as a single node and the nearest * timeout is used to sort it on. */ static CURLMcode add_next_timeout(const struct curltime *pnow, diff --git a/lib/url.c b/lib/url.c index 6dff5102ccbe..12a630e76dfd 100644 --- a/lib/url.c +++ b/lib/url.c @@ -196,7 +196,7 @@ static void up_free(struct Curl_easy *data) /* * This is the internal function curl_easy_cleanup() calls. This should - * cleanup and free all resources associated with this sessionhandle. + * cleanup and free all resources associated with this Curl_easy. * * We ignore SIGPIPE when this is called from curl_easy_cleanup. */ @@ -454,7 +454,7 @@ static void easy_meta_freeentry(void *p) /** * Curl_open() * - * @param curl is a pointer to a sessionhandle pointer that gets set by this + * @param curl is a pointer to a Curl_easy pointer that gets set by this * function. * @return CURLcode */ From a946d40822ed091100c92a80fe51ef6afde60596 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 04:52:17 +0000 Subject: [PATCH 0796/1062] GHA: Update pizlonator/fil-c to v0.682 Closes #22464 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ce194f016dc9..1f1d9b6dc1f5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,7 +40,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.681 + FIL_C_VERSION: 0.682 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com From 08679d89f53f76dd8e182286c7bac9b8710684b4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 10:37:28 +0200 Subject: [PATCH 0797/1062] multi: remove #if 0'ed code that uses old struct Closes #22467 --- lib/multi.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index c2595b990a7d..7c815b354965 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -578,22 +578,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) return mresult; } -#if 0 -/* Debug-function, used like this: - * - * Curl_hash_print(&multi->sockhash, debug_print_sock_hash); - * - * Enable the hash print function first by editing hash.c - */ -static void debug_print_sock_hash(void *p) -{ - struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; - - curl_mfprintf(stderr, " [readers %u][writers %u]", - sh->readers, sh->writers); -} -#endif - struct multi_done_ctx { BIT(premature); }; From b3cd31965520616c77822347a1c7da8dfb46df84 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 3 Aug 2026 12:14:17 +0200 Subject: [PATCH 0798/1062] curl_trc: remove unused expire timers The expire timers - DNS_PER_NAME - DNS_PER_NAME2 - HAPPY_EYEBALLS_DNS are unused since we changed our happy eyeballing and handling of partial resolve results. Closes #22468 --- lib/curl_trc.c | 3 --- lib/urldata.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/lib/curl_trc.c b/lib/curl_trc.c index dd3cd7bdd543..6fd9507a8e5b 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -308,9 +308,6 @@ static const char * const Curl_trc_timer_names[] = { "100_TIMEOUT", "ASYNC_NAME", "CONNECTTIMEOUT", - "DNS_PER_NAME", - "DNS_PER_NAME2", - "HAPPY_EYEBALLS_DNS", "HAPPY_EYEBALLS", "MULTI_PENDING", "SPEEDCHECK", diff --git a/lib/urldata.h b/lib/urldata.h index 1be8da2dd64f..11c11daafb2d 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -525,9 +525,6 @@ typedef enum { EXPIRE_100_TIMEOUT, EXPIRE_ASYNC_NAME, EXPIRE_CONNECTTIMEOUT, - EXPIRE_DNS_PER_NAME, /* family1 */ - EXPIRE_DNS_PER_NAME2, /* family2 */ - EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */ EXPIRE_HAPPY_EYEBALLS, EXPIRE_MULTI_PENDING, EXPIRE_SPEEDCHECK, From a2b178d378c0e307bda0bd82028afbe546057f1a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 13:31:33 +0200 Subject: [PATCH 0799/1062] tests: convert unit test 1396 and 1398 into libtests They were previously unit tests but used only public library functions. Closes #22471 --- tests/data/test1396 | 7 +++---- tests/data/test1398 | 9 ++++----- tests/libtest/Makefile.inc | 1 + tests/{unit/unit1396.c => libtest/lib1396.c} | 2 +- tests/{unit/unit1398.c => libtest/lib1398.c} | 2 +- tests/unit/Makefile.inc | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) rename tests/{unit/unit1396.c => libtest/lib1396.c} (98%) rename tests/{unit/unit1398.c => libtest/lib1398.c} (99%) diff --git a/tests/data/test1396 b/tests/data/test1396 index 3e4555633654..88062c82985c 100644 --- a/tests/data/test1396 +++ b/tests/data/test1396 @@ -2,7 +2,6 @@ -unittest curl_easy_escape curl_easy_unescape @@ -10,9 +9,9 @@ curl_easy_unescape # Client-side - -unittest - + +lib%TESTNUMBER + curl_easy_escape and curl_easy_unescape diff --git a/tests/data/test1398 b/tests/data/test1398 index 025c955562fc..dde51f699407 100644 --- a/tests/data/test1398 +++ b/tests/data/test1398 @@ -2,18 +2,17 @@ -unittest curl_msnprintf # Client-side - -unittest - + +lib%TESTNUMBER + -curl_msnprintf unit tests +curl_msnprintf tests diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 89f6a8254634..0787bfc91166 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -89,6 +89,7 @@ TESTS_C = \ lib766.c \ lib1156.c \ lib1301.c lib1308.c \ + lib1396.c lib1398.c \ lib1485.c \ lib1500.c lib1501.c lib1502.c lib1506.c \ lib1507.c lib1508.c lib1509.c lib1510.c lib1511.c lib1512.c lib1513.c \ diff --git a/tests/unit/unit1396.c b/tests/libtest/lib1396.c similarity index 98% rename from tests/unit/unit1396.c rename to tests/libtest/lib1396.c index 1dd3c171125b..60d139a8500f 100644 --- a/tests/unit/unit1396.c +++ b/tests/libtest/lib1396.c @@ -37,7 +37,7 @@ static void t1396_stop(CURL *easy) curl_global_cleanup(); } -static CURLcode test_unit1396(const char *arg) +static CURLcode test_lib1396(const char *arg) { CURL *easy; diff --git a/tests/unit/unit1398.c b/tests/libtest/lib1398.c similarity index 99% rename from tests/unit/unit1398.c rename to tests/libtest/lib1398.c index 2147a4a28304..6fd9cebb0409 100644 --- a/tests/unit/unit1398.c +++ b/tests/libtest/lib1398.c @@ -28,7 +28,7 @@ #pragma GCC diagnostic ignored "-Wformat" #endif -static CURLcode test_unit1398(const char *arg) +static CURLcode test_lib1398(const char *arg) { UNITTEST_BEGIN_SIMPLE diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index fd05c070e42c..a2bfa273d8c5 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -33,7 +33,7 @@ TESTS_C = \ unit1300.c unit1302.c unit1303.c unit1304.c unit1305.c \ unit1307.c unit1309.c \ unit1323.c unit1330.c \ - unit1395.c unit1396.c unit1397.c unit1398.c unit1399.c \ + unit1395.c unit1397.c unit1399.c \ unit1600.c unit1601.c unit1602.c unit1603.c unit1605.c unit1606.c \ unit1607.c unit1608.c unit1609.c unit1610.c unit1611.c unit1612.c unit1614.c \ unit1615.c unit1616.c unit1620.c \ From 1f860394e611e937b02d5ac72d4622583ec50e84 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 12:51:02 +0200 Subject: [PATCH 0800/1062] lib1560: add CURLU_NO_GUESS_SCHEME tests Closes #22469 --- tests/data/test1560 | 2 +- tests/libtest/lib1560.c | 86 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/tests/data/test1560 b/tests/data/test1560 index f4cbaa8e0e58..a710c8a683b5 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -36,7 +36,7 @@ lib%TESTNUMBER success -Allocations: 3550 +Allocations: 3650 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index fafdd8de9917..e18b2d6508e7 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -2416,12 +2416,98 @@ static int test_api_errors(void) return 0; } +struct guessscheme1560 { + const char *url; + bool expectedfail; /* the URL should fail to parse */ + unsigned int setflags; + const char *scheme; + unsigned int getflags; +}; + +static int test_scheme_guess(void) +{ + const struct guessscheme1560 g[] = { + { "https://example.com", FALSE, 0, "https", 0 }, + { "example.com", TRUE, 0, NULL, 0 }, + { "example.com", FALSE, CURLU_GUESS_SCHEME, "http", 0 }, + { "ftp.example.com:22", FALSE, CURLU_GUESS_SCHEME, "ftp", 0 }, + { "ftp.example.com/foo", FALSE, CURLU_GUESS_SCHEME, + NULL, CURLU_NO_GUESS_SCHEME }, + { "dict.example.com:33", FALSE, CURLU_GUESS_SCHEME, "dict", 0 }, + { "dict.example.com/bar", FALSE, CURLU_GUESS_SCHEME, + NULL, CURLU_NO_GUESS_SCHEME }, + { "ldap.example.com#none", FALSE, CURLU_GUESS_SCHEME, "ldap", 0 }, + { "ldap.example.com?special", FALSE, CURLU_GUESS_SCHEME, + NULL, CURLU_NO_GUESS_SCHEME }, + { "smtp.example.com?hey#ho", FALSE, CURLU_GUESS_SCHEME, "smtp", 0 }, + { "smtp.example.com:99/moo", FALSE, CURLU_GUESS_SCHEME, + NULL, CURLU_NO_GUESS_SCHEME }, + { "pop3.example.com:100#foobar", FALSE, CURLU_GUESS_SCHEME, "pop3", 0 }, + { "pop3.example.com:101?gg", FALSE, CURLU_GUESS_SCHEME, + NULL, CURLU_NO_GUESS_SCHEME }, + { "example.com", FALSE, CURLU_GUESS_SCHEME, NULL, CURLU_NO_GUESS_SCHEME }, + { "https://example.com", FALSE, CURLU_GUESS_SCHEME, + "https", CURLU_NO_GUESS_SCHEME }, + { "foobar://example.com", FALSE, + CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME, + "foobar", CURLU_NO_GUESS_SCHEME }, + { "foobar://example.com", FALSE, CURLU_NON_SUPPORT_SCHEME, + "foobar", CURLU_NO_GUESS_SCHEME }, + { "foobar://example.com", FALSE, CURLU_NON_SUPPORT_SCHEME, "foobar", 0 }, + }; + int error = 0; + unsigned int i; + for(i = 0; i < CURL_ARRAYSIZE(g) && !error; i++) { + CURLU *u = curl_url(); + char *schemep = NULL; + if(u) { + int rc = curl_url_set(u, CURLUPART_URL, g[i].url, g[i].setflags); + if(!rc) { + rc = curl_url_get(u, CURLUPART_SCHEME, &schemep, g[i].getflags); + if(!rc && !schemep) { + curl_mfprintf(stderr, "%u: returned success but no scheme\n", i); + error++; + } + else if(rc && schemep) { + curl_mfprintf(stderr, "%u: returned error but with scheme\n", i); + error++; + } + } + else if(!g[i].expectedfail) { + curl_mfprintf(stderr, "%u: URL parsing failed unexpectedly\n", i); + error++; + } + } + if(!schemep && g[i].scheme) { + curl_mfprintf(stderr, "%u: got no scheme when %s was expected\n", i, + g[i].scheme); + error++; + } + else if(schemep && !g[i].scheme) { + curl_mfprintf(stderr, "%u: got scheme %s when none was expected\n", i, + schemep); + error++; + } + else if(schemep && g[i].scheme && strcmp(schemep, g[i].scheme)) { + curl_mfprintf(stderr, "%u: got scheme %s when %s was expected\n", i, + schemep, g[i].scheme); + error++; + } + curl_free(schemep); + curl_url_cleanup(u); + } + return error; +} + static CURLcode test_lib1560(const char *URL) { bool has_utf8 = !!getenv("CURL_TEST_HAVE_CODESET_UTF8"); (void)URL; + if(test_scheme_guess()) + return (CURLcode)13; + if(test_api_errors()) return (CURLcode)12; From 56457f838c0b8e4e83d73fa350bddb28cf81061e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 14:38:38 +0200 Subject: [PATCH 0801/1062] test557: test curl_mv*printf() functions These functions were previously untested in the test suite. This is just a set of basic invokes to make sure they work. The core of these functions is identical and is tested already. - curl_mvfprintf - curl_mvprintf - curl_mvsnprintf - curl_mvsprintf - curl_mvaprintf Closes #22472 --- tests/data/test557 | 2 + tests/libtest/lib557.c | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/tests/data/test557 b/tests/data/test557 index 47242b1487c8..df859e97c7c6 100644 --- a/tests/data/test557 +++ b/tests/data/test557 @@ -35,6 +35,8 @@ All curl_mprintf() curl_off_t tests OK! All curl_mprintf() strings tests OK! All float strings tests OK! All curl_mprintf() octal and hexadecimal tests OK! +testing a string and -443 is super fun and 9876543 +All v-functions test OK! diff --git a/tests/libtest/lib557.c b/tests/libtest/lib557.c index e7fb499fb829..0b175c193fbb 100644 --- a/tests/libtest/lib557.c +++ b/tests/libtest/lib557.c @@ -1237,6 +1237,102 @@ static int test_return_codes(void) return 0; } +/* + Basic verification of the vararg versions of the printf functions: + + - curl_mvfprintf + - curl_mvprintf + - curl_mvsnprintf + - curl_mvsprintf + - curl_mvaprintf + */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +static int var557(int expected_len, const char *format, ...) +{ + va_list arg; + int len; + int errors = 1; + char buffer[80]; + char *ptr; + va_start(arg, format); + + len = curl_mvfprintf(stderr, format, arg); + if(len != expected_len) { + curl_mfprintf(stderr, "curl_mvfprintf: expected length %d but got %d\n", + expected_len, len); + goto error; + } + + va_end(arg); + va_start(arg, format); + /* this unfortunately writes to stdout but we can't prevent it */ + len = curl_mvprintf(format, arg); + if(len != expected_len) { + curl_mfprintf(stderr, "curl_mvprintf: expected length %d but got %d\n", + expected_len, len); + goto error; + } + + va_end(arg); + va_start(arg, format); + len = curl_mvsnprintf(buffer, sizeof(buffer), format, arg); + if(len != expected_len) { + curl_mfprintf(stderr, "curl_mvsnprintf: expected length %d but got %d\n", + expected_len, len); + goto error; + } + + va_end(arg); + va_start(arg, format); + /* no buffer size, but we know it fits */ + len = curl_mvsprintf(buffer, format, arg); + if(len != expected_len) { + curl_mfprintf(stderr, "curl_mvsprintf: expected length %d but got %d\n", + expected_len, len); + goto error; + } + + va_end(arg); + va_start(arg, format); + ptr = curl_mvaprintf(format, arg); + len = ptr ? (int)strlen(ptr) : 0; + curl_free(ptr); + if(len != expected_len) { + curl_mfprintf(stderr, "curl_mvaprintf: expected length %d but got %d\n", + expected_len, len); + goto error; + } + + errors = 0; /* success */ +error: + va_end(arg); + if(!errors) + curl_mprintf("All v-functions test OK!\n"); + else + curl_mprintf("The v-functions test failed!\n"); + return errors; +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif + +static int test_vversions(void) +{ + int rc = var557(51, "testing %s and %d is %s and %ld\n", + "a string", -443, "super fun", (long)9876543); + return rc; +} + static CURLcode test_lib557(const char *URL) { int errors = 0; @@ -1263,6 +1359,7 @@ static CURLcode test_lib557(const char *URL) errors += test_float_formatting(); errors += test_oct_hex_formatting(); errors += test_return_codes(); + errors += test_vversions(); if(errors) return TEST_ERR_MAJOR_BAD; From a368fbe968f6aa4ef88c7d3469eb4f90124e5dbc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 1 Aug 2026 00:45:57 +0200 Subject: [PATCH 0802/1062] curl_ed25519: add GnuTLS support (via nettle, hogweed) The necessary cryptography API is provided by nettle 3.1+, via its 'hogweed' library. The minimum GnuTLS version required by curl is 3.6.5, which requires nettle 3.4.1+, so the API is always available. Also: - autotools: detect and use nettle's hogweed library. - cmake/FindNettle: add support for the hogweed library. - GHA/http3-linux: enable in the autotools/cmake GnuTLS jobs. Ref: https://github.com/gnutls/gnutls/commit/4353ea025ae032887f3e8cf5aadace25662c6b35 Closes #22456 --- .github/workflows/http3-linux.yml | 4 +- CMake/FindNettle.cmake | 22 ++++++----- CMakeLists.txt | 4 +- configure.ac | 4 +- docs/INSTALL-CMAKE.md | 1 + lib/curl_ed25519.c | 20 ++++++++++ m4/curl-gnutls.m4 | 61 +++++++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 15 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 6d107895c1e1..d880b1688508 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -473,7 +473,7 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/gnutls/build/lib -Wl,-rpath,/home/runner/nettle/build/lib64 -Wl,-rpath,/home/runner/ngtcp2/build/lib PKG_CONFIG_PATH: /home/runner/nettle/build/lib64/pkgconfig:/home/runner/gnutls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-gnutls=/home/runner/gnutls/build --with-ngtcp2=/home/runner/ngtcp2/build --with-libssh --enable-ssls-export + --with-gnutls=/home/runner/gnutls/build --with-ngtcp2=/home/runner/ngtcp2/build --with-libssh --enable-ssls-export --enable-httpsig - name: 'gnutls' install_packages: libp11-kit-dev libssh-dev @@ -486,7 +486,7 @@ jobs: /home/runner/nghttp2/build/lib/pkgconfig" generate: >- -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH=ON - -DCMAKE_UNITY_BUILD=ON + -DCMAKE_UNITY_BUILD=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'libressl' install_steps: skipall diff --git a/CMake/FindNettle.cmake b/CMake/FindNettle.cmake index c963180cba0e..f14cf23886ef 100644 --- a/CMake/FindNettle.cmake +++ b/CMake/FindNettle.cmake @@ -25,19 +25,21 @@ # # Input variables: # -# - `NETTLE_INCLUDE_DIR`: Absolute path to nettle include directory. -# - `NETTLE_LIBRARY`: Absolute path to `nettle` library. +# - `NETTLE_INCLUDE_DIR`: Absolute path to nettle include directory. +# - `NETTLE_HOGWEED_LIBRARY`: Absolute path to `hogweed` library. +# - `NETTLE_LIBRARY`: Absolute path to `nettle` library. # # Defines: # -# - `NETTLE_FOUND`: System has nettle. -# - `NETTLE_VERSION`: Version of nettle. -# - `CURL::nettle`: nettle library target. +# - `NETTLE_FOUND`: System has nettle. +# - `NETTLE_VERSION`: Version of nettle. +# - `CURL::nettle`: nettle library target. -set(_nettle_pc_requires "nettle") +set(_nettle_pc_requires "hogweed" "nettle") if(CURL_USE_PKGCONFIG AND NOT DEFINED NETTLE_INCLUDE_DIR AND + NOT DEFINED NETTLE_HOGWEED_LIBRARY AND NOT DEFINED NETTLE_LIBRARY) find_package(PkgConfig QUIET) pkg_check_modules(_nettle ${_nettle_pc_requires}) @@ -46,10 +48,11 @@ endif() if(_nettle_FOUND) set(Nettle_FOUND TRUE) set(NETTLE_FOUND TRUE) - set(NETTLE_VERSION ${_nettle_VERSION}) + set(NETTLE_VERSION ${_nettle_nettle_VERSION}) message(STATUS "Found Nettle (via pkg-config): ${_nettle_INCLUDE_DIRS} (found version \"${NETTLE_VERSION}\")") else() find_path(NETTLE_INCLUDE_DIR NAMES "nettle/sha2.h") + find_library(NETTLE_HOGWEED_LIBRARY NAMES "hogweed") find_library(NETTLE_LIBRARY NAMES "nettle") unset(NETTLE_VERSION CACHE) @@ -71,6 +74,7 @@ else() find_package_handle_standard_args(Nettle REQUIRED_VARS NETTLE_INCLUDE_DIR + NETTLE_HOGWEED_LIBRARY NETTLE_LIBRARY VERSION_VAR NETTLE_VERSION @@ -78,10 +82,10 @@ else() if(NETTLE_FOUND) set(_nettle_INCLUDE_DIRS ${NETTLE_INCLUDE_DIR}) - set(_nettle_LIBRARIES ${NETTLE_LIBRARY}) + set(_nettle_LIBRARIES ${NETTLE_HOGWEED_LIBRARY} ${NETTLE_LIBRARY}) endif() - mark_as_advanced(NETTLE_INCLUDE_DIR NETTLE_LIBRARY) + mark_as_advanced(NETTLE_INCLUDE_DIR NETTLE_HOGWEED_LIBRARY NETTLE_LIBRARY) endif() if(NETTLE_FOUND) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae1152c9b3f9..868234926516 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1106,8 +1106,8 @@ if(USE_ECH) endif() endif() -if(NOT CURL_DISABLE_HTTPSIG AND (NOT USE_OPENSSL AND NOT USE_WOLFSSL)) - message(WARNING "HTTPSIG requires OpenSSL or wolfSSL. HTTPSIG support disabled.") +if(NOT CURL_DISABLE_HTTPSIG AND NOT USE_GNUTLS AND NOT USE_OPENSSL AND NOT USE_WOLFSSL) + message(WARNING "HTTPSIG requires GnuTLS, OpenSSL or wolfSSL. HTTPSIG support disabled.") set(CURL_DISABLE_HTTPSIG ON) endif() diff --git a/configure.ac b/configure.ac index a00a2f8aa65c..6ae2e7b57ead 100644 --- a/configure.ac +++ b/configure.ac @@ -4608,8 +4608,8 @@ AS_HELP_STRING([--disable-httpsig],[Disable HTTP Message Signatures support (exp want_httpsig="no" ) -if test "$want_httpsig" = "yes" && test "$OPENSSL_ENABLED" != "1" && test "$WOLFSSL_ENABLED" != "1"; then - AC_MSG_WARN([HTTPSIG requires OpenSSL or wolfSSL. HTTPSIG support disabled.]) +if test "$want_httpsig" = "yes" && test "$GNUTLS_ENABLED" != "1" && test "$OPENSSL_ENABLED" != "1" && test "$WOLFSSL_ENABLED" != "1"; then + AC_MSG_WARN([HTTPSIG requires GnuTLS, OpenSSL or wolfSSL. HTTPSIG support disabled.]) want_httpsig="no" fi if test "$want_httpsig" != "yes"; then diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 1e7b1639e951..f240d9c52e9d 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -456,6 +456,7 @@ Details via CMake - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_wolfssl` library. - `NGTCP2_USE_STATIC_LIBS`: Configure for static ngtcp2 libraries. (experimental) - `NETTLE_INCLUDE_DIR`: Absolute path to nettle include directory. +- `NETTLE_HOGWEED_LIBRARY`: Absolute path to `hogweed` library. - `NETTLE_LIBRARY`: Absolute path to `nettle` library. - `PTHREAD_LIBRARY`: Absolute path to `pthread` library. (for Rustls) - `QUICHE_INCLUDE_DIR`: Absolute path to quiche include directory. diff --git a/lib/curl_ed25519.c b/lib/curl_ed25519.c index cc95a7d526cc..44311ec54e4c 100644 --- a/lib/curl_ed25519.c +++ b/lib/curl_ed25519.c @@ -135,6 +135,26 @@ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, return CURLE_AUTH_ERROR; } +#elif defined(USE_GNUTLS) +#include + +CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, + const unsigned char *msg, size_t msglen, + unsigned char *sig, size_t *siglen) +{ + uint8_t pubkey[ED25519_KEY_SIZE]; + + if(keylen != ED25519_KEY_SIZE) + return CURLE_BAD_FUNCTION_ARGUMENT; + + nettle_ed25519_sha512_public_key(pubkey, key); + + nettle_ed25519_sha512_sign(pubkey, key, msglen, msg, sig); + *siglen = CURL_ED25519_SIGLEN; + + return CURLE_OK; +} + #else /* no Ed25519-capable backend */ CURLcode Curl_ed25519_sign(const unsigned char *key, size_t keylen, diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index 9da5cf6bd5c6..59012a1ab5d9 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -199,5 +199,66 @@ if test "$GNUTLS_ENABLED" = "1"; then if test "$USE_GNUTLS_NETTLE" = "1"; then LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE nettle" fi + + USE_GNUTLS_HOGWEED= + dnl First check if we can detect either crypto library via transitive linking + AC_CHECK_LIB(gnutls, nettle_ed25519_sha512_sign, [ USE_GNUTLS_HOGWEED=1 ]) + + dnl If not, try linking directly to both of them to see if they are available + if test -z "$USE_GNUTLS_HOGWEED"; then + + dnl this is with no particular path given + CURL_CHECK_PKGCONFIG(hogweed) + + if test "$PKGCONFIG" != "no"; then + addlib=`$PKGCONFIG --libs-only-l hogweed` + addld=`$PKGCONFIG --libs-only-L hogweed` + addcflags=`$PKGCONFIG --cflags-only-I hogweed` + version=`$PKGCONFIG --modversion hogweed` + gtlslib=`echo $addld | $SED -e 's/^-L//'` + + if test -n "$addlib"; then + + CLEANLIBS="$LIBS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" + + LIBS="$addlib $LIBS" + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + fi + + AC_CHECK_LIB(hogweed, nettle_ed25519_sha512_sign, + [ + USE_GNUTLS_HOGWEED=1 + ], + [ + LIBS="$CLEANLIBS" + CPPFLAGS="$CLEANCPPFLAGS" + LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" + ]) + + if test "$USE_GNUTLS_HOGWEED" = "1"; then + if test -z "$version"; then + version="unknown" + fi + AC_MSG_NOTICE([detected hogweed version $version]) + fi + fi + fi + if test -z "$USE_GNUTLS_HOGWEED"; then + AC_MSG_ERROR([GnuTLS found, but hogweed was not found]) + fi + else + LIBS="-lhogweed $LIBS" + fi + + if test "$USE_GNUTLS_HOGWEED" = "1"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE hogweed" + fi fi ]) From 7acf124614179f18a66a6d54eef911e75a12db53 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2026 16:49:40 +0200 Subject: [PATCH 0803/1062] url: rename Curl_init_do => Curl_init_transfer And correct some comments Closes #22474 --- lib/multi.c | 2 +- lib/url.c | 23 ++++++++++------------- lib/url.h | 2 +- lib/urldata.h | 6 +++--- tests/unit/unit1620.c | 8 ++++---- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 7c815b354965..72a6524f8faa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1751,7 +1751,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* pass in NULL for 'conn' here since we do not want to init the connection, only this transfer */ - result = Curl_init_do(data, NULL); + result = Curl_init_transfer(data, NULL); if(result) { Curl_multi_remove_handle(multi, data); return CURLM_INTERNAL_ERROR; diff --git a/lib/url.c b/lib/url.c index 12a630e76dfd..a77309d372ab 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1051,7 +1051,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_http_multiplex(conn, m)) return FALSE; else if(m->wait_pipe) - /* we decided to wait on PIPELINING */ + /* wait on multiplexing */ return TRUE; if(!url_match_auth(conn, m)) @@ -2005,10 +2005,8 @@ static CURLcode url_create_needle(struct Curl_easy *data, CURLcode result = CURLE_OK; bool network_scheme = TRUE; /* almost all are */ - /* First, split up the current URL in parts so that we can use the - parts for checking against the already present connections. In order - to not have to modify everything at once, we allocate a temporary - connection data struct and fill in for comparison purposes. */ + /* Allocate a temporary connection data struct (needle) and fill in for + comparison purposes. */ needle = allocate_conn(data); if(!needle) { result = CURLE_OUT_OF_MEMORY; @@ -2279,7 +2277,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) result = setup_range(data); if(!result) { Curl_xfer_setup_nop(data); - result = Curl_init_do(data, data->conn); + result = Curl_init_transfer(data, data->conn); } } @@ -2404,7 +2402,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) } /* Setup and init stuff before DO starts, in preparing for the transfer. */ - result = Curl_init_do(data, data->conn); + result = Curl_init_transfer(data, data->conn); if(result) goto out; @@ -2497,16 +2495,15 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) } /* - * Curl_init_do() inits the readwrite session. This is inited each time (in - * the DO function before the protocol-specific DO functions are invoked) for - * a transfer, sometimes multiple times on the same Curl_easy. Make sure - * nothing in here depends on stuff that are setup dynamically for the - * transfer. + * Curl_init_transfer() is called each time before the transfer starts - to + * prepare for a transfer, sometimes multiple times on the same Curl_easy. + * Make sure nothing in here depends on stuff that is setup dynamically for + * the transfer. * * Allow this function to get called with 'conn' set to NULL. */ -CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) +CURLcode Curl_init_transfer(struct Curl_easy *data, struct connectdata *conn) { CURLcode result; diff --git a/lib/url.h b/lib/url.h index c1df2fe81193..d481dabb80bc 100644 --- a/lib/url.h +++ b/lib/url.h @@ -32,7 +32,7 @@ * Prototypes for library-wide functions */ -CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); +CURLcode Curl_init_transfer(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_open(struct Curl_easy **curl); void Curl_init_userdefined(struct Curl_easy *data); diff --git a/lib/urldata.h b/lib/urldata.h index 11c11daafb2d..2b8712f8ca69 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -703,9 +703,9 @@ struct UrlState { 417 response */ BIT(use_range); BIT(rangestringalloc); /* the range string is malloc()'ed */ - BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE - when multi_done() is called, to prevent multi_done() to get - invoked twice when the multi interface is used. */ + BIT(done); /* set to FALSE when Curl_init_transfer() is called and set to + TRUE when multi_done() is called, to prevent multi_done() from + being invoked twice. */ #ifndef CURL_DISABLE_COOKIES BIT(cookie_engine); #endif diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c index 6ba9a5bb0d60..a58a586afe51 100644 --- a/tests/unit/unit1620.c +++ b/tests/unit/unit1620.c @@ -92,8 +92,8 @@ static CURLcode test_unit1620(const char *arg) fail_unless(result == CURLE_URL_MALFORMAT, "Curl_connect() failed to return CURLE_URL_MALFORMAT"); - result = Curl_init_do(empty, empty->conn); - fail_unless(result == CURLE_OK, "Curl_init_do() failed"); + result = Curl_init_transfer(empty, empty->conn); + fail_unless(result == CURLE_OK, "Curl_init_transfer() failed"); result = curl_easy_setopt((CURL *)empty, CURLOPT_NOBODY, 1L); fail_unless(result == CURLE_OK, "curl_easy_setopt(CURLOPT_NOBODY) failed"); @@ -103,8 +103,8 @@ static CURLcode test_unit1620(const char *arg) Curl_close(&empty); abort_unless(dupe, "curl_easy_duphandle() failed"); - result = Curl_init_do(dupe, NULL); - fail_unless(result == CURLE_OK, "Curl_init_do() on duplicate failed"); + result = Curl_init_transfer(dupe, NULL); + fail_unless(result == CURLE_OK, "Curl_init_transfer() on duplicate failed"); fail_unless(dupe->req.no_body, "duplicate handle should keep no_body"); fail_unless(dupe->state.httpreq == HTTPREQ_HEAD, "duplicate handle should use HTTPREQ_HEAD"); From 73a05428d4c659d950bcf26aad9d7bd3164b8f7e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 30 Jul 2026 13:49:58 +0200 Subject: [PATCH 0804/1062] dnscache: use Curl_peer in resolve and dnscache operations Removes unused/duplicate members in async/ares/doh structs. Closes #22446 --- lib/asyn-ares.c | 114 ++++-------------- lib/asyn-base.c | 12 +- lib/asyn-thrdd.c | 167 ++++++++------------------ lib/asyn.h | 32 ++--- lib/cf-dns.c | 37 ++---- lib/curl_addrinfo.c | 15 +++ lib/curl_addrinfo.h | 5 + lib/dnscache.c | 284 +++++++++++++++++++++++++------------------- lib/dnscache.h | 17 ++- lib/doh.c | 29 ++--- lib/doh.h | 2 - lib/hostip.c | 182 ++++++++++++++-------------- 12 files changed, 395 insertions(+), 501 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 72bfda510a0b..a3dcd7071d5c 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -208,19 +208,6 @@ void Curl_async_ares_destroy(struct Curl_easy *data, ares_destroy(ares->channel); ares->channel = NULL; } - if(ares->res_A) { - Curl_freeaddrinfo(ares->res_A); - ares->res_A = NULL; - } - if(ares->res_AAAA) { - Curl_freeaddrinfo(ares->res_AAAA); - ares->res_AAAA = NULL; - } -#ifdef USE_HTTPSRR - curlx_safefree(ares->https_name); - Curl_httpsrr_destroy(ares->hinfo); - ares->hinfo = NULL; -#endif } CURLcode Curl_async_pollset(struct Curl_easy *data, @@ -276,8 +263,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { dns = Curl_dnsc_mk_addr2(data, async->dns_queries, - &ares->res_AAAA, &ares->res_A, - async->hostname, async->port); + &async->ai_AAAA, &async->ai_A, + async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -286,8 +273,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, #ifdef HTTPSRR_WORKS if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { - dns = Curl_dnsc_mk_https(data, &ares->hinfo, - async->hostname, async->port); + dns = Curl_dnsc_mk_https(data, &async->httpsrr, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -349,67 +335,6 @@ static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares, return 1000; } -static const struct Curl_addrinfo *async_ares_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_ares_ctx *ares = &async->ares; - - (void)data; - switch(ai_family) { - case AF_INET: - if(ares->res_A) - return async_ares_get_ai(ares->res_A, ai_family, index); - break; - case AF_INET6: - if(ares->res_AAAA) - return async_ares_get_ai(ares->res_AAAA, ai_family, index); - break; - default: - break; - } - return NULL; -} - -#ifdef USE_HTTPSRR -const struct Curl_https_rrinfo *Curl_async_get_https( - struct Curl_easy *data, - struct Curl_resolv_async *async) -{ - if(Curl_async_knows_https(data, async)) - return async->ares.hinfo; - return NULL; -} - -bool Curl_async_knows_https(struct Curl_easy *data, - struct Curl_resolv_async *async) -{ - (void)data; - if(async->dns_queries & CURL_DNSQ_HTTPS) - return ((async->dns_responses & CURL_DNSQ_HTTPS) || - !async->queries_ongoing); - return TRUE; /* we know it will never come */ -} - -#endif /* USE_HTTPSRR */ - /* * Curl_async_await() * @@ -566,7 +491,7 @@ static void async_ares_A_cb(void *user_data, int status, int timeouts, async->done = !async->queries_ongoing; if(status == ARES_SUCCESS) { ares->ares_status = ARES_SUCCESS; - ares->res_A = async_ares_node2addr(ares_ai->nodes); + async->ai_A = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } else { @@ -593,7 +518,7 @@ static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, async->done = !async->queries_ongoing; if(status == ARES_SUCCESS) { ares->ares_status = ARES_SUCCESS; - ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); + async->ai_AAAA = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } else { @@ -625,7 +550,7 @@ static void async_ares_rr_done(void *user_data, ares_status_t status, ares->transient_err = TRUE; if((ARES_SUCCESS != status) || !dnsrec) return; - ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); + ares->result = Curl_httpsrr_from_ares(dnsrec, &async->httpsrr); } #endif /* USE_HTTPSRR */ @@ -664,7 +589,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, } #endif - curl_msnprintf(service, sizeof(service), "%d", async->port); + curl_msnprintf(service, sizeof(service), "%d", async->peer->port); socktype = (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; @@ -674,12 +599,13 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "[AAAA] ares: query records for %s", async->hostname); + CURL_TRC_DNS(data, "[AAAA] ares: query records for %s", + async->peer->hostname); hints.ai_family = PF_INET6; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; async->queries_ongoing++; - ares_getaddrinfo(ares->channel, async->hostname, + ares_getaddrinfo(ares->channel, async->peer->hostname, service, &hints, async_ares_AAAA_cb, async); } #endif /* CURLRES_IPV6 */ @@ -688,31 +614,33 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "[A] ares: query records for %s", async->hostname); + CURL_TRC_DNS(data, "[A] ares: query records for %s", + async->peer->hostname); hints.ai_family = PF_INET; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; async->queries_ongoing++; - ares_getaddrinfo(ares->channel, async->hostname, + ares_getaddrinfo(ares->channel, async->peer->hostname, service, &hints, async_ares_A_cb, async); } #ifdef USE_HTTPSRR if(async->dns_queries & CURL_DNSQ_HTTPS) { - ares->https_name = NULL; - if(async->port != 443) { - ares->https_name = curl_maprintf("_%d._https.%s", - async->port, async->hostname); - if(!ares->https_name) + char *https_name = NULL; + if(async->peer->port != 443) { + https_name = curl_maprintf("_%u._https.%s", + async->peer->port, async->peer->hostname); + if(!https_name) return CURLE_OUT_OF_MEMORY; } CURL_TRC_DNS(data, "[HTTPS] ares: query records for %s", - ares->https_name ? ares->https_name : async->hostname); + https_name ? https_name : async->peer->hostname); async->queries_ongoing++; ares_query_dnsrec(ares->channel, - ares->https_name ? ares->https_name : async->hostname, + https_name ? https_name : async->peer->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, async_ares_rr_done, async, NULL); + curlx_free(https_name); } #endif /* USE_HTTPSRR */ diff --git a/lib/asyn-base.c b/lib/asyn-base.c index b140b0f5b986..28e0c671d11a 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -43,8 +43,10 @@ #include "urldata.h" #include "connect.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" +#include "httpsrr.h" #include "multiif.h" #include "progress.h" #include "select.h" @@ -238,6 +240,14 @@ void Curl_async_destroy(struct Curl_easy *data, #ifndef CURL_DISABLE_DOH Curl_doh_cleanup(data, async); #endif + if(async->ai_A) + Curl_freeaddrinfo(async->ai_A); + if(async->ai_AAAA) + Curl_freeaddrinfo(async->ai_AAAA); +#ifdef USE_HTTPSRR + Curl_httpsrr_destroy(async->httpsrr); +#endif + Curl_peer_unlink(&async->peer); curlx_safefree(async); } } @@ -258,7 +268,7 @@ CURLcode Curl_async_failed(struct Curl_easy *data, if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) failf(data, "Could not resolve %s: %s%s%s%s", - host_or_proxy, async->hostname, + host_or_proxy, async->peer->hostname, detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); return result; } diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 0941c08f5ea9..5e7c40eb53b7 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -209,48 +209,54 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status, async->queries_ongoing--; async->done = !async->queries_ongoing; if((ARES_SUCCESS == status) && dnsrec) - async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); + async->result = Curl_httpsrr_from_ares(dnsrec, &async->httpsrr); } static CURLcode async_rr_start(struct Curl_easy *data, struct Curl_resolv_async *async) { struct async_thrdd_ctx *thrdd = &async->thrdd; + char *https_name = NULL; int status; - char *rrname = NULL; + CURLcode result = CURLE_OK; DEBUGASSERT(!thrdd->rr.channel); - if(async->port != 443) { - rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname); - if(!rrname) - return CURLE_OUT_OF_MEMORY; + if(async->peer->port != 443) { + https_name = curl_maprintf("_%u._https.%s", + async->peer->port, async->peer->hostname); + if(!https_name) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } status = ares_init_options(&thrdd->rr.channel, NULL, 0); if(status != ARES_SUCCESS) { thrdd->rr.channel = NULL; - curlx_free(rrname); - return CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; + goto out; } #ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) { const char *servers = getenv("CURL_DNS_SERVER"); status = ares_set_servers_ports_csv(thrdd->rr.channel, servers); if(status) { - curlx_free(rrname); - return CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; + goto out; } } #endif - thrdd->rr.https_name = rrname; async->queries_ongoing++; ares_query_dnsrec(thrdd->rr.channel, - rrname ? rrname : async->hostname, ARES_CLASS_IN, - ARES_REC_TYPE_HTTPS, + https_name ? https_name : async->peer->hostname, + ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, async_thrdd_rr_done, async, NULL); - CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", - rrname ? rrname : async->hostname); - return CURLE_OK; + CURL_TRC_DNS(data, "[HTTPS] query records for %s", + https_name ? https_name : async->peer->hostname); + +out: + curlx_free(https_name); + return result; } #endif @@ -290,15 +296,9 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, ares_destroy(async->thrdd.rr.channel); async->thrdd.rr.channel = NULL; } - curlx_safefree(async->thrdd.rr.https_name); - Curl_httpsrr_destroy(async->thrdd.rr.hinfo); #endif - async_thrdd_item_destroy(async->thrdd.inc_A); - async->thrdd.inc_A = NULL; async_thrdd_item_destroy(async->thrdd.res_A); async->thrdd.res_A = NULL; - async_thrdd_item_destroy(async->thrdd.inc_AAAA); - async->thrdd.inc_AAAA = NULL; async_thrdd_item_destroy(async->thrdd.res_AAAA); async->thrdd.res_AAAA = NULL; } @@ -569,11 +569,11 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi) if(data) async = Curl_async_get(data, item->resolv_id); if(async) { - struct async_thrdd_item **pdest = &async->thrdd.inc_A; + struct async_thrdd_item **pdest = &async->thrdd.res_A; #ifdef CURLRES_IPV6 if(item->dns_queries & CURL_DNSQ_AAAA) - pdest = &async->thrdd.inc_AAAA; + pdest = &async->thrdd.res_AAAA; #endif if(!*pdest) { *pdest = item; @@ -607,7 +607,7 @@ static CURLcode async_thrdd_query(struct Curl_easy *data, CURLcode result; item = async_thrdd_item_create(data, async->id, dns_queries, - async->hostname, async->port, + async->peer->hostname, async->peer->port, async->transport); if(!item) { result = CURLE_OUT_OF_MEMORY; @@ -676,7 +676,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", - async->hostname, async->port, (int)result); + async->peer->hostname, async->peer->port, (int)result); return result; } @@ -772,33 +772,31 @@ static CURLcode async_thrdd_check_done(struct Curl_easy *data, struct async_thrdd_ctx *thrdd = &async->thrdd; (void)data; - if(thrdd->inc_A) { - thrdd->res_A = thrdd->inc_A; - thrdd->inc_A = NULL; + if(thrdd->res_A && !thrdd->processed_A) { VERBOSE(async_thrdd_report_item(data, thrdd->res_A)); + /* move addrinfos to the async result member */ async->dns_responses |= thrdd->res_A->dns_queries; + async->ai_A = thrdd->res_A->res; + thrdd->res_A->res = NULL; + thrdd->processed_A = TRUE; } - if(thrdd->inc_AAAA) { - if(thrdd->res_AAAA) { - DEBUGASSERT(0); /* should not happen */ - return CURLE_FAILED_INIT; - } + if(thrdd->res_AAAA && !thrdd->processed_AAAA) { #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) /* do we accept the incoming AAAA response? */ - if(!(thrdd->inc_AAAA->dns_queries & CURL_DNSQ_A) && - async_thrdd_item_missing_scope(data, thrdd->inc_AAAA)) { + if(!(thrdd->res_AAAA->dns_queries & CURL_DNSQ_A) && + async_thrdd_item_missing_scope(data, thrdd->res_AAAA)) { /* We queried "only" AF_INET6. This may be problematic when the * result has ipv6 link-local addresses and did not give * any scope id for it. glibc has a long outstanding bug * * that gives scope ids only on AF_UNSPEC queries. */ - struct async_thrdd_item *item = thrdd->inc_AAAA; + struct async_thrdd_item *item = thrdd->res_AAAA; CURLcode result; /* Reuse the item and queue it again, this time with added * CURL_DNSQ_A which resolves using AF_UNSPEC. */ - thrdd->inc_AAAA = NULL; + thrdd->res_AAAA = NULL; item->dns_queries |= CURL_DNSQ_A; if(item->res) { Curl_freeaddrinfo(item->res); @@ -819,12 +817,14 @@ static CURLcode async_thrdd_check_done(struct Curl_easy *data, } /* accepting the AAAA result, strip it of any AF_INET entries, * as we might have resolved it with AF_UNSPEC. */ - async_thrdd_item_strip_results(thrdd->inc_AAAA, AF_INET); + async_thrdd_item_strip_results(thrdd->res_AAAA, AF_INET); #endif /* USE_IPV6 && HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID */ - thrdd->res_AAAA = thrdd->inc_AAAA; - thrdd->inc_AAAA = NULL; VERBOSE(async_thrdd_report_item(data, thrdd->res_AAAA)); + /* move addrinfos to the async result member */ async->dns_responses |= thrdd->res_AAAA->dns_queries; + async->ai_AAAA = thrdd->res_AAAA->res; + thrdd->res_AAAA->res = NULL; + thrdd->processed_AAAA = TRUE; } if(async->queries_ongoing) @@ -872,9 +872,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, const uint8_t ip_queries = async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA); bool negative = (async->dns_responses & ip_queries) == ip_queries; - if(thrdd->res_A && (thrdd->res_A->res || !thrdd->res_A->negative)) + if(thrdd->res_A && (async->ai_A || !thrdd->res_A->negative)) negative = FALSE; - if(thrdd->res_AAAA && (thrdd->res_AAAA->res || !thrdd->res_AAAA->negative)) + if(thrdd->res_AAAA && (async->ai_AAAA || !thrdd->res_AAAA->negative)) negative = FALSE; if(!thrdd->res_A && !thrdd->res_AAAA) negative = FALSE; @@ -886,13 +886,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, goto out; } - if((thrdd->res_A && thrdd->res_A->res) || - (thrdd->res_AAAA && thrdd->res_AAAA->res)) { + if(async->ai_A || async->ai_AAAA) { dns = Curl_dnsc_mk_addr2( - data, async->dns_queries, - thrdd->res_A ? &thrdd->res_A->res : NULL, - thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, - async->hostname, async->port); + data, async->dns_queries, &async->ai_A, &async->ai_AAAA, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -901,9 +897,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, #ifdef USE_HTTPSRR_ARES if(!dns && thrdd->rr.channel) { - Curl_httpsrr_trace(data, thrdd->rr.hinfo); - dns = Curl_dnsc_mk_https(data, &thrdd->rr.hinfo, - async->hostname, async->port); + Curl_httpsrr_trace(data, async->httpsrr); + dns = Curl_dnsc_mk_https(data, &async->httpsrr, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -928,75 +923,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, (result != CURLE_COULDNT_RESOLVE_HOST) && (result != CURLE_COULDNT_RESOLVE_PROXY)) { CURL_TRC_DNS(data, "Error %d resolving %s:%d", - (int)result, async->hostname, async->port); + (int)result, async->peer->hostname, async->peer->port); } 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; -#ifdef USE_IPV6 - case AF_INET6: - if(thrdd->res_AAAA) - return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); - break; -#endif - default: - break; - } - return NULL; -} - -#ifdef USE_HTTPSRR -const struct Curl_https_rrinfo *Curl_async_get_https( - struct Curl_easy *data, - struct Curl_resolv_async *async) -{ -#ifdef USE_HTTPSRR_ARES - if(Curl_async_knows_https(data, async)) - return async->thrdd.rr.hinfo; -#else - (void)data; - (void)async; -#endif - return NULL; -} - -bool Curl_async_knows_https(struct Curl_easy *data, - struct Curl_resolv_async *async) -{ - (void)data; - if(async->dns_queries & CURL_DNSQ_HTTPS) - return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); - return TRUE; /* we know it will never come */ -} - -#endif /* USE_HTTPSRR */ - #endif /* USE_RESOLV_THREADED */ diff --git a/lib/asyn.h b/lib/asyn.h index 43e1dc646302..01611175be14 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -82,15 +82,7 @@ void Curl_async_global_cleanup(void); 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_HTTPSRR -const struct Curl_https_rrinfo *Curl_async_get_https( - struct Curl_easy *data, - struct Curl_resolv_async *async); bool Curl_async_knows_https(struct Curl_easy *data, struct Curl_resolv_async *async); #endif /* USE_HTTPSRR */ @@ -114,15 +106,8 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); /* async resolving implementation using c-ares alone */ struct async_ares_ctx { ares_channel channel; - struct Curl_addrinfo *res_A; - struct Curl_addrinfo *res_AAAA; int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ CURLcode result; /* CURLE_OK or error handling response */ - struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#ifdef USE_HTTPSRR - char *https_name; - struct Curl_https_rrinfo *hinfo; -#endif BIT(transient_err); /* an A/AAAA query failed without the resolver answering that the name does not exist */ }; @@ -140,17 +125,15 @@ struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - struct async_thrdd_item *inc_A; /* IPv4 incoming result */ struct async_thrdd_item *res_A; /* IPv4 final result */ - struct async_thrdd_item *inc_AAAA; /* IPv6 incoming result */ struct async_thrdd_item *res_AAAA; /* IPv6 final result */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; - char *https_name; - struct Curl_https_rrinfo *hinfo; } rr; #endif + BIT(processed_A); + BIT(processed_AAAA); }; void Curl_async_thrdd_shutdown(struct Curl_easy *data, @@ -216,11 +199,9 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, /* convert these functions if an asynch resolver is not used */ #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 #define Curl_async_await(a, b, c) CURLE_COULDNT_RESOLVE_HOST #define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST #define Curl_async_pollset(x, y, z) CURLE_OK -#define Curl_async_get_https(x, y) NULL #define Curl_async_knows_https(x, y) TRUE #endif /* !CURLRES_ASYNCH */ @@ -232,6 +213,12 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, struct Curl_resolv_async { struct Curl_resolv_async *next; + struct Curl_peer *peer; + struct Curl_addrinfo *ai_A; + struct Curl_addrinfo *ai_AAAA; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *httpsrr; +#endif #ifdef USE_RESOLV_ARES struct async_ares_ctx ares; #elif defined(USE_RESOLV_THREADED) @@ -246,8 +233,6 @@ struct Curl_resolv_async { CURLcode result; uint32_t poll_interval; uint32_t id; /* unique id per easy handle of the resolve operation */ - /* what is being resolved */ - uint16_t port; uint8_t dns_queries; /* what queries are being performed */ uint8_t dns_responses; /* what queries had responses so far. */ uint8_t transport; @@ -261,7 +246,6 @@ struct Curl_resolv_async { exist. Only such failures may be cached as negative entries, not transient or local resolver failures. */ - char hostname[1]; }; timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, diff --git a/lib/cf-dns.c b/lib/cf-dns.c index e3693e3acc24..984d7a404d2b 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -567,30 +567,6 @@ CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, return CURLE_FAILED_INIT; /* no one is resolving */ } -static const struct Curl_addrinfo *cf_dns_get_nth_ai( - struct Curl_cfilter *cf, - const struct Curl_addrinfo *ai, - int ai_family, unsigned int index) -{ - struct cf_dns_ctx *ctx = cf->ctx; - unsigned int i = 0; - - if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A)) - return NULL; -#ifdef USE_IPV6 - if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA)) - return NULL; -#endif - 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 at the connection. If the DNS resolving is * not done yet or if no address for the family exists, returns NULL. @@ -613,8 +589,17 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, index, peer->hostname, peer->port, ai_family, !!ctx->dns); if(ctx->resolv_result) return NULL; - else if(ctx->dns) - return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else if(ctx->dns) { + /* A cached DNS entry may contain address families that we + * here never queried for. We want to give no results for those. */ + if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + return Curl_addrinfo_get(ctx->dns->addr, ai_family, index); + } else return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); } diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index ea818a38e52b..98ba7aec1c1f 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -79,6 +79,21 @@ void Curl_freeaddrinfo(struct Curl_addrinfo *cahead) } } +struct Curl_addrinfo *Curl_addrinfo_get(struct Curl_addrinfo *ai, + int ai_family, + unsigned int n) +{ + unsigned int i; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == n) + return ai; + ++i; + } + } + return NULL; +} + #ifdef HAVE_GETADDRINFO /* * Curl_getaddrinfo_ex() diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 046be238169d..7b2b4d28345f 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -62,6 +62,11 @@ struct Curl_addrinfo { void Curl_freeaddrinfo(struct Curl_addrinfo *cahead); +/* Get the n-th addrinfo of family ai_family. */ +struct Curl_addrinfo *Curl_addrinfo_get(struct Curl_addrinfo *ai, + int ai_family, + unsigned int n); + #ifdef HAVE_GETADDRINFO int Curl_getaddrinfo_ex(const char *nodename, const char *servname, diff --git a/lib/dnscache.c b/lib/dnscache.c index 73de3cacb318..44db67720b9b 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -60,32 +60,58 @@ #define MAX_DNS_CACHE_SIZE 29999 -static void dnscache_entry_free(struct Curl_dns_entry *dns) +struct dnsc_id { + struct Curl_str name; + uint16_t port; + char type; +}; + +static void dnsc_peer2id(struct dnsc_id *pid, char type, + struct Curl_peer *peer) { - Curl_freeaddrinfo(dns->addr); -#ifdef USE_HTTPSRR - Curl_httpsrr_destroy(dns->hinfo); -#endif - curlx_free(dns); + curlx_str_assign(&pid->name, peer->hostname, strlen(peer->hostname)); + pid->port = peer->port; + pid->type = type; +} + +static void dnsc_str2id(struct dnsc_id *pid, char type, + struct Curl_str *name, uint16_t port) +{ + pid->name = *name; + pid->port = port; + pid->type = type; } +struct dnsc_key { + char data[MAX_HOSTCACHE_LEN]; + size_t len; +}; + /* * Create a hostcache id string for the provided host + port, to be used by * the DNS caching. Without alloc. Return length of the id string. */ -static size_t create_dnscache_id(char type, const char *name, - size_t nlen, /* 0 or actual name length */ - uint16_t port, - char *buf, size_t buflen) -{ - size_t len = nlen ? nlen : strlen(name); - DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 8)) - len = buflen - 8; +static void dnsc_id2key(struct dnsc_key *key, struct dnsc_id *id) +{ + size_t namelen = curlx_strlen(&id->name); + if(namelen > (sizeof(key->data) - 8)) + namelen = sizeof(key->data) - 8; /* store and lower case the name */ - buf[0] = type; - Curl_strntolower(buf + 1, name, len); - return curl_msnprintf(&buf[len + 1], 7, ":%u", port) + len + 1; + key->data[0] = id->type; + Curl_strntolower(key->data + 1, curlx_str(&id->name), namelen); + /* include the terminating 0 in key length */ + key->len = namelen + 2 + + curl_msnprintf(&key->data[namelen + 1], 7, ":%u", id->port); +} + + +static void dnscache_entry_free(struct Curl_dns_entry *dns) +{ + Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + Curl_httpsrr_destroy(dns->hinfo); +#endif + curlx_free(dns); } struct dnscache_prune_data { @@ -211,35 +237,36 @@ void Curl_dnscache_clear(struct Curl_easy *data) static CURLcode fetch_addr(struct Curl_easy *data, struct Curl_dnscache *dnscache, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, struct Curl_dns_entry **pdns) { struct Curl_dns_entry *dns = NULL; - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - char entry_type = CURL_DNSQ_IS_ADDR(dns_queries) ? - CURL_DNST_ADDR : CURL_DNST_HTTPS; + struct dnsc_id id; + struct dnsc_key key; + char type = CURL_DNSQ_IS_ADDR(dns_queries) ? + CURL_DNST_ADDR : CURL_DNST_HTTPS; CURLcode result = CURLE_OK; *pdns = NULL; if(!dnscache) return CURLE_OK; - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(entry_type, hostname, 0, port, - entry_id, sizeof(entry_id)); + dnsc_peer2id(&id, type, peer); + dnsc_id2key(&key, &id); /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + dns = Curl_hash_pick(&dnscache->entries, key.data, key.len); /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve && CURL_DNSQ_IS_ADDR(dns_queries)) { - entry_len = create_dnscache_id(CURL_DNST_ADDR, "*", 1, port, - entry_id, sizeof(entry_id)); + if(!dns && (type == CURL_DNST_ADDR) && data->state.wildcard_resolve) { + struct Curl_str wildname; + + curlx_str_assign(&wildname, "*", 1); + dnsc_str2id(&id, CURL_DNST_ADDR, &wildname, peer->port); + dnsc_id2key(&key, &id); /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + dns = Curl_hash_pick(&dnscache->entries, key.data, key.len); } if(dns && (data->set.dns_cache_timeout_ms != -1)) { @@ -253,7 +280,7 @@ static CURLcode fetch_addr(struct Curl_easy *data, if(dnscache_entry_is_stale(&user, dns)) { infof(data, "Hostname in DNS cache was stale, zapped"); dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + Curl_hash_delete(&dnscache->entries, key.data, key.len); } } @@ -297,8 +324,7 @@ static CURLcode fetch_addr(struct Curl_easy *data, */ CURLcode Curl_dnscache_get(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, struct Curl_dns_entry **pentry) { struct Curl_dnscache *dnscache = dnscache_get(data); @@ -306,7 +332,7 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data, CURLcode result = CURLE_OK; dnscache_lock(data, dnscache); - result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns); + result = fetch_addr(data, dnscache, dns_queries, peer, &dns); if(!result && dns) dns->refcount++; /* we pass out a reference */ else if(result) { @@ -316,7 +342,7 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data, dnscache_unlock(data, dnscache); CURL_TRC_DNS(data, "cache lookup %s:%u queries=%s -> %d %sfound", - hostname, port, Curl_resolv_query_str(dns_queries), + peer->hostname, peer->port, Curl_resolv_query_str(dns_queries), (int)result, dns ? "" : "not "); *pentry = dns; return result; @@ -414,24 +440,23 @@ static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, return FALSE; } -static struct Curl_dns_entry *dnsc_entry_create( - struct Curl_easy *data, - const char *hostname, - size_t hostlen, - uint16_t port, - bool permanent) +static struct Curl_dns_entry *dnsc_entry_create(struct Curl_easy *data, + struct dnsc_id *pid, + bool permanent) { struct Curl_dns_entry *dns = NULL; /* Create a new cache entry, struct already has the hostname NUL */ - dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); + dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + + curlx_strlen(&pid->name)); if(!dns) goto out; dns->refcount = 1; /* the cache has the first reference */ - dns->port = port; - if(hostlen) - memcpy(dns->hostname, hostname, hostlen); + dns->hostlen = curlx_strlen(&pid->name); + dns->port = pid->port; + if(dns->hostlen) + memcpy(dns->hostname, curlx_str(&pid->name), dns->hostlen); if(permanent) { dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ @@ -515,26 +540,30 @@ dnsc_entry_assign_addr( } struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr, - const char *hostname, - uint16_t port) + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + struct Curl_peer *peer) { - struct Curl_dns_entry *dns = dnsc_entry_create( - data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + struct dnsc_id id; + struct Curl_dns_entry *dns; + + dnsc_peer2id(&id, CURL_DNST_ADDR, peer); + dns = dnsc_entry_create(data, &id, FALSE); dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); return dns; } struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr1, - struct Curl_addrinfo **paddr2, - const char *hostname, - uint16_t port) -{ - struct Curl_dns_entry *dns = dnsc_entry_create( - data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + struct Curl_peer *peer) +{ + struct dnsc_id id; + struct Curl_dns_entry *dns; + + dnsc_peer2id(&id, CURL_DNST_ADDR, peer); + dns = dnsc_entry_create(data, &id, FALSE); dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr1, paddr2); return dns; } @@ -570,11 +599,13 @@ dnsc_entry_assign_https(struct Curl_dns_entry *dns, struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, struct Curl_https_rrinfo **phinfo, - const char *hostname, - uint16_t port) + struct Curl_peer *peer) { - struct Curl_dns_entry *dns = dnsc_entry_create( - data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + struct dnsc_id id; + struct Curl_dns_entry *dns; + + dnsc_peer2id(&id, CURL_DNST_HTTPS, peer); + dns = dnsc_entry_create(data, &id, FALSE); dns = dnsc_entry_assign_https(dns, phinfo); return dns; } @@ -582,28 +613,20 @@ struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, static struct Curl_dns_entry *dnsc_add_https(struct Curl_easy *data, struct Curl_dnscache *dnscache, struct Curl_https_rrinfo **phinfo, - const char *hostname, - size_t hlen, - uint16_t port, + struct dnsc_id *id, bool permanent) { - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; struct Curl_dns_entry *dns, *dns2; + struct dnsc_key key; - dns = dnsc_entry_create(data, hostname, hostname ? strlen(hostname) : 0, - port, permanent); + dns = dnsc_entry_create(data, id, permanent); dns = dnsc_entry_assign_https(dns, phinfo); if(!dns) return NULL; - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(CURL_DNST_HTTPS, hostname, hlen, port, - entry_id, sizeof(entry_id)); - /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, - (void *)dns); + dnsc_id2key(&key, id); + dns2 = Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)dns); if(!dns2) { dnscache_entry_free(dns); return NULL; @@ -620,28 +643,51 @@ static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data, struct Curl_dnscache *dnscache, uint8_t dns_queries, struct Curl_addrinfo **paddr, - const char *hostname, - size_t hlen, - uint16_t port, + struct dnsc_id *id, + struct dnsc_key *key, bool permanent) { - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; - dns = dnsc_entry_create(data, hostname, hlen, port, permanent); + dns = dnsc_entry_create(data, id, permanent); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); + if(!dns) + return NULL; + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, key->data, key->len, (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + +static struct Curl_dns_entry * +dnsc_add_peer_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + struct dnsc_id *id, + bool permanent) +{ + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; + struct dnsc_key key; + + dns = dnsc_entry_create(data, id, permanent); dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); if(!dns) return NULL; - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(CURL_DNST_ADDR, hostname, hlen, port, - entry_id, sizeof(entry_id)); /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, - (void *)dns); + dnsc_id2key(&key, id); + dns2 = Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)dns); if(!dns2) { dnscache_entry_free(dns); return NULL; @@ -656,21 +702,22 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, struct Curl_dns_entry *entry) { struct Curl_dnscache *dnscache = dnscache_get(data); - char id[MAX_HOSTCACHE_LEN]; - size_t idlen; + struct Curl_str name; + struct dnsc_id id; + struct dnsc_key key; if(!dnscache) return CURLE_FAILED_INIT; if(!entry || (entry->type == CURL_DNST_INIT)) return CURLE_BAD_FUNCTION_ARGUMENT; - /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->type, entry->hostname, 0, entry->port, - id, sizeof(id)); + curlx_str_assign(&name, entry->hostname, entry->hostlen); + dnsc_str2id(&id, entry->type, &name, entry->port); + dnsc_id2key(&key, &id); /* Store the resolved data in our DNS cache and up ref count */ dnscache_lock(data, dnscache); - if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { + if(!Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)entry)) { dnscache_unlock(data, dnscache); return CURLE_OUT_OF_MEMORY; } @@ -684,11 +731,11 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, uint8_t dns_queries, - const char *host, - uint16_t port) + struct Curl_peer *peer) { struct Curl_dnscache *dnscache = dnscache_get(data); struct Curl_dns_entry *dns = NULL; + struct dnsc_id id; CURLcode result = CURLE_OK; DEBUGASSERT(dnscache); @@ -699,15 +746,15 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, if(dns_queries & CURL_DNSQ_ADDR) { /* put this new host in the cache */ - dns = dnsc_add_addr(data, dnscache, dns_queries, NULL, - host, strlen(host), port, FALSE); + dnsc_peer2id(&id, CURL_DNST_ADDR, peer); + dns = dnsc_add_peer_addr(data, dnscache, dns_queries, NULL, &id, FALSE); if(!dns) result = CURLE_OUT_OF_MEMORY; } #ifdef USE_HTTPSRR else if(dns_queries == CURL_DNSQ_HTTPS) { - dns = dnsc_add_https(data, dnscache, NULL, - host, strlen(host), port, FALSE); + dnsc_peer2id(&id, CURL_DNST_HTTPS, peer); + dns = dnsc_add_https(data, dnscache, NULL, &id, FALSE); if(!dns) result = CURLE_OUT_OF_MEMORY; } @@ -722,7 +769,8 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, * entry alive: */ dns->refcount--; CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", - host, port, Curl_resolv_query_str(dns_queries)); + peer->hostname, peer->port, + Curl_resolv_query_str(dns_queries)); } dnscache_unlock(data, dnscache); return result; @@ -777,6 +825,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) { struct Curl_dnscache *dnscache = dnscache_get(data); struct curl_slist *hostp; + struct dnsc_id id; + struct dnsc_key key; if(!dnscache) return CURLE_FAILED_INIT; @@ -785,14 +835,12 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) data->state.wildcard_resolve = FALSE; for(hostp = data->state.resolve; hostp; hostp = hostp->next) { - char entry_id[MAX_HOSTCACHE_LEN]; const char *host = hostp->data; struct Curl_str source; if(!host) continue; if(*host == '-') { curl_off_t num = 0; - size_t entry_len; host++; if(!curlx_str_single(&host, '[')) { if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || @@ -809,19 +857,17 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!curlx_str_number(&host, &num, 0xffff)) { /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(CURL_DNST_ADDR, curlx_str(&source), - curlx_strlen(&source), (uint16_t)num, - entry_id, sizeof(entry_id)); + dnsc_str2id(&id, CURL_DNST_ADDR, &source, (uint16_t)num); + dnsc_id2key(&key, &id); dnscache_lock(data, dnscache); /* delete entry, ignore if it did not exist */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + Curl_hash_delete(&dnscache->entries, key.data, key.len); dnscache_unlock(data, dnscache); } } else { struct Curl_dns_entry *dns; struct Curl_addrinfo *head = NULL, *tail = NULL; - size_t entry_len; char address[64]; curl_off_t tmpofft = 0; uint16_t port = 0; @@ -913,15 +959,12 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) return CURLE_SETOPT_OPTION_SYNTAX; } - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(CURL_DNST_ADDR, - curlx_str(&source), curlx_strlen(&source), - port, entry_id, sizeof(entry_id)); - + dnsc_str2id(&id, CURL_DNST_ADDR, &source, port); + dnsc_id2key(&key, &id); dnscache_lock(data, dnscache); /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + dns = Curl_hash_pick(&dnscache->entries, key.data, key.len); if(dns) { infof(data, "RESOLVE %.*s:%u - old addresses discarded", @@ -938,13 +981,12 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) 4. when adding a non-permanent entry, we want it to get a "fresh" timeout that starts _now_. */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + Curl_hash_delete(&dnscache->entries, key.data, key.len); } /* put this new host in the cache, override all address queries */ - dns = dnsc_add_addr(data, dnscache, CURL_DNSQ_ADDR, - &head, curlx_str(&source), - curlx_strlen(&source), port, permanent); + dns = dnsc_add_addr(data, dnscache, CURL_DNSQ_ADDR, &head, + &id, &key, permanent); if(dns) /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -956,8 +998,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; infof(data, "[DNS] added %.*s:%u:%s to cache%s", - (int)curlx_strlen(&source), curlx_str(&source), port, addresses, - permanent ? "" : " (non-permanent)"); + (int)curlx_strlen(&id.name), curlx_str(&id.name), id.port, + addresses, permanent ? "" : " (non-permanent)"); /* Wildcard hostname */ if(curlx_str_casecompare(&source, "*")) { diff --git a/lib/dnscache.h b/lib/dnscache.h index c029eb9d3041..1b9518b3bf10 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -35,6 +35,7 @@ struct connectdata; struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; +struct Curl_peer; #define CURL_DNST_INIT '\0' #define CURL_DNST_ADDR 'A' @@ -47,6 +48,7 @@ struct Curl_dns_entry { #endif /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ struct curltime timestamp; + size_t hostlen; /* reference counter, entry is freed on reaching 0 */ uint32_t refcount; /* hostname port number that resolved to addr. */ @@ -70,21 +72,18 @@ struct Curl_dns_entry { struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr, - const char *hostname, - uint16_t port); + struct Curl_peer *peer); struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr1, struct Curl_addrinfo **paddr2, - const char *hostname, - uint16_t port); + struct Curl_peer *peer); #ifdef USE_HTTPSRR struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, struct Curl_https_rrinfo **phinfo, - const char *hostname, - uint16_t port); + struct Curl_peer *peer); #endif /* USE_HTTPSRR */ /* unlink a dns entry, frees all resources if it was the last reference. @@ -119,8 +118,7 @@ void Curl_dnscache_clear(struct Curl_easy *data); */ CURLcode Curl_dnscache_get(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, struct Curl_dns_entry **pentry); /* @@ -134,8 +132,7 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, * it could not be resolved. */ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, uint8_t dns_queries, - const char *host, - uint16_t port); + struct Curl_peer *peer); /* * Populate the cache with specified entries from CURLOPT_RESOLVE. diff --git a/lib/doh.c b/lib/doh.c index 6d39f6ff0eba..d335204199e1 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -463,7 +463,7 @@ CURLcode Curl_doh(struct Curl_easy *data, size_t i; DEBUGASSERT(!async->doh); - DEBUGASSERT(async->hostname[0]); + DEBUGASSERT(async->peer->hostname[0]); if(async->doh) { DEBUGASSERT(0); /* should not happen */ Curl_doh_cleanup(data, async); @@ -479,13 +479,10 @@ CURLcode Curl_doh(struct Curl_easy *data, curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE); } - dohp->host = async->hostname; - dohp->port = async->port; - /* create IPv4 DoH request */ if(async->dns_queries & CURL_DNSQ_A) { result = doh_probe_run(data, CURL_DNS_TYPE_A, - async->hostname, data->set.str[STRING_DOH], + async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); if(result) @@ -497,7 +494,7 @@ CURLcode Curl_doh(struct Curl_easy *data, if(async->dns_queries & CURL_DNSQ_AAAA) { /* create IPv6 DoH request */ result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, - async->hostname, data->set.str[STRING_DOH], + async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); if(result) @@ -509,13 +506,14 @@ CURLcode Curl_doh(struct Curl_easy *data, #ifdef USE_HTTPSRR if(async->dns_queries & CURL_DNSQ_HTTPS) { char *qname = NULL; - if(async->port != PORT_HTTPS) { - qname = curl_maprintf("_%d._https.%s", async->port, async->hostname); + if(async->peer->port != PORT_HTTPS) { + qname = curl_maprintf("_%u._https.%s", + async->peer->port, async->peer->hostname); if(!qname) goto error; } result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS, - qname ? qname : async->hostname, + qname ? qname : async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid); @@ -1209,7 +1207,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, if(CURL_DNSQ_IS_ADDR(async->dns_queries) && dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { - failf(data, "Could not DoH-resolve: %s", dohp->host); + failf(data, "Could not DoH-resolve: %s", async->peer->hostname); return async->for_proxy ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } @@ -1240,7 +1238,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, CURL_TRC_DNS(data, "[%s] [DoH] error: %s type %s for %s", Curl_resolv_query_str(async->dns_queries), doh_strerror(rc[slot]), - doh_type2name(p->dnstype), dohp->host); + doh_type2name(p->dnstype), async->peer->hostname); } } /* next slot */ @@ -1250,11 +1248,11 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, struct Curl_addrinfo *ai; if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_doh)) { - CURL_TRC_DNS(data, "hostname: %s", dohp->host); + CURL_TRC_DNS(data, "hostname: %s", async->peer->hostname); doh_show(data, &de); } - result = doh2ai(&de, dohp->host, dohp->port, &ai); + result = doh2ai(&de, async->peer->hostname, async->peer->port, &ai); if(result) { /* a decoded response without any usable address, e.g. only CNAME records, is an authoritative "no data" answer */ @@ -1264,8 +1262,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, } /* we got a response, create a dns entry. */ - dns = Curl_dnsc_mk_addr(data, async->dns_queries, - &ai, dohp->host, dohp->port); + dns = Curl_dnsc_mk_addr(data, async->dns_queries, &ai, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto error; @@ -1297,7 +1294,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, infof(data, "Some HTTPS RR to process"); } Curl_httpsrr_trace(data, hrr); - dns = Curl_dnsc_mk_https(data, &hrr, async->hostname, async->port); + dns = Curl_dnsc_mk_https(data, &hrr, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto error; diff --git a/lib/doh.h b/lib/doh.h index f4ecec0881e4..5179c28caecf 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -110,8 +110,6 @@ struct doh_response { struct doh_probes { struct doh_response probe_resp[DOH_SLOT_COUNT]; unsigned int pending; /* still outstanding probes */ - uint16_t port; - const char *host; }; /* diff --git a/lib/hostip.c b/lib/hostip.c index 7427f8fdd9a6..3eb85d7e3ff1 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -342,11 +342,11 @@ static bool tailmatch(const char *full, size_t flen, } static CURLcode hostip_resolv_failed(struct Curl_easy *data, - const char *hostname, + struct Curl_peer *peer, bool for_proxy) { failf(data, "Could not resolve %s: %s", - for_proxy ? "proxy" : "host", hostname); + for_proxy ? "proxy" : "host", peer->hostname); return RESOLV_FAIL(for_proxy); } @@ -383,14 +383,12 @@ CURLcode Curl_resolv_announce_start(struct Curl_easy *data, static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool for_proxy, timediff_t timeout_ms) { struct Curl_resolv_async *async; - size_t hostlen = strlen(hostname); if(!data->multi) { DEBUGASSERT(0); @@ -398,7 +396,7 @@ static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, } /* struct size already includes the NUL for hostname */ - async = curlx_calloc(1, sizeof(*async) + hostlen); + async = curlx_calloc(1, sizeof(*async)); if(!async) return NULL; @@ -412,18 +410,15 @@ static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, else data->multi->last_resolv_id++; async->id = data->multi->last_resolv_id; + Curl_peer_link(&async->peer, peer); async->dns_queries = dns_queries; - async->port = port; async->transport = transport; async->for_proxy = for_proxy; async->start = *Curl_pgrs_now(data); async->timeout_ms = timeout_ms; - if(hostlen) { - memcpy(async->hostname, hostname, hostlen); - async->is_ipaddr = Curl_is_ipaddr(async->hostname); - if(async->is_ipaddr) - async->is_ipv4addr = Curl_is_ipv4addr(async->hostname); - } + async->is_ipaddr = Curl_is_ipaddr(peer->hostname); + if(async->is_ipaddr) + async->is_ipv4addr = Curl_is_ipv4addr(peer->hostname); return async; } @@ -450,7 +445,8 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, "ongoing=%d for %s:%d", Curl_resolv_query_str(async->dns_queries), Curl_resolv_query_str(async->dns_responses), - async->queries_ongoing, async->hostname, async->port); + async->queries_ongoing, + async->peer->hostname, async->peer->port); result = CURLE_OK; } else if(IS_RESOLV_FAIL(result)) { @@ -461,12 +457,12 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, is so it does not get treated as one. */ CURL_TRC_DNS(data, "[%s] resolve error %d for %s:%u", Curl_resolv_query_str(async->dns_queries), - (int)result, async->hostname, async->port); + (int)result, async->peer->hostname, async->peer->port); } else { CURL_TRC_DNS(data, "[%s] resolve complete for %s:%u", Curl_resolv_query_str(async->dns_queries), - async->hostname, async->port); + async->peer->hostname, async->peer->port); DEBUGASSERT(*pdns); } @@ -504,16 +500,18 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, unsigned int index) { struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); - (void)index; - if(!async) - return NULL; - if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A)) + if(!async || !CURL_DNSQ_IS_ADDR(async->dns_queries)) return NULL; + switch(ai_family) { + case AF_INET: + return Curl_addrinfo_get(async->ai_A, ai_family, index); #ifdef USE_IPV6 - if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA)) - return NULL; + case AF_INET6: + return Curl_addrinfo_get(async->ai_AAAA, ai_family, index); #endif - return Curl_async_get_ai(data, async, ai_family, index); + default: + return NULL; + } } #ifdef USE_HTTPSRR @@ -532,19 +530,22 @@ CURLcode Curl_resolv_https(struct Curl_easy *data, const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) { - struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); - if(!async) + struct Curl_resolv_async *async; + if(!Curl_resolv_knows_https(data, resolv_id)) return NULL; - return Curl_async_get_https(data, async); + async = Curl_async_get(data, resolv_id); + return async ? async->httpsrr : NULL; } bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) { struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); - if(!async) - return TRUE; - return Curl_async_knows_https(data, async); + if(async && (async->dns_queries & CURL_DNSQ_HTTPS)) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || + !async->queries_ongoing); + return TRUE; /* we know it will never come */ } + #endif /* USE_HTTPSRR */ #endif /* USE_CURL_ASYNC */ @@ -555,8 +556,7 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) that must not be cached as negative entries. */ static CURLcode hostip_resolv_start(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool for_proxy, timediff_t timeout_ms, @@ -582,40 +582,40 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, /* Check for "known" things to resolve ourselves. */ if(addr_queries) { #ifndef USE_RESOLVE_ON_IPS - if(Curl_is_ipaddr(hostname)) { + if(Curl_is_ipaddr(peer->hostname)) { /* test655 verifies that the announce is done, even though there * is no real resolving. So, keep doing this. */ result = Curl_resolv_announce_start(data, NULL); if(result) goto out; /* shortcut literal IP addresses, if we are not told to resolve them. */ - result = Curl_str2addr(hostname, port, &addr); + result = Curl_str2addr(peer->hostname, peer->port, &addr); goto out; } #endif - hostname_len = strlen(hostname); - if(curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { + hostname_len = strlen(peer->hostname); + if(curl_strequal(peer->hostname, "localhost") || + curl_strequal(peer->hostname, "localhost.") || + tailmatch(peer->hostname, hostname_len, STRCONST(".localhost")) || + tailmatch(peer->hostname, hostname_len, STRCONST(".localhost."))) { result = Curl_resolv_announce_start(data, NULL); if(result) goto out; - addr = get_localhost(port, hostname); + addr = get_localhost(peer->port, peer->hostname); if(!addr) result = CURLE_OUT_OF_MEMORY; goto out; } } #ifndef CURL_DISABLE_DOH - if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { + if(!Curl_is_ipaddr(peer->hostname) && allowDOH && data->set.doh) { result = Curl_resolv_announce_start(data, NULL); if(result) goto out; if(!async) { - async = hostip_async_new(data, dns_queries, hostname, port, - transport, for_proxy, timeout_ms); + async = hostip_async_new(data, dns_queries, peer, transport, + for_proxy, timeout_ms); if(!async) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -637,8 +637,8 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, #ifdef CURLRES_ASYNCH (void)addr; if(!async) { - async = hostip_async_new(data, dns_queries, hostname, port, - transport, for_proxy, timeout_ms); + async = hostip_async_new(data, dns_queries, peer, transport, + for_proxy, timeout_ms); if(!async) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -657,7 +657,8 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, result = Curl_resolv_announce_start(data, NULL); if(result) goto out; - addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); + addr = Curl_sync_getaddrinfo(data, dns_queries, peer->hostname, peer->port, + transport); if(!addr) { result = RESOLV_FAIL(for_proxy); /* the synchronous resolvers do not tell a transient failure from @@ -671,7 +672,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, if(addr) { /* we got a response, create a dns entry, add to cache, return */ DEBUGASSERT(!*pdns); - *pdns = Curl_dnsc_mk_addr(data, dns_queries, &addr, hostname, port); + *pdns = Curl_dnsc_mk_addr(data, dns_queries, &addr, peer); if(!*pdns) result = CURLE_OUT_OF_MEMORY; } @@ -701,8 +702,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, static CURLcode hostip_resolv(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool for_proxy, timediff_t timeout_ms, @@ -724,40 +724,40 @@ static CURLcode hostip_resolv(struct Curl_easy *data, #endif /* We should intentionally error and not resolve .onion TLDs */ - hostname_len = strlen(hostname); + hostname_len = strlen(peer->hostname); DEBUGASSERT(hostname_len); if(hostname_len >= 7 && - (curl_strequal(&hostname[hostname_len - 6], ".onion") || - curl_strequal(&hostname[hostname_len - 7], ".onion."))) { + (curl_strequal(&peer->hostname[hostname_len - 6], ".onion") || + curl_strequal(&peer->hostname[hostname_len - 7], ".onion."))) { failf(data, "Not resolving .onion address (RFC 7686)"); goto out; } #ifdef DEBUGBUILD CURL_TRC_DNS(data, "[%s] hostip_resolv(%s:%u)", - Curl_resolv_query_str(dns_queries), hostname, port); + Curl_resolv_query_str(dns_queries), peer->hostname, peer->port); if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { infof(data, "DEBUG fail ipv6 resolve"); - result = hostip_resolv_failed(data, hostname, for_proxy); + result = hostip_resolv_failed(data, peer, for_proxy); goto out; } #endif /* Let's check our DNS cache first */ - result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns); + result = Curl_dnscache_get(data, dns_queries, peer, pdns); if(*pdns) { - infof(data, "Hostname %s was found in DNS cache", hostname); + infof(data, "Hostname %s was found in DNS cache", peer->hostname); result = CURLE_OK; } else if(result) { infof(data, "Negative DNS entry"); - result = hostip_resolv_failed(data, hostname, for_proxy); + result = hostip_resolv_failed(data, peer, for_proxy); } else { /* No luck, we need to start resolving. */ cache_dns = TRUE; - result = hostip_resolv_start(data, dns_queries, hostname, port, - transport, for_proxy, timeout_ms, allowDOH, + result = hostip_resolv_start(data, dns_queries, peer, transport, + for_proxy, timeout_ms, allowDOH, presolv_id, pdns, &negative); CURL_TRC_DNS(data, "[%s] hostip_resolv started -> %d", Curl_resolv_query_str(dns_queries), (int)result); @@ -768,12 +768,13 @@ static CURLcode hostip_resolv(struct Curl_easy *data, Curl_dns_entry_unlink(data, pdns); if(IS_RESOLV_FAIL(result)) { if(cache_dns && negative) - Curl_dnscache_add_negative(data, dns_queries, hostname, port); + Curl_dnscache_add_negative(data, dns_queries, peer); if(dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) - failf(data, "Could not resolve: %s:%u", hostname, port); + failf(data, "Could not resolve: %s:%u", peer->hostname, peer->port); } else { - failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); + failf(data, "Error %d resolving %s:%u", + (int)result, peer->hostname, peer->port); } } else if(cache_dns && *pdns) { @@ -792,13 +793,19 @@ CURLcode Curl_resolv_blocking(struct Curl_easy *data, uint8_t transport, struct Curl_dns_entry **pdns) { + struct Curl_peer *peer = NULL; CURLcode result; uint32_t resolv_id; + DEBUGASSERT(hostname && *hostname); *pdns = NULL; + + result = Curl_peer_create(data, data->conn->scheme, hostname, port, &peer); + if(result) + goto out; + /* We cannot do a blocking resolve using DoH currently */ - result = hostip_resolv(data, dns_queries, - hostname, port, transport, FALSE, 0, FALSE, + result = hostip_resolv(data, dns_queries, peer, transport, FALSE, 0, FALSE, &resolv_id, pdns); switch(result) { case CURLE_OK: @@ -814,6 +821,9 @@ CURLcode Curl_resolv_blocking(struct Curl_easy *data, default: break; } + +out: + Curl_peer_unlink(&peer); return result; } @@ -831,8 +841,7 @@ CURL_NORETURN static void alarmfunc(int sig) static CURLcode resolv_alarm_timeout(struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool for_proxy, timediff_t timeout_ms, @@ -852,7 +861,7 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, volatile unsigned int prev_alarm = 0; CURLcode result; - DEBUGASSERT(hostname && *hostname); + DEBUGASSERT(peer->hostname && *peer->hostname); DEBUGASSERT(timeout_ms > 0); DEBUGASSERT(!data->set.no_signal); #ifndef CURL_DISABLE_DOH @@ -913,7 +922,7 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, /* Perform the actual name resolution. This might be interrupted by an * alarm if it takes too long. */ - result = hostip_resolv(data, dns_queries, hostname, port, transport, + result = hostip_resolv(data, dns_queries, peer, transport, for_proxy, timeout_ms, FALSE, presolv_id, entry); clean_up: @@ -967,27 +976,26 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS static CURLcode resolv_unix(struct Curl_easy *data, - const char *unix_path, - bool abstract_path, + struct Curl_peer *peer, struct Curl_dns_entry **pdns) { struct Curl_addrinfo *addr; CURLcode result; - DEBUGASSERT(unix_path); + DEBUGASSERT(peer->unix_socket); *pdns = NULL; - result = Curl_unix2addr(unix_path, abstract_path, &addr); + result = Curl_unix2addr(peer->hostname, (bool)peer->abstract_uds, &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); + failf(data, "Unix socket path too long: '%s'", peer->hostname); result = CURLE_COULDNT_RESOLVE_HOST; } return result; } - *pdns = Curl_dnsc_mk_addr(data, 0, &addr, NULL, 0); + *pdns = Curl_dnsc_mk_addr(data, 0, &addr, peer); return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; } #endif /* USE_UNIX_SOCKETS */ @@ -1032,10 +1040,10 @@ CURLcode Curl_resolv(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS if((dns_queries & CURL_DNSQ_ADDR) && peer->unix_socket) - return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); + return resolv_unix(data, peer, pdns); #else if(peer->unix_socket) - return hostip_resolv_failed(data, peer->hostname, for_proxy); + return hostip_resolv_failed(data, peer, for_proxy); #endif #ifdef USE_ALARM_TIMEOUT @@ -1045,10 +1053,8 @@ CURLcode Curl_resolv(struct Curl_easy *data, timeout_ms = 0; } if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, - peer->hostname, peer->port, - transport, for_proxy, timeout_ms, - presolv_id, pdns); + return resolv_alarm_timeout(data, dns_queries, peer, transport, + for_proxy, timeout_ms, presolv_id, pdns); } } #endif /* !USE_ALARM_TIMEOUT */ @@ -1058,9 +1064,8 @@ CURLcode Curl_resolv(struct Curl_easy *data, infof(data, "timeout on name lookup is not supported"); #endif - return hostip_resolv(data, dns_queries, peer->hostname, peer->port, - transport, for_proxy, timeout_ms, TRUE, presolv_id, - pdns); + return hostip_resolv(data, dns_queries, peer, transport, + for_proxy, timeout_ms, TRUE, presolv_id, pdns); } #ifdef USE_CURL_ASYNC @@ -1087,11 +1092,10 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, return CURLE_FAILED_INIT; /* check if we have the name resolved by now (from someone else) */ - result = Curl_dnscache_get(data, async->dns_queries, - async->hostname, async->port, pdns); + result = Curl_dnscache_get(data, async->dns_queries, async->peer, pdns); if(*pdns) { /* Tell a possibly async resolver we no longer need the results. */ - infof(data, "Hostname '%s' was found in DNS cache", async->hostname); + infof(data, "Hostname '%s' was found in DNS cache", async->peer->hostname); Curl_async_shutdown(data, async); return CURLE_OK; } @@ -1115,14 +1119,14 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, nothing about the name and would poison the cache for every transfer using it. */ if(async->negative_answer) - Curl_dnscache_add_negative(data, async->dns_queries, - async->hostname, async->port); + Curl_dnscache_add_negative(data, async->dns_queries, async->peer); if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) - failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + failf(data, "Could not resolve: %s:%u", + async->peer->hostname, async->peer->port); } else if(result) { failf(data, "Error %d resolving %s:%u", - (int)result, async->hostname, async->port); + (int)result, async->peer->hostname, async->peer->port); } return result; } From 2112f185c0d906e02939048e24a85f60a8671327 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 4 Aug 2026 10:17:03 +0200 Subject: [PATCH 0805/1062] TODO: do not consider APPDATA for netrc See #22462 Closes #22480 --- docs/TODO.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index aa6611a0c66e..0c65089b26c7 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -19,12 +19,6 @@ document](https://curl.se/docs/knownbugs.html) are subject for fixing. # libcurl -## Consult `%APPDATA%` also for `.netrc` - -`%APPDATA%\.netrc` is not considered when running on Windows. Should it not? - -See [curl issue 4016](https://github.com/curl/curl/issues/4016) - ## `struct lifreq` Use `struct lifreq` and `SIOCGLIFADDR` instead of `struct ifreq` and From 69e8278149ce5e476bf4020b52e3928ec9dbf095 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 03:07:58 +0200 Subject: [PATCH 0806/1062] servers: fix to reverse `SA_RESTART` option for `sigaction()` on modern codepath Historically servers used the deprecated `siginterrupt()` function to configure restart behavior on specific signals. It accepts a flag, where 1 means to remove the `SA_RESTART` option, and 0 means to enable it. In year 2021 3fb6e5a01001b8c7dfdc33a89041178aac381a27 introduced the modern alternative to the codebase, replacing `siginterrupt()` with `sigaction()`. After this patch, supporting, modern, systems reacted on the same flag, but, by accident, set the `SA_RESTART` bit when flag is 1, and did not set it when 0. This reversed the previous behavior, and the one still used on the `siginterrupt()` legacy codepath. Fix it by revesring the `SA_RESTART` logic for the `sigaction()` codepath, syncing it with the pre-existing behavior. I find it odd this did not cause any perceivable issue for 5 years, even though it's the active one in most Unix envs. Spotted by GitHub Code Quality, though suggesting to fix `siginterrupt()` calls. But looking into the history, those were correct all along. Refs: https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html https://www.man7.org/linux/man-pages/man3/siginterrupt.3.html https://www.man7.org/linux/man-pages/man2/sigaction.2.html Follow-up to 3fb6e5a01001b8c7dfdc33a89041178aac381a27 #6529 Closes #22037 --- tests/server/util.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tests/server/util.c b/tests/server/util.c index 805f615f4cb8..2bb5b3f96df0 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -555,8 +555,7 @@ static DWORD WINAPI main_window_loop(void *lpParameter) } #endif -static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, - bool restartable) +static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, int norestart) { #if defined(HAVE_SIGACTION) && defined(SA_RESTART) struct sigaction sa, oldsa; @@ -565,7 +564,7 @@ static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, signum); - sa.sa_flags = restartable ? SA_RESTART : 0; + sa.sa_flags = norestart ? 0 : SA_RESTART; if(sigaction(signum, &sa, &oldsa)) return SIG_ERR; @@ -576,9 +575,9 @@ static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, #ifdef HAVE_SIGINTERRUPT if(oldhdlr != SIG_ERR) - siginterrupt(signum, (int)restartable); + siginterrupt(signum, norestart); #else - (void)restartable; + (void)norestart; #endif return oldhdlr; @@ -597,14 +596,14 @@ void install_signal_handlers(bool keep_sigalrm) #endif #ifdef SIGHUP /* ignore SIGHUP signal */ - old_sighup_handler = set_signal(SIGHUP, SIG_IGN, FALSE); + old_sighup_handler = set_signal(SIGHUP, SIG_IGN, 0); if(old_sighup_handler == SIG_ERR) logmsg("cannot install SIGHUP handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #ifdef SIGPIPE /* ignore SIGPIPE signal */ - old_sigpipe_handler = set_signal(SIGPIPE, SIG_IGN, FALSE); + old_sigpipe_handler = set_signal(SIGPIPE, SIG_IGN, 0); if(old_sigpipe_handler == SIG_ERR) logmsg("cannot install SIGPIPE handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); @@ -612,7 +611,7 @@ void install_signal_handlers(bool keep_sigalrm) #ifdef SIGALRM if(!keep_sigalrm) { /* ignore SIGALRM signal */ - old_sigalrm_handler = set_signal(SIGALRM, SIG_IGN, FALSE); + old_sigalrm_handler = set_signal(SIGALRM, SIG_IGN, 0); if(old_sigalrm_handler == SIG_ERR) logmsg("cannot install SIGALRM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); @@ -622,21 +621,21 @@ void install_signal_handlers(bool keep_sigalrm) #endif #ifdef SIGINT /* handle SIGINT signal with our exit_signal_handler */ - old_sigint_handler = set_signal(SIGINT, exit_signal_handler, TRUE); + old_sigint_handler = set_signal(SIGINT, exit_signal_handler, 1); if(old_sigint_handler == SIG_ERR) logmsg("cannot install SIGINT handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #ifdef SIGTERM /* handle SIGTERM signal with our exit_signal_handler */ - old_sigterm_handler = set_signal(SIGTERM, exit_signal_handler, TRUE); + old_sigterm_handler = set_signal(SIGTERM, exit_signal_handler, 1); if(old_sigterm_handler == SIG_ERR) logmsg("cannot install SIGTERM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #if defined(SIGBREAK) && defined(_WIN32) /* handle SIGBREAK signal with our exit_signal_handler */ - old_sigbreak_handler = set_signal(SIGBREAK, exit_signal_handler, TRUE); + old_sigbreak_handler = set_signal(SIGBREAK, exit_signal_handler, 1); if(old_sigbreak_handler == SIG_ERR) logmsg("cannot install SIGBREAK handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); @@ -658,31 +657,31 @@ void restore_signal_handlers(bool keep_sigalrm) { #ifdef SIGHUP if(old_sighup_handler != SIG_ERR) - (void)set_signal(SIGHUP, old_sighup_handler, FALSE); + (void)set_signal(SIGHUP, old_sighup_handler, 0); #endif #ifdef SIGPIPE if(old_sigpipe_handler != SIG_ERR) - (void)set_signal(SIGPIPE, old_sigpipe_handler, FALSE); + (void)set_signal(SIGPIPE, old_sigpipe_handler, 0); #endif #ifdef SIGALRM if(!keep_sigalrm) { if(old_sigalrm_handler != SIG_ERR) - (void)set_signal(SIGALRM, old_sigalrm_handler, FALSE); + (void)set_signal(SIGALRM, old_sigalrm_handler, 0); } #else (void)keep_sigalrm; #endif #ifdef SIGINT if(old_sigint_handler != SIG_ERR) - (void)set_signal(SIGINT, old_sigint_handler, FALSE); + (void)set_signal(SIGINT, old_sigint_handler, 0); #endif #ifdef SIGTERM if(old_sigterm_handler != SIG_ERR) - (void)set_signal(SIGTERM, old_sigterm_handler, FALSE); + (void)set_signal(SIGTERM, old_sigterm_handler, 0); #endif #if defined(SIGBREAK) && defined(_WIN32) if(old_sigbreak_handler != SIG_ERR) - (void)set_signal(SIGBREAK, old_sigbreak_handler, FALSE); + (void)set_signal(SIGBREAK, old_sigbreak_handler, 0); #endif #ifdef _WIN32 (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); From e5b5846c10d181b4a6b9695412b7557f62ae530f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 04:44:44 +0000 Subject: [PATCH 0807/1062] Dockerfile: Update debian:bookworm-slim Docker digest to abd67ff Closes #22486 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6ab1d0dfed4d..00450978b44f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:7b140f374b289a7c2befc338f42ebe6441b7ea838a042bbd5acbfca6ec875818 +FROM debian:bookworm-slim@sha256:abd67ffcfa541b485a3dff59865ab629aa048a6c613e639d36e7456b0b229241 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From 69a224d6b48edb43df29cb69881ca8edc90f1527 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 4 Aug 2026 11:52:00 +0200 Subject: [PATCH 0808/1062] vdns: directory for all DNS related sources Move all DNS related source files from lib to lib/vdns. Fix include paths, no furher changes. Closes #22482 --- lib/Makefile.inc | 46 +++++++++++++++------------ lib/amigaos.c | 2 +- lib/cf-https-connect.c | 6 ++-- lib/cf-ip-happy.c | 2 +- lib/connect.c | 2 +- lib/connect.h | 2 +- lib/curl_trc.c | 2 +- lib/easy.c | 2 +- lib/ftp.c | 2 +- lib/http.c | 1 - lib/imap.c | 1 - lib/multihandle.h | 2 +- lib/pop3.c | 1 - lib/request.c | 1 - lib/setopt.c | 1 - lib/smtp.c | 1 - lib/socks.c | 2 +- lib/transfer.c | 2 -- lib/url.c | 1 - lib/urldata.h | 4 +-- lib/{ => vdns}/asyn-ares.c | 4 +-- lib/{ => vdns}/asyn-base.c | 4 +-- lib/{ => vdns}/asyn-thrdd.c | 4 +-- lib/{ => vdns}/asyn.h | 2 +- lib/{ => vdns}/cf-dns.c | 6 ++-- lib/{ => vdns}/cf-dns.h | 0 lib/{ => vdns}/dnscache.c | 6 ++-- lib/{ => vdns}/dnscache.h | 0 lib/{ => vdns}/doh.c | 4 +-- lib/{ => vdns}/doh.h | 0 lib/{ => vdns}/hostip.c | 8 ++--- lib/{ => vdns}/hostip.h | 0 lib/{ => vdns}/hostip4.c | 2 +- lib/{ => vdns}/hostip6.c | 2 +- lib/{ => vdns}/httpsrr.c | 2 +- lib/{ => vdns}/httpsrr.h | 0 lib/vquic/cf-ngtcp2-cmn.c | 2 +- lib/vquic/cf-ngtcp2.c | 2 +- lib/vquic/cf-quiche.c | 2 +- lib/vquic/vquic.c | 2 +- lib/vssh/libssh.c | 1 - lib/vssh/libssh2.c | 1 - lib/vtls/hostcheck.c | 2 +- lib/vtls/openssl.c | 4 +-- lib/vtls/rustls.c | 4 +-- lib/vtls/vtls.c | 2 +- lib/vtls/wolfssl.c | 4 +-- projects/Windows/generate.bat | 4 +++ projects/Windows/tmpl/libcurl.vcxproj | 2 ++ src/curlinfo.c | 2 +- tests/unit/unit1305.c | 2 +- tests/unit/unit1650.c | 2 +- tests/unit/unit1655.c | 2 +- tests/unit/unit1658.c | 4 +-- 54 files changed, 86 insertions(+), 85 deletions(-) rename lib/{ => vdns}/asyn-ares.c (99%) rename lib/{ => vdns}/asyn-base.c (99%) rename lib/{ => vdns}/asyn-thrdd.c (99%) rename lib/{ => vdns}/asyn.h (99%) rename lib/{ => vdns}/cf-dns.c (99%) rename lib/{ => vdns}/cf-dns.h (100%) rename lib/{ => vdns}/dnscache.c (99%) rename lib/{ => vdns}/dnscache.h (100%) rename lib/{ => vdns}/doh.c (99%) rename lib/{ => vdns}/doh.h (100%) rename lib/{ => vdns}/hostip.c (99%) rename lib/{ => vdns}/hostip.h (100%) rename lib/{ => vdns}/hostip4.c (99%) rename lib/{ => vdns}/hostip6.c (99%) rename lib/{ => vdns}/httpsrr.c (99%) rename lib/{ => vdns}/httpsrr.h (100%) diff --git a/lib/Makefile.inc b/lib/Makefile.inc index e00e599d6ccc..754275b78c95 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -84,6 +84,26 @@ LIB_VAUTH_HFILES = \ vauth/digest.h \ vauth/vauth.h +LIB_VDNS_CFILES = \ + vdns/asyn-ares.c \ + vdns/asyn-base.c \ + vdns/asyn-thrdd.c \ + vdns/cf-dns.c \ + vdns/dnscache.c \ + vdns/doh.c \ + vdns/hostip.c \ + vdns/hostip4.c \ + vdns/hostip6.c \ + vdns/httpsrr.c + +LIB_VDNS_HFILES = \ + vdns/asyn.h \ + vdns/cf-dns.h \ + vdns/dnscache.h \ + vdns/doh.h \ + vdns/hostip.h \ + vdns/httpsrr.h + LIB_VTLS_CFILES = \ vtls/apple.c \ vtls/cipher_suite.c \ @@ -155,12 +175,8 @@ LIB_CFILES = \ altsvc.c \ amigaos.c \ api.c \ - asyn-ares.c \ - asyn-base.c \ - asyn-thrdd.c \ bufq.c \ bufref.c \ - cf-dns.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ cf-haproxy.c \ @@ -195,8 +211,6 @@ LIB_CFILES = \ cw-out.c \ cw-pause.c \ dict.c \ - dnscache.c \ - doh.c \ dynhds.c \ easy.c \ easygetopt.c \ @@ -214,9 +228,6 @@ LIB_CFILES = \ hash.c \ headers.c \ hmac.c \ - hostip.c \ - hostip4.c \ - hostip6.c \ hsts.c \ http.c \ http1.c \ @@ -229,7 +240,6 @@ LIB_CFILES = \ http_negotiate.c \ http_ntlm.c \ http_proxy.c \ - httpsrr.c \ idn.c \ if2ip.c \ imap.c \ @@ -294,10 +304,8 @@ LIB_HFILES = \ amigaos.h \ api.h \ arpa_telnet.h \ - asyn.h \ bufq.h \ bufref.h \ - cf-dns.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ cf-haproxy.h \ @@ -340,8 +348,6 @@ LIB_HFILES = \ cw-out.h \ cw-pause.h \ dict.h \ - dnscache.h \ - doh.h \ dynhds.h \ easy_lock.h \ easyif.h \ @@ -359,7 +365,6 @@ LIB_HFILES = \ gopher.h \ hash.h \ headers.h \ - hostip.h \ hsts.h \ http.h \ http1.h \ @@ -372,7 +377,6 @@ LIB_HFILES = \ http_negotiate.h \ http_ntlm.h \ http_proxy.h \ - httpsrr.h \ idn.h \ if2ip.h \ imap.h \ @@ -430,7 +434,9 @@ LIB_HFILES = \ LIB_RCFILES = libcurl.rc -CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ - $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES) -HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ - $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES) +CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VDNS_CFILES) \ + $(LIB_VTLS_CFILES) $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) \ + $(LIB_CURLX_CFILES) +HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VDNS_HFILES) \ + $(LIB_VTLS_HFILES) $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) \ + $(LIB_CURLX_HFILES) diff --git a/lib/amigaos.c b/lib/amigaos.c index e4f3bfb77c1c..3f615651838f 100644 --- a/lib/amigaos.c +++ b/lib/amigaos.c @@ -25,7 +25,7 @@ #ifdef __AMIGA__ -#include "hostip.h" +#include "vdns/hostip.h" #include "curl_addrinfo.h" #include "amigaos.h" diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index c39f59f2ee3a..e1a97097ac26 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -28,16 +28,16 @@ #include "urldata.h" #include "curl_trc.h" #include "cfilters.h" -#include "cf-dns.h" #include "cf-setup.h" #include "connect.h" -#include "hostip.h" -#include "httpsrr.h" #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" #include "progress.h" #include "select.h" +#include "vdns/cf-dns.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" #include "vquic/vquic.h" typedef enum { diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 794d5e4cb9ab..c9c65bacb717 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -49,7 +49,6 @@ #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" @@ -57,6 +56,7 @@ #include "progress.h" #include "select.h" #include "sockaddr.h" +#include "vdns/cf-dns.h" #include "vquic/vquic.h" /* for quic cfilters */ diff --git a/lib/connect.c b/lib/connect.c index c22047538907..2a6c4f767741 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -28,7 +28,6 @@ #include "strerror.h" #include "cfilters.h" #include "connect.h" -#include "cf-dns.h" #include "cf-https-connect.h" #include "cf-setup.h" #include "multiif.h" @@ -36,6 +35,7 @@ #include "conncache.h" #include "multihandle.h" #include "select.h" +#include "vdns/cf-dns.h" #include "curlx/strparse.h" #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) diff --git a/lib/connect.h b/lib/connect.h index 91cd92490112..33bc1cad49a6 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#include "hostip.h" +#include "vdns/hostip.h" #include "curlx/timeval.h" struct Curl_peer; diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 6fd9507a8e5b..44eeef88f412 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -28,7 +28,6 @@ #include "cfilters.h" #include "multiif.h" -#include "cf-dns.h" #include "cf-recvbuf.h" #include "cf-socket.h" #include "cf-setup.h" @@ -42,6 +41,7 @@ #include "progress.h" #include "socks.h" #include "curlx/strparse.h" +#include "vdns/cf-dns.h" #include "vtls/vtls.h" #include "vquic/vquic.h" #include "curlx/strcopy.h" diff --git a/lib/easy.c b/lib/easy.c index 88b4c194c29e..eeb2791706d3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -46,12 +46,12 @@ #include "urldata.h" #include "api.h" #include "transfer.h" +#include "vdns/hostip.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "vquic/vquic.h" #include "url.h" #include "getinfo.h" -#include "hostip.h" #include "curlx/strdup.h" #include "easyif.h" #include "multiif.h" diff --git a/lib/ftp.c b/lib/ftp.c index 811e58508b0a..c1b05eeccb98 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -44,7 +44,6 @@ #include "curl_addrinfo.h" #include "curl_trc.h" #include "if2ip.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" @@ -65,6 +64,7 @@ #include "multiif.h" #include "url.h" #include "http_proxy.h" +#include "vdns/hostip.h" #include "curlx/strdup.h" #include "curlx/strerr.h" #include "curlx/strparse.h" diff --git a/lib/http.c b/lib/http.c index 4feb930069c4..98d01c4305e4 100644 --- a/lib/http.c +++ b/lib/http.c @@ -65,7 +65,6 @@ #include "url.h" #include "urlapi-int.h" #include "curl_share.h" -#include "hostip.h" #include "dynhds.h" #include "http.h" #include "headers.h" diff --git a/lib/imap.c b/lib/imap.c index cb8f7e8cca55..a1a4ac4363b1 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -57,7 +57,6 @@ #include "curlx/dynbuf.h" #include "sendf.h" #include "curl_trc.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" diff --git a/lib/multihandle.h b/lib/multihandle.h index b0c10e92c181..60d49af1e952 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -28,7 +28,6 @@ #include "hash.h" #include "conncache.h" #include "cshutdn.h" -#include "dnscache.h" #include "multi_ev.h" #include "multi_ntfy.h" #include "psl.h" @@ -36,6 +35,7 @@ #include "uint-bset.h" #include "uint-spbset.h" #include "uint-table.h" +#include "vdns/dnscache.h" struct connectdata; struct Curl_easy; diff --git a/lib/pop3.c b/lib/pop3.c index 9ebe7ce4bffd..7634dfb6294e 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -58,7 +58,6 @@ #include "sendf.h" #include "curl_trc.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" diff --git a/lib/request.c b/lib/request.c index 56dd2c4a15c9..51672bfe00cb 100644 --- a/lib/request.c +++ b/lib/request.c @@ -26,7 +26,6 @@ #include "urldata.h" #include "cfilters.h" #include "curlx/dynbuf.h" -#include "doh.h" #include "progress.h" #include "request.h" #include "sendf.h" diff --git a/lib/setopt.c b/lib/setopt.c index 427cce07af39..ef4be53386a6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -41,7 +41,6 @@ #include "curl_share.h" #include "vtls/vtls.h" #include "curl_trc.h" -#include "hostip.h" #include "setopt.h" #include "altsvc.h" #include "hsts.h" diff --git a/lib/smtp.c b/lib/smtp.c index 616fa8c00f05..c01151e45573 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -60,7 +60,6 @@ #include "sendf.h" #include "curl_trc.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" diff --git a/lib/socks.c b/lib/socks.c index 67810d3fca9f..4f98912a54c4 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -38,9 +38,9 @@ #include "curl_trc.h" #include "select.h" #include "cfilters.h" -#include "cf-dns.h" #include "connect.h" #include "socks.h" +#include "vdns/cf-dns.h" #include "curlx/inet_pton.h" /* for the (SOCKS) connect state machine */ diff --git a/lib/transfer.c b/lib/transfer.c index 3a7b6bc1360f..569cdeec057e 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -56,10 +56,8 @@ #include "urldata.h" -#include "hostip.h" #include "cfilters.h" #include "cw-out.h" -#include "dnscache.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" diff --git a/lib/url.c b/lib/url.c index a77309d372ab..8272a85e4e85 100644 --- a/lib/url.c +++ b/lib/url.c @@ -70,7 +70,6 @@ #include "bufref.h" #include "vtls/vtls.h" #include "vssh/vssh.h" -#include "hostip.h" #include "transfer.h" #include "curl_addrinfo.h" #include "curl_trc.h" diff --git a/lib/urldata.h b/lib/urldata.h index 2b8712f8ca69..ce991ac249c6 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -54,13 +54,11 @@ #include "curlx/timeval.h" #include "api.h" -#include "asyn.h" #include "cookie.h" #include "creds.h" #include "psl.h" #include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ -#include "hostip.h" #include "hash.h" #include "peer.h" #include "proxy.h" @@ -71,6 +69,8 @@ #include "request.h" #include "ratelimit.h" #include "netrc.h" +#include "vdns/asyn.h" +#include "vdns/hostip.h" #include "vtls/vtls_config.h" /* On error return, the value of `pnwritten` has no meaning */ diff --git a/lib/asyn-ares.c b/lib/vdns/asyn-ares.c similarity index 99% rename from lib/asyn-ares.c rename to lib/vdns/asyn-ares.c index a3dcd7071d5c..7d27c7fc8d73 100644 --- a/lib/asyn-ares.c +++ b/lib/vdns/asyn-ares.c @@ -49,7 +49,6 @@ #include "cfilters.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "hostip.h" #include "url.h" #include "multiif.h" #include "curlx/inet_pton.h" @@ -57,7 +56,8 @@ #include "select.h" #include "progress.h" #include "curlx/timediff.h" -#include "httpsrr.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" #include #if ARES_VERSION < 0x011000 diff --git a/lib/asyn-base.c b/lib/vdns/asyn-base.c similarity index 99% rename from lib/asyn-base.c rename to lib/vdns/asyn-base.c index 28e0c671d11a..fafc39485065 100644 --- a/lib/asyn-base.c +++ b/lib/vdns/asyn-base.c @@ -45,12 +45,12 @@ #include "connect.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "hostip.h" -#include "httpsrr.h" #include "multiif.h" #include "progress.h" #include "select.h" #include "url.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" /*********************************************************************** * Only for builds using asynchronous name resolves diff --git a/lib/asyn-thrdd.c b/lib/vdns/asyn-thrdd.c similarity index 99% rename from lib/asyn-thrdd.c rename to lib/vdns/asyn-thrdd.c index 5e7c40eb53b7..13c918bd816b 100644 --- a/lib/asyn-thrdd.c +++ b/lib/vdns/asyn-thrdd.c @@ -52,8 +52,6 @@ #include "cfilters.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "hostip.h" -#include "httpsrr.h" #include "url.h" #include "multiif.h" #include "curl_threads.h" @@ -61,6 +59,8 @@ #include "rand.h" #include "select.h" #include "thrdqueue.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" #include "curlx/strparse.h" #include "curlx/wait.h" diff --git a/lib/asyn.h b/lib/vdns/asyn.h similarity index 99% rename from lib/asyn.h rename to lib/vdns/asyn.h index 01611175be14..7eade656a52b 100644 --- a/lib/asyn.h +++ b/lib/vdns/asyn.h @@ -26,7 +26,7 @@ #include "curl_setup.h" #if defined(USE_HTTPSRR) && defined(USE_ARES) -#include "httpsrr.h" +#include "vdns/httpsrr.h" #endif struct Curl_easy; diff --git a/lib/cf-dns.c b/lib/vdns/cf-dns.c similarity index 99% rename from lib/cf-dns.c rename to lib/vdns/cf-dns.c index 984d7a404d2b..f02b37f126ff 100644 --- a/lib/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -27,13 +27,13 @@ #include "curl_addrinfo.h" #include "cfilters.h" #include "connect.h" -#include "dnscache.h" -#include "httpsrr.h" #include "curl_trc.h" #include "multiif.h" #include "progress.h" #include "url.h" -#include "cf-dns.h" +#include "vdns/cf-dns.h" +#include "vdns/dnscache.h" +#include "vdns/httpsrr.h" struct cf_dns_ctx { diff --git a/lib/cf-dns.h b/lib/vdns/cf-dns.h similarity index 100% rename from lib/cf-dns.h rename to lib/vdns/cf-dns.h diff --git a/lib/dnscache.c b/lib/vdns/dnscache.c similarity index 99% rename from lib/dnscache.c rename to lib/vdns/dnscache.c index 44db67720b9b..c1523af84d21 100644 --- a/lib/dnscache.c +++ b/lib/vdns/dnscache.c @@ -44,13 +44,13 @@ #include "curl_addrinfo.h" #include "curl_share.h" #include "curl_trc.h" -#include "dnscache.h" #include "hash.h" -#include "hostip.h" -#include "httpsrr.h" #include "progress.h" #include "rand.h" #include "strcase.h" +#include "vdns/dnscache.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" #include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "curlx/strcopy.h" diff --git a/lib/dnscache.h b/lib/vdns/dnscache.h similarity index 100% rename from lib/dnscache.h rename to lib/vdns/dnscache.h diff --git a/lib/doh.c b/lib/vdns/doh.c similarity index 99% rename from lib/doh.c rename to lib/vdns/doh.c index d335204199e1..592cb07583fe 100644 --- a/lib/doh.c +++ b/lib/vdns/doh.c @@ -27,12 +27,12 @@ #include "urldata.h" #include "curl_addrinfo.h" -#include "doh.h" #include "curl_trc.h" -#include "httpsrr.h" #include "multiif.h" #include "url.h" #include "connect.h" +#include "vdns/doh.h" +#include "vdns/httpsrr.h" #include "curlx/strdup.h" #include "curlx/dynbuf.h" #include "escape.h" /* for Curl_hexencode() */ diff --git a/lib/doh.h b/lib/vdns/doh.h similarity index 100% rename from lib/doh.h rename to lib/vdns/doh.h diff --git a/lib/hostip.c b/lib/vdns/hostip.c similarity index 99% rename from lib/hostip.c rename to lib/vdns/hostip.c index 3eb85d7e3ff1..747acac5e578 100644 --- a/lib/hostip.c +++ b/lib/vdns/hostip.c @@ -46,16 +46,16 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "dnscache.h" -#include "hostip.h" -#include "httpsrr.h" #include "url.h" #include "multiif.h" #include "progress.h" -#include "doh.h" #include "select.h" #include "strcase.h" #include "easy_lock.h" +#include "vdns/dnscache.h" +#include "vdns/doh.h" +#include "vdns/hostip.h" +#include "vdns/httpsrr.h" #include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "curlx/strcopy.h" diff --git a/lib/hostip.h b/lib/vdns/hostip.h similarity index 100% rename from lib/hostip.h rename to lib/vdns/hostip.h diff --git a/lib/hostip4.c b/lib/vdns/hostip4.c similarity index 99% rename from lib/hostip4.c rename to lib/vdns/hostip4.c index b4566c5baada..83896a82b27f 100644 --- a/lib/hostip4.c +++ b/lib/vdns/hostip4.c @@ -45,7 +45,7 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "hostip.h" +#include "vdns/hostip.h" #include "url.h" diff --git a/lib/hostip6.c b/lib/vdns/hostip6.c similarity index 99% rename from lib/hostip6.c rename to lib/vdns/hostip6.c index 7412f428a4fd..e33a154d2f42 100644 --- a/lib/hostip6.c +++ b/lib/vdns/hostip6.c @@ -46,8 +46,8 @@ #include "cfilters.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "hostip.h" #include "url.h" +#include "vdns/hostip.h" #include "curlx/inet_pton.h" #include "connect.h" diff --git a/lib/httpsrr.c b/lib/vdns/httpsrr.c similarity index 99% rename from lib/httpsrr.c rename to lib/vdns/httpsrr.c index 441fe350b117..29c78a333b9f 100644 --- a/lib/httpsrr.c +++ b/lib/vdns/httpsrr.c @@ -26,9 +26,9 @@ #ifdef USE_HTTPSRR #include "urldata.h" -#include "httpsrr.h" #include "connect.h" #include "curl_trc.h" +#include "vdns/httpsrr.h" #include "curlx/strdup.h" #include "curlx/inet_ntop.h" diff --git a/lib/httpsrr.h b/lib/vdns/httpsrr.h similarity index 100% rename from lib/httpsrr.h rename to lib/vdns/httpsrr.h diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index ea214ff8b06b..771b77b43431 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -54,7 +54,6 @@ #include "rand.h" #include "multiif.h" #include "cfilters.h" -#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" #include "progress.h" @@ -65,6 +64,7 @@ #include "sockaddr.h" #include "transfer.h" #include "bufref.h" +#include "vdns/cf-dns.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 4d5c122183fb..e97a3d827140 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -32,7 +32,6 @@ #include "rand.h" #include "multiif.h" #include "cfilters.h" -#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" #include "progress.h" @@ -42,6 +41,7 @@ #include "select.h" #include "transfer.h" #include "bufref.h" +#include "vdns/cf-dns.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/cf-ngtcp2-cmn.h" diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index bfc68adf14ff..fa3367549731 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -32,7 +32,6 @@ #include "uint-hash.h" #include "urldata.h" #include "cfilters.h" -#include "cf-dns.h" #include "cf-socket.h" #include "curl_trc.h" #include "rand.h" @@ -43,6 +42,7 @@ #include "http.h" #include "http1.h" #include "sockaddr.h" +#include "vdns/cf-dns.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 3844f4e88a59..4975b8794d1f 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -48,7 +48,7 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" -#include "cf-dns.h" +#include "vdns/cf-dns.h" #include "vquic/cf-ngtcp2.h" #include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2-proxy.h" diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index f1a206ba2d1d..3a72b31bc882 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -45,7 +45,6 @@ #include "urldata.h" #include "sendf.h" #include "curl_trc.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "vssh/ssh.h" diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 940c67835805..65a03ef3116e 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -44,7 +44,6 @@ #include "urldata.h" #include "sendf.h" #include "curl_trc.h" -#include "hostip.h" #include "progress.h" #include "transfer.h" #include "vssh/ssh.h" diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c index ad09b8319413..0af540993ffd 100644 --- a/lib/vtls/hostcheck.c +++ b/lib/vtls/hostcheck.c @@ -34,8 +34,8 @@ #endif #include "curl_memrchr.h" +#include "vdns/hostip.h" #include "vtls/hostcheck.h" -#include "hostip.h" /* check the two input strings with given length, but do not assume they end in nul-bytes */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 12903038f176..8c98d1541243 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -31,13 +31,11 @@ #include "urldata.h" #include "curl_trc.h" -#include "httpsrr.h" #include "formdata.h" /* for the boundary function */ #include "url.h" /* for the SSL config check function */ #include "curlx/inet_pton.h" #include "vtls/openssl.h" #include "connect.h" -#include "cf-dns.h" #include "progress.h" #include "vtls/vtls.h" #include "vtls/vtls_int.h" @@ -51,6 +49,8 @@ #include "curlx/strparse.h" #include "curlx/strcopy.h" #include "curlx/strdup.h" +#include "vdns/cf-dns.h" +#include "vdns/httpsrr.h" #include "vtls/apple.h" #ifdef USE_ECH #include "curlx/base64.h" diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 950f17021239..6590adc40724 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -32,9 +32,9 @@ #include "curlx/fopen.h" #include "curlx/strerr.h" #include "urldata.h" -#include "cf-dns.h" #include "curl_trc.h" -#include "httpsrr.h" +#include "vdns/cf-dns.h" +#include "vdns/httpsrr.h" #include "vtls/vtls.h" #include "vtls/vtls_int.h" #include "vtls/rustls.h" diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 952c5e5e7118..4a9d9df1ce78 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -42,7 +42,6 @@ #include "urldata.h" #include "cfilters.h" -#include "cf-dns.h" #include "vtls/vtls.h" /* generic SSL protos etc */ #include "vtls/vtls_int.h" @@ -68,6 +67,7 @@ #include "connect.h" #include "select.h" #include "setopt.h" +#include "vdns/cf-dns.h" #include "curlx/strdup.h" #include "curlx/strcopy.h" diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 9fe0dc1c884a..d1642f77ccab 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -55,8 +55,8 @@ #include "urldata.h" #include "curl_trc.h" -#include "httpsrr.h" -#include "cf-dns.h" +#include "vdns/cf-dns.h" +#include "vdns/httpsrr.h" #include "vtls/vtls.h" #include "vtls/vtls_int.h" #include "vtls/vtls_scache.h" diff --git a/projects/Windows/generate.bat b/projects/Windows/generate.bat index b5da6d9a798c..7bfb98efd3a4 100644 --- a/projects/Windows/generate.bat +++ b/projects/Windows/generate.bat @@ -214,6 +214,10 @@ rem for /f "delims=" %%c in ('dir /b ..\..\lib\vauth\*.c') do call :element lib\vauth "%%c" %3 ) else if "!var!" == "CURL_LIB_VAUTH_H_FILES" ( for /f "delims=" %%h in ('dir /b ..\..\lib\vauth\*.h') do call :element lib\vauth "%%h" %3 + ) else if "!var!" == "CURL_LIB_VDNS_C_FILES" ( + for /f "delims=" %%c in ('dir /b ..\..\lib\vdns\*.c') do call :element lib\vdns "%%c" %3 + ) else if "!var!" == "CURL_LIB_VDNS_H_FILES" ( + for /f "delims=" %%h in ('dir /b ..\..\lib\vdns\*.h') do call :element lib\vdns "%%h" %3 ) else if "!var!" == "CURL_LIB_VQUIC_C_FILES" ( for /f "delims=" %%c in ('dir /b ..\..\lib\vquic\*.c') do call :element lib\vquic "%%c" %3 ) else if "!var!" == "CURL_LIB_VQUIC_H_FILES" ( diff --git a/projects/Windows/tmpl/libcurl.vcxproj b/projects/Windows/tmpl/libcurl.vcxproj index af141d0bd11f..767256f6a52b 100644 --- a/projects/Windows/tmpl/libcurl.vcxproj +++ b/projects/Windows/tmpl/libcurl.vcxproj @@ -2034,6 +2034,7 @@ CURL_LIB_C_FILES CURL_LIB_CURLX_C_FILES CURL_LIB_VAUTH_C_FILES +CURL_LIB_VDNS_C_FILES CURL_LIB_VQUIC_C_FILES CURL_LIB_VSSH_C_FILES CURL_LIB_VTLS_C_FILES @@ -2042,6 +2043,7 @@ CURL_LIB_VTLS_C_FILES CURL_LIB_H_FILES CURL_LIB_CURLX_H_FILES CURL_LIB_VAUTH_H_FILES +CURL_LIB_VDNS_H_FILES CURL_LIB_VQUIC_H_FILES CURL_LIB_VSSH_H_FILES CURL_LIB_VTLS_H_FILES diff --git a/src/curlinfo.c b/src/curlinfo.c index 8b4659200fb2..5357a36fa853 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -34,7 +34,7 @@ #include "multihandle.h" /* for ENABLE_WAKEUP */ #include "tool_xattr.h" /* for USE_XATTR */ #include "curl_sha512_256.h" /* for CURL_HAVE_SHA512_256 */ -#include "asyn.h" /* for USE_RESOLV_ARES, USE_RESOLV_THREADED */ +#include "vdns/asyn.h" /* for USE_RESOLV_ARES, USE_RESOLV_THREADED */ #include "fake_addrinfo.h" /* for USE_FAKE_GETADDRINFO */ #include diff --git a/tests/unit/unit1305.c b/tests/unit/unit1305.c index 93f2e7cc7b61..f6bf67ed36d4 100644 --- a/tests/unit/unit1305.c +++ b/tests/unit/unit1305.c @@ -35,7 +35,7 @@ #include "curl_addrinfo.h" #include "hash.h" -#include "dnscache.h" +#include "vdns/dnscache.h" static struct Curl_dnscache hp; static char *data_key; diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index 299a19bfd47b..1e6217176d94 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -22,7 +22,7 @@ * ***************************************************************************/ #include "unitcheck.h" -#include "doh.h" +#include "vdns/doh.h" static CURLcode test_unit1650(const char *arg) { diff --git a/tests/unit/unit1655.c b/tests/unit/unit1655.c index 55e902098977..12ea149c2b1d 100644 --- a/tests/unit/unit1655.c +++ b/tests/unit/unit1655.c @@ -22,7 +22,7 @@ * ***************************************************************************/ #include "unitcheck.h" -#include "doh.h" +#include "vdns/doh.h" static CURLcode test_unit1655(const char *arg) { diff --git a/tests/unit/unit1658.c b/tests/unit/unit1658.c index 041ced7f293d..f8d2531dc05a 100644 --- a/tests/unit/unit1658.c +++ b/tests/unit/unit1658.c @@ -25,8 +25,8 @@ /* DoH + HTTPSRR are required */ #if !defined(CURL_DISABLE_DOH) && defined(USE_HTTPSRR) -#include "doh.h" -#include "httpsrr.h" +#include "vdns/doh.h" +#include "vdns/httpsrr.h" static CURLcode t1658_setup(void) { From 90325ff0444cbdff368bda5d26d6405a0bb6ee43 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 10:02:53 +0200 Subject: [PATCH 0809/1062] http2: make server push transfers inherit share from parent Reported-by: Stephan Zeisberg Closes #22488 --- lib/http2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/http2.c b/lib/http2.c index 08dfb6d91973..03f226c92ec3 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -46,6 +46,7 @@ #include "bufref.h" #include "curlx/dynbuf.h" #include "headers.h" +#include "curl_share.h" #if NGHTTP2_VERSION_NUM < 0x010f00 #error "nghttp2 1.15.0 or greater required" @@ -704,6 +705,8 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, struct h2_stream_ctx *second_stream; http2_data_setup(cf, second, &second_stream); second->state.priority.weight = data->state.priority.weight; + if(data->share) + (void)Curl_share_easy_link(second, data->share); } return second; } From 7babac86904c7ac0b34a22240737c7261cb059eb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 12:42:15 +0200 Subject: [PATCH 0810/1062] servers: fix HANDLE leak in UWP builds Also: shorten code. Reported by Copilot Bug: https://github.com/curl/curl/pull/22487#pullrequestreview-4863399903 Closes #22489 --- tests/server/util.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/server/util.c b/tests/server/util.c index 2bb5b3f96df0..41bfe6aa9936 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -696,12 +696,9 @@ void restore_signal_handlers(bool keep_sigalrm) } } } - if(exit_event) { - if(CloseHandle(exit_event)) { - exit_event = NULL; - } - } #endif + if(exit_event && CloseHandle(exit_event)) + exit_event = NULL; #endif } From 518a4dadff4a9fc89d8aebbb6bf48cd279235028 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 13:19:30 +0200 Subject: [PATCH 0811/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8c709a35d39a..907e2afc4d3a 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1502 - Contributors: 3756 + Authors: 1503 + Contributors: 3759 This release includes the following changes: @@ -21,6 +21,7 @@ This release includes the following bugfixes: o asyn-thrdd: retry link-local ipv6 if missing scope id [118] o autotools: minor fixes and improvements [33] o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] + o build: assume POSIX `select()` is available [166] o build: drop superfluous `STDC_HEADERS` macro [51] o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] o cd2nroff: fix backslashes for 4-space indent lines [104] @@ -47,7 +48,9 @@ This release includes the following bugfixes: o content_encoding: give a clear error on multi-member gzip [46] o CREDENTIALS.md: remove comment about empty user/pass [50] o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] + o curl: help category cleanups [169] o curl_gssapi: document/update feature availability [145] + o curl_trc: remove unused expire timers [147] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] @@ -57,10 +60,13 @@ This release includes the following bugfixes: o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] + o gitignore: maintenance updates [170] o gopher: reject CR and LF in the selector [1] o h2: bootstrap max streams from multi handle if in use [132] + o h3-proxy: fix NULL deref when non-:status header arrives before :status [167] o HISTORY: add when c-ares support was introduced (2004) o hostip: only cache negative resolves for authoritative answers [16] + o http2: make server push transfers inherit share from parent [81] o http: avoid length underflow in Curl_compareheader [78] o http: fix non-tunneling proxy hostname use [116] o http: stop dropping large custom headers [69] @@ -77,6 +83,7 @@ This release includes the following bugfixes: o lib: add multi_wakeup_internal [86] o lib: fix 'ns' -> 'us' in trace messages [57] o lib: ratelimit timestamps [14] + o lib: update mentions of the legacy "sessionhandle" [157] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] o md5: replace magic numbers with `MD5_DIGEST_LEN` [122] o mime.c: avoid integer overflow in base64 size calculation [105] @@ -84,6 +91,7 @@ This release includes the following bugfixes: o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] + o multi: remove #if 0'ed code that uses old struct [150] o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] @@ -105,11 +113,14 @@ This release includes the following bugfixes: o scripts/badwords.txt: do not recommend using 'will' in rewrites [141] o scripts: replace/extend `--` with `--end-of-options` in git commands [128] o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] + o servers: fix HANDLE leak in UWP builds [65] + o servers: fix to reverse `SA_RESTART` option for `sigaction()` on modern codepath [131] o setopt: error for CURLOPT_SHARE when easy handle is used [68] o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] o spacecheck: cap number of lines per file [111] o src: safely clear certain buffers [125] + o sshserver.pl: bump an sshd config to use its modern name [160] o ssls: fix potential memory leak on import [96] o sws: allow connection-monitor to log all disconnects [2] o sws: log the exact closing reason better, to help debugging tests [85] @@ -120,6 +131,7 @@ This release includes the following bugfixes: o test: adjust test_06_13 for 0100::/64 being blackholed [13] o tests: address mutable class vars and naive datetime in Python code o tests: change whitespace and comments in Python test code + o tests: convert unit test 1396 and 1398 into libtests [146] o tests: fix the FTP check for unexpected RST [117] o tests: fix type promotion on 32-bit arches in http test code [88] o tests: improve exception handling in Python test code @@ -130,8 +142,10 @@ This release includes the following bugfixes: o tests: use simpler constructions in Python code o thrdpool: retry failed thread starts while items wait [62] o tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) [102] + o tidy-up: comments, messages, formatting [172] o tidy-up: drop redundant includes [110] o tidy-up: fix Perl syntax and formatting nits [123] + o tidy-up: minor code fixes and improvements [171] o tidy-up: typos, comment nits [60] o timeval: make `Curl_freq` variable static (Windows) [90] o tool: do not flush on out-null [38] @@ -191,11 +205,12 @@ advice from friends like these: Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, Laurent Sabourin, Matthew John Cheetham, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, - pszemus on github, Ray Satiro, renovate[bot], RMMoreton on github, - Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, Samuel Dainard, - Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, stze on hackerone, - Viktor Szakats, xmoezzz on github, Yoshiro Yoneya - (51 contributors) + pszemus on github, Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, + RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, + Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, + Stephan Zeisberg, stze on hackerone, Viktor Szakats, xmoezzz on github, + Yoshiro Yoneya + (54 contributors) References to bug reports and discussions on issues: @@ -263,6 +278,7 @@ References to bug reports and discussions on issues: [62] = https://curl.se/bug/?i=22303 [63] = https://curl.se/bug/?i=22286 [64] = https://curl.se/bug/?i=22237 + [65] = https://curl.se/bug/?i=22487 [66] = https://curl.se/bug/?i=22388 [67] = https://curl.se/bug/?i=22340 [68] = https://curl.se/bug/?i=22253 @@ -278,6 +294,7 @@ References to bug reports and discussions on issues: [78] = https://curl.se/bug/?i=22338 [79] = https://curl.se/bug/?i=22251 [80] = https://curl.se/bug/?i=22225 + [81] = https://curl.se/bug/?i=22488 [82] = https://curl.se/bug/?i=22248 [83] = https://curl.se/bug/?i=22243 [84] = https://curl.se/bug/?i=22245 @@ -325,6 +342,7 @@ References to bug reports and discussions on issues: [128] = https://curl.se/bug/?i=22369 [129] = https://curl.se/bug/?i=22354 [130] = https://curl.se/bug/?i=21467 + [131] = https://curl.se/bug/?i=22037 [132] = https://curl.se/bug/?i=22418 [135] = https://curl.se/bug/?i=22413 [137] = https://curl.se/bug/?i=22341 @@ -333,12 +351,23 @@ References to bug reports and discussions on issues: [143] = https://curl.se/bug/?i=22420 [144] = https://curl.se/bug/?i=22415 [145] = https://curl.se/bug/?i=22419 + [146] = https://curl.se/bug/?i=22471 + [147] = https://curl.se/bug/?i=22468 [148] = https://curl.se/bug/?i=22337 [149] = https://curl.se/bug/?i=22414 + [150] = https://curl.se/bug/?i=22467 [151] = https://curl.se/bug/?i=21315 [152] = https://curl.se/bug/?i=21806 [153] = https://curl.se/bug/?i=22409 [154] = https://curl.se/bug/?i=22270 [156] = https://curl.se/bug/?i=22401 + [157] = https://curl.se/bug/?i=22463 [158] = https://curl.se/bug/?i=22397 [159] = https://curl.se/bug/?i=22400 + [160] = https://curl.se/bug/?i=22460 + [166] = https://curl.se/bug/?i=22448 + [167] = https://curl.se/bug/?i=22449 + [169] = https://curl.se/bug/?i=22447 + [170] = https://curl.se/bug/?i=22445 + [171] = https://curl.se/bug/?i=22444 + [172] = https://curl.se/bug/?i=22443 From b260e9f841dc4e3c04da45e325ce23a11fec514d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 14:06:57 +0200 Subject: [PATCH 0812/1062] servers: fix to avoid a non-signal-safe call in signal handler `strlen()` is only guaranteed to be signal-safe since POSIX.1-2008. Ref: https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 Reported by Copilot Bug: https://github.com/curl/curl/pull/22487#pullrequestreview-4863961494 Follow-up to e95f509c66abdd88ae02e3243cdc217f19c4a330 #16852 Closes #22491 --- tests/server/util.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/server/util.c b/tests/server/util.c index 41bfe6aa9936..148a2e4786a4 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -399,8 +399,11 @@ static void exit_signal_handler(int signum) } else { static const char msg[] = "exit_signal_handler: failed opening "; + const char *str; (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); - (void)write(STDERR_FILENO, serverlogfile, strlen(serverlogfile)); + str = serverlogfile; + while(*str) + (void)write(STDERR_FILENO, str++, 1); (void)write(STDERR_FILENO, "\n", 1); } } From 947ae0e0ccec071e32e2a4f69834e03c209fa518 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 14:40:35 +0200 Subject: [PATCH 0813/1062] servers: drop redundant guards on Windows These `SIG*` macro are provided by all supported Windows toolchains. Cherry-picked from #22487 Closes #22493 --- tests/server/util.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/tests/server/util.c b/tests/server/util.c index 148a2e4786a4..819e3432dc0c 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -348,7 +348,7 @@ static SIGHANDLER_T old_sigint_handler = SIG_ERR; #ifdef SIGTERM static SIGHANDLER_T old_sigterm_handler = SIG_ERR; #endif -#if defined(SIGBREAK) && defined(_WIN32) +#ifdef _WIN32 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; #endif @@ -444,21 +444,15 @@ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) int signum = 0; logmsg("ctrl_event_handler: %lu", dwCtrlType); switch(dwCtrlType) { -#ifdef SIGINT case CTRL_C_EVENT: signum = SIGINT; break; -#endif -#ifdef SIGTERM case CTRL_CLOSE_EVENT: signum = SIGTERM; break; -#endif -#ifdef SIGBREAK case CTRL_BREAK_EVENT: signum = SIGBREAK; break; -#endif default: return FALSE; } @@ -484,11 +478,9 @@ static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, int signum = 0; if(hwnd == hidden_main_window) { switch(uMsg) { -#ifdef SIGTERM case WM_CLOSE: signum = SIGTERM; break; -#endif case WM_DESTROY: PostQuitMessage(0); break; @@ -636,14 +628,12 @@ void install_signal_handlers(bool keep_sigalrm) logmsg("cannot install SIGTERM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif -#if defined(SIGBREAK) && defined(_WIN32) +#ifdef _WIN32 /* handle SIGBREAK signal with our exit_signal_handler */ old_sigbreak_handler = set_signal(SIGBREAK, exit_signal_handler, 1); if(old_sigbreak_handler == SIG_ERR) logmsg("cannot install SIGBREAK handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); -#endif -#ifdef _WIN32 if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE)) logmsg("cannot install CTRL event handler"); @@ -682,11 +672,9 @@ void restore_signal_handlers(bool keep_sigalrm) if(old_sigterm_handler != SIG_ERR) (void)set_signal(SIGTERM, old_sigterm_handler, 0); #endif -#if defined(SIGBREAK) && defined(_WIN32) +#ifdef _WIN32 if(old_sigbreak_handler != SIG_ERR) (void)set_signal(SIGBREAK, old_sigbreak_handler, 0); -#endif -#ifdef _WIN32 (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); #ifndef CURL_WINDOWS_UWP if(thread_main_window && thread_main_id) { From b11e0026f71aa33b47d3b31f7c6a2aee4f929b12 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 15:59:30 +0200 Subject: [PATCH 0814/1062] GHA/windows: re-enable taskkill in torture jobs Torture jobs are arguably the most flaky nowadays. Make a blind try to see if re-enabling taskkill makes an observable improvement for torture. Follow-up to 208b87744e8ce326e2569c2efd735da43ce74a0b #21039 Follow-up to f450f3801b6b9dff0ea280f5fb4bf28203f7b313 #19897 Follow-up to 2701ac6a4d16a62130dad05be1c484903b8545c7 #19421 Closes #22495 --- .github/workflows/windows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 828a04c29eb9..28645b2303a5 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -514,7 +514,6 @@ jobs: fi if [[ "${TFLAGS}" = *'-t'* ]]; then TFLAGS+=' !2300' # Leaks memory and file handle via tool_doswin.c / win32_stdin_read_thread() - export CURL_TEST_NO_TASKKILL=1 # experiment to see if it reduces flaky failures fi if [[ "${MATRIX_INSTALL} " = *'-libssh '* && \ "${MATRIX_ENV}" = *'x86_64'* ]]; then From 133785b15936d9417fdc41f77b87304b6bf458d5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 19:10:52 +0200 Subject: [PATCH 0815/1062] servers: drop re-registering the signal handler on modern systems Before this patch modern systems used `sigaction()` and `SA_RESTART` to install signal handlers, but the signal handler function itself still made a call to the legacy `signal()` function to re-register itself before returning. Re-registering the handler is not necessary with `sigaction()`. It's also undesired to use the legacy API when the modern one is available. Fix by guarding off this call in builds that support the modern API. Follow-up to 3fb6e5a01001b8c7dfdc33a89041178aac381a27 #6529 Follow-up to 18cbb4d7d645016d1a9c0ee772c724d8c41db3bd Closes #22497 --- tests/server/util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/server/util.c b/tests/server/util.c index 819e3432dc0c..397484a594d1 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -415,7 +415,9 @@ static void exit_signal_handler(int signum) (void)SetEvent(exit_event); #endif } +#if !(defined(HAVE_SIGACTION) && defined(SA_RESTART)) (void)signal(signum, exit_signal_handler); +#endif errno = old_errno; } #if defined(CURL_HAVE_DIAG) && !defined(__clang__) From 1637bbc9ced4213b902703b9223273ab2dcd2c6e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 07:59:29 +0200 Subject: [PATCH 0816/1062] servers: sync socket type global variables Replace `ipv_inuse` and `use_ipv6` with `socket_type` and `socket_domain` (where missing) to avoid dupliicate globals with overlapping purposes. The replacement variables also support Unix sockets. Also: - simplify/reduce IPv6 guards. - socksd: fix to reset `socket_domain` for `--ipv4` option. Closes #22498 --- tests/server/dnsd.c | 60 ++++++++++++---------------- tests/server/first.h | 4 -- tests/server/mqttd.c | 11 ++---- tests/server/resolve.c | 17 ++++---- tests/server/rtspd.c | 51 +++++++++++------------- tests/server/sockfilt.c | 11 ++---- tests/server/socksd.c | 8 ++-- tests/server/sws.c | 8 ++-- tests/server/tftpd.c | 88 ++++++++++++++++++----------------------- 9 files changed, 107 insertions(+), 151 deletions(-) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 9d460d9452e1..2d85550e5f23 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -829,16 +829,14 @@ static int test_dnsd(int argc, const char **argv) logdir = argv[arg++]; } else if(!strcmp("--ipv4", argv[arg])) { -#ifdef USE_IPV6 - ipv_inuse = "IPv4"; - use_ipv6 = FALSE; -#endif + socket_type = "IPv4"; + socket_domain = AF_INET; arg++; } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 - ipv_inuse = "IPv6"; - use_ipv6 = TRUE; + socket_type = "IPv6"; + socket_domain = AF_INET6; #endif arg++; } @@ -868,16 +866,14 @@ static int test_dnsd(int argc, const char **argv) } snprintf(loglockfile, sizeof(loglockfile), "%s/%s/dnsd-%s.lock", - logdir, SERVERLOGS_LOCKDIR, ipv_inuse); + logdir, SERVERLOGS_LOCKDIR, socket_type); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - sock = socket(AF_INET, SOCK_DGRAM, 0); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) sock = socket(AF_INET6, SOCK_DGRAM, 0); + else #endif + sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; @@ -897,23 +893,22 @@ static int test_dnsd(int argc, const char **argv) } #ifdef USE_IPV6 - if(!use_ipv6) { + if(socket_domain == AF_INET6) { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } + else #endif + { memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; me.sa4.sin_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa4)); -#ifdef USE_IPV6 } - else { - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); - } -#endif /* USE_IPV6 */ if(rc) { sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, @@ -929,13 +924,12 @@ static int test_dnsd(int argc, const char **argv) srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - la_size = sizeof(localaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); + else #endif + la_size = sizeof(localaddr.sa4); + if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", @@ -980,7 +974,7 @@ static int test_dnsd(int argc, const char **argv) } } - logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port); + logmsg("Running %s version on port UDP/%d", socket_type, (int)port); curlx_nonblock(sock, TRUE); for(;;) { @@ -994,13 +988,11 @@ static int test_dnsd(int argc, const char **argv) timediff_t timeout_ms = 0; fromlen = sizeof(from); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - fromlen = sizeof(from.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) fromlen = sizeof(from.sa6); + else #endif + fromlen = sizeof(from.sa4); timeout_ms = send_resp_queue(sock); @@ -1089,7 +1081,7 @@ static int test_dnsd(int argc, const char **argv) if(got_exit_signal) { logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", - ipv_inuse, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we diff --git a/tests/server/first.h b/tests/server/first.h index 3bd75a2d88f0..73ab1dbe8552 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -168,10 +168,6 @@ static int serverlogslocked; static const char *configfile = NULL; static const char *logdir = "log"; static char loglockfile[256]; -#ifdef USE_IPV6 -static bool use_ipv6 = FALSE; -#endif -static const char *ipv_inuse = "IPv4"; static unsigned short server_port = 0; static const char *socket_type = "IPv4"; static int socket_domain = AF_INET; diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 732bcd2efb75..9eb58d147371 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -814,17 +814,14 @@ static int test_mqttd(int argc, const char *argv[]) } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 + socket_type = "IPv6"; socket_domain = AF_INET6; - ipv_inuse = "IPv6"; #endif arg++; } else if(!strcmp("--ipv4", argv[arg])) { - /* for completeness, we support this option as well */ -#ifdef USE_IPV6 + socket_type = "IPv4"; socket_domain = AF_INET; - ipv_inuse = "IPv4"; -#endif arg++; } else if(!strcmp("--port", argv[arg])) { @@ -855,7 +852,7 @@ static int test_mqttd(int argc, const char *argv[]) } snprintf(loglockfile, sizeof(loglockfile), "%s/%s/mqtt-%s.lock", - logdir, SERVERLOGS_LOCKDIR, ipv_inuse); + logdir, SERVERLOGS_LOCKDIR, socket_type); CURL_BINMODE(stdin); CURL_BINMODE(stdout); @@ -881,7 +878,7 @@ static int test_mqttd(int argc, const char *argv[]) msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ } - logmsg("Running %s version", ipv_inuse); + logmsg("Running %s version", socket_type); logmsg("Listening on port %hu", server_port); wrotepidfile = write_pidfile(pidname); diff --git a/tests/server/resolve.c b/tests/server/resolve.c index ac72cddd3a8b..43690e7d237d 100644 --- a/tests/server/resolve.c +++ b/tests/server/resolve.c @@ -52,8 +52,8 @@ static int test_resolve(int argc, const char *argv[]) } else if(!strcmp("--ipv6", argv[arg])) { #ifdef CURLRES_IPV6 - ipv_inuse = "IPv6"; - use_ipv6 = TRUE; + socket_type = "IPv6"; + socket_domain = AF_INET6; arg++; #else puts("IPv6 support has been disabled in this program"); @@ -61,11 +61,8 @@ static int test_resolve(int argc, const char *argv[]) #endif } else if(!strcmp("--ipv4", argv[arg])) { - /* for completeness, we support this option as well */ - ipv_inuse = "IPv4"; -#ifdef CURLRES_IPV6 - use_ipv6 = FALSE; -#endif + socket_type = "IPv4"; + socket_domain = AF_INET; arg++; } else { @@ -84,7 +81,7 @@ static int test_resolve(int argc, const char *argv[]) } #ifdef CURLRES_IPV6 - if(use_ipv6) { + if(socket_domain == AF_INET6) { /* Check that the system has IPv6 enabled before checking the resolver */ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); if(s == CURL_SOCKET_BAD) @@ -101,7 +98,7 @@ static int test_resolve(int argc, const char *argv[]) struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = use_ipv6 ? PF_INET6 : PF_INET; + hints.ai_family = socket_domain; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; rc = getaddrinfo(host, "80", &hints, &ai); @@ -123,7 +120,7 @@ static int test_resolve(int argc, const char *argv[]) #endif if(rc) - printf("Resolving %s '%s' did not work\n", ipv_inuse, host); + printf("Resolving %s '%s' did not work\n", socket_type, host); return !!rc; } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index c7c7ed75e522..715b4c24f6fc 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -1000,16 +1000,14 @@ static int test_rtspd(int argc, const char *argv[]) logdir = argv[arg++]; } else if(!strcmp("--ipv4", argv[arg])) { -#ifdef USE_IPV6 - ipv_inuse = "IPv4"; - use_ipv6 = FALSE; -#endif + socket_type = "IPv4"; + socket_domain = AF_INET; arg++; } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 - ipv_inuse = "IPv6"; - use_ipv6 = TRUE; + socket_type = "IPv6"; + socket_domain = AF_INET6; #endif arg++; } @@ -1045,18 +1043,16 @@ static int test_rtspd(int argc, const char *argv[]) } snprintf(loglockfile, sizeof(loglockfile), "%s/%s/rtsp-%s.lock", - logdir, SERVERLOGS_LOCKDIR, ipv_inuse); + logdir, SERVERLOGS_LOCKDIR, socket_type); install_signal_handlers(FALSE); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - sock = socket(AF_INET, SOCK_STREAM, 0); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) sock = socket(AF_INET6, SOCK_STREAM, 0); + else #endif + sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; @@ -1074,23 +1070,22 @@ static int test_rtspd(int argc, const char *argv[]) } #ifdef USE_IPV6 - if(!use_ipv6) { + if(socket_domain == AF_INET6) { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } + else #endif + { memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; me.sa4.sin_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa4)); -#ifdef USE_IPV6 - } - else { - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); } -#endif /* USE_IPV6 */ if(rc) { sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, @@ -1105,13 +1100,11 @@ static int test_rtspd(int argc, const char *argv[]) srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - la_size = sizeof(localaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); + else #endif + la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", @@ -1141,7 +1134,7 @@ static int test_rtspd(int argc, const char *argv[]) goto server_cleanup; } } - logmsg("Running %s version on port %d", ipv_inuse, (int)port); + logmsg("Running %s version on port %d", socket_type, (int)port); /* start accepting connections */ if(listen(sock, 5)) { @@ -1287,7 +1280,7 @@ static int test_rtspd(int argc, const char *argv[]) if(got_exit_signal) { logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", - ipv_inuse, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 32838f987c96..85a281de572c 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -1057,7 +1057,7 @@ static bool juggle(curl_socket_t *sockfdp, /* Question asking us what PORT number we are listening to. Replies to PORT with "IPv[num]/[port]" */ snprintf((char *)buffer, sizeof(buffer), "%s/%hu\n", - ipv_inuse, server_port); + socket_type, server_port); buffer_len = (ssize_t)strlen((const char *)buffer); snprintf(data, sizeof(data), "PORT\n%04x\n", (unsigned int)buffer_len); if(!write_stdout(data, 10)) @@ -1216,17 +1216,14 @@ static int test_sockfilt(int argc, const char *argv[]) } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 + socket_type = "IPv6"; socket_domain = AF_INET6; - ipv_inuse = "IPv6"; #endif arg++; } else if(!strcmp("--ipv4", argv[arg])) { - /* for completeness, we support this option as well */ -#ifdef USE_IPV6 + socket_type = "IPv4"; socket_domain = AF_INET; - ipv_inuse = "IPv4"; -#endif arg++; } else if(!strcmp("--bindonly", argv[arg])) { @@ -1348,7 +1345,7 @@ static int test_sockfilt(int argc, const char *argv[]) msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ } - logmsg("Running %s version", ipv_inuse); + logmsg("Running %s version", socket_type); if(server_connectport) logmsg("Connected to port %hu", server_connectport); diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 4de563b06cab..7144c687bfe2 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -793,16 +793,14 @@ static int test_socksd(int argc, const char *argv[]) } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 - socket_domain = AF_INET6; socket_type = "IPv6"; + socket_domain = AF_INET6; #endif arg++; } else if(!strcmp("--ipv4", argv[arg])) { - /* for completeness, we support this option as well */ -#ifdef USE_IPV6 socket_type = "IPv4"; -#endif + socket_domain = AF_INET; arg++; } else if(!strcmp("--unix-socket", argv[arg])) { @@ -817,8 +815,8 @@ static int test_socksd(int argc, const char *argv[]) (unsigned int)sizeof(sau.sun_path), unix_socket); return 0; } - socket_domain = AF_UNIX; socket_type = "unix"; + socket_domain = AF_UNIX; #endif arg++; } diff --git a/tests/server/sws.c b/tests/server/sws.c index d68f60b5b803..1dee5da2aea5 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -2167,13 +2167,11 @@ static int test_sws(int argc, const char *argv[]) srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 - if(socket_domain != AF_INET6) -#endif - la_size = sizeof(localaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); + else #endif + la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 8144f81be887..eee7750db2f8 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -516,13 +516,11 @@ static int synchnet(curl_socket_t f /* socket to flush */) if(i) { j++; #ifdef USE_IPV6 - if(!use_ipv6) -#endif - fromaddrlen = sizeof(fromaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) fromaddrlen = sizeof(fromaddr.sa6); + else #endif + fromaddrlen = sizeof(fromaddr.sa4); (void)recvfrom(f, rbuf, sizeof(rbuf), 0, &fromaddr.sa, &fromaddrlen); } else @@ -1062,16 +1060,14 @@ static int test_tftpd(int argc, const char **argv) logdir = argv[arg++]; } else if(!strcmp("--ipv4", argv[arg])) { -#ifdef USE_IPV6 - ipv_inuse = "IPv4"; - use_ipv6 = FALSE; -#endif + socket_type = "IPv4"; + socket_domain = AF_INET; arg++; } else if(!strcmp("--ipv6", argv[arg])) { #ifdef USE_IPV6 - ipv_inuse = "IPv6"; - use_ipv6 = TRUE; + socket_type = "IPv6"; + socket_domain = AF_INET6; #endif arg++; } @@ -1107,18 +1103,16 @@ static int test_tftpd(int argc, const char **argv) } snprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock", - logdir, SERVERLOGS_LOCKDIR, ipv_inuse); + logdir, SERVERLOGS_LOCKDIR, socket_type); install_signal_handlers(TRUE); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - sock = socket(AF_INET, SOCK_DGRAM, 0); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) sock = socket(AF_INET6, SOCK_DGRAM, 0); + else #endif + sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; @@ -1138,23 +1132,22 @@ static int test_tftpd(int argc, const char **argv) } #ifdef USE_IPV6 - if(!use_ipv6) { + if(socket_domain == AF_INET6) { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } + else #endif + { memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; me.sa4.sin_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa4)); -#ifdef USE_IPV6 } - else { - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); - } -#endif /* USE_IPV6 */ if(rc) { sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, @@ -1170,13 +1163,11 @@ static int test_tftpd(int argc, const char **argv) srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - la_size = sizeof(localaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); + else #endif + la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", @@ -1221,18 +1212,16 @@ static int test_tftpd(int argc, const char **argv) } } - logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port); + logmsg("Running %s version on port UDP/%d", socket_type, (int)port); for(;;) { fromlen = sizeof(from); #ifdef USE_IPV6 - if(!use_ipv6) -#endif - fromlen = sizeof(from.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) fromlen = sizeof(from.sa6); + else #endif + fromlen = sizeof(from.sa4); n = (ssize_t)recvfrom(sock, &trsbuf.storage[0], sizeof(trsbuf.storage), 0, &from.sa, &fromlen); if(got_exit_signal) @@ -1247,37 +1236,36 @@ static int test_tftpd(int argc, const char **argv) serverlogslocked = 1; #ifdef USE_IPV6 - if(!use_ipv6) { -#endif - from.sa4.sin_family = AF_INET; - peer = socket(AF_INET, SOCK_DGRAM, 0); + if(socket_domain == AF_INET6) { + from.sa6.sin6_family = AF_INET6; + peer = socket(AF_INET6, SOCK_DGRAM, 0); if(peer == CURL_SOCKET_BAD) { logmsg("socket"); result = 2; break; } - if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) { + if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) { logmsg("connect: fail"); result = 1; break; } -#ifdef USE_IPV6 } - else { - from.sa6.sin6_family = AF_INET6; - peer = socket(AF_INET6, SOCK_DGRAM, 0); + else +#endif + { + from.sa4.sin_family = AF_INET; + peer = socket(AF_INET, SOCK_DGRAM, 0); if(peer == CURL_SOCKET_BAD) { logmsg("socket"); result = 2; break; } - if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) { + if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) { logmsg("connect: fail"); result = 1; break; } } -#endif maxtimeout = 5 * TIMEOUT; @@ -1331,7 +1319,7 @@ static int test_tftpd(int argc, const char **argv) if(got_exit_signal) { logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", - ipv_inuse, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we From fd24c4bcf4880b0b8ca02e48075abd3adb02af18 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 08:58:22 +0200 Subject: [PATCH 0817/1062] tests/server/dnsd: fix to install signal handlers on startup To sync with the rest of code which already handled `got_exit_signal` and did the restoration on exit. Also syncing with rest of servers. Also: turn off `keep_sigalrm` to sync with most servers. Closes #22499 --- tests/server/dnsd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 2d85550e5f23..f7a506fe0c02 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -868,6 +868,8 @@ static int test_dnsd(int argc, const char **argv) snprintf(loglockfile, sizeof(loglockfile), "%s/%s/dnsd-%s.lock", logdir, SERVERLOGS_LOCKDIR, socket_type); + install_signal_handlers(FALSE); + #ifdef USE_IPV6 if(socket_domain == AF_INET6) sock = socket(AF_INET6, SOCK_DGRAM, 0); @@ -1077,7 +1079,8 @@ static int test_dnsd(int argc, const char **argv) } clear_resp_queue(); - restore_signal_handlers(TRUE); + + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", From d1eca3861c9cf8f3cd03274ce0db3c500bd246ae Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 09:27:41 +0200 Subject: [PATCH 0818/1062] servers: sync server port global variable across servers, make port variables `uint16_t` Follow-up to 1637bbc9ced4213b902703b9223273ab2dcd2c6e #22498 Closes #22501 --- tests/server/dnsd.c | 24 ++++++++++++------------ tests/server/first.h | 4 ++-- tests/server/mqttd.c | 2 +- tests/server/rtspd.c | 24 ++++++++++++------------ tests/server/sockfilt.c | 8 ++++---- tests/server/socksd.c | 24 ++++++++++++------------ tests/server/sws.c | 32 ++++++++++++++++---------------- tests/server/tftpd.c | 24 ++++++++++++------------ tests/server/util.c | 2 +- 9 files changed, 72 insertions(+), 72 deletions(-) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index f7a506fe0c02..230204b52bb1 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -778,7 +778,6 @@ static int test_dnsd(int argc, const char **argv) srvr_sockaddr_union_t me; ssize_t n = 0; int arg = 1; - uint16_t port = 9123; /* UDP */ curl_socket_t sock = CURL_SOCKET_BAD; int flag; int rc; @@ -790,6 +789,7 @@ static int test_dnsd(int argc, const char **argv) pidname = ".dnsd.pid"; serverlogfile = "log/dnsd.log"; serverlogslocked = 0; + server_port = 9123; /* UDP */ while(argc > arg) { const char *opt; @@ -845,7 +845,7 @@ static int test_dnsd(int argc, const char **argv) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - port = (uint16_t)num; + server_port = (uint16_t)num; arg++; } } @@ -899,7 +899,7 @@ static int test_dnsd(int argc, const char **argv) memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); + me.sa6.sin6_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa6)); } else @@ -908,18 +908,18 @@ static int test_dnsd(int argc, const char **argv) memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(port); + me.sa4.sin_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa4)); } if(rc) { sockerr = SOCKERRNO; - logmsg("Error binding socket on port %hu (%d) %s", port, + logmsg("Error binding socket on port %hu (%d) %s", server_port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto dnsd_cleanup; } - if(!port) { + if(!server_port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; @@ -941,17 +941,17 @@ static int test_dnsd(int argc, const char **argv) } switch(localaddr.sa.sa_family) { case AF_INET: - port = ntohs(localaddr.sa4.sin_port); + server_port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: - port = ntohs(localaddr.sa6.sin6_port); + server_port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } - if(!port) { + if(!server_port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); @@ -969,14 +969,14 @@ static int test_dnsd(int argc, const char **argv) } if(portname) { - dnsd_wroteportfile = write_portfile(portname, port); + dnsd_wroteportfile = write_portfile(portname, server_port); if(!dnsd_wroteportfile) { result = 1; goto dnsd_cleanup; } } - logmsg("Running %s version on port UDP/%d", socket_type, (int)port); + logmsg("Running %s version on port UDP/%d", socket_type, (int)server_port); curlx_nonblock(sock, TRUE); for(;;) { @@ -1084,7 +1084,7 @@ static int test_dnsd(int argc, const char **argv) if(got_exit_signal) { logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)server_port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we diff --git a/tests/server/first.h b/tests/server/first.h index 73ab1dbe8552..64c7c63719b2 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -155,7 +155,7 @@ extern int bind_unix_socket(curl_socket_t sock, const char *unix_socket, struct sockaddr_un *sau); #endif extern curl_socket_t sockdaemon(curl_socket_t sock, - unsigned short *listenport, + uint16_t *listenport, const char *unix_socket, bool bind_only); @@ -168,7 +168,7 @@ static int serverlogslocked; static const char *configfile = NULL; static const char *logdir = "log"; static char loglockfile[256]; -static unsigned short server_port = 0; +static uint16_t server_port = 0; static const char *socket_type = "IPv4"; static int socket_domain = AF_INET; diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 9eb58d147371..9be49513e2c8 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -832,7 +832,7 @@ static int test_mqttd(int argc, const char *argv[]) fprintf(stderr, "mqttd: invalid --port argument (%s)\n", argv[arg]); return 0; } - server_port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 715b4c24f6fc..573865f3cfbf 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -952,7 +952,6 @@ static int test_rtspd(int argc, const char *argv[]) int wrotepidfile = 0; int wroteportfile = 0; int flag; - unsigned short port = 8999; struct rtspd_httprequest req; int rc; int sockerr; @@ -964,6 +963,7 @@ static int test_rtspd(int argc, const char *argv[]) pidname = ".rtsp.pid"; serverlogfile = "log/rtspd.log"; serverlogslocked = 0; + server_port = 8999; while(argc > arg) { const char *opt; @@ -1016,7 +1016,7 @@ static int test_rtspd(int argc, const char *argv[]) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } @@ -1074,7 +1074,7 @@ static int test_rtspd(int argc, const char *argv[]) memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); + me.sa6.sin6_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa6)); } else @@ -1083,17 +1083,17 @@ static int test_rtspd(int argc, const char *argv[]) memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(port); + me.sa4.sin_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa4)); } if(rc) { sockerr = SOCKERRNO; - logmsg("Error binding socket on port %hu (%d) %s", port, + logmsg("Error binding socket on port %hu (%d) %s", server_port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto server_cleanup; } - if(!port) { + if(!server_port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; @@ -1114,17 +1114,17 @@ static int test_rtspd(int argc, const char *argv[]) } switch(localaddr.sa.sa_family) { case AF_INET: - port = ntohs(localaddr.sa4.sin_port); + server_port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: - port = ntohs(localaddr.sa6.sin6_port); + server_port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } - if(!port) { + if(!server_port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); @@ -1134,7 +1134,7 @@ static int test_rtspd(int argc, const char *argv[]) goto server_cleanup; } } - logmsg("Running %s version on port %d", socket_type, (int)port); + logmsg("Running %s version on port %d", socket_type, (int)server_port); /* start accepting connections */ if(listen(sock, 5)) { @@ -1154,7 +1154,7 @@ static int test_rtspd(int argc, const char *argv[]) goto server_cleanup; if(portname) { - wroteportfile = write_portfile(portname, port); + wroteportfile = write_portfile(portname, server_port); if(!wroteportfile) goto server_cleanup; } @@ -1280,7 +1280,7 @@ static int test_rtspd(int argc, const char *argv[]) if(got_exit_signal) { logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)server_port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 85a281de572c..a5fde92599c5 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -90,8 +90,8 @@ static bool verbose = FALSE; static bool s_bind_only = FALSE; -static unsigned short server_connectport = 0; /* if non-zero, - we activate this mode */ +static uint16_t server_connectport = 0; /* if non-zero, + we activate this mode */ enum sockmode { PASSIVE_LISTEN, /* as a server waiting for connections */ @@ -1235,7 +1235,7 @@ static int test_sockfilt(int argc, const char *argv[]) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - server_port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } @@ -1250,7 +1250,7 @@ static int test_sockfilt(int argc, const char *argv[]) argv[arg]); return 0; } - server_connectport = (unsigned short)num; + server_connectport = (uint16_t)num; arg++; } } diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 7144c687bfe2..2eba8b1df0b3 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -57,7 +57,7 @@ /* based on sockfilt.c */ static const char *backendaddr = "127.0.0.1"; -static unsigned short backendport = 0; /* default is use client's */ +static uint16_t backendport = 0; /* default is use client's */ struct socksd_configurable { unsigned char version; /* initial version byte in the request must match @@ -68,7 +68,7 @@ struct socksd_configurable { unsigned char responsemethod; unsigned char reqcmd; unsigned char connectrep; - unsigned short port; /* backend port */ + uint16_t port; /* backend port */ char addr[32]; /* backend IPv4 numerical */ char user[256]; char password[256]; @@ -149,7 +149,7 @@ static void socksd_getconfig(void) else if(!strcmp(key, "backendport")) { pval = value; if(!curlx_str_number(&pval, &num, 0xffff)) { - s_config.port = (unsigned short)num; + s_config.port = (uint16_t)num; logmsg("backendport [%d] set", s_config.port); } } @@ -210,7 +210,7 @@ static void socksd_getconfig(void) #define SOCKS4_DSTPORT 2 /* connect to a given IPv4 address, not the one asked for */ -static curl_socket_t socksconnect(unsigned short connectport, +static curl_socket_t socksconnect(uint16_t connectport, const char *connectaddr) { srvr_sockaddr_union_t me; @@ -241,7 +241,7 @@ static curl_socket_t socks4(curl_socket_t fd, unsigned char response[256 + 16]; curl_socket_t connfd; unsigned char cd; - unsigned short s4port; + uint16_t s4port; if(buffer[SOCKS4_CD] != 1) { logmsg("SOCKS4 CD is not 1: %d", buffer[SOCKS4_CD]); @@ -252,8 +252,8 @@ static curl_socket_t socks4(curl_socket_t fd, return CURL_SOCKET_BAD; } if(!s_config.port) - s4port = (unsigned short)((buffer[SOCKS4_DSTPORT] << 8) | - (buffer[SOCKS4_DSTPORT + 1])); + s4port = (uint16_t)((buffer[SOCKS4_DSTPORT] << 8) | + (buffer[SOCKS4_DSTPORT + 1])); else s4port = s_config.port; @@ -297,9 +297,9 @@ static curl_socket_t sockit(curl_socket_t fd) unsigned char type; unsigned char rep = 0; const unsigned char *address; - unsigned short socksport; + uint16_t socksport; curl_socket_t connfd = CURL_SOCKET_BAD; - unsigned short s5port; + uint16_t s5port; socksd_getconfig(); @@ -500,7 +500,7 @@ static curl_socket_t sockit(curl_socket_t fd) if(!s_config.port) { const unsigned char *portp = &buffer[SOCKS5_DSTADDR + len]; - s5port = (unsigned short)((portp[0] << 8) | (portp[1])); + s5port = (uint16_t)((portp[0] << 8) | (portp[1])); } else s5port = s_config.port; @@ -777,7 +777,7 @@ static int test_socksd(int argc, const char *argv[]) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - backendport = (unsigned short)num; + backendport = (uint16_t)num; arg++; } } @@ -826,7 +826,7 @@ static int test_socksd(int argc, const char *argv[]) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - server_port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } diff --git a/tests/server/sws.c b/tests/server/sws.c index 1dee5da2aea5..310c7263e168 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -53,7 +53,7 @@ static bool sws_prevbounce = FALSE; /* instructs the server to override the struct sws_httprequest { char reqbuf[2 * 1024 * 1024]; /* buffer area for the incoming request */ bool connect_request; /* if a CONNECT */ - unsigned short connect_port; /* the port number CONNECT used */ + uint16_t connect_port; /* the port number CONNECT used */ size_t checkindex; /* where to start checking of the request */ size_t offset; /* size of the incoming request */ long testno; /* test number found in the request */ @@ -487,9 +487,9 @@ static int sws_ProcessRequest(struct sws_httprequest *req) (num <= 0) || (num > 65535)) logmsg("Invalid CONNECT port received"); else - req->connect_port = (unsigned short)num; + req->connect_port = (uint16_t)num; } - logmsg("Port number: %d, test case number: %ld", + logmsg("Port number: %hu, test case number: %ld", req->connect_port, req->testno); } } @@ -1193,7 +1193,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) return fail ? -1 : 1; } -static curl_socket_t connect_to(const char *ipaddr, unsigned short port) +static curl_socket_t connect_to(const char *ipaddr, uint16_t port) { srvr_sockaddr_union_t serveraddr; curl_socket_t serverfd; @@ -1355,7 +1355,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) static void http_connect(curl_socket_t *infdp, curl_socket_t rootfd, const char *ipaddr, - unsigned short ipport, + uint16_t ipport, int keepalive_secs) { curl_socket_t serverfd[2] = { CURL_SOCKET_BAD, CURL_SOCKET_BAD }; @@ -1919,7 +1919,6 @@ static int test_sws(int argc, const char *argv[]) int wrotepidfile = 0; int wroteportfile = 0; int flag; - unsigned short port = 8999; #ifdef USE_UNIX_SOCKETS const char *unix_socket = NULL; bool unlink_socket = FALSE; @@ -1942,6 +1941,7 @@ static int test_sws(int argc, const char *argv[]) portname = ".http.port"; serverlogfile = "log/sws.log"; serverlogslocked = 0; + server_port = 8999; while(argc > arg) { const char *opt; @@ -2028,7 +2028,7 @@ static int test_sws(int argc, const char *argv[]) fprintf(stderr, "sws: invalid --port argument (%s)\n", argv[arg]); return 0; } - port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } @@ -2130,7 +2130,7 @@ static int test_sws(int argc, const char *argv[]) memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(port); + me.sa4.sin_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa4)); break; #ifdef USE_IPV6 @@ -2138,7 +2138,7 @@ static int test_sws(int argc, const char *argv[]) memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); + me.sa6.sin6_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa6)); break; #endif /* USE_IPV6 */ @@ -2155,12 +2155,12 @@ static int test_sws(int argc, const char *argv[]) sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif - logmsg("Error binding socket on port %hu (%d) %s", port, + logmsg("Error binding socket on port %hu (%d) %s", server_port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } - if(!port) { + if(!server_port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; @@ -2181,17 +2181,17 @@ static int test_sws(int argc, const char *argv[]) } switch(localaddr.sa.sa_family) { case AF_INET: - port = ntohs(localaddr.sa4.sin_port); + server_port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: - port = ntohs(localaddr.sa6.sin6_port); + server_port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } - if(!port) { + if(!server_port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); @@ -2204,7 +2204,7 @@ static int test_sws(int argc, const char *argv[]) #ifdef USE_UNIX_SOCKETS if(socket_domain != AF_UNIX) #endif - snprintf(port_str, sizeof(port_str), "port %hu", port); + snprintf(port_str, sizeof(port_str), "port %hu", server_port); logmsg("Running %s %s version on %s", protocol_type, socket_type, location_str); @@ -2231,7 +2231,7 @@ static int test_sws(int argc, const char *argv[]) if(!wrotepidfile) goto sws_cleanup; - wroteportfile = write_portfile(portname, port); + wroteportfile = write_portfile(portname, server_port); if(!wroteportfile) goto sws_cleanup; diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index eee7750db2f8..43298c2a132d 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -1009,7 +1009,6 @@ static int test_tftpd(int argc, const char **argv) struct tftphdr *tp; ssize_t n = 0; int arg = 1; - unsigned short port = 8999; /* UDP */ curl_socket_t sock = CURL_SOCKET_BAD; int flag; int rc; @@ -1025,6 +1024,7 @@ static int test_tftpd(int argc, const char **argv) pidname = ".tftpd.pid"; serverlogfile = "log/tftpd.log"; serverlogslocked = 0; + server_port = 8999; /* UDP */ while(argc > arg) { const char *opt; @@ -1076,7 +1076,7 @@ static int test_tftpd(int argc, const char **argv) if(argc > arg) { opt = argv[arg]; if(!curlx_str_number(&opt, &num, 0xffff)) - port = (unsigned short)num; + server_port = (uint16_t)num; arg++; } } @@ -1136,7 +1136,7 @@ static int test_tftpd(int argc, const char **argv) memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(port); + me.sa6.sin6_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa6)); } else @@ -1145,18 +1145,18 @@ static int test_tftpd(int argc, const char **argv) memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(port); + me.sa4.sin_port = htons(server_port); rc = bind(sock, &me.sa, sizeof(me.sa4)); } if(rc) { sockerr = SOCKERRNO; - logmsg("Error binding socket on port %hu (%d) %s", port, + logmsg("Error binding socket on port %hu (%d) %s", server_port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto tftpd_cleanup; } - if(!port) { + if(!server_port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; @@ -1177,17 +1177,17 @@ static int test_tftpd(int argc, const char **argv) } switch(localaddr.sa.sa_family) { case AF_INET: - port = ntohs(localaddr.sa4.sin_port); + server_port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: - port = ntohs(localaddr.sa6.sin6_port); + server_port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } - if(!port) { + if(!server_port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); @@ -1205,14 +1205,14 @@ static int test_tftpd(int argc, const char **argv) } if(portname) { - tftpd_wroteportfile = write_portfile(portname, port); + tftpd_wroteportfile = write_portfile(portname, server_port); if(!tftpd_wroteportfile) { result = 1; goto tftpd_cleanup; } } - logmsg("Running %s version on port UDP/%d", socket_type, (int)port); + logmsg("Running %s version on port UDP/%d", socket_type, (int)server_port); for(;;) { fromlen = sizeof(from); @@ -1319,7 +1319,7 @@ static int test_tftpd(int argc, const char **argv) if(got_exit_signal) { logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)port, (long)our_getpid(), exit_signal); + socket_type, (int)server_port, (long)our_getpid(), exit_signal); /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we diff --git a/tests/server/util.c b/tests/server/util.c index 397484a594d1..6843707b46e7 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -768,7 +768,7 @@ int bind_unix_socket(curl_socket_t sock, const char *unix_socket, #endif curl_socket_t sockdaemon(curl_socket_t sock, - unsigned short *listenport, + uint16_t *listenport, const char *unix_socket, bool bind_only) { From 6e96c468d4078f451f889e73106252c9a72dcf8a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 13:40:18 +0200 Subject: [PATCH 0819/1062] DEPRECATE.md: HTTP/2 Server Push gets removed in March 2027 URL: https://curl.se/mail/lib-2026-08/0003.html Closes #22490 --- docs/DEPRECATE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/DEPRECATE.md b/docs/DEPRECATE.md index 62822375291a..e20fba5d617d 100644 --- a/docs/DEPRECATE.md +++ b/docs/DEPRECATE.md @@ -47,6 +47,16 @@ future curl versions when built without TLS support. For example Digest. Local crypto gets removed in October 2026. +## HTTP/2 Server Push + +This protocol feature has been deprecated in specifications, by major browsers, +and in server implementations. It was never supported by the curl command line +tool, only by libcurl. + +We estimate that barely any libcurl users still use this feature. + +HTTP/2 Server Push gets removed in March 2027. + ## Past removals - axTLS (removed in 7.63.0) From 3822fa865894da8c3a94aee23f14551c60af05c2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 07:45:14 +0200 Subject: [PATCH 0820/1062] servers: de-duplicate shutdown signal/logging code To share more common code between servers, and to log the same set of information for all of them. Also: - move server unix socket to global variable. Follow-up to d1eca3861c9cf8f3cd03274ce0db3c500bd246ae #22501 Follow-up to 1637bbc9ced4213b902703b9223273ab2dcd2c6e #22498 Closes #22503 --- tests/server/dnsd.c | 12 ------------ tests/server/first.c | 30 +++++++++++++++++++++++++++++- tests/server/first.h | 1 + tests/server/mqttd.c | 11 ----------- tests/server/rtspd.c | 12 ------------ tests/server/sockfilt.c | 11 ----------- tests/server/socksd.c | 29 ++++++++--------------------- tests/server/sws.c | 31 +++++++++---------------------- tests/server/tftpd.c | 12 ------------ 9 files changed, 47 insertions(+), 102 deletions(-) diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 230204b52bb1..8e195e5c61a9 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -1082,18 +1082,6 @@ static int test_dnsd(int argc, const char **argv) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)server_port, (long)our_getpid(), exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("========> dnsd quits"); return result; } #else diff --git a/tests/server/first.c b/tests/server/first.c index 31ba5f72b4df..a3fc4308f7d0 100644 --- a/tests/server/first.c +++ b/tests/server/first.c @@ -29,6 +29,7 @@ int main(int argc, const char **argv) { entry_func_t entry_func; const char *entry_name; + int result; size_t tmp; if(argc < 2) { @@ -55,5 +56,32 @@ int main(int argc, const char **argv) return 2; #endif - return entry_func(argc - 1, argv + 1); + result = entry_func(argc - 1, argv + 1); + + if(got_exit_signal) { + char port_str[11]; + const char *location_str = port_str; + snprintf(port_str, sizeof(port_str), "port %hu", server_port); + +#ifdef USE_UNIX_SOCKETS + if(socket_domain == AF_UNIX) + location_str = server_unix_socket ? server_unix_socket + : ""; +#endif + + logmsg("========> %s %s (%s pid: %ld) exits with signal (%d)", + socket_type, entry_name, + location_str, (long)our_getpid(), exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } + + if(serverlogfile) + logmsg("========> %s quits", entry_name); + + return result; } diff --git a/tests/server/first.h b/tests/server/first.h index 64c7c63719b2..73657a86ba71 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -168,6 +168,7 @@ static int serverlogslocked; static const char *configfile = NULL; static const char *logdir = "log"; static char loglockfile[256]; +static const char *server_unix_socket = NULL; static uint16_t server_port = 0; static const char *socket_type = "IPv4"; static int socket_domain = AF_INET; diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 9be49513e2c8..766b86ca8a32 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -910,16 +910,5 @@ static int test_mqttd(int argc, const char *argv[]) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("============> mqttd exits with signal (%d)", exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("============> mqttd quits"); return 0; } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 573865f3cfbf..44e4f68655a4 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -1278,17 +1278,5 @@ static int test_rtspd(int argc, const char *argv[]) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)server_port, (long)our_getpid(), exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("========> rtspd quits"); return 0; } diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index a5fde92599c5..ff962f925fae 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -1386,16 +1386,5 @@ static int test_sockfilt(int argc, const char *argv[]) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("============> sockfilt exits with signal (%d)", exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("============> sockfilt quits"); return 0; } diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 2eba8b1df0b3..02ed6e8e4fc3 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -728,8 +728,6 @@ static int test_socksd(int argc, const char *argv[]) bool juggle_again; char errbuf[STRERROR_LEN]; int arg = 1; - - const char *unix_socket = NULL; #ifdef USE_UNIX_SOCKETS bool unlink_socket = FALSE; #endif @@ -808,11 +806,11 @@ static int test_socksd(int argc, const char *argv[]) if(argc > arg) { #ifdef USE_UNIX_SOCKETS struct sockaddr_un sau; - unix_socket = argv[arg]; - if(strlen(unix_socket) >= sizeof(sau.sun_path)) { + server_unix_socket = argv[arg]; + if(strlen(server_unix_socket) >= sizeof(sau.sun_path)) { fprintf(stderr, "socksd: socket path must be shorter than %u chars: %s\n", - (unsigned int)sizeof(sau.sun_path), unix_socket); + (unsigned int)sizeof(sau.sun_path), server_unix_socket); return 0; } socket_type = "unix"; @@ -865,7 +863,7 @@ static int test_socksd(int argc, const char *argv[]) { /* passive daemon style */ - sock = sockdaemon(sock, &server_port, unix_socket, FALSE); + sock = sockdaemon(sock, &server_port, server_unix_socket, FALSE); if(sock == CURL_SOCKET_BAD) { goto socks5_cleanup; } @@ -879,7 +877,7 @@ static int test_socksd(int argc, const char *argv[]) #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) - logmsg("Listening on Unix socket %s", unix_socket); + logmsg("Listening on Unix socket %s", server_unix_socket); else #endif logmsg("Listening on port %hu", server_port); @@ -909,9 +907,9 @@ static int test_socksd(int argc, const char *argv[]) sclose(sock); #ifdef USE_UNIX_SOCKETS - if(unlink_socket && socket_domain == AF_UNIX && unix_socket && - unlink(unix_socket)) - logmsg("unlink(%s): %d (%s)", unix_socket, + if(unlink_socket && socket_domain == AF_UNIX && server_unix_socket && + unlink(server_unix_socket)) + logmsg("unlink(%s): %d (%s)", server_unix_socket, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif @@ -922,16 +920,5 @@ static int test_socksd(int argc, const char *argv[]) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("============> socksd exits with signal (%d)", exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("============> socksd quits"); return 0; } diff --git a/tests/server/sws.c b/tests/server/sws.c index 310c7263e168..6c18b9a3995b 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -1920,7 +1920,6 @@ static int test_sws(int argc, const char *argv[]) int wroteportfile = 0; int flag; #ifdef USE_UNIX_SOCKETS - const char *unix_socket = NULL; bool unlink_socket = FALSE; #endif struct sws_httprequest *req = NULL; @@ -2006,16 +2005,16 @@ static int test_sws(int argc, const char *argv[]) arg++; if(argc > arg) { #ifdef USE_UNIX_SOCKETS - unix_socket = argv[arg]; - if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) { + server_unix_socket = argv[arg]; + if(strlen(server_unix_socket) >= sizeof(me.sau.sun_path)) { fprintf(stderr, "sws: socket path must be shorter than %u chars: %s\n", - (unsigned int)sizeof(me.sau.sun_path), unix_socket); + (unsigned int)sizeof(me.sau.sun_path), server_unix_socket); return 0; } socket_type = "unix"; socket_domain = AF_UNIX; - location_str = unix_socket; + location_str = server_unix_socket; #endif arg++; } @@ -2144,14 +2143,14 @@ static int test_sws(int argc, const char *argv[]) #endif /* USE_IPV6 */ #ifdef USE_UNIX_SOCKETS case AF_UNIX: - rc = bind_unix_socket(sock, unix_socket, &me.sau); + rc = bind_unix_socket(sock, server_unix_socket, &me.sau); #endif /* USE_UNIX_SOCKETS */ } if(rc) { sockerr = SOCKERRNO; #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) - logmsg("Error binding socket on path %s (%d) %s", unix_socket, + logmsg("Error binding socket on path %s (%d) %s", server_unix_socket, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif @@ -2392,9 +2391,9 @@ static int test_sws(int argc, const char *argv[]) sclose(sock); #ifdef USE_UNIX_SOCKETS - if(unlink_socket && socket_domain == AF_UNIX && unix_socket && - unlink(unix_socket)) - logmsg("unlink(%s): %d (%s)", unix_socket, + if(unlink_socket && socket_domain == AF_UNIX && server_unix_socket && + unlink(server_unix_socket)) + logmsg("unlink(%s): %d (%s)", server_unix_socket, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif @@ -2415,17 +2414,5 @@ static int test_sws(int argc, const char *argv[]) restore_signal_handlers(FALSE); - if(got_exit_signal) { - logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)", - socket_type, location_str, (long)our_getpid(), exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("========> sws quits"); return 0; } diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 43298c2a132d..86d42f6f558c 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -1317,17 +1317,5 @@ static int test_tftpd(int argc, const char **argv) restore_signal_handlers(TRUE); - if(got_exit_signal) { - logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", - socket_type, (int)server_port, (long)our_getpid(), exit_signal); - /* - * To properly set the return status of the process we - * must raise the same signal SIGINT or SIGTERM that we - * caught and let the old handler take care of it. - */ - raise(exit_signal); - } - - logmsg("========> tftpd quits"); return result; } From 3aae64e4fbee7c1fa408c54df18d3f631781c283 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 11:54:04 +0200 Subject: [PATCH 0821/1062] servers: drop complex and redundant signal handler output In year 2020 the Unixy signal handler received a `logmsg()` call to log the signal number, but at the same time it already saved it to a global variable and logged it on exit, meaning this extra `logmsg()` was redundant. Because `logmsg()` is not signal-safe, this call was replaced in 2025 with signal-safe logging, but without the signal number, while also adding complexity, spent on trying to open the log file and handle errors. All for nothing, because the signal number was logged all along. This patch removes all this, and simplifies it to a single, signal-safe `write()` to STDERR to say that the signal handler triggered. This is also non-critical, but may help debugging. Also: point the POSIX documentation to the 2004 revision, which has a shorter list of safe functions. (was: 2018) Follow-up to e95f509c66abdd88ae02e3243cdc217f19c4a330 #16852 Follow-up to 9869f6dc5af85caf2e0fd4c56713a7f2049ecfff #5218 Closes #22507 --- tests/server/util.c | 49 +++++++++------------------------------------ 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/tests/server/util.c b/tests/server/util.c index 6843707b46e7..6782c0f1e97d 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -365,48 +365,22 @@ static HWND hidden_main_window = NULL; * * Only call signal-safe functions from the signal handler, as required by * the POSIX specification: - * https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html - * Hence, do not call 'logmsg()', and instead use 'open/write/close' to - * log errors. + * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 */ -/* suppress warnings seen in configurations where 'write()' has the attribute - 'warn_unused_result', which is not silenced by casting to '(void)'. */ +static void exit_signal_handler(int signum) +{ + int old_errno = errno; + static const char msg[] = "exit_signal_handler(): triggered\n"; + /* suppress warning seen in configurations where 'write()' has the attribute + 'warn_unused_result', which is not silenced by casting to '(void)'. */ #if defined(CURL_HAVE_DIAG) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" /* GCC 4.5+ */ #endif -static void exit_signal_handler(int signum) -{ - int old_errno = errno; - if(!serverlogfile) { - static const char msg[] = "exit_signal_handler: serverlogfile not set\n"; - (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); - } - else { - int fd = -1; -#ifdef _WIN32 - if(!_sopen_s(&fd, serverlogfile, _O_WRONLY | _O_CREAT | _O_APPEND, - _SH_DENYNO, _S_IREAD | _S_IWRITE) && - fd != -1) { -#else - /* !checksrc! disable BANNEDFUNC 1 */ - fd = open(serverlogfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); - if(fd != -1) { + (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); +#if defined(CURL_HAVE_DIAG) && !defined(__clang__) +#pragma GCC diagnostic pop #endif - static const char msg[] = "exit_signal_handler: called\n"; - (void)write(fd, msg, CURL_CSTRLEN(msg)); - curlx_close(fd); - } - else { - static const char msg[] = "exit_signal_handler: failed opening "; - const char *str; - (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); - str = serverlogfile; - while(*str) - (void)write(STDERR_FILENO, str++, 1); - (void)write(STDERR_FILENO, "\n", 1); - } - } if(got_exit_signal == 0) { got_exit_signal = 1; exit_signal = signum; @@ -420,9 +394,6 @@ static void exit_signal_handler(int signum) #endif errno = old_errno; } -#if defined(CURL_HAVE_DIAG) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif #ifdef _WIN32 /* CTRL event handler for Windows Console applications to simulate From 9ea48811fed455d4a869eb100b2216f039f8677a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 5 Aug 2026 09:43:57 +0200 Subject: [PATCH 0822/1062] servers: drop duplicate (and interacting) ctrl handlers on Windows, add exit message On Windows, the init code calls `SetConsoleCtrlHandler()`, and before this patch also set handlers for all Unixy signals. Of these, `SIGBREAK` (used on Windows-only), `SIGINT`, `SIGABRT` and `SIGTERM` were also setting up a `SetConsoleCtrlHandler()`, in addition to the call made directly. (The rest, `SIGHUP`, `SIGPIPE`, `SIGALRM` are either missing the macros, or ignored by `signal()` on Windows.) As per WINE sources, `SetConsolCtrlHandler(, TRUE)` calls are additive, which means the test server set up two console ctrl handlers. Then the ctrl handler set directly (`ctrl_event_handler()`), was triggering the other signal handler via `raise()`, for the 'initiate exit' logic, which in turn triggered exiting a wait within `select_ws()` and other loops. The Windows window handler also made use of the `SIGTERM` event to initiate exit via `raise()` and the second signal handler. To simplify, de-duplicate the ctrl handlers by dropping `signal()` calls and keeping the direct Win32 call with `ctrl_event_handler()` doing all the signal handling on Windows. Break out the 'initiate exit' logic into a function and call it from both Unix and Windows signal/ctrl/window handlers. Also drop calling `raise()` on exit, because it's a no-op without a `signal()` pair. Also: - drop logging the actual ctrl type number, replace with just logging whether we handled the event, in `ctrl_event_handler()`. To avoid using non-signal-safe functions (e.g. `fprintf()`) from the handler. - also replace `logmsg()` with `WriteFile()` to prevent regressions. Ref: #22045 - replace `logmsg()` with `WriteFile()` in `main_window_proc()`. - fix to forward ctrl handling to the OS in the rare case of failed `exit_event` initialization on startup. To swap a possible hang (within `WaitForMultipleObjectsEx()`) with an ungraceful shutdown. - add support for an 'exit message' string, set by signal/ctrl handlers, and log it on app exit. To avoid the need to deal with logging within the handlers, yet have a static trace message about the event. Complementing the already logged signal number. - drop stderr trace message from `exit_signal_handler()` in favor of an exit message. runtests triggers it frequantly, which added much noise to stderr. As a bonus, this also allows dropping the compiler warning suppression. Reported-by: Stefan Eissing Bug: https://github.com/curl/curl/pull/22487#issuecomment-5204092974 Follow-up to 3aae64e4fbee7c1fa408c54df18d3f631781c283 #22507 Refs: https://learn.microsoft.com/windows/console/setconsolectrlhandler https://learn.microsoft.com/windows/console/registering-a-control-handler-function https://learn.microsoft.com/cpp/c-runtime-library/reference/raise https://learn.microsoft.com/cpp/c-runtime-library/reference/signal https://gitlab.winehq.org/wine/wine/-/blob/wine-11.14/dlls/kernelbase/console.c#L1517-1526 https://github.com/huangqinjin/ucrt/blob/d6e817a4cc90f6f1fe54f8a0aa4af4fff0bb647d/misc/signal.cpp#L286-L348 Follow-up to fe28fcf04cdfe7c6e1ab4499a33f9b8479839f14 7dc8a981fa043b9dbbae3a632229b74dcd868bd7 0e058776c02cf8ddc753a36f9cde98cc87899d51 #5260 Closes #22487 --- tests/server/first.c | 6 ++ tests/server/first.h | 1 + tests/server/util.c | 207 ++++++++++++++++++++----------------------- 3 files changed, 101 insertions(+), 113 deletions(-) diff --git a/tests/server/first.c b/tests/server/first.c index a3fc4308f7d0..c19018bc0ec4 100644 --- a/tests/server/first.c +++ b/tests/server/first.c @@ -58,6 +58,9 @@ int main(int argc, const char **argv) result = entry_func(argc - 1, argv + 1); + if(serverlogfile && exit_msg) + logmsg("========> exit message: %s", exit_msg); + if(got_exit_signal) { char port_str[11]; const char *location_str = port_str; @@ -72,12 +75,15 @@ int main(int argc, const char **argv) logmsg("========> %s %s (%s pid: %ld) exits with signal (%d)", socket_type, entry_name, location_str, (long)our_getpid(), exit_signal); + +#ifndef _WIN32 /* * To properly set the return status of the process we * must raise the same signal SIGINT or SIGTERM that we * caught and let the old handler take care of it. */ raise(exit_signal); +#endif } if(serverlogfile) diff --git a/tests/server/first.h b/tests/server/first.h index 73657a86ba71..aa81a5798eb9 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -148,6 +148,7 @@ static volatile int exit_signal = 0; #ifdef _WIN32 static HANDLE exit_event = NULL; #endif +static volatile const char *exit_msg = NULL; extern void install_signal_handlers(bool keep_sigalrm); extern void restore_signal_handlers(bool keep_sigalrm); #ifdef USE_UNIX_SOCKETS diff --git a/tests/server/util.c b/tests/server/util.c index 6782c0f1e97d..ee8d401d5c40 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -322,17 +322,26 @@ void storerequest(const char *reqbuf, size_t totalsize, const char *filename) errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } +static bool initiate_exit(int signum) /* stay signal-safe */ +{ + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } +#ifdef _WIN32 + if(!exit_event) + return FALSE; + (void)SetEvent(exit_event); +#endif + return TRUE; +} + +#ifndef _WIN32 + /* vars used to keep around previous signal handlers */ typedef void (*SIGHANDLER_T)(int); -#if defined(_MSC_VER) && (_MSC_VER <= 1700) -/* Workaround for warning C4306: - 'type cast' : conversion from 'int' to 'void (__cdecl *)(int)' */ -#undef SIG_ERR -#define SIG_ERR ((SIGHANDLER_T)(size_t)-1) -#endif - #ifdef SIGHUP static SIGHANDLER_T old_sighup_handler = SIG_ERR; #endif @@ -348,15 +357,6 @@ static SIGHANDLER_T old_sigint_handler = SIG_ERR; #ifdef SIGTERM static SIGHANDLER_T old_sigterm_handler = SIG_ERR; #endif -#ifdef _WIN32 -static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; -#endif - -#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) -static DWORD thread_main_id = 0; -static HANDLE thread_main_window = NULL; -static HWND hidden_main_window = NULL; -#endif /* signal handler that is triggered to indicate that the program * should finish its execution in a controlled manner as soon as possible. @@ -367,55 +367,64 @@ static HWND hidden_main_window = NULL; * the POSIX specification: * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 */ -static void exit_signal_handler(int signum) +static void exit_signal_handler(int signum) /* stay signal-safe */ { int old_errno = errno; - static const char msg[] = "exit_signal_handler(): triggered\n"; - /* suppress warning seen in configurations where 'write()' has the attribute - 'warn_unused_result', which is not silenced by casting to '(void)'. */ -#if defined(CURL_HAVE_DIAG) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-result" /* GCC 4.5+ */ -#endif - (void)write(STDERR_FILENO, msg, CURL_CSTRLEN(msg)); -#if defined(CURL_HAVE_DIAG) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - if(got_exit_signal == 0) { - got_exit_signal = 1; - exit_signal = signum; -#ifdef _WIN32 - if(exit_event) - (void)SetEvent(exit_event); -#endif - } + exit_msg = "exit_signal_handler(): triggered"; + (void)initiate_exit(signum); #if !(defined(HAVE_SIGACTION) && defined(SA_RESTART)) (void)signal(signum, exit_signal_handler); #endif errno = old_errno; } -#ifdef _WIN32 -/* CTRL event handler for Windows Console applications to simulate - * SIGINT, SIGTERM and SIGBREAK on CTRL events and trigger signal handler. +static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, int norestart) +{ +#if defined(HAVE_SIGACTION) && defined(SA_RESTART) + struct sigaction sa, oldsa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, signum); + sa.sa_flags = norestart ? 0 : SA_RESTART; + + if(sigaction(signum, &sa, &oldsa)) + return SIG_ERR; + + return oldsa.sa_handler; +#else + SIGHANDLER_T oldhdlr = signal(signum, handler); + +#ifdef HAVE_SIGINTERRUPT + if(oldhdlr != SIG_ERR) + siginterrupt(signum, norestart); +#else + (void)norestart; +#endif + + return oldhdlr; +#endif +} + +#else /* _WIN32 */ + +/* CTRL event handler for Windows Console applications to handle exit events. * * Background information from MSDN: - * SIGINT is not supported for any Win32 application. When a CTRL+C - * interrupt occurs, Win32 operating systems generate a new thread - * to specifically handle that interrupt. This can cause a single-thread + * When a CTRL+C interrupt occurs, Win32 operating systems generate a new + * thread to specifically handle that interrupt. This can cause a single-thread * application, such as one in UNIX, to become multi-threaded and cause * unexpected behavior. - * [...] - * The SIGKILL and SIGTERM signals are not generated under Windows. - * They are included for ANSI compatibility. Therefore, you can set - * signal handlers for these signals by using signal, and you can also - * explicitly generate these signals by calling raise. Source: - * https://learn.microsoft.com/cpp/c-runtime-library/reference/signal */ -static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) +static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) /* stay signal-safe */ { + static const char msgU[] = "ctrl_event_handler(): unhandled\n"; + static const char msgH[] = "ctrl_event_handler(): handled\n"; + static const char msgF[] = "ctrl_event_handler(): failed to handle\n"; + HANDLE out = GetStdHandle(STD_ERROR_HANDLE); + DWORD dwWritten; int signum = 0; - logmsg("ctrl_event_handler: %lu", dwCtrlType); switch(dwCtrlType) { case CTRL_C_EVENT: signum = SIGINT; @@ -427,17 +436,25 @@ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) signum = SIGBREAK; break; default: + WriteFile(out, msgU, CURL_CSTRLEN(msgU), &dwWritten, NULL); + exit_msg = msgU; return FALSE; } - if(signum) { - logmsg("ctrl_event_handler: %lu -> %d", dwCtrlType, signum); - raise(signum); + if(!initiate_exit(signum)) { + WriteFile(out, msgF, CURL_CSTRLEN(msgF), &dwWritten, NULL); + exit_msg = msgF; + return FALSE; } + WriteFile(out, msgH, CURL_CSTRLEN(msgH), &dwWritten, NULL); + exit_msg = msgH; return TRUE; } -#endif -#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) +#ifndef CURL_WINDOWS_UWP +static DWORD thread_main_id = 0; +static HANDLE thread_main_window = NULL; +static HWND hidden_main_window = NULL; + /* Window message handler for Windows applications to add support * for graceful process termination via taskkill (without /f) which * sends WM_CLOSE to all Windows of a process (even hidden ones). @@ -448,20 +465,21 @@ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - int signum = 0; if(hwnd == hidden_main_window) { switch(uMsg) { - case WM_CLOSE: - signum = SIGTERM; + case WM_CLOSE: { + static const char msg[] = "main_window_proc(): WM_CLOSE -> SIGTERM\n"; + DWORD dwWritten; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, CURL_CSTRLEN(msg), + &dwWritten, NULL); + exit_msg = msg; + initiate_exit(SIGTERM); break; + } case WM_DESTROY: PostQuitMessage(0); break; } - if(signum) { - logmsg("main_window_proc: %u -> %d", uMsg, signum); - raise(signum); - } } return DefWindowProc(hwnd, uMsg, wParam, lParam); } @@ -521,47 +539,15 @@ static DWORD WINAPI main_window_loop(void *lpParameter) hidden_main_window = NULL; return (DWORD)msg.wParam; } -#endif - -static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, int norestart) -{ -#if defined(HAVE_SIGACTION) && defined(SA_RESTART) - struct sigaction sa, oldsa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, signum); - sa.sa_flags = norestart ? 0 : SA_RESTART; - - if(sigaction(signum, &sa, &oldsa)) - return SIG_ERR; - - return oldsa.sa_handler; -#else - SIGHANDLER_T oldhdlr = signal(signum, handler); - -#ifdef HAVE_SIGINTERRUPT - if(oldhdlr != SIG_ERR) - siginterrupt(signum, norestart); -#else - (void)norestart; -#endif - - return oldhdlr; -#endif -} +#endif /* CURL_WINDOWS_UWP */ +#endif /* !_WIN32 */ void install_signal_handlers(bool keep_sigalrm) { char errbuf[STRERROR_LEN]; (void)errbuf; -#ifdef _WIN32 - /* setup Windows exit event before any signal can trigger */ - exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!exit_event) - logmsg("cannot create exit event"); -#endif + (void)keep_sigalrm; +#ifndef _WIN32 #ifdef SIGHUP /* ignore SIGHUP signal */ old_sighup_handler = set_signal(SIGHUP, SIG_IGN, 0); @@ -584,8 +570,6 @@ void install_signal_handlers(bool keep_sigalrm) logmsg("cannot install SIGALRM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } -#else - (void)keep_sigalrm; #endif #ifdef SIGINT /* handle SIGINT signal with our exit_signal_handler */ @@ -601,12 +585,11 @@ void install_signal_handlers(bool keep_sigalrm) logmsg("cannot install SIGTERM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif -#ifdef _WIN32 - /* handle SIGBREAK signal with our exit_signal_handler */ - old_sigbreak_handler = set_signal(SIGBREAK, exit_signal_handler, 1); - if(old_sigbreak_handler == SIG_ERR) - logmsg("cannot install SIGBREAK handler: (%d) %s", - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); +#else /* _WIN32 */ + /* setup Windows exit event before any signal can trigger */ + exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!exit_event) + logmsg("cannot create exit event"); if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE)) logmsg("cannot install CTRL event handler"); @@ -616,11 +599,13 @@ void install_signal_handlers(bool keep_sigalrm) if(!thread_main_window || !thread_main_id) logmsg("cannot start main window loop"); #endif -#endif +#endif /* !_WIN32 */ } void restore_signal_handlers(bool keep_sigalrm) { + (void)keep_sigalrm; +#ifndef _WIN32 #ifdef SIGHUP if(old_sighup_handler != SIG_ERR) (void)set_signal(SIGHUP, old_sighup_handler, 0); @@ -634,8 +619,6 @@ void restore_signal_handlers(bool keep_sigalrm) if(old_sigalrm_handler != SIG_ERR) (void)set_signal(SIGALRM, old_sigalrm_handler, 0); } -#else - (void)keep_sigalrm; #endif #ifdef SIGINT if(old_sigint_handler != SIG_ERR) @@ -645,9 +628,7 @@ void restore_signal_handlers(bool keep_sigalrm) if(old_sigterm_handler != SIG_ERR) (void)set_signal(SIGTERM, old_sigterm_handler, 0); #endif -#ifdef _WIN32 - if(old_sigbreak_handler != SIG_ERR) - (void)set_signal(SIGBREAK, old_sigbreak_handler, 0); +#else /* _WIN32 */ (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); #ifndef CURL_WINDOWS_UWP if(thread_main_window && thread_main_id) { @@ -663,7 +644,7 @@ void restore_signal_handlers(bool keep_sigalrm) #endif if(exit_event && CloseHandle(exit_event)) exit_event = NULL; -#endif +#endif /* !_WIN32 */ } #ifdef USE_UNIX_SOCKETS From abcb5349e3f38decdb483a22720057036ac77862 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 14:56:40 +0200 Subject: [PATCH 0823/1062] cf-socket: disable TCP SYN retransmissions for localhost on Windows Suggested-by: Marcel Jamin URL: https://curl.se/mail/lib-2026-08/0002.html URL: https://daniel.haxx.se/blog/2024/08/14/slow-tcp-connect-on-windows/ Closes #22494 --- .github/scripts/typos.toml | 2 ++ lib/cf-socket.c | 61 ++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 3192a2987cf7..880664361dc7 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -14,6 +14,8 @@ extend-ignore-identifiers-re = [ "^(clen|req_clen|smtp_perform_helo|smtp_state_helo_resp|Tru64|_stati64)$", "(_ccontains|_controllen|O_WRONLY|secur32)", "proxys", # this should be limited to tests/http/*. Short for secure proxy. + "rto", # (TCP) retransmission timeout + "RTO" ] extend-ignore-re = [ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 9c65963ce3d4..54df7cc2a3d3 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -81,7 +81,9 @@ #include "curlx/version_win32.h" #include "curlx/strerr.h" #include "curlx/strparse.h" - +#ifdef _WIN32 +#include /* for TCP_INITIAL_RTO_PARAMETERS */ +#endif /* retrieves ip address and port from a sockaddr structure. note it calls * curlx_inet_ntop which sets errno on fail, not SOCKERRNO. @@ -1141,6 +1143,51 @@ static int cf_socktype(int x) return x; } +#ifdef _WIN32 + +/* Offered by mingw-w64 v10+, MS SDK 8.0/~VS2012+ */ +#ifndef SIO_TCP_INITIAL_RTO +#define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR, 17) +#define TCP_INITIAL_RTO_DEFAULT_RTT 0 + +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct _TCP_INITIAL_RTO_PARAMETERS { + USHORT Rtt; + UCHAR MaxSynRetransmissions; +} TCP_INITIAL_RTO_PARAMETERS; +#endif + +#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS +#define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS 0xFE /* -2 */ +#endif + +static bool targets_localhost(struct cf_socket_ctx *ctx) +{ + return (((ctx->addr.family == AF_INET) && + !strcmp(ctx->ip.remote_ip, "127.0.0.1")) || + ((ctx->addr.family == AF_INET6) && + !strcmp(ctx->ip.remote_ip, "::1"))); +} + +/* disable TCP SYN retransmissions for localhost connection on Windows to + detect problems faster */ +static void tcplocalhost(struct Curl_cfilter *cf, + curl_socket_t sockfd) +{ + if(targets_localhost(cf->ctx)) { + TCP_INITIAL_RTO_PARAMETERS rto; + DWORD bytes = 0; + memset(&rto, 0, sizeof(rto)); + rto.Rtt = TCP_INITIAL_RTO_DEFAULT_RTT; + rto.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + (void)WSAIoctl(sockfd, SIO_TCP_INITIAL_RTO, &rto, sizeof(rto), + NULL, 0, &bytes, NULL, NULL); + } +} +#else +#define tcplocalhost(x, y) +#endif + static CURLcode cf_socket_open(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1214,11 +1261,15 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, is_tcp = (ctx->addr.family == AF_INET) && cf_socktype(ctx->addr.socktype) == SOCK_STREAM; #endif - if(is_tcp && data->set.tcp_nodelay) - tcpnodelay(cf, data, ctx->sock); + if(is_tcp) { + if(data->set.tcp_nodelay) + tcpnodelay(cf, data, ctx->sock); - if(is_tcp && data->set.tcp_keepalive) - tcpkeepalive(cf, data, ctx->sock); + if(data->set.tcp_keepalive) + tcpkeepalive(cf, data, ctx->sock); + + tcplocalhost(cf, ctx->sock); + } if(data->set.fsockopt) { /* activate callback for setting socket options */ From b5716286e9a6064ad202c653c0cc7fb8077418a1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 14:19:14 +0200 Subject: [PATCH 0824/1062] tests: keep test names shorter than 70 columns - makes test names less complicated - makes them less likely to wrap lines when using narrow terminals - runtests now returns error for the test if the name is longer - replace the "..." with a singe space Closes #22492 --- tests/data/test1062 | 2 +- tests/data/test1088 | 2 +- tests/data/test1113 | 2 +- tests/data/test1133 | 2 +- tests/data/test1177 | 2 +- tests/data/test1253 | 2 +- tests/data/test1255 | 2 +- tests/data/test1315 | 2 +- tests/data/test1479 | 2 +- tests/data/test1557 | 2 +- tests/data/test1616 | 2 +- tests/data/test162 | 2 +- tests/data/test1641 | 2 +- tests/data/test1642 | 2 +- tests/data/test1677 | 2 +- tests/data/test2010 | 2 +- tests/data/test2039 | 2 +- tests/data/test2052 | 2 +- tests/data/test2054 | 2 +- tests/data/test2055 | 2 +- tests/data/test2059 | 2 +- tests/data/test2067 | 2 +- tests/data/test2072 | 2 +- tests/data/test2087 | 2 +- tests/data/test2113 | 2 +- tests/data/test2311 | 2 +- tests/data/test234 | 2 +- tests/data/test258 | 2 +- tests/data/test3002 | 2 +- tests/data/test3003 | 2 +- tests/data/test3004 | 2 +- tests/data/test3005 | 2 +- tests/data/test3006 | 2 +- tests/data/test3023 | 2 +- tests/data/test316 | 2 +- tests/data/test3302 | 2 +- tests/data/test3303 | 2 +- tests/data/test3304 | 2 +- tests/data/test345 | 2 +- tests/data/test467 | 2 +- tests/data/test485 | 2 +- tests/data/test5000 | 2 +- tests/data/test5001 | 2 +- tests/data/test5002 | 2 +- tests/data/test5003 | 2 +- tests/data/test5004 | 2 +- tests/data/test5005 | 2 +- tests/data/test5006 | 2 +- tests/data/test5007 | 2 +- tests/data/test5008 | 2 +- tests/data/test5009 | 2 +- tests/data/test5010 | 2 +- tests/data/test5011 | 2 +- tests/data/test5012 | 2 +- tests/data/test5013 | 2 +- tests/data/test5014 | 2 +- tests/data/test5015 | 2 +- tests/data/test5016 | 2 +- tests/data/test5017 | 2 +- tests/data/test5018 | 2 +- tests/data/test5019 | 2 +- tests/data/test5020 | 2 +- tests/data/test5021 | 2 +- tests/data/test5022 | 2 +- tests/data/test5023 | 2 +- tests/data/test5024 | 2 +- tests/data/test5025 | 2 +- tests/data/test529 | 2 +- tests/data/test546 | 2 +- tests/data/test550 | 2 +- tests/data/test565 | 2 +- tests/data/test574 | 2 +- tests/data/test73 | 2 +- tests/data/test739 | 2 +- tests/data/test845 | 2 +- tests/data/test890 | 2 +- tests/data/test91 | 2 +- tests/data/test949 | 2 +- tests/data/test956 | 2 +- tests/data/test958 | 2 +- tests/data/test964 | 2 +- tests/runtests.pl | 12 ++++++++++-- 82 files changed, 91 insertions(+), 83 deletions(-) diff --git a/tests/data/test1062 b/tests/data/test1062 index b95818d2bf24..f8a0ae146082 100644 --- a/tests/data/test1062 +++ b/tests/data/test1062 @@ -26,7 +26,7 @@ REPLY CWD %repeat[218 x 250-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ftp -FTP with excessively long server command response lines, boundary condition +FTP excessively long server command response lines, boundary condition ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER diff --git a/tests/data/test1088 b/tests/data/test1088 index 36d8e2496f7e..04484add385c 100644 --- a/tests/data/test1088 +++ b/tests/data/test1088 @@ -75,7 +75,7 @@ contents http -HTTP, proxy with --anyauth and Location: to new host using location-trusted +HTTP, proxy with --anyauth and Location: using location-trusted http://first.host.it.is/we/want/that/page/%TESTNUMBER1000 -x %HOSTIP:%HTTPPORT --user iam:myself --location-trusted --anyauth diff --git a/tests/data/test1113 b/tests/data/test1113 index 7486952f6e1a..ed606ec667b1 100644 --- a/tests/data/test1113 +++ b/tests/data/test1113 @@ -26,7 +26,7 @@ ftp lib574 -FTP wildcard download - changed fnmatch, 2x perform (DOS LIST response) +FTP wildcard download - changed fnmatch, 2x perform (DOS LIST) "ftp://%HOSTIP:%FTPPORT/fully_simulated/DOS/*.txt" diff --git a/tests/data/test1133 b/tests/data/test1133 index bb25c0166625..109d6be5c948 100644 --- a/tests/data/test1133 +++ b/tests/data/test1133 @@ -27,7 +27,7 @@ Mime http -HTTP RFC1867-type formposting with filename/data contains ',', ';', '"' +HTTP formpost with filename/data containing ',', ';', '"' http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER diff --git a/tests/data/test1177 b/tests/data/test1177 index ae340e8bf5a8..19697e6b1418 100644 --- a/tests/data/test1177 +++ b/tests/data/test1177 @@ -11,7 +11,7 @@ documentation # Client-side -Verify that feature names and CURL_VERSION_* in lib and docs are in sync +Verify feature names and CURL_VERSION_* in lib and docs in sync diff --git a/tests/data/test1253 b/tests/data/test1253 index 621ecf9401e1..92eff37774f3 100644 --- a/tests/data/test1253 +++ b/tests/data/test1253 @@ -28,7 +28,7 @@ foo http ---proxy, override NO_PROXY by --noproxy and access target URL through proxy +--proxy, override NO_PROXY by --noproxy and access URL through proxy NO_PROXY=example.com diff --git a/tests/data/test1255 b/tests/data/test1255 index 57780f110d03..fac18021827f 100644 --- a/tests/data/test1255 +++ b/tests/data/test1255 @@ -29,7 +29,7 @@ foo http -http_proxy, override NO_PROXY by --noproxy and access target URL directly +http_proxy, override NO_PROXY by --noproxy and access URL directly http_proxy=http://%HOSTIP:%HTTPPORT diff --git a/tests/data/test1315 b/tests/data/test1315 index 49788684e966..698e26bf532e 100644 --- a/tests/data/test1315 +++ b/tests/data/test1315 @@ -30,7 +30,7 @@ Mime http -HTTP RFC1867-type formposting - -F with three files, one with explicit type +HTTP -F with three files, one with explicit type http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER -F name=value -F 'file=@%LOGDIR/test%TESTNUMBER.txt,%LOGDIR/test%TESTNUMBER.txt;type=magic/content,%LOGDIR/test%TESTNUMBER.txt' diff --git a/tests/data/test1479 b/tests/data/test1479 index 95dc3d11bc07..ccb6ced8208c 100644 --- a/tests/data/test1479 +++ b/tests/data/test1479 @@ -30,7 +30,7 @@ Data http -HTTP/1.1 response followed by an HTTP/0.9 response over the same connection +HTTP/1.1 response followed by HTTP/0.9 response over same connection http://%HOSTIP:%HTTPPORT/%TESTNUMBER http://%HOSTIP:%HTTPPORT/%TESTNUMBER0002 diff --git a/tests/data/test1557 b/tests/data/test1557 index a96647690c39..9b4ed73fa732 100644 --- a/tests/data/test1557 +++ b/tests/data/test1557 @@ -16,7 +16,7 @@ lib%TESTNUMBER -Remove easy handle in pending connections does not leave dangling entry +Remove easy handle in pending connections, no dangling entry hostname.invalid diff --git a/tests/data/test1616 b/tests/data/test1616 index dc4ebc13f95e..c0719b091f9f 100644 --- a/tests/data/test1616 +++ b/tests/data/test1616 @@ -13,7 +13,7 @@ uint_hash unittest -Internal uint_hash create/add/destroy testing, exercising clean functions +uint_hash create/add/destroy testing, exercising clean functions diff --git a/tests/data/test162 b/tests/data/test162 index d03df40bb497..e650b7cf4f83 100644 --- a/tests/data/test162 +++ b/tests/data/test162 @@ -35,7 +35,7 @@ proxy http -HTTP GET asking for --proxy-ntlm when some other authentication is required +HTTP GET --proxy-ntlm when some other authentication is required http://%HOSTIP:%HTTPPORT/%TESTNUMBER --proxy http://%HOSTIP:%HTTPPORT --proxy-user foo:bar --proxy-ntlm --fail diff --git a/tests/data/test1641 b/tests/data/test1641 index a71a59b056dc..46c4f23813b8 100644 --- a/tests/data/test1641 +++ b/tests/data/test1641 @@ -33,7 +33,7 @@ Content-Disposition: filename=name%TESTNUMBER; charset=funny; option=strange http -HTTP GET with -J, a redirect and Content-Disposition in the second response +HTTP GET -J, a redirect and Content-Disposition in the second response http://%HOSTIP:%HTTPPORT/%TESTNUMBER -J -L -O --output-dir %LOGDIR diff --git a/tests/data/test1642 b/tests/data/test1642 index 81f0ae8fc65f..2745b8bb20f8 100644 --- a/tests/data/test1642 +++ b/tests/data/test1642 @@ -32,7 +32,7 @@ Content-Type: text/html http -HTTP GET with -J, redirect, no Content-Disposition use the Location: name +HTTP GET -J, redirect, no Content-Disposition use the Location: name http://%HOSTIP:%HTTPPORT/%TESTNUMBER -J -L -O --output-dir %LOGDIR diff --git a/tests/data/test1677 b/tests/data/test1677 index dbf5ca17a744..e06dc7679cf1 100644 --- a/tests/data/test1677 +++ b/tests/data/test1677 @@ -45,7 +45,7 @@ random data in first chunk http -HTTP POST response with both Transfer-Encoding chunked and Content-Length 0 +HTTP POST response with both chunked and zero Content-Length http://%HOSTIP:%HTTPPORT/%TESTNUMBER -d "testdata" diff --git a/tests/data/test2010 b/tests/data/test2010 index 443ae9d2f979..19a18920206b 100644 --- a/tests/data/test2010 +++ b/tests/data/test2010 @@ -35,7 +35,7 @@ http https -proxy credentials via options for two proxies, redirect from http to https +proxy creds via options for two proxies, redir from http to https diff --git a/tests/data/test2039 b/tests/data/test2039 index bd9558a4bd67..6ef3b9d983d6 100644 --- a/tests/data/test2039 +++ b/tests/data/test2039 @@ -31,7 +31,7 @@ dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr ftp -FTP (optional .netrc with 'default' override; no user/pass) dir list PASV +FTP optional .netrc with 'default' override; no user/pass, dir list --netrc-optional --netrc-file %LOGDIR/netrc%TESTNUMBER ftp://%HOSTIP:%FTPPORT/ diff --git a/tests/data/test2052 b/tests/data/test2052 index 85d3d0988d41..bd9a5eb9adfb 100644 --- a/tests/data/test2052 +++ b/tests/data/test2052 @@ -27,7 +27,7 @@ OK http ---connect-to: do not mix connections with and without a "connect to host" +--connect-to: do not mix connections with and w/o a "connect to host" diff --git a/tests/data/test2054 b/tests/data/test2054 index 83f203586dd9..245db971cfb5 100644 --- a/tests/data/test2054 +++ b/tests/data/test2054 @@ -26,7 +26,7 @@ OK http -Connect to specific host: use the first "connect-to" string that matches +Connect to specific host: use the first "connect-to" string diff --git a/tests/data/test2055 b/tests/data/test2055 index 6a7a8d34ac61..aad42d630236 100644 --- a/tests/data/test2055 +++ b/tests/data/test2055 @@ -48,7 +48,7 @@ http-proxy socks5 ---connect-to via SOCKS proxy and HTTP proxy (tunnel mode automatically) +--connect-to via SOCKS proxy and HTTP proxy (tunnel mode auto) proxy diff --git a/tests/data/test2059 b/tests/data/test2059 index 4726c0a1e66f..71dc55f05963 100644 --- a/tests/data/test2059 +++ b/tests/data/test2059 @@ -70,7 +70,7 @@ proxy digest -HTTP Digest with PUT, resumed upload, modified method, SHA-256 and userhash +HTTP Digest PUT, resumed upload, modified method, SHA-256 and userhash http://%HOSTIP:%HTTPPORT/%TESTNUMBER -u auser:apasswd --digest -T %LOGDIR/%TESTNUMBER -x http://%HOSTIP:%HTTPPORT -C 2 -X GET diff --git a/tests/data/test2067 b/tests/data/test2067 index b6de16d0bd75..4b6030252c2f 100644 --- a/tests/data/test2067 +++ b/tests/data/test2067 @@ -55,7 +55,7 @@ crypto digest -HTTP POST --digest with SHA256 and user-specified Content-Length header +HTTP POST --digest with SHA256 and user-specified Content-Length # This test is to ensure 'Content-Length: 0' is sent while negotiating auth # even when there is a user-specified Content-Length header. diff --git a/tests/data/test2072 b/tests/data/test2072 index c7731a83e3c1..efad82012a8b 100644 --- a/tests/data/test2072 +++ b/tests/data/test2072 @@ -22,7 +22,7 @@ moo file -file:// with Unix path resolution behavior for the case of extra slashes +file:// with Unix path for the case of extra slashes file:////%FILE_PWD/%LOGDIR/test%TESTNUMBER.txt diff --git a/tests/data/test2087 b/tests/data/test2087 index c3a5901d172d..cfa51368406d 100644 --- a/tests/data/test2087 +++ b/tests/data/test2087 @@ -32,7 +32,7 @@ local-http https test-localhost.pem -simple HTTPS GET with base64-sha256 public key pinning (Schannel variant) +simple HTTPS GET with base64-sha256 public key pinning (Schannel) # This test is pointless if we are not using the Schannel backend diff --git a/tests/data/test2113 b/tests/data/test2113 index 884c0af0a732..1033c3d2b421 100644 --- a/tests/data/test2113 +++ b/tests/data/test2113 @@ -61,7 +61,7 @@ contents http -HTTP custom Authorization: with whitespace before colon and redirect to new host +Authorization: with whitespace before colon and redir to new host http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -H "Authorization : s3cr3t" --proxy-user testing:this --location diff --git a/tests/data/test2311 b/tests/data/test2311 index 91032b81d241..1d43a3292956 100644 --- a/tests/data/test2311 +++ b/tests/data/test2311 @@ -26,7 +26,7 @@ This server says moo http -Cookie from file: control octet rejected, prefixes matched case sensitively +Cookie from file: control octet rejected, prefixes case sensitive http://example.fake/%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER -x %HOSTIP:%HTTPPORT diff --git a/tests/data/test234 b/tests/data/test234 index 0672a25bde44..aa13dccc7aba 100644 --- a/tests/data/test234 +++ b/tests/data/test234 @@ -63,7 +63,7 @@ contents http -HTTP, proxy, site+proxy auth and Location: to new host location-trusted +HTTP, proxy, site+proxy auth and Location: --location-trusted http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT --user iam:myself --proxy-user testing:this --location-trusted diff --git a/tests/data/test258 b/tests/data/test258 index 25a2a8f596f1..7471c7072ef7 100644 --- a/tests/data/test258 +++ b/tests/data/test258 @@ -64,7 +64,7 @@ proxy digest -HTTP POST multipart without Expect: header using proxy anyauth (Digest) +HTTP multipart POST without Expect: using proxy anyauth (Digest) -x http://%HOSTIP:%HTTPPORT http://remotehost:54321/we/want/%TESTNUMBER -F name=daniel -F tool=curl -F file=@%LOGDIR/test%TESTNUMBER.txt -H "Expect:" -U uuuser:pppassword --proxy-anyauth diff --git a/tests/data/test3002 b/tests/data/test3002 index 8c0f063ece3c..7aa195444ad8 100644 --- a/tests/data/test3002 +++ b/tests/data/test3002 @@ -16,7 +16,7 @@ SMTP smtp -SMTP multiple and invalid (first) --mail-rcpt and --mail-rcpt-allowfails +SMTP invalid (first) --mail-rcpt and --mail-rcpt-allowfails From: different diff --git a/tests/data/test3003 b/tests/data/test3003 index cbeb24316344..66e7627290cd 100644 --- a/tests/data/test3003 +++ b/tests/data/test3003 @@ -16,7 +16,7 @@ SMTP smtp -SMTP multiple and invalid (last) --mail-rcpt and --mail-rcpt-allowfails +SMTP invalid (last) --mail-rcpt and --mail-rcpt-allowfails From: different diff --git a/tests/data/test3004 b/tests/data/test3004 index 732c47835241..ac348339ad90 100644 --- a/tests/data/test3004 +++ b/tests/data/test3004 @@ -16,7 +16,7 @@ SMTP smtp -SMTP multiple and invalid (middle) --mail-rcpt and --mail-rcpt-allowfails +SMTP invalid (middle) --mail-rcpt and --mail-rcpt-allowfails From: different diff --git a/tests/data/test3005 b/tests/data/test3005 index 6799b3136db6..7bdb503e3f83 100644 --- a/tests/data/test3005 +++ b/tests/data/test3005 @@ -16,7 +16,7 @@ SMTP smtp -SMTP multiple invalid (all but one) --mail-rcpt and --mail-rcpt-allowfails +SMTP invalid (all but one) --mail-rcpt and --mail-rcpt-allowfails From: different diff --git a/tests/data/test3006 b/tests/data/test3006 index c1583f639765..518f328e290b 100644 --- a/tests/data/test3006 +++ b/tests/data/test3006 @@ -16,7 +16,7 @@ SMTP smtp -SMTP with multiple invalid (all) --mail-rcpt and --mail-rcpt-allowfails +SMTP invalid (all) --mail-rcpt and --mail-rcpt-allowfails From: different diff --git a/tests/data/test3023 b/tests/data/test3023 index e6ffc6a00559..7aed3bd4f9f0 100644 --- a/tests/data/test3023 +++ b/tests/data/test3023 @@ -31,7 +31,7 @@ local-http https test-localhost-san-first.pem -HTTPS localhost, first subaltname matches, CN does not match (Schannel) +HTTPS localhost, first SAN matches, CN does not match (Schannel) # This test is pointless if we are not using the Schannel backend diff --git a/tests/data/test316 b/tests/data/test316 index 0780acce1ae5..1adf9996047f 100644 --- a/tests/data/test316 +++ b/tests/data/test316 @@ -46,7 +46,7 @@ brotli http -HTTP GET brotli compressed content of size more than CURL_MAX_WRITE_SIZE +HTTP brotli compressed content of size more than CURL_MAX_WRITE_SIZE http://%HOSTIP:%HTTPPORT/%TESTNUMBER --compressed diff --git a/tests/data/test3302 b/tests/data/test3302 index 35ccfc79af00..6dc504f6cf96 100644 --- a/tests/data/test3302 +++ b/tests/data/test3302 @@ -13,7 +13,7 @@ CURLOPT_GSSAPI_DELEGATION unittest -CURLOPT_GSSAPI_DELEGATION stores flags in data->set on GSS-API and SSPI builds +CURLOPT_GSSAPI_DELEGATION stores flags in data->set diff --git a/tests/data/test3303 b/tests/data/test3303 index 697049f013cc..0bd9e8a7f043 100644 --- a/tests/data/test3303 +++ b/tests/data/test3303 @@ -14,7 +14,7 @@ mTLS unittest -conn-reuse match distinguishes mTLS key, cert_type, key_type and key_passwd fields +conn-reuse distinguishes mTLS key, cert_type, key_type and key_passwd diff --git a/tests/data/test3304 b/tests/data/test3304 index 4380c0819f02..738fd5664f6c 100644 --- a/tests/data/test3304 +++ b/tests/data/test3304 @@ -14,7 +14,7 @@ mTLS unittest -TLS session cache peer key discriminates on mTLS key, key_type and cert_type fields +TLS session cache peer key uses mTLS key, key_type and cert_type diff --git a/tests/data/test345 b/tests/data/test345 index d87cf0696c33..2836773889a6 100644 --- a/tests/data/test345 +++ b/tests/data/test345 @@ -30,7 +30,7 @@ Funny-head: yesyes http -Both --etag-compare and -save store new Etag using one pre-existing file +Both --etag-compare and -save store new Etag in pre-existing file "21025-dc7-39462498" diff --git a/tests/data/test467 b/tests/data/test467 index 799e1609f670..ba4673920331 100644 --- a/tests/data/test467 +++ b/tests/data/test467 @@ -9,7 +9,7 @@ cmdline # Client-side -use a bad short option letter that does not exist (after one does exist) +non-existing short option letter (after one that exists) # the second option is outside the normal accepted range diff --git a/tests/data/test485 b/tests/data/test485 index f530d8389e5f..8f934a83be57 100644 --- a/tests/data/test485 +++ b/tests/data/test485 @@ -14,7 +14,7 @@ etag # Client-side -Use --etag-compare and -save with more than one URL, URLs specified first +--etag-compare and -save with more than one URL, URLs specified first http://example.com/%TESTNUMBER http://example.net/fooo --etag-save %LOGDIR/etag%TESTNUMBER diff --git a/tests/data/test5000 b/tests/data/test5000 index b935ecdf8aaf..3f2a98280206 100644 --- a/tests/data/test5000 +++ b/tests/data/test5000 @@ -32,7 +32,7 @@ httpsig -HTTP RFC 9421 Message Signatures: basic GET with Ed25519 +httpsig: basic GET with Ed25519 lib%TESTNUMBER diff --git a/tests/data/test5001 b/tests/data/test5001 index 7465aab2b7ff..72ae3e07cffb 100644 --- a/tests/data/test5001 +++ b/tests/data/test5001 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: CLI GET with query +httpsig: CLI GET with query "http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5002 b/tests/data/test5002 index 143d06d12217..e09762243be4 100644 --- a/tests/data/test5002 +++ b/tests/data/test5002 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: CLI GET with HMAC-SHA256 +httpsig: CLI GET with HMAC-SHA256 "http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5003 b/tests/data/test5003 index 6468aa60e3f8..3520feab40fa 100644 --- a/tests/data/test5003 +++ b/tests/data/test5003 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: custom --httpsig-headers +httpsig: custom --httpsig-headers "http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5004 b/tests/data/test5004 index c916131762a7..33822b6b6d65 100644 --- a/tests/data/test5004 +++ b/tests/data/test5004 @@ -33,7 +33,7 @@ httpsig -HTTP RFC 9421 B.2.6: Ed25519 POST with headers (RFC test vector) +httpsig: Ed25519 POST with headers (RFC test vector) lib%TESTNUMBER diff --git a/tests/data/test5005 b/tests/data/test5005 index e090c943de3e..210eac29dbb4 100644 --- a/tests/data/test5005 +++ b/tests/data/test5005 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: sign with a regular HTTP header +httpsig: sign with a regular HTTP header "http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5006 b/tests/data/test5006 index 046f499648ca..602404e2a4f3 100644 --- a/tests/data/test5006 +++ b/tests/data/test5006 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: component name lowercasing +httpsig: component name lowercasing "http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5007 b/tests/data/test5007 index 1184ebcffb06..2ea42a9ffc4a 100644 --- a/tests/data/test5007 +++ b/tests/data/test5007 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: POST method +httpsig: POST method "http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5008 b/tests/data/test5008 index 01e628322412..bbb7e1370e7e 100644 --- a/tests/data/test5008 +++ b/tests/data/test5008 @@ -30,7 +30,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: error - missing --httpsig-key +httpsig: error - missing --httpsig-key "http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5009 b/tests/data/test5009 index ef7bc5e6a75f..2b5771c00486 100644 --- a/tests/data/test5009 +++ b/tests/data/test5009 @@ -30,7 +30,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: error - unsupported algorithm +httpsig: error - unsupported algorithm "http://example.com/%TESTNUMBER/resource" --httpsig-algo "rsa-pss-sha512" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5010 b/tests/data/test5010 index 51fd8dd888c0..6d6293e140a0 100644 --- a/tests/data/test5010 +++ b/tests/data/test5010 @@ -30,7 +30,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: error - non-existent key file +httpsig: error - non-existent key file "http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @/nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5011 b/tests/data/test5011 index c883bbe249e1..d0a5004d22dc 100644 --- a/tests/data/test5011 +++ b/tests/data/test5011 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: query string with special characters +httpsig: query string with special characters "http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5012 b/tests/data/test5012 index 8f183738e51d..528e70838350 100644 --- a/tests/data/test5012 +++ b/tests/data/test5012 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: default port omitted from @authority +httpsig: default port omitted from @authority "http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5013 b/tests/data/test5013 index beb3a1cf7014..7e2844e2015d 100644 --- a/tests/data/test5013 +++ b/tests/data/test5013 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: non-default port in @authority +httpsig: non-default port in @authority "http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5014 b/tests/data/test5014 index 51fab716bd02..0be72c8927c7 100644 --- a/tests/data/test5014 +++ b/tests/data/test5014 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: @query explicitly requested, no query in URL +httpsig: @query explicitly requested, no query in URL "http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5015 b/tests/data/test5015 index 5f6af68bc07a..e8739e013209 100644 --- a/tests/data/test5015 +++ b/tests/data/test5015 @@ -34,7 +34,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: POST with HMAC-SHA256 +httpsig: POST with HMAC-SHA256 "http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5016 b/tests/data/test5016 index 01a12a1ab0e4..fba1ae0156ef 100644 --- a/tests/data/test5016 +++ b/tests/data/test5016 @@ -34,7 +34,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: HMAC-SHA256 with custom headers +httpsig: HMAC-SHA256 with custom headers "http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5017 b/tests/data/test5017 index 04a582e0d34e..f734593cab5b 100644 --- a/tests/data/test5017 +++ b/tests/data/test5017 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: user-supplied Signature header skips signing +httpsig: user-supplied Signature header skips signing "http://example.com:5700/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -H "Signature: preexisting" -H "Signature-Input: preexisting" --connect-to example.com:5700:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5018 b/tests/data/test5018 index ab037470703c..365a85fb2b23 100644 --- a/tests/data/test5018 +++ b/tests/data/test5018 @@ -36,7 +36,7 @@ httpsig putdata -HTTP RFC 9421 Message Signatures: PUT upload +httpsig: PUT upload "http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5019 b/tests/data/test5019 index b07df9aa185d..d958836d3327 100644 --- a/tests/data/test5019 +++ b/tests/data/test5019 @@ -30,7 +30,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: error - duplicate component +httpsig: error - duplicate component "http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --httpsig-headers "method method" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5020 b/tests/data/test5020 index f6c27e877d1b..33b1c2b6e9dd 100644 --- a/tests/data/test5020 +++ b/tests/data/test5020 @@ -30,7 +30,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: error - rejects legacy '@' component syntax +httpsig: error - rejects legacy '@' component syntax "http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key" --httpsig-headers "@method authority" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5021 b/tests/data/test5021 index 9370943853eb..cc96dfc50b80 100644 --- a/tests/data/test5021 +++ b/tests/data/test5021 @@ -32,7 +32,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: default algorithm (ed25519) when --httpsig-algo omitted +httpsig: default algorithm (ed25519) when --httpsig-algo omitted "http://example.com:6100/%TESTNUMBER/resource" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5022 b/tests/data/test5022 index a27ab7701fb4..40699719376b 100644 --- a/tests/data/test5022 +++ b/tests/data/test5022 @@ -33,7 +33,7 @@ Debug httpsig -HTTP RFC 9421 Message Signatures: pass key directly +httpsig: pass key directly "http://example.com:8000/5001/resource?action=read" --httpsig-algo "ed25519" --variable key@%SRCDIR/data/data-httpsig-ed25519.key --expand-httpsig-key '{{key:trim}}' --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5023 b/tests/data/test5023 index 1d59c239943f..d706ec70c01d 100644 --- a/tests/data/test5023 +++ b/tests/data/test5023 @@ -39,7 +39,7 @@ Debug httpsig -HTTP Message Signature with cross-origin redirect +httpsig: cross-origin redirect "http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --connect-to example.org:9000:%HOSTIP:%HTTPPORT --location diff --git a/tests/data/test5024 b/tests/data/test5024 index 930b0de65d0a..eefbe9564e96 100644 --- a/tests/data/test5024 +++ b/tests/data/test5024 @@ -39,7 +39,7 @@ Debug httpsig -HTTP Message Signature with same-origin redirect +httpsig: same-origin redirect "http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --location diff --git a/tests/data/test5025 b/tests/data/test5025 index 10c48073def8..0e45de0d818f 100644 --- a/tests/data/test5025 +++ b/tests/data/test5025 @@ -39,7 +39,7 @@ Debug httpsig -HTTP Message Signature with cross-origin + user auth redirect +httpsig: cross-origin + user auth redirect "http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT --connect-to example.org:9000:%HOSTIP:%HTTPPORT --location diff --git a/tests/data/test529 b/tests/data/test529 index 473bf165f3ac..52495ce63c6e 100644 --- a/tests/data/test529 +++ b/tests/data/test529 @@ -24,7 +24,7 @@ ftp lib525 -FTP PORT upload using multi interface (weird cleanup function sequence) +FTP PORT upload using multi interface (weird cleanup function seq) ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER %LOGDIR/upload%TESTNUMBER diff --git a/tests/data/test546 b/tests/data/test546 index 06bc27501d79..97459e9e3b52 100644 --- a/tests/data/test546 +++ b/tests/data/test546 @@ -36,7 +36,7 @@ ftp lib533 -FTP RETR a non-existing file then a found one using the multi interface +FTP RETR non-existing file then a found one using multi ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER diff --git a/tests/data/test550 b/tests/data/test550 index c3b74717851f..7ef61f89a431 100644 --- a/tests/data/test550 +++ b/tests/data/test550 @@ -37,7 +37,7 @@ proxy lib549 -FTP RETR over proxy with CURLOPT_PROXY_TRANSFER_MODE and ASCII transfer +FTP RETR over proxy with CURLOPT_PROXY_TRANSFER_MODE using ASCII # first URL then proxy diff --git a/tests/data/test565 b/tests/data/test565 index 432e4177e6f0..6975239b77de 100644 --- a/tests/data/test565 +++ b/tests/data/test565 @@ -68,7 +68,7 @@ lib510 -send HTTP POST using read callback, chunked transfer-encoding and Digest +HTTP POST using read callback, chunked transfer-encoding and Digest http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test574 b/tests/data/test574 index 6681bfaa254c..38dfb7ab800d 100644 --- a/tests/data/test574 +++ b/tests/data/test574 @@ -22,7 +22,7 @@ ftp lib%TESTNUMBER -FTP wildcard download - changed fnmatch, 2x perform (Unix LIST response) +FTP wildcard download - changed fnmatch, 2x perform (Unix LIST) ftp://%HOSTIP:%FTPPORT/fully_simulated/UNIX/*.txt diff --git a/tests/data/test73 b/tests/data/test73 index b9864e83f7ee..4a4ba413f3ae 100644 --- a/tests/data/test73 +++ b/tests/data/test73 @@ -27,7 +27,7 @@ boo http -HTTP, receive cookies when using custom Host:, domain using only two dots +HTTP cookies when using custom Host:, domain using only two dots http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER -c %LOGDIR/jar%TESTNUMBER.txt -H "Host: host.NOT_DISCLOSED.se" diff --git a/tests/data/test739 b/tests/data/test739 index 06b73f9027a4..bf7dc42ce459 100644 --- a/tests/data/test739 +++ b/tests/data/test739 @@ -19,7 +19,7 @@ ipfs http -IPNS path and query args for gateway and IPFS URL (malformed gateway URL) +IPNS path and query args for gw and IPFS URL (malformed gw URL) --ipfs-gateway "http://%HOSTIP:%HTTPPORT/some/path?biz=baz" "ipns://fancy.tld/a/b?foo=bar%AMPaaa=bbb" diff --git a/tests/data/test845 b/tests/data/test845 index 97f61b77812c..39f2c46e6488 100644 --- a/tests/data/test845 +++ b/tests/data/test845 @@ -27,7 +27,7 @@ REPLY AQ== A002 NO Authentication failed imap -IMAP OAuth 2.0 (OAUTHBEARER) failure as continuation with initial response +IMAP OAuth failure as continuation with initial response 'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER/;MAILINDEX=1' -u user --oauth2-bearer mF_9.B5f-4.1JqM diff --git a/tests/data/test890 b/tests/data/test890 index 0892ba5b476b..68d36f0c50b0 100644 --- a/tests/data/test890 +++ b/tests/data/test890 @@ -28,7 +28,7 @@ REPLY AQ== -ERR Authentication failed pop3 -POP3 OAuth 2.0 (OAUTHBEARER) failure as continuation with initial response +POP3 OAuth failure as continuation with initial response pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user --oauth2-bearer mF_9.B5f-4.1JqM --sasl-ir diff --git a/tests/data/test91 b/tests/data/test91 index 004b8cb4709d..07d4e92adf44 100644 --- a/tests/data/test91 +++ b/tests/data/test91 @@ -82,7 +82,7 @@ SSL http -HTTP with NTLM/Negotiate/Basic, anyauth and user with domain, with size 0 +HTTP NTLM/Negotiate/Basic, anyauth and user with domain, with size 0 http://%HOSTIP:%HTTPPORT/%TESTNUMBER --anyauth -u mydomain\\myself:secret diff --git a/tests/data/test949 b/tests/data/test949 index d2407048c58b..95d049282359 100644 --- a/tests/data/test949 +++ b/tests/data/test949 @@ -27,7 +27,7 @@ REPLY AQ== 535 Username and Password not accepted. Learn more at\r\n535 http://s smtp -SMTP OAuth 2.0 (OAUTHBEARER) failure as continuation with initial response +SMTP OAuth failure as continuation with initial response mail body diff --git a/tests/data/test956 b/tests/data/test956 index 5ee26997b214..1e31ad163b68 100644 --- a/tests/data/test956 +++ b/tests/data/test956 @@ -23,7 +23,7 @@ codeset-utf8 LC_ALL=C.UTF-8 -SMTP without SMTPUTF8 support - UTF-8 based recipient (local part only) +SMTP without SMTPUTF8 support - UTF-8 based recipient (local part) From: different diff --git a/tests/data/test958 b/tests/data/test958 index 9ee2bb681ed3..ff9ffe35aa7a 100644 --- a/tests/data/test958 +++ b/tests/data/test958 @@ -24,7 +24,7 @@ codeset-utf8 LC_ALL=C.UTF-8 -SMTP external VRFY without SMTPUTF8 - UTF-8 recipient (local part only) +SMTP external VRFY without SMTPUTF8 - UTF-8 recipient (local part) smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt Anv%hex[%c3%a4]hex%ndaren@example.com diff --git a/tests/data/test964 b/tests/data/test964 index 3465d20211d3..4c001a082e97 100644 --- a/tests/data/test964 +++ b/tests/data/test964 @@ -26,7 +26,7 @@ codeset-utf8 LC_ALL=C.UTF-8 -SMTP external VRFY without SMTPUTF8 (IDN) - UTF-8 recipient (host part) +SMTP external VRFY w/o SMTPUTF8 (IDN) - UTF-8 recipient (host part) smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt user@%hex[%c3%a5%c3%a4%c3%b6]hex%.se diff --git a/tests/runtests.pl b/tests/runtests.pl index eee5c2950e6a..4b5b5b0826fb 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -967,6 +967,11 @@ sub citest_starttest { my $testname = (getpart("client", "name"))[0]; chomp $testname; + if(length($testname) > 70) { + logmsg "ERROR: test $testnum has a too long name, wider than 70 columns\n"; + return 1; + } + # create test result in CI services if(azure_check_environment() && $AZURE_RUN_ID) { $AZURE_RESULT_ID = azure_create_test_result($ACURL, $AZURE_RUN_ID, $testnum, $testname); @@ -974,6 +979,7 @@ sub citest_starttest { elsif(appveyor_check_environment()) { appveyor_create_test_result($ACURL, $testnum, $testname); } + return 0; } # Submit the test case result with the CI runner @@ -1212,7 +1218,7 @@ sub singletest_count { } # At this point we have committed to run this test - logmsg sprintf("test %04d...", $testnum) if(!$automakestyle); + logmsg sprintf("test %04d ", $testnum) if(!$automakestyle); # name of the test my $testname = (getpart("client", "name"))[0]; @@ -2002,7 +2008,9 @@ sub singletest { ################################################################### # Register the test case with the CI environment - citest_starttest($testnum); + if(citest_starttest($testnum)) { + return (-1, 0); + } if(runnerac_test_preprocess($runnerid, $testnum)) { logmsg "ERROR: runner $runnerid seems to have died\n"; From a478393759f302d3a55e62094ceb61e64ca610f4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 10:03:56 +0200 Subject: [PATCH 0825/1062] psl: update a comment to understandable English Closes #22502 --- lib/psl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/psl.c b/lib/psl.c index 195841f3a3f4..1e3efbb0fadc 100644 --- a/lib/psl.c +++ b/lib/psl.c @@ -55,7 +55,8 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); now_sec = Curl_pgrs_now(easy)->tv_sec; if(!pslcache->psl || pslcache->expires <= now_sec) { - /* Let a chance to other threads to do the job: avoids deadlock. */ + /* Release the shared lock so another thread can refresh the cache and + avoid deadlock. */ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Update cache: this needs an exclusive lock. */ From 26b9f3aa9b2531d5da74ce5472bc8f59ea01bd6d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Aug 2026 23:40:18 +0200 Subject: [PATCH 0826/1062] rtsp: refactor method handling and improve error checks - convert the method switch() to a simple table - avoid converting the methods from external to internal numbers, they were the same anyway so keep the external ones, just use the old defines. - fix range check. It wrongly used the method numbers as bitmask, which made the check not work previously. Also error on OOM. - Dropped the session-id check. It too wrongly did a bitmask check which was wrong and never worked. When fixed, it broke test cases so I dropped the entire check. - split out rtsp_setup_request() from rtsp_do() - replace the httpversion variable with a define Closes #22505 --- lib/rtsp.c | 294 ++++++++++++++++++++++++------------------------- lib/rtsp.h | 14 +++ lib/setopt.c | 40 +------ lib/transfer.c | 1 + lib/urldata.h | 18 +-- 5 files changed, 165 insertions(+), 202 deletions(-) diff --git a/lib/rtsp.c b/lib/rtsp.c index bc8d32622a95..1831a8479b23 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -195,7 +195,7 @@ static CURLcode rtsp_done(struct Curl_easy *data, } static CURLcode rtsp_setup_body(struct Curl_easy *data, - Curl_RtspReq rtspreq, + unsigned char rtspreq, struct dynbuf *reqp) { CURLcode result; @@ -270,154 +270,115 @@ static CURLcode rtsp_setup_body(struct Curl_easy *data, return result; } -static CURLcode rtsp_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - const Curl_RtspReq rtspreq = data->set.rtspreq; - struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); - struct dynbuf req_buffer; - const unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort - of... */ - const char *p_request = NULL; - const char *p_session_id = NULL; - const char *p_accept = NULL; - const char *p_accept_encoding = NULL; - const char *p_range = NULL; - const char *p_referrer = NULL; - const char *p_stream_uri = NULL; - const char *p_transport = NULL; - const char *p_uagent = NULL; - const char *p_hd_proxy_auth = NULL; - const char *p_hd_auth = NULL; - - *done = TRUE; - if(!rtsp) - return CURLE_FAILED_INIT; +struct rtspselect { + const char *method; + bool no_body; +}; - /* Initialize a dynamic send buffer */ - curlx_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); +static CURLcode pick_method(struct Curl_easy *data, + const unsigned char rtspreq, + const char **p) +{ + static const struct rtspselect req[] = { + { "OPTIONS", TRUE }, + { "DESCRIBE", FALSE }, + { "ANNOUNCE", TRUE }, + { "SETUP", TRUE }, + { "PLAY", TRUE }, + { "PAUSE", TRUE }, + { "TEARDOWN", TRUE }, + { "GET_PARAMETER", FALSE }, + { "SET_PARAMETER", TRUE }, + { "RECORD", TRUE }, + { "", FALSE }, /* RECEIVE: treat interleaved RTP as body */ + }; + /* this is verified already in setopt, this is just added precaution */ + DEBUGASSERT((rtspreq > RTSPREQ_NONE) && (rtspreq < RTSPREQ_LAST)); + if((rtspreq <= RTSPREQ_NONE) || (rtspreq >= RTSPREQ_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + *p = req[rtspreq - 1].method; + data->req.no_body = req[rtspreq - 1].no_body; + return CURLE_OK; +} - rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; - rtsp->CSeq_recv = 0; +/* Allocate and store a header string. */ +static CURLcode rtsp_header_alloc(const char *header_name, + const char *value, + char **target) +{ + if(!value) + return CURLE_OK; + curlx_free(*target); + *target = curl_maprintf("%s: %s\r\n", header_name, value); + if(!*target) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} - /* Setup the 'p_request' pointer to the proper p_request string - * Since all RTSP requests are included here, there is no need to - * support custom requests like HTTP. - **/ - data->req.no_body = TRUE; /* most requests do not contain a body */ - switch(rtspreq) { - default: - failf(data, "Got invalid RTSP request"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case RTSPREQ_OPTIONS: - p_request = "OPTIONS"; - break; - case RTSPREQ_DESCRIBE: - p_request = "DESCRIBE"; - data->req.no_body = FALSE; - break; - case RTSPREQ_ANNOUNCE: - p_request = "ANNOUNCE"; - break; - case RTSPREQ_SETUP: - p_request = "SETUP"; - break; - case RTSPREQ_PLAY: - p_request = "PLAY"; - break; - case RTSPREQ_PAUSE: - p_request = "PAUSE"; - break; - case RTSPREQ_TEARDOWN: - p_request = "TEARDOWN"; - break; - case RTSPREQ_GET_PARAMETER: - /* GET_PARAMETER's no_body status is determined later */ - p_request = "GET_PARAMETER"; - data->req.no_body = FALSE; - break; - case RTSPREQ_SET_PARAMETER: - p_request = "SET_PARAMETER"; - break; - case RTSPREQ_RECORD: - p_request = "RECORD"; - break; - case RTSPREQ_RECEIVE: - p_request = ""; - /* Treat interleaved RTP as body */ - data->req.no_body = FALSE; - break; - case RTSPREQ_LAST: - failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } +struct rtsp_blocks { + const char *request; + const char *session_id; + const char *accept; + const char *accept_encoding; + const char *range; + const char *referrer; + const char *stream_uri; + const char *transport; + const char *uagent; + const char *hd_proxy_auth; + const char *hd_auth; +}; - if(rtspreq == RTSPREQ_RECEIVE) { - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); - goto out; - } +static CURLcode rtsp_setup_request(struct Curl_easy *data, + struct rtsp_blocks *b, + const unsigned char rtspreq) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; - p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; - if(!p_session_id && - (rtspreq & ~(Curl_RtspReq)(RTSPREQ_OPTIONS | - RTSPREQ_DESCRIBE | - RTSPREQ_SETUP))) { - failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", - p_request); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto out; - } + b->session_id = data->set.str[STRING_RTSP_SESSION_ID]; /* Stream URI. Default to server '*' if not specified */ - if(data->set.str[STRING_RTSP_STREAM_URI]) { - p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; - } - else { - p_stream_uri = "*"; - } + if(data->set.str[STRING_RTSP_STREAM_URI]) + b->stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; + else + b->stream_uri = "*"; /* Transport Header for SETUP requests */ - p_transport = Curl_checkheaders(data, STRCONST("Transport")); - if(rtspreq == RTSPREQ_SETUP && !p_transport) { + b->transport = Curl_checkheaders(data, STRCONST("Transport")); + if(rtspreq == RTSPREQ_SETUP && !b->transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { - curlx_free(data->state.aptr.rtsp_transport); - data->state.aptr.rtsp_transport = - curl_maprintf("Transport: %s\r\n", - data->set.str[STRING_RTSP_TRANSPORT]); - if(!data->state.aptr.rtsp_transport) - return CURLE_OUT_OF_MEMORY; + result = rtsp_header_alloc("Transport", + data->set.str[STRING_RTSP_TRANSPORT], + &data->state.aptr.rtsp_transport); + if(result) + return result; } else { failf(data, "Refusing to issue an RTSP SETUP without a Transport: header."); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto out; + return CURLE_BAD_FUNCTION_ARGUMENT; } - p_transport = data->state.aptr.rtsp_transport; + b->transport = data->state.aptr.rtsp_transport; } /* Accept Headers for DESCRIBE requests */ if(rtspreq == RTSPREQ_DESCRIBE) { /* Accept Header */ - p_accept = Curl_checkheaders(data, STRCONST("Accept")) ? + b->accept = Curl_checkheaders(data, STRCONST("Accept")) ? NULL : "Accept: application/sdp\r\n"; /* Accept-Encoding header */ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { - curlx_free(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - curl_maprintf("Accept-Encoding: %s\r\n", - data->set.str[STRING_ENCODING]); - - if(!data->state.aptr.accept_encoding) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - p_accept_encoding = data->state.aptr.accept_encoding; + result = rtsp_header_alloc("Accept-Encoding", + data->set.str[STRING_ENCODING], + &data->state.aptr.accept_encoding); + if(result) + return result; + b->accept_encoding = data->state.aptr.accept_encoding; } } @@ -431,19 +392,19 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { - p_uagent = data->state.aptr.uagent; + b->uagent = data->state.aptr.uagent; } /* setup the authentication headers */ - result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, - p_stream_uri, NULL, FALSE); + result = Curl_http_output_auth(data, conn, b->request, HTTPREQ_GET, + b->stream_uri, NULL, FALSE); if(result) - goto out; + return result; #ifndef CURL_DISABLE_PROXY - p_hd_proxy_auth = data->req.hd_proxy_auth; + b->hd_proxy_auth = data->req.hd_proxy_auth; #endif - p_hd_auth = data->req.hd_auth; + b->hd_auth = data->req.hd_auth; /* Referrer */ curlx_safefree(data->state.aptr.ref); @@ -452,7 +413,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) data->state.aptr.ref = curl_maprintf("Referer: %s\r\n", Curl_bufref_ptr(&data->state.referer)); - p_referrer = data->state.aptr.ref; + b->referrer = data->state.aptr.ref; /* * Range Header @@ -461,17 +422,56 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * Go ahead and use the Range stuff supplied for HTTP */ if(data->state.use_range && - (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { + ((rtspreq == RTSPREQ_PLAY) || + (rtspreq == RTSPREQ_PAUSE) || + (rtspreq == RTSPREQ_RECORD))) { /* Check to see if there is a range set in the custom headers */ if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { - curlx_free(data->state.aptr.rangeline); - data->state.aptr.rangeline = curl_maprintf("Range: %s\r\n", - data->state.range); - p_range = data->state.aptr.rangeline; + result = rtsp_header_alloc("Range", + data->state.range, + &data->state.aptr.rangeline); + if(!result) + b->range = data->state.aptr.rangeline; } } + return result; +} + +#define HTTPVERSION 11 /* RTSP is close to HTTP/1.1, sort of... */ + +static CURLcode rtsp_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + const unsigned char rtspreq = data->set.rtspreq; + struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); + struct dynbuf req_buffer; + struct rtsp_blocks block; + memset(&block, 0, sizeof(block)); + + *done = TRUE; + if(!rtsp) + return CURLE_FAILED_INIT; + + /* Initialize a dynamic send buffer */ + curlx_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); + + rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; + rtsp->CSeq_recv = 0; + + /* Setup the 'p_request' pointer to the proper method. */ + result = pick_method(data, rtspreq, &block.request); + if(result) + goto out; + + if(rtspreq == RTSPREQ_RECEIVE) { + Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); + goto out; + } + result = rtsp_setup_request(data, &block, rtspreq); + if(result) + goto out; /* * Sanity check the custom headers */ @@ -490,7 +490,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) curlx_dyn_addf(&req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ "CSeq: %u\r\n", /* CSeq */ - p_request, p_stream_uri, rtsp->CSeq_sent); + block.request, block.stream_uri, rtsp->CSeq_sent); if(result) goto out; @@ -498,8 +498,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * Rather than do a normal alloc line, keep the session_id unformatted * to make comparison easier */ - if(p_session_id) { - result = curlx_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); + if(block.session_id) { + result = curlx_dyn_addf(&req_buffer, "Session: %s\r\n", block.session_id); if(result) goto out; } @@ -517,14 +517,14 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) "%s" /* hd_proxy_auth */ "%s" /* hd_auth */ , - p_transport ? p_transport : "", - p_accept ? p_accept : "", - p_accept_encoding ? p_accept_encoding : "", - p_range ? p_range : "", - p_referrer ? p_referrer : "", - p_uagent ? p_uagent : "", - p_hd_proxy_auth ? p_hd_proxy_auth : "", - p_hd_auth ? p_hd_auth : ""); + block.transport ? block.transport : "", + block.accept ? block.accept : "", + block.accept_encoding ? block.accept_encoding : "", + block.range ? block.range : "", + block.referrer ? block.referrer : "", + block.uagent ? block.uagent : "", + block.hd_proxy_auth ? block.hd_proxy_auth : "", + block.hd_auth ? block.hd_auth : ""); if(result) goto out; @@ -535,7 +535,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) goto out; } - result = Curl_add_custom_headers(data, FALSE, httpversion, &req_buffer); + result = Curl_add_custom_headers(data, FALSE, HTTPVERSION, &req_buffer); if(result) goto out; @@ -551,7 +551,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1); /* issue the request */ - result = Curl_req_send(data, &req_buffer, httpversion); + result = Curl_req_send(data, &req_buffer, HTTPVERSION); if(result) { failf(data, "Failed sending RTSP request"); goto out; diff --git a/lib/rtsp.h b/lib/rtsp.h index dd5df3ff73c5..791cbff33701 100644 --- a/lib/rtsp.h +++ b/lib/rtsp.h @@ -30,4 +30,18 @@ extern const struct Curl_protocol Curl_protocol_rtsp; #define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN #endif +#define RTSPREQ_NONE CURL_RTSPREQ_NONE +#define RTSPREQ_OPTIONS CURL_RTSPREQ_OPTIONS +#define RTSPREQ_DESCRIBE CURL_RTSPREQ_DESCRIBE +#define RTSPREQ_ANNOUNCE CURL_RTSPREQ_ANNOUNCE +#define RTSPREQ_SETUP CURL_RTSPREQ_SETUP +#define RTSPREQ_PLAY CURL_RTSPREQ_PLAY +#define RTSPREQ_PAUSE CURL_RTSPREQ_PAUSE +#define RTSPREQ_TEARDOWN CURL_RTSPREQ_TEARDOWN +#define RTSPREQ_GET_PARAMETER CURL_RTSPREQ_GET_PARAMETER +#define RTSPREQ_SET_PARAMETER CURL_RTSPREQ_SET_PARAMETER +#define RTSPREQ_RECORD CURL_RTSPREQ_RECORD +#define RTSPREQ_RECEIVE CURL_RTSPREQ_RECEIVE +#define RTSPREQ_LAST CURL_RTSPREQ_LAST + #endif /* HEADER_CURL_RTSP_H */ diff --git a/lib/setopt.c b/lib/setopt.c index ef4be53386a6..2e70c77b992f 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -355,46 +355,10 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) Would this be * better if the RTSPREQ_* were moved into here? */ - Curl_RtspReq rtspreq = RTSPREQ_NONE; - switch(arg) { - case CURL_RTSPREQ_OPTIONS: - rtspreq = RTSPREQ_OPTIONS; - break; - case CURL_RTSPREQ_DESCRIBE: - rtspreq = RTSPREQ_DESCRIBE; - break; - case CURL_RTSPREQ_ANNOUNCE: - rtspreq = RTSPREQ_ANNOUNCE; - break; - case CURL_RTSPREQ_SETUP: - rtspreq = RTSPREQ_SETUP; - break; - case CURL_RTSPREQ_PLAY: - rtspreq = RTSPREQ_PLAY; - break; - case CURL_RTSPREQ_PAUSE: - rtspreq = RTSPREQ_PAUSE; - break; - case CURL_RTSPREQ_TEARDOWN: - rtspreq = RTSPREQ_TEARDOWN; - break; - case CURL_RTSPREQ_GET_PARAMETER: - rtspreq = RTSPREQ_GET_PARAMETER; - break; - case CURL_RTSPREQ_SET_PARAMETER: - rtspreq = RTSPREQ_SET_PARAMETER; - break; - case CURL_RTSPREQ_RECORD: - rtspreq = RTSPREQ_RECORD; - break; - case CURL_RTSPREQ_RECEIVE: - rtspreq = RTSPREQ_RECEIVE; - break; - default: + if((arg <= CURL_RTSPREQ_NONE) || (arg >= CURL_RTSPREQ_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - } - data->set.rtspreq = rtspreq; + data->set.rtspreq = (unsigned char)arg; return CURLE_OK; } #endif /* !CURL_DISABLE_RTSP */ diff --git a/lib/transfer.c b/lib/transfer.c index 569cdeec057e..1834c6606e59 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -71,6 +71,7 @@ #include "setopt.h" #include "headers.h" #include "bufref.h" +#include "rtsp.h" #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_IMAP) diff --git a/lib/urldata.h b/lib/urldata.h index ce991ac249c6..4108f287d705 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -477,22 +477,6 @@ struct Progress { BIT(is_t_startransfer_set); }; -typedef enum { - RTSPREQ_NONE, /* first in list */ - RTSPREQ_OPTIONS, - RTSPREQ_DESCRIBE, - RTSPREQ_ANNOUNCE, - RTSPREQ_SETUP, - RTSPREQ_PLAY, - RTSPREQ_PAUSE, - RTSPREQ_TEARDOWN, - RTSPREQ_GET_PARAMETER, - RTSPREQ_SET_PARAMETER, - RTSPREQ_RECORD, - RTSPREQ_RECEIVE, - RTSPREQ_LAST /* last in list */ -} Curl_RtspReq; - struct auth { uint32_t want; /* Bitmask set to the authentication methods wanted by app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ @@ -1002,7 +986,7 @@ struct UserDefined { #ifndef CURL_DISABLE_RTSP void *rtp_out; /* write RTP to this if non-NULL */ /* Common RTSP header options */ - Curl_RtspReq rtspreq; /* RTSP request type */ + unsigned char rtspreq; /* RTSP request type */ #endif #ifndef CURL_DISABLE_FTP curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer From de9919f38a0504d3eba66da35aa9d93c49f34e4a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 11:05:01 +0200 Subject: [PATCH 0827/1062] TODO: ECH for QUIC And drop: - Consider OCSP stapling by default It is a practice that is going out-of-style, so doing this by default now seems wrong. - Provide callback for cert verification We have lots of options already. Let's not do this. Closes #22504 --- docs/TODO.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 0c65089b26c7..62030569946d 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -479,20 +479,6 @@ By changing the order of TLS extensions provided in the TLS handshake, it is sometimes possible to circumvent TLS fingerprinting by servers. The TLS extension order is of course not the only way to fingerprint a client. -## Consider OCSP stapling by default - -Treat a negative response a reason for aborting the connection. Since OCSP -stapling is presumed to get used much less in the future when Let's Encrypt -drops the OCSP support, the benefit of this might however be limited. - -[curl issue 15483](https://github.com/curl/curl/issues/15483) - -## Provide callback for cert verification - -OpenSSL supports a callback for customized verification of the peer -certificate, but this does not seem to be exposed in the libcurl APIs. Could -it be? There is so much that could be done if it were. - ## Less memory massaging with Schannel The Schannel backend does a lot of custom memory management we would rather @@ -572,6 +558,10 @@ The feature matrix at https://curl.se/libcurl/c/tls-options.html shows which features are supported by which TLS backends, and thus also where there are feature gaps. +## ECH for QUIC + +curl's support for ECH is currently limited to TCP only. + # Proxy ## Retry SOCKS handshake on address type not supported From 7db9947fcf5d3deebe98e7992ebedc9a2f624bf5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 16 Jun 2026 13:50:51 +0200 Subject: [PATCH 0828/1062] servers: drop CRT and curlx calls from `main_window_loop()` (Windows) To simplify and to avoid the chance of potential interference or thread-safety issues. If one these 3 Win32 API calls fail, there is likely a serious problem, out of the code's control. Knowing `GetLastError()` is unlikely to help. Refs: https://learn.microsoft.com/windows/win32/api/winuser/nc-winuser-wndproc https://learn.microsoft.com/previous-versions/windows/desktop/legacy/ms686736(v=vs.85) https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-getmessage https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-createwindowexa https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-registerclassa Ref: 9ea48811fed455d4a869eb100b2216f039f8677a #22487 Ref: 1c49f2f26d0f200bb9de61f795f06a1bc56845e9 #18451 Follow-up to ac1e206278b98fbe762f4b554803c64e8b562156 Closes #22045 --- tests/server/util.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/server/util.c b/tests/server/util.c index ee8d401d5c40..34d9a6858726 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -471,7 +471,7 @@ static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, static const char msg[] = "main_window_proc(): WM_CLOSE -> SIGTERM\n"; DWORD dwWritten; WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, CURL_CSTRLEN(msg), - &dwWritten, NULL); + &dwWritten, NULL); exit_msg = msg; initiate_exit(SIGTERM); break; @@ -490,17 +490,16 @@ static DWORD WINAPI main_window_loop(void *lpParameter) WNDCLASS wc; BOOL ret; MSG msg; - DWORD err; - char buffer[WINAPI_ERROR_LEN]; + DWORD dwWritten; ZeroMemory(&wc, sizeof(wc)); wc.lpfnWndProc = (WNDPROC)main_window_proc; wc.hInstance = (HINSTANCE)lpParameter; wc.lpszClassName = TEXT("MainWClass"); if(!RegisterClass(&wc)) { - err = GetLastError(); - curlx_winapi_strerror(err, buffer, sizeof(buffer)); - fprintf(stderr, "RegisterClass failed: %s\n", buffer); + static const char str[] = "RegisterClass() failed\n"; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), + &dwWritten, NULL); return (DWORD)-1; } @@ -512,18 +511,18 @@ static DWORD WINAPI main_window_loop(void *lpParameter) (HWND)NULL, (HMENU)NULL, wc.hInstance, NULL); if(!hidden_main_window) { - err = GetLastError(); - curlx_winapi_strerror(err, buffer, sizeof(buffer)); - fprintf(stderr, "CreateWindowEx failed: (0x%08lx) - %s\n", err, buffer); + static const char str[] = "CreateWindowEx() failed\n"; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), + &dwWritten, NULL); return (DWORD)-1; } do { ret = GetMessage(&msg, NULL, 0, 0); if(ret == -1) { - err = GetLastError(); - curlx_winapi_strerror(err, buffer, sizeof(buffer)); - fprintf(stderr, "GetMessage failed: (0x%08lx) - %s\n", err, buffer); + static const char str[] = "GetMessage() failed\n"; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), + &dwWritten, NULL); return (DWORD)-1; } else if(ret) { From c04189523cb848922d7ea03898b525cfffe3bc9c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 09:13:30 +0200 Subject: [PATCH 0829/1062] cookie: refuse to load cookies set against a PSL domain Verified by test 409 Reported-by: 1rhino2 on hackerone Closes #22500 --- docs/libcurl/opts/CURLOPT_COOKIELIST.md | 16 +++- lib/cookie.c | 99 ++++++++++++++----------- lib/cookie.h | 20 +++-- lib/http.c | 8 +- lib/setopt.c | 17 +++-- lib/transfer.c | 4 +- tests/data/Makefile.am | 2 +- tests/data/test409 | 60 +++++++++++++++ 8 files changed, 165 insertions(+), 61 deletions(-) create mode 100644 tests/data/test409 diff --git a/docs/libcurl/opts/CURLOPT_COOKIELIST.md b/docs/libcurl/opts/CURLOPT_COOKIELIST.md index 9b9456ec1233..e55699b49809 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIELIST.md +++ b/docs/libcurl/opts/CURLOPT_COOKIELIST.md @@ -47,6 +47,18 @@ future transfers to that server, likely not what you intended. To address these issues set a domain in `Set-Cookie` (doing that includes subdomains) or much better: use the Netscape file format. +Cookies added through this API bypass automatic Public Suffix List (PSL) +checking because the handle's internal PSL engine has not yet been initialized +when the call is made. Under normal transfer operations, PSL validation +prevents cookies from being set on broad or shared domains - such as `.com`, +`.co.uk`, or `.github.io` - which would otherwise create security +vulnerabilities by allowing unrelated subdomains to access sensitive cookie +data. Because the library skips this safety check during manual cookie +insertion, the caller assumes full responsibility for domain validation. +Applications using this interface must independently verify that the target +domain attribute represents a valid host and does not match a public suffix +before injecting the cookie into the handle. + Additionally, there are commands available that perform actions if you pass in these exact strings: @@ -64,7 +76,9 @@ writes all known cookies to the file specified by CURLOPT_COOKIEJAR(3) ## `RELOAD` -loads all cookies from the files specified by CURLOPT_COOKIEFILE(3) +loads all cookies from the files specified by CURLOPT_COOKIEFILE(3). If +CURLOPT_COOKIESESSION(3) is enabled before this reload, it is applied to this +load operation as well and all session cookies are discarded. # DEFAULT diff --git a/lib/cookie.c b/lib/cookie.c index 83baf435f118..c1a28bf8b145 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -803,17 +803,23 @@ static bool is_public_suffix(struct Curl_easy *data, { #ifdef USE_LIBPSL /* - * Check if the domain is a Public Suffix and if yes, ignore the cookie. We - * must also check that the data handle is not NULL since the psl code will - * dereference it. + * Check if the domain is a Public Suffix and if yes, ignore the cookie. + * 'domain' is NULL when the cookie is loaded from file or + * CURLOPT_COOKIELIST. */ + DEBUGASSERT(data); + DEBUGASSERT(co); DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); - if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { + co->name, co->domain ? co->domain : "[blank]", + domain ? domain : "[file]")); + if(!co->domain || Curl_host_is_ipnum(co->domain)) + return FALSE; + + else { bool acceptable = FALSE; char lcase[256]; char lcookie[256]; - size_t dlen = strlen(domain); + size_t dlen = domain ? strlen(domain) : 0; size_t clen = strlen(co->domain); /* trim trailing dots */ @@ -826,11 +832,17 @@ static bool is_public_suffix(struct Curl_easy *data, const psl_ctx_t *psl = Curl_psl_use(data); if(psl) { /* the PSL check requires lowercase domain name and pattern */ - Curl_strntolower(lcase, domain, dlen); - lcase[dlen] = 0; Curl_strntolower(lcookie, co->domain, clen); lcookie[clen] = 0; - acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + if(domain) { + Curl_strntolower(lcase, domain, dlen); + lcase[dlen] = 0; + acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + } + else + /* note that this PSL function returns the opposite value than + psl_is_cookie_domain_acceptable() does */ + acceptable = !psl_is_public_suffix(psl, lcookie); Curl_psl_release(data); } else @@ -839,7 +851,8 @@ static bool is_public_suffix(struct Curl_easy *data, if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " - "set cookies for '%s'", co->name, domain, co->domain); + "set cookies for '%s'", co->name, + domain ? domain : "[file]", co->domain); return TRUE; } } @@ -848,7 +861,7 @@ static bool is_public_suffix(struct Curl_easy *data, (void)co; (void)domain; DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); + co->name, co->domain, domain ? domain : "[file]")); #endif return FALSE; } @@ -968,16 +981,15 @@ static bool replace_existing(struct Curl_easy *data, * IPv6 address. * */ -CURLcode Curl_cookie_add( - struct Curl_easy *data, - struct CookieInfo *ci, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - const char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, used - to get default path for the cookie unless set */ - bool secure) /* TRUE if connection is over secure origin */ +CURLcode Curl_cookie_add(struct Curl_easy *data, + struct CookieInfo *ci, + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this + cookie is set, used to get + default path for the cookie + unless set */ + const int flags) { struct Cookie comem; struct Cookie *co; @@ -994,11 +1006,11 @@ CURLcode Curl_cookie_add( co = &comem; memset(co, 0, sizeof(comem)); - if(httpheader) + if(flags & COOKIE_HTTPHEADER) result = parse_cookie_header(data, co, ci, &okay, - lineptr, domain, path, secure); + lineptr, domain, path, flags & COOKIE_SECURE); else - result = parse_netscape(co, ci, &okay, lineptr, secure); + result = parse_netscape(co, ci, &okay, lineptr, flags & COOKIE_SECURE); if(result || !okay) goto fail; @@ -1026,20 +1038,19 @@ CURLcode Curl_cookie_add( co->livecookie = ci->running; co->creationtime = ++ci->lastct; - /* - * Now we have parsed the incoming line, we must now check if this supersedes - * an already existing cookie, which it may if the previous have the same - * domain and path as this. - */ - /* remove expired cookies */ - if(!noexpire) + if(!(flags & COOKIE_NOEXPIRE)) remove_expired(ci); - if(is_public_suffix(data, co, domain)) + if(!(flags & COOKIE_NOPSL) && is_public_suffix(data, co, domain)) goto fail; - if(!replace_existing(data, co, ci, secure, &replaces)) + /* + * Now we have parsed the incoming line, we must now check if this supersedes + * an already existing cookie, which it may if the previous have the same + * domain and path as this. + */ + if(!replace_existing(data, co, ci, flags & COOKIE_SECURE, &replaces)) goto fail; /* clone the stack struct into heap */ @@ -1071,7 +1082,7 @@ CURLcode Curl_cookie_add( if(co->expires && (co->expires < ci->next_expiration)) ci->next_expiration = co->expires; - if(httpheader) + if(flags & COOKIE_HTTPHEADER) data->req.setcookies++; return result; @@ -1120,11 +1131,11 @@ struct CookieInfo *Curl_cookie_init(void) * Reads cookies from a local file. This is always called before any cookies * are set. If file is "-" then STDIN is read. * - * If 'newsession' is TRUE, discard all "session cookies" on read from file. - * + * If 'flags' has the COOKIE_NOSESSION bit set, discard all "session cookies" + * read from file. */ static CURLcode cookie_load(struct Curl_easy *data, const char *file, - struct CookieInfo *ci, bool newsession) + struct CookieInfo *ci, int flags) { FILE *handle = NULL; CURLcode result = CURLE_OK; @@ -1133,7 +1144,7 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, DEBUGASSERT(data); DEBUGASSERT(file); - ci->newsession = newsession; /* new session? */ + ci->newsession = !!(flags & COOKIE_NOSESSION); /* new session? */ ci->running = FALSE; /* this is not running, this is init */ if(file && *file) { @@ -1173,8 +1184,10 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, curlx_str_passblanks(&lineptr); } - result = Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, - NULL, TRUE); + result = Curl_cookie_add(data, ci, lineptr, NULL, NULL, + (headerline ? COOKIE_HTTPHEADER : 0) | + COOKIE_NOEXPIRE | COOKIE_SECURE | + (flags & COOKIE_NOPSL)); /* File reading cookie failures are not propagated back to the caller because there is no way to do that */ } @@ -1199,7 +1212,8 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, /* * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). */ -CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) +CURLcode Curl_cookie_loadfiles(struct Curl_easy *data, + int flags) { CURLcode result = CURLE_OK; struct curl_slist *list = data->state.cookielist; @@ -1212,8 +1226,7 @@ CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) else { data->state.cookie_engine = TRUE; while(list) { - result = cookie_load(data, list->data, data->cookies, - (bool)data->set.cookiesession); + result = cookie_load(data, list->data, data->cookies, flags); if(result) break; list = list->next; diff --git a/lib/cookie.h b/lib/cookie.h index 286c47fd3afa..973ec0954761 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -104,20 +104,25 @@ struct CookieInfo { struct Curl_easy; struct connectdata; +bool Curl_secure_context(struct Curl_easy *data, const char *host); + /* * Add a cookie to the internal list of cookies. The domain and path arguments - * are only used if the header boolean is TRUE. + * are only used if the COOKIE_HTTPHEADER bit is set in the flags. */ -bool Curl_secure_context(struct Curl_easy *data, const char *host); +#define COOKIE_HTTPHEADER (1<<0) /* if HTTP header-style line */ +#define COOKIE_NOEXPIRE (1<<1) /* skip remove_expired() */ +#define COOKIE_SECURE (1<<2) /* connection is over secure origin */ +#define COOKIE_NOPSL (1<<3) /* skip PSL check */ +#define COOKIE_NOSESSION (1<<6) /* drop session cookies */ + CURLcode Curl_cookie_add(struct Curl_easy *data, struct CookieInfo *ci, - bool httpheader, - bool noexpire, const char *lineptr, const char *domain, const char *path, - bool secure) WARN_UNUSED_RESULT; + const int flags) WARN_UNUSED_RESULT; CURLcode Curl_cookie_getlist(struct Curl_easy *data, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; @@ -126,7 +131,7 @@ void Curl_cookie_clearsess(struct CookieInfo *ci); #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) #define Curl_cookie_list(x) NULL -#define Curl_cookie_loadfiles(x) CURLE_OK +#define Curl_cookie_loadfiles(x, y) CURLE_OK #define Curl_cookie_init() NULL #define Curl_cookie_run(x) Curl_nop_stmt #define Curl_cookie_cleanup(x) Curl_nop_stmt @@ -136,7 +141,8 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup); void Curl_cookie_cleanup(struct CookieInfo *ci); struct CookieInfo *Curl_cookie_init(void); struct curl_slist *Curl_cookie_list(struct Curl_easy *data); -CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) WARN_UNUSED_RESULT; +CURLcode Curl_cookie_loadfiles(struct Curl_easy *data, + int flags) WARN_UNUSED_RESULT; void Curl_cookie_run(struct Curl_easy *data); #endif diff --git a/lib/http.c b/lib/http.c index 98d01c4305e4..d3cb5f72b5eb 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3584,11 +3584,13 @@ static CURLcode http_header_s(struct Curl_easy *data, * real peer hostname. */ const char *host = data->req.cookiehost ? data->req.cookiehost : data->state.origin->hostname; - const bool secure_context = Curl_secure_context(data, host); + const unsigned char secure_context = Curl_secure_context(data, host) ? + COOKIE_SECURE : 0; CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, - data->state.up.path, secure_context); + result = Curl_cookie_add(data, data->cookies, v, host, + data->state.up.path, + COOKIE_HTTPHEADER | secure_context); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); return result; } diff --git a/lib/setopt.c b/lib/setopt.c index 2e70c77b992f..924263478b24 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1527,7 +1527,9 @@ static CURLcode cookielist(struct Curl_easy *data, const char *ptr) } else if(curl_strequal(ptr, "RELOAD")) { /* reload cookies from file */ - return Curl_cookie_loadfiles(data); + return Curl_cookie_loadfiles(data, COOKIE_NOPSL | + (data->set.cookiesession ? + COOKIE_NOSESSION : 0)); } else { if(!data->cookies) { @@ -1542,15 +1544,20 @@ static CURLcode cookielist(struct Curl_easy *data, const char *ptr) if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) return CURLE_BAD_FUNCTION_ARGUMENT; + /* Adding these cookies without the PSL check, because the PSL is not + initialized until *perform() time, and this might be called before + that */ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); if(checkprefix("Set-Cookie:", ptr)) /* HTTP Header format line */ - result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, - NULL, NULL, TRUE); + result = Curl_cookie_add(data, data->cookies, ptr + 11, + NULL, NULL, + COOKIE_HTTPHEADER | COOKIE_SECURE | + COOKIE_NOPSL); else /* Netscape format line */ - result = Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, - NULL, TRUE); + result = Curl_cookie_add(data, data->cookies, ptr, NULL, + NULL, COOKIE_SECURE | COOKIE_NOPSL); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } return result; diff --git a/lib/transfer.c b/lib/transfer.c index 1834c6606e59..673d79837da2 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -525,7 +525,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.infilesize = 0; /* If there is a list of cookie files to read, do it now! */ - result = Curl_cookie_loadfiles(data); + result = Curl_cookie_loadfiles(data, + data->set.cookiesession ? + COOKIE_NOSESSION : 0); if(!result) Curl_cookie_run(data); /* activate */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 76ac5be1bf26..4993864a5a36 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -70,7 +70,7 @@ test370 test371 test372 test373 test374 test375 test376 test378 test379 \ test380 test381 test383 test384 test385 test386 test387 test388 test389 \ test390 test391 test392 test393 test394 test395 test396 test397 test398 \ test399 test400 test401 test402 test403 test404 test405 test406 test407 \ -test408 test410 test411 test412 test413 test414 test415 test416 \ +test408 test409 test410 test411 test412 test413 test414 test415 test416 \ test417 test418 test419 test420 test421 test422 test423 test424 test425 \ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ test435 test436 test437 test438 test439 test440 test441 test442 test443 \ diff --git a/tests/data/test409 b/tests/data/test409 new file mode 100644 index 000000000000..f9a7131ce23c --- /dev/null +++ b/tests/data/test409 @@ -0,0 +1,60 @@ + + + + +HTTP +cookies + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: something-close, close-something, close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +cookies +PSL + + +http + + +Load cookies from file where some are PSL domains + + +http://foo.co.uk:%HTTPPORT/%TESTNUMBER -b %LOGDIR/cookies.txt --resolve foo.co.uk:%HTTPPORT:%HOSTIP + + +.co.uk%TABTRUE%TAB/%TABFALSE%TAB0%TABbad%TABnot-allowed +foo.co.uk%TABTRUE%TAB/%TABFALSE%TAB0%TABgood%TABallowed + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: foo.co.uk:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: good=allowed + + + + From 24ff74fa8db2dd84b2acd190a1bcc04b7d69e1e7 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 6 Aug 2026 08:24:02 -0700 Subject: [PATCH 0830/1062] CI: fix labeler matches for vdns file move --- .github/labeler.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index b298918ec86c..1219b549fdee 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -388,16 +388,12 @@ name lookup: docs/libcurl/opts/CURLOPT_DOH*,\ docs/libcurl/opts/CURLOPT_RESOLVE*,\ lib/*addrinfo*,\ - lib/asyn*,\ - lib/cf-dns.*,\ lib/curl_gethostname.*,\ - lib/dns*,\ - lib/doh*,\ - lib/host*,\ lib/idn*,\ lib/socketpair*,\ lib/thrdpool.*,\ lib/thrdqueue.*,\ + lib/vdns/*,\ tests/http/testenv/dnsd.*,\ tests/http/*httpsrr*,\ tests/http/*resolve.py,\ From 7f6a75664f9fb45390193e492ee3361fb795d098 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 6 Aug 2026 13:46:41 +0200 Subject: [PATCH 0831/1062] dnsd: add support for DoH dnsd now opens UDP+TCP sockets and accepts http: DoH requests to obtain the same, configured answers (records, delays, error codes) as over UDP. DoH: use `async->queries_ongoing` like all other resolvers instead of the internal `pending` counter. Fixes waiting for results. Tests: in pytest, parameterize dnsd tests to use both DNS and DoH. Closes #22506 --- lib/vdns/doh.c | 18 +- lib/vdns/doh.h | 1 - tests/http/test_21_resolve.py | 85 ++- tests/http/test_22_httpsrr.py | 85 +-- tests/http/testenv/env.py | 5 + tests/server/dnsd.c | 1359 ++++++++++++++++++++++----------- tests/server/first.h | 8 + tests/server/sws.c | 225 +----- tests/server/util.c | 339 ++++++++ 9 files changed, 1379 insertions(+), 746 deletions(-) diff --git a/lib/vdns/doh.c b/lib/vdns/doh.c index 592cb07583fe..ccf763dc54d8 100644 --- a/lib/vdns/doh.c +++ b/lib/vdns/doh.c @@ -251,8 +251,8 @@ static void doh_probe_done(struct Curl_easy *doh, return; } - dohp->pending--; - infof(doh, "a DoH request is completed, %u to go", dohp->pending); + async->queries_ongoing--; + infof(doh, "a DoH request is completed, %u to go", async->queries_ongoing); dohp->probe_resp[i].result = result; /* We expect either the meta data still to exist or the sub request * to have already failed. */ @@ -267,7 +267,7 @@ static void doh_probe_done(struct Curl_easy *doh, if(result) infof(doh, "DoH request %s", curl_easy_strerror(result)); - if(!dohp->pending) { + if(!async->queries_ongoing) { /* DoH completed, run master to act on results */ Curl_multi_mark_dirty(master); } @@ -487,7 +487,7 @@ CURLcode Curl_doh(struct Curl_easy *data, &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); if(result) goto error; - dohp->pending++; + async->queries_ongoing++; } #ifdef USE_IPV6 @@ -499,7 +499,7 @@ CURLcode Curl_doh(struct Curl_easy *data, &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); if(result) goto error; - dohp->pending++; + async->queries_ongoing++; } #endif @@ -520,7 +520,7 @@ CURLcode Curl_doh(struct Curl_easy *data, curlx_free(qname); if(result) goto error; - dohp->pending++; + async->queries_ongoing++; } #endif return CURLE_OK; @@ -1211,7 +1211,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, return async->for_proxy ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } - else if(!dohp->pending) { + else if(!async->queries_ongoing) { struct Curl_dns_entry *dns = NULL; DOHcode rc[DOH_SLOT_COUNT]; bool negative = TRUE; @@ -1235,7 +1235,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, if(rc[slot] && (rc[slot] != DOH_DNS_NXDOMAIN)) negative = FALSE; if(rc[slot]) { - CURL_TRC_DNS(data, "[%s] [DoH] error: %s type %s for %s", + CURL_TRC_DNS(data, "[%s] [DoH] error: %s of type %s for %s", Curl_resolv_query_str(async->dns_queries), doh_strerror(rc[slot]), doh_type2name(p->dnstype), async->peer->hostname); @@ -1306,7 +1306,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, if(dns) result = Curl_dnscache_add(data, dns); *pdns = dns; - } /* !dohp->pending */ + } /* !async->queries_ongoing */ else /* wait for pending DoH transactions to complete */ return CURLE_AGAIN; diff --git a/lib/vdns/doh.h b/lib/vdns/doh.h index 5179c28caecf..1e4271796fe5 100644 --- a/lib/vdns/doh.h +++ b/lib/vdns/doh.h @@ -109,7 +109,6 @@ struct doh_response { * as easy meta for CURL_EZM_DOH_MASTER */ struct doh_probes { struct doh_response probe_resp[DOH_SLOT_COUNT]; - unsigned int pending; /* still outstanding probes */ }; /* diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index ec15de7a9c19..685a9792aec7 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -124,73 +124,84 @@ def test_21_05_resolv_single_thread(self, env: Env, httpd, nghttpx): r.check_stats(count=count, http_status=0, exitcode=6) assert r.duration > timedelta(milliseconds=count * delay_ms), f'{r}' + def dns_settings(self, dns_method, dnsd): + xargs = [] + run_env = os.environ.copy() + run_env['CURL_DEBUG'] = 'dns,doh' + if dns_method == 'DoH': + if not Env.curl_can_doh(): + pytest.skip(reason="curl built without DoH") + xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}/'] + else: + if not Env.curl_override_dns(): + pytest.skip(reason="no DNS override") + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env['CURL_QUICK_EXIT'] = '1' + return run_env, xargs + # dnsd with no answers - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") - def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd, dns_method): dnsd.set_answers() - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = 'https://test-dnsd.http.curl.invalid/' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(6) # could not resolve host r.check_stats(count=1, http_status=0, exitcode=6) # dnsd with one answer for A - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") - def test_21_07_dnsd_a(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_07_dnsd_a(self, env: Env, httpd, dnsd, dns_method): dnsd.set_answers(addr_a=['127.0.0.1']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['remote_ip'] == '127.0.0.1' # dnsd with one answer for AAAA - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") - def test_21_08_dnsd_aaaa(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_08_dnsd_aaaa(self, env: Env, httpd, dnsd, dns_method): dnsd.set_answers(addr_aaaa=['[::1]']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_QUICK_EXIT'] = '1' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['remote_ip'] == '::1' # dnsd with one answer for A, delayed one for AAAA - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") - def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd, dns_method): + if dns_method == 'DoH': + pytest.skip(reason='DoH does not handle partial responses') dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'], delay_aaaa_ms=env.test_timeout * 1000) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_QUICK_EXIT'] = '1' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['remote_ip'] == '127.0.0.1' # dnsd with one answer for AAAA, delayed one for A - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") - def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd, dns_method): + if dns_method == 'DoH': + pytest.skip(reason='DoH does not handle partial responses') dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'], delay_a_ms=env.test_timeout * 1000) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_QUICK_EXIT'] = '1' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['remote_ip'] == '::1' @@ -234,15 +245,14 @@ def test_21_12_resolv_negative_cached(self, env: Env, httpd, nghttpx): # dnsd giving NXDOMAIN for all families: the negative answer is # cached and a second lookup of the same name uses the cache - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") - def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd, dns_method): count = 2 dnsd.set_answers(rcode_a=3, rcode_aaaa=3) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) urls = [f'https://test-nx.http.curl.invalid/?id={i}' for i in range(count)] - r = curl.http_download(urls=urls, with_stats=True) + r = curl.http_download(urls=urls, with_stats=True, extra_args=xargs) r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) if env.curl_is_verbose(): @@ -250,15 +260,14 @@ def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd): # dnsd failing one family with SERVFAIL: not an authoritative # negative answer, a second lookup of the same name tries again - @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") - def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd, dns_method): count = 2 dnsd.set_answers(rcode_a=2, rcode_aaaa=3) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) urls = [f'https://test-sf.http.curl.invalid/?id={i}' for i in range(count)] - r = curl.http_download(urls=urls, with_stats=True) + r = curl.http_download(urls=urls, with_stats=True, extra_args=xargs) r.check_exit_code(6) r.check_stats(count=count, http_status=0, exitcode=6) if env.curl_is_verbose(): diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 6ae5d0c72825..63bb2c029ed9 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -33,7 +33,6 @@ @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") -@pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override") @pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPSRR'), reason="no HTTPSRR support") class TestHTTPSRR: @@ -44,86 +43,86 @@ def dnsd(self, env: Env) -> Generator[Dnsd, None, None]: yield dnsd dnsd.stop() - # dnsd a HTTPS-RR that prefers HTTP/1.1. - def test_22_01_httpsrr_h1(self, env: Env, httpd, dnsd): - dnsd.set_answers(addr_a=['127.0.0.1'], - https=['10 . alpn=http/1.1']) + def dns_settings(self, dns_method, dnsd): run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' run_env['CURL_QUICK_EXIT'] = '1' run_env['CURL_DEBUG'] = 'dns,https-connect' + run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' + xargs = [] + if dns_method == 'DoH': + if not Env.curl_can_doh(): + pytest.skip(reason="curl built without DoH") + xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}/'] + else: + if not Env.curl_override_dns(): + pytest.skip(reason="no DNS override") + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + return run_env, xargs + + # dnsd a HTTPS-RR that prefers HTTP/1.1. + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_01_httpsrr_h1(self, env: Env, httpd, dnsd, dns_method): + dnsd.set_answers(addr_a=['127.0.0.1'], + https=['10 . alpn=http/1.1']) + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '1.1', f'{r}' # dnsd a HTTPS-RR that prefers HTTP/2, this overrides the --http3 option. @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") - def test_22_02_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_02_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx, dns_method): dnsd.set_answers(addr_a=['127.0.0.1'], https=['10 . alpn=h2']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' - run_env['CURL_QUICK_EXIT'] = '1' - run_env['CURL_DEBUG'] = 'dns,https-connect' + run_env, xargs = self.dns_settings(dns_method, dnsd) + xargs.append('--http3') curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True, extra_args=[ - '--http3' - ]) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '2', f'{r}' # dnsd a HTTPS-RR that prefers HTTP/3. @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") - def test_22_03_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_03_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx, dns_method): dnsd.set_answers(addr_a=['127.0.0.1'], https=['10 . alpn=h3,h2']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' - run_env['CURL_QUICK_EXIT'] = '1' - run_env['CURL_DEBUG'] = 'dns,https-connect' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '3', f'{r}' # dnsd a HTTPS-RR that prefers HTTP/1.1 for another target, so ignored. - def test_22_04_httpsrr_wrong_target(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_04_httpsrr_wrong_target(self, env: Env, httpd, dnsd, dns_method): dnsd.set_answers(addr_a=['127.0.0.1'], https=['10 another alpn=http/1.1']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' - run_env['CURL_QUICK_EXIT'] = '1' - run_env['CURL_DEBUG'] = 'dns,https-connect' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '2', f'{r}' # dnsd a HTTPS-RR with no-default-alpn, ignored by curl for now - def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd, dns_method): dnsd.set_answers(addr_a=['127.0.0.1'], https=['10 . no-default-alpn alpn=http/1.1']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' - run_env['CURL_QUICK_EXIT'] = '1' - run_env['CURL_DEBUG'] = 'dns,https-connect' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env, force_resolv=False) url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json' - r = curl.http_download(urls=[url], with_stats=True) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '2', f'{r}' @@ -132,16 +131,14 @@ def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd): @pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'), reason='curl lacks HTTPS-proxy support') @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available") - def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd): + @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) + def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd, dns_method): dnsd.set_answers(addr_a=['127.0.0.1'], https=['10 . alpn=http/1.1']) - run_env = os.environ.copy() - run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' - run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' - run_env['CURL_QUICK_EXIT'] = '1' + run_env, xargs = self.dns_settings(dns_method, dnsd) curl = CurlClient(env=env, run_env=run_env) url = f'https://localhost:{env.https_port}/data.json' - xargs = curl.get_proxy_args(tunnel=True) + xargs.extend(curl.get_proxy_args(tunnel=True)) r = curl.http_download(urls=[url], alpn_proto='h2', with_stats=True, extra_args=xargs) r.check_response(count=1, http_status=200) diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 0c19d6bd1619..baabbe72549a 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -163,6 +163,7 @@ def __init__( self.curl_can_cert_status = 'cert-status: ON' in p.stdout self.curl_override_dns = 'override-dns: ON' in p.stdout self.curl_resolv_threaded = 'resolv-threaded: ON' in p.stdout + self.curl_can_doh = 'DoH: ON' in p.stdout self.ports = {} @@ -590,6 +591,10 @@ def curl_override_dns() -> bool: def curl_resolv_threaded() -> bool: return Env.CONFIG.curl_resolv_threaded + @staticmethod + def curl_can_doh() -> bool: + return Env.CONFIG.curl_can_doh + @staticmethod def curl_can_early_data() -> bool: if Env.curl_uses_lib('gnutls'): diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 8e195e5c61a9..4c512a3701b0 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -187,7 +187,8 @@ static const char *type2string(uint16_t qtype) * * Return query (qname + type + class), type and id. */ -static int store_incoming(int qid, const unsigned char *data, size_t size, +static int store_incoming(const char *source, int query_id, + const unsigned char *data, size_t size, unsigned char *qbuf, size_t qbuflen, size_t *qlen, uint16_t *qtype, uint16_t *idp) { @@ -214,7 +215,7 @@ static int store_incoming(int qid, const unsigned char *data, size_t size, int error = errno; logmsg("fopen() failed with error (%d) %s", error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Error opening file '%s'", dumpfile); + logmsg("[%s] Error opening file '%s'", source, dumpfile); return -1; } @@ -239,12 +240,12 @@ static int store_incoming(int qid, const unsigned char *data, size_t size, data += 2; /* skip the next 16 bits */ size -= 2; #if 0 - fprintf(server, "QR: %x\n", (*idp & 0x8000) > 15); - fprintf(server, "OPCODE: %x\n", (*idp & 0x7800) >> 11); - fprintf(server, "TC: %x\n", (*idp & 0x200) >> 9); - fprintf(server, "RD: %x\n", (*idp & 0x100) >> 8); - fprintf(server, "Z: %x\n", (*idp & 0x70) >> 4); - fprintf(server, "RCODE: %x\n", (*idp & 0x0f)); + fprintf(server, "[%s] QR: %x\n", source, (*idp & 0x8000) > 15); + fprintf(server, "[%s] OPCODE: %x\n", source, (*idp & 0x7800) >> 11); + fprintf(server, "[%s] TC: %x\n", source, (*idp & 0x200) >> 9); + fprintf(server, "[%s] RD: %x\n", source, (*idp & 0x100) >> 8); + fprintf(server, "[%s] Z: %x\n", source, (*idp & 0x70) >> 4); + fprintf(server, "[%s] RCODE: %x\n", source, (*idp & 0x0f)); #endif (void)get16bit(&data, &size); @@ -259,8 +260,8 @@ static int store_incoming(int qid, const unsigned char *data, size_t size, qd = get16bit(&data, &size); fprintf(server, "QNAME %s QTYPE %s\n", name, type2string(qd)); *qtype = qd; - logmsg("[%d] Question for '%s' type %x / %s", - qid, name, qd, type2string(qd)); + logmsg("[%d] [%s] Question for '%s' type %x / %s", + query_id, source, name, qd, type2string(qd)); (void)get16bit(&data, &size); @@ -310,256 +311,26 @@ static int add_answer(struct blob *body, return blob_addn(body, a, alen); } -#ifdef _WIN32 -#define SENDTO3 int -#else -#define SENDTO3 size_t -#endif - -#define INSTRUCTIONS "dnsd.cmd" - -static curlx_struct_stat finfo_last; -static unsigned char ipv4_pref[4]; -static unsigned char ipv6_pref[16]; -static unsigned char ancount_a; -static unsigned char ancount_aaaa; - -static timediff_t a_delay_ms; -static timediff_t aaaa_delay_ms; -static timediff_t https_delay_ms; -static unsigned char rcode_a; -static unsigned char rcode_aaaa; - -static int query_id = -1; - -static struct blob httpsrr; - -struct resp { - struct resp *next; - int qid; - struct curltime send_ts; - struct sockaddr addr; - curl_socklen_t addrlen; - struct blob body; -}; - -static struct resp *resp_queue; - -static CURLcode send_resp(curl_socket_t sock, struct resp *resp) -{ - ssize_t rc; - int sockerr = 0; - - do { - rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen, - 0, &resp->addr, resp->addrlen); - } while((rc < 0) && ((sockerr = SOCKERRNO) == SOCKEINTR)); - if(rc < 0) { - char errbuf[STRERROR_LEN]; - logmsg("failed sending %zu bytes, error: (%d) %s", resp->body.dlen, - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - return CURLE_SEND_ERROR; - } - else if(rc != (ssize_t)resp->body.dlen) { - logmsg("failed sending %zu bytes, sent: %zd", resp->body.dlen, rc); - return CURLE_SEND_ERROR; - } - logmsg("[%d] sent response", resp->qid); - return CURLE_OK; -} - -static void queue_resp(struct resp *resp) -{ - struct resp **panchor = &resp_queue; - while(*panchor) { - timediff_t ms = curlx_ptimediff_ms(&(*panchor)->send_ts, &resp->send_ts); - if(ms > 0) /* resp is to be sent before *panchor */ - break; - panchor = &(*panchor)->next; - } - resp->next = *panchor; - *panchor = resp; -} - -static timediff_t send_resp_queue(curl_socket_t sock) -{ - struct resp **panchor = &resp_queue; - struct curltime now = curlx_now(); - timediff_t timeout_ms = 0; - - while(*panchor) { - struct resp *resp = *panchor; - timediff_t ms = curlx_ptimediff_ms(&resp->send_ts, &now); - - if(ms > 0) { - timeout_ms = ms; - break; - } - *panchor = resp->next; - send_resp(sock, resp); - curlx_free(resp); - } - return timeout_ms; -} - -static void clear_resp_queue(void) +static void fdset_add_sock(fd_set *fds, curl_socket_t sock, int *pmaxfd) { - while(resp_queue) { - struct resp *resp = resp_queue; - resp_queue = resp->next; - curlx_free(resp); - } + FD_SET(sock, fds); + if((int)sock > *pmaxfd) + *pmaxfd = (int)sock; } -/* this is an answer to a question */ -static struct resp * -create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, - const unsigned char *qbuf, size_t qlen, - uint16_t qtype, uint16_t id) +static struct curltime now_plus(timediff_t delta_ms) { - struct resp *resp; - int a; - timediff_t delay_ms = 0; - char addrbuf[128]; /* IP address buffer */ - uint8_t header[12] = { - 0x80, 0xea, /* ID, overwrite */ - 0x81, 0x80, - /* Flags: 0x8180 Standard query response, No error - - 1... .... .... .... = Response: Message is a response - .000 0... .... .... = Opcode: Standard query (0) - .... .0.. .... .... = Authoritative: Server is not an authority for - domain - .... ..0. .... .... = Truncated: Message is not truncated - .... ...1 .... .... = Recursion desired: Do query recursively - .... .... 1... .... = Recursion available: Server can do recursive - queries - .... .... .0.. .... = Z: reserved (0) - .... .... ..0. .... = Answer authenticated: Answer/authority portion - was not authenticated by the server - .... .... ...0 .... = Non-authenticated data: Unacceptable - .... .... .... 0000 = Reply code: No error (0) - */ - 0x0, 0x1, /* QDCOUNT a single question */ - 0x0, 0x0, /* ANCOUNT number of answers */ - 0x0, 0x0, /* NSCOUNT */ - 0x0, 0x0 /* ARCOUNT */ - }; - uint16_t ancount = 0; - unsigned char rcode = 0; - - switch(qtype) { - case QTYPE_A: - ancount = ancount_a; - delay_ms = a_delay_ms; - rcode = rcode_a; - break; - case QTYPE_AAAA: - ancount = ancount_aaaa; - delay_ms = aaaa_delay_ms; - rcode = rcode_aaaa; - break; - case QTYPE_HTTPS: - if(httpsrr.dlen) - ancount = 1; - delay_ms = https_delay_ms; - break; - } - if(rcode) - ancount = 0; - - resp = curlx_calloc(1, sizeof(*resp)); - if(!resp) - goto error; - - resp->qid = qid; - /* on some platforms `curl_socklen_t` is an `int`. Casting might - * wrap this, but then it still has to fit our record size. */ - if((size_t)addrlen > sizeof(resp->addr)) { - logmsg("unable to handle addrlen of %zu", (size_t)addrlen); - goto error; - } - memcpy(&resp->addr, CURL_UNCONST(addr), addrlen); - resp->addrlen = addrlen; - - header[0] = (uint8_t)(id >> 8); - header[1] = (uint8_t)(id & 0xff); - - if(rcode) { - header[3] = (uint8_t)((header[3] & 0xf0) | (rcode & 0x0f)); - logmsg("[%d] response rcode %u", qid, (unsigned int)rcode); - } - - header[6] = (uint8_t)(ancount >> 8); - header[7] = (uint8_t)(ancount & 0xff); - - if(blob_addn(&resp->body, header, sizeof(header))) - goto error; - - if(blob_addn(&resp->body, qbuf, qlen)) { - logmsg("unable to handle query of length %zu", qlen); - goto error; - } - - switch(qtype) { - case QTYPE_A: - for(a = 0; !rcode && (a < ancount_a); a++) { - const unsigned char *store = ipv4_pref; - const char *ip; - if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A)) - goto error; - ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)); - logmsg("[%d] response A (%x) '%s'", qid, (unsigned int)QTYPE_A, - ip ? ip : "(null)"); - } - if(!ancount_a) - logmsg("[%d] response A empty", qid); - break; - case QTYPE_AAAA: - for(a = 0; !rcode && (a < ancount_aaaa); a++) { - const unsigned char *store = ipv6_pref; - const char *ip; - if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA)) - goto error; - ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)); - logmsg("[%d] response AAAA (%x) '%s'", qid, (unsigned int)QTYPE_AAAA, - ip ? ip : "(null)"); - } - if(!ancount_aaaa) - logmsg("[%d] response AAAA empty", qid); - break; - case QTYPE_HTTPS: - if(httpsrr.dlen) { - if(add_answer(&resp->body, httpsrr.data, httpsrr.dlen, QTYPE_HTTPS)) { - logmsg("[%d] error adding https %zu response bytes", qid, - httpsrr.dlen); - goto error; - } - logmsg("[%d] response HTTPS (%x), %zu bytes", qid, - (unsigned int)QTYPE_HTTPS, httpsrr.dlen); - } - else - logmsg("[%d] response HTTPS, no record", qid); - break; - } - - resp->send_ts = curlx_now(); - if(delay_ms > 0) { - int usec = (int)((delay_ms % 1000) * 1000); - resp->send_ts.tv_sec += (time_t)(delay_ms / 1000); - resp->send_ts.tv_usec += usec; - if(resp->send_ts.tv_usec >= 1000000) { - resp->send_ts.tv_sec++; - resp->send_ts.tv_usec -= 1000000; + struct curltime ts = curlx_now(); + if(delta_ms > 0) { + int usec = (int)((delta_ms % 1000) * 1000); + ts.tv_sec += (time_t)(delta_ms / 1000); + ts.tv_usec += usec; + if(ts.tv_usec >= 1000000) { + ts.tv_sec++; + ts.tv_usec -= 1000000; } - logmsg("[%d] delay response by %" FMT_TIMEDIFF_T "ms", qid, delay_ms); } - return resp; - -error: - logmsg("[%d] failed to create response", qid); - curlx_free(resp); - return NULL; + return ts; } static int read_https_alpn_part(struct blob *b, struct Curl_str *str) @@ -580,6 +351,27 @@ static int read_https_alpn_part(struct blob *b, struct Curl_str *str) return 0; } +#ifdef _WIN32 +#define SENDTO3 int +#else +#define SENDTO3 size_t +#endif + +#define INSTRUCTIONS "dnsd.cmd" + +static curlx_struct_stat finfo_last; +static unsigned char ipv4_pref[4]; +static unsigned char ipv6_pref[16]; +static unsigned char ancount_a; +static unsigned char ancount_aaaa; + +static timediff_t a_delay_ms; +static timediff_t aaaa_delay_ms; +static timediff_t https_delay_ms; +static unsigned char rcode_a; +static unsigned char rcode_aaaa; +static struct blob httpsrr; + static int read_https_alpn(struct blob *b, const char **ps) { struct Curl_str word; @@ -618,7 +410,7 @@ static int read_https(struct blob *b, const char *s) curlx_str_passblanks(&s); if(curlx_str_word(&s, &word, UINT16_MAX)) { - logmsg("https: unable to read target qname, input=%s", s); + logmsg("[CONFIG] https: unable to read target qname, input=%s", s); return 1; } if(blob_add_qname(b, &word)) @@ -675,7 +467,7 @@ static void read_instructions(void) blob_reset(&httpsrr); finfo_last = finfo; - logmsg("read instructions from %s", file); + logmsg("[CONFIG] reading from %s", file); f = curlx_fopen(file, FOPEN_READTEXT); if(f) { char buf[256]; @@ -757,55 +549,820 @@ static void read_instructions(void) rc = buf[0] ? 0 : 1; } if(rc != 1) { - logmsg("Bad line in %s: '%s'", file, buf); + logmsg("[CONFIG] Bad line in %s: '%s'", file, buf); } else if(rtype) { - logmsg("added %s record via '%s'", rtype, buf); + logmsg("[CONFIG] added %s record via '%s'", rtype, buf); } } } - logmsg("set delays: A=%" FMT_TIMEDIFF_T "ms AAAA=%" FMT_TIMEDIFF_T - "ms HTTPS=%" FMT_TIMEDIFF_T "ms", + logmsg("[CONFIG] set delays: A=%" FMT_TIMEDIFF_T "ms AAAA=%" + FMT_TIMEDIFF_T "ms HTTPS=%" FMT_TIMEDIFF_T "ms", a_delay_ms, aaaa_delay_ms, https_delay_ms); curlx_fclose(f); } else - logmsg("Error opening file '%s'", file); + logmsg("[CONFIG] Error opening file '%s'", file); } -static int test_dnsd(int argc, const char **argv) +static int last_query_id = -1; + +static int dnsd_make_answer(struct blob *blob, int query_id, + const uint8_t *qbuf, size_t qlen, + uint16_t qtype, uint16_t id, + timediff_t *pdelay_ms) { - srvr_sockaddr_union_t me; - ssize_t n = 0; - int arg = 1; - curl_socket_t sock = CURL_SOCKET_BAD; - int flag; - int rc; - int sockerr; - char errbuf[STRERROR_LEN]; - int result = 0; - struct resp *resp; + int a; + char addrbuf[128]; /* IP address buffer */ + uint8_t header[12] = { + 0x80, 0xea, /* ID, overwrite */ + 0x81, 0x80, + /* Flags: 0x8180 Standard query response, No error - pidname = ".dnsd.pid"; - serverlogfile = "log/dnsd.log"; - serverlogslocked = 0; - server_port = 9123; /* UDP */ + 1... .... .... .... = Response: Message is a response + .000 0... .... .... = Opcode: Standard query (0) + .... .0.. .... .... = Authoritative: Server is not an authority for + domain + .... ..0. .... .... = Truncated: Message is not truncated + .... ...1 .... .... = Recursion desired: Do query recursively + .... .... 1... .... = Recursion available: Server can do recursive + queries + .... .... .0.. .... = Z: reserved (0) + .... .... ..0. .... = Answer authenticated: Answer/authority portion + was not authenticated by the server + .... .... ...0 .... = Non-authenticated data: Unacceptable + .... .... .... 0000 = Reply code: No error (0) + */ + 0x0, 0x1, /* QDCOUNT a single question */ + 0x0, 0x0, /* ANCOUNT number of answers */ + 0x0, 0x0, /* NSCOUNT */ + 0x0, 0x0 /* ARCOUNT */ + }; + uint16_t ancount = 0; + unsigned char rcode = 0; - while(argc > arg) { - const char *opt; - curl_off_t num; - if(!strcmp("--verbose", argv[arg])) { - arg++; - /* nothing yet */ - } - else if(!strcmp("--version", argv[arg])) { - printf("dnsd IPv4%s\n", -#ifdef USE_IPV6 - "/IPv6" -#else - "" -#endif - ); + /* read once per incoming query, which is probably more than one + per test case */ + read_instructions(); + + switch(qtype) { + case QTYPE_A: + ancount = ancount_a; + *pdelay_ms = a_delay_ms; + rcode = rcode_a; + break; + case QTYPE_AAAA: + ancount = ancount_aaaa; + *pdelay_ms = aaaa_delay_ms; + rcode = rcode_aaaa; + break; + case QTYPE_HTTPS: + if(httpsrr.dlen) + ancount = 1; + *pdelay_ms = https_delay_ms; + break; + default: + *pdelay_ms = 0; + } + if(rcode) + ancount = 0; + + header[0] = (uint8_t)(id >> 8); + header[1] = (uint8_t)(id & 0xff); + + if(rcode) { + header[3] = (uint8_t)((header[3] & 0xf0) | (rcode & 0x0f)); + logmsg("[%d] response rcode %u", query_id, (unsigned int)rcode); + } + + header[6] = (uint8_t)(ancount >> 8); + header[7] = (uint8_t)(ancount & 0xff); + + if(blob_addn(blob, header, sizeof(header))) + return 1; + + if(blob_addn(blob, qbuf, qlen)) { + logmsg("unable to handle query of length %zu", qlen); + return 1; + } + + switch(qtype) { + case QTYPE_A: + for(a = 0; !rcode && (a < ancount_a); a++) { + const unsigned char *store = ipv4_pref; + const char *ip; + if(add_answer(blob, store, sizeof(ipv4_pref), QTYPE_A)) + return 1; + ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)); + logmsg("[%d] response A (%x) '%s'", query_id, (unsigned int)QTYPE_A, + ip ? ip : "(null)"); + } + if(!ancount_a) + logmsg("[%d] response A empty", query_id); + break; + case QTYPE_AAAA: + for(a = 0; !rcode && (a < ancount_aaaa); a++) { + const unsigned char *store = ipv6_pref; + const char *ip; + if(add_answer(blob, store, sizeof(ipv6_pref), QTYPE_AAAA)) + return 1; + ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)); + logmsg("[%d] response AAAA (%x) '%s'", query_id, + (unsigned int)QTYPE_AAAA, ip ? ip : "(null)"); + } + if(!ancount_aaaa) + logmsg("[%d] response AAAA empty", query_id); + break; + case QTYPE_HTTPS: + if(httpsrr.dlen) { + if(add_answer(blob, httpsrr.data, httpsrr.dlen, QTYPE_HTTPS)) { + logmsg("[%d] error adding https %zu response bytes", query_id, + httpsrr.dlen); + return 1; + } + logmsg("[%d] response HTTPS (%x), %zu bytes", query_id, + (unsigned int)QTYPE_HTTPS, httpsrr.dlen); + } + else + logmsg("[%d] response HTTPS, no record", query_id); + break; + } + + return 0; +} + +struct udp_resp { + struct udp_resp *next; + int query_id; + struct curltime send_ts; + struct sockaddr addr; + curl_socklen_t addrlen; + struct blob body; +}; + +static struct udp_resp *udp_resp_queue; + +static CURLcode send_udp_resp(curl_socket_t sock, struct udp_resp *resp) +{ + ssize_t rc; + int sockerr = 0; + + do { + rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen, + 0, &resp->addr, resp->addrlen); + } while((rc < 0) && ((sockerr = SOCKERRNO) == SOCKEINTR)); + if(rc < 0) { + char errbuf[STRERROR_LEN]; + logmsg("[%d-UDP] failed sending %zu bytes, error: (%d) %s", + resp->query_id, resp->body.dlen, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + return CURLE_SEND_ERROR; + } + else if(rc != (ssize_t)resp->body.dlen) { + logmsg("[%d-UDP] failed sending %zu bytes, sent: %zd", + resp->query_id, resp->body.dlen, rc); + return CURLE_SEND_ERROR; + } + logmsg("[%d-UDP] sent response", resp->query_id); + return CURLE_OK; +} + +static void queue_udp_resp(struct udp_resp *resp) +{ + struct udp_resp **panchor = &udp_resp_queue; + while(*panchor) { + timediff_t ms = curlx_ptimediff_ms(&(*panchor)->send_ts, &resp->send_ts); + if(ms > 0) /* resp is to be sent before *panchor */ + break; + panchor = &(*panchor)->next; + } + resp->next = *panchor; + *panchor = resp; +} + +static timediff_t queue_udp_next_ms(struct curltime *pnow) +{ + timediff_t next_ms = 1000; + struct udp_resp *r; + + for(r = udp_resp_queue; r; r = r->next) { + timediff_t ms = curlx_ptimediff_ms(&r->send_ts, pnow); + if((ms > 0) && (ms < next_ms)) + next_ms = ms; + else if(ms <= 0) + return 0; + } + return next_ms; +} + +static void queue_udp_send(curl_socket_t sock, struct curltime *pnow) +{ + struct udp_resp **panchor = &udp_resp_queue; + + while(*panchor) { + struct udp_resp *resp = *panchor; + timediff_t ms = curlx_ptimediff_ms(&resp->send_ts, pnow); + + /* if not due yet, break as response queue is time sorted */ + if(ms > 0) + break; + *panchor = resp->next; + send_udp_resp(sock, resp); + curlx_free(resp); + } +} + +static void queue_udp_clear(void) +{ + while(udp_resp_queue) { + struct udp_resp *resp = udp_resp_queue; + udp_resp_queue = resp->next; + curlx_free(resp); + } +} + +/* this is an answer to a question */ +static struct udp_resp * +udp_resp_create(int query_id, + const struct sockaddr *addr, curl_socklen_t addrlen, + const unsigned char *qbuf, size_t qlen, + uint16_t qtype, uint16_t id) +{ + struct udp_resp *resp; + timediff_t delay_ms = 0; + + resp = curlx_calloc(1, sizeof(*resp)); + if(!resp) + goto error; + + resp->query_id = query_id; + /* on some platforms `curl_socklen_t` is an `int`. Casting might + * wrap this, but then it still has to fit our record size. */ + if((size_t)addrlen > sizeof(resp->addr)) { + logmsg("[%d-UDP] unable to handle addrlen of %zu", + query_id, (size_t)addrlen); + goto error; + } + memcpy(&resp->addr, CURL_UNCONST(addr), addrlen); + resp->addrlen = addrlen; + + if(dnsd_make_answer(&resp->body, query_id, qbuf, qlen, qtype, id, &delay_ms)) + goto error; + + resp->send_ts = now_plus(delay_ms); + if(delay_ms > 0) + logmsg("[%d-UDP] delay response by %" FMT_TIMEDIFF_T "ms", + query_id, delay_ms); + return resp; + +error: + logmsg("[%d-UDP] failed to create response", query_id); + curlx_free(resp); + return NULL; +} + +static int udp_recv_req(curl_socket_t sock) +{ + srvr_sockaddr_union_t from; + curl_socklen_t fromlen; + uint8_t inbuffer[1500]; + uint8_t qbuf[256]; /* query storage */ + size_t qlen = 0; /* query size */ + struct udp_resp *resp; + uint16_t qtype = 0, id; + ssize_t n; + int result = 0; + + fromlen = sizeof(from); +#ifdef USE_IPV6 + if(socket_domain == AF_INET6) + fromlen = sizeof(from.sa6); + else +#endif + fromlen = sizeof(from.sa4); + + n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0, + &from.sa, &fromlen); + if(got_exit_signal) + goto out; + if(n < 0) { + logmsg("UDP, recvfrom error"); + result = 3; + goto out; + } + + ++last_query_id; + store_incoming("UDP", last_query_id, inbuffer, n, + qbuf, sizeof(qbuf), &qlen, &qtype, &id); + + set_advisor_read_lock(loglockfile); + serverlogslocked = 1; + + resp = udp_resp_create(last_query_id, &from.sa, fromlen, qbuf, + qlen, qtype, id); + if(!resp) + logmsg("[%d-UDP] error creating response", last_query_id); + else + queue_udp_resp(resp); + +out: + return result; +} + +#define MAX_DOH_CONNS 512 +#define MAX_DOH_INBUF_LEN (8 * 1024) +#define MAX_DOH_OUTBUF_LEN (8 * 1024) + +struct doh_conn { + curl_socket_t sock; + int index; + int query_id; + char inbuf[MAX_DOH_INBUF_LEN]; + size_t inblen; + size_t inbody_offset; + size_t inbody_len; + char outbuf[MAX_DOH_OUTBUF_LEN]; + size_t outblen; + struct curltime send_ts; + BIT(want_recv); + BIT(want_send); + BIT(close_pending); +}; + +static struct doh_conn doh_conns[MAX_DOH_CONNS]; + +static void doh_conns_init(void) +{ + int i; + for(i = 0; i < MAX_DOH_CONNS; ++i) { + doh_conns[i].sock = CURL_SOCKET_BAD; + doh_conns[i].index = i; + } +} + +static int doh_conns_add(curl_socket_t sock) +{ + int i; + for(i = 0; i < MAX_DOH_CONNS; ++i) { + if(doh_conns[i].sock == CURL_SOCKET_BAD) { + doh_conns[i].sock = sock; + doh_conns[i].want_recv = TRUE; + logmsg("[x-%d-DOH] accepted new connection, fd=%ld", i, (long)sock); + return 0; + } + } + logmsg("Too many open DoH connections, closing incoming."); + sclose(sock); + return 1; +} + +static void doh_conn_close(size_t i) +{ + if(i >= MAX_DOH_CONNS) + return; + if(doh_conns[i].sock != CURL_SOCKET_BAD) + sclose(doh_conns[i].sock); + doh_conns[i].sock = CURL_SOCKET_BAD; + doh_conns[i].want_recv = FALSE; + doh_conns[i].want_send = FALSE; + logmsg("[x-%d-DOH] connection closed", (int)i); +} + +static void doh_conns_close_all(void) +{ + size_t i; + for(i = 0; i < MAX_DOH_CONNS; ++i) { + doh_conn_close(i); + } +} + +static void doh_conns_fdsets(fd_set *readfds, fd_set *writefds, + struct curltime *pnow, + int *pmaxfd, + timediff_t *ptimeout_ms) +{ + size_t i; + for(i = 0; i < MAX_DOH_CONNS; ++i) { + struct doh_conn *c = &doh_conns[i]; + if((c->sock != CURL_SOCKET_BAD)) { + if(!c->want_send && c->outblen) { /* check delayed send */ + timediff_t ms = curlx_ptimediff_ms(&c->send_ts, pnow); + if(ms <= 0) + c->want_send = TRUE; + else if((ms < *ptimeout_ms) || !*ptimeout_ms) + *ptimeout_ms = ms; + } + if(c->want_send) + FD_SET(c->sock, writefds); + if(c->want_recv) + FD_SET(c->sock, readfds); + + if((FD_ISSET(c->sock, readfds) || FD_ISSET(c->sock, writefds)) && + (int)c->sock > *pmaxfd) + *pmaxfd = (int)c->sock; + } + } +} + +static int doh_conn_send(struct doh_conn *c, struct curltime *pnow) +{ + char errbuf[STRERROR_LEN]; + ssize_t rc; + int sockerr; + + if(c->outblen) { + timediff_t ms = curlx_ptimediff_ms(&c->send_ts, pnow); + size_t n; + + if(ms > 0) { /* not due yet */ + c->want_send = FALSE; + goto out; + } + + rc = swrite(c->sock, c->outbuf, c->outblen); + if(rc < 0) { + sockerr = SOCKERRNO; + if((sockerr == SOCKEINPROGRESS) || SOCK_EAGAIN(sockerr)) + return 0; + sockerr = SOCKERRNO; + logmsg("[%d-%d-DOH] swrite(%ld) failed with error (%d) %s", + c->query_id, c->index, (long)c->sock, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + return 1; + } + n = (size_t)rc; + if(n >= c->outblen) { /* all sent */ + c->outblen = 0; + logmsg("[%d-%d-DOH] last response byte sent", c->query_id, c->index); + } + else { + c->outblen -= n; + memmove(c->outbuf, c->outbuf + n, c->outblen); + } + } + +out: + if(!c->outblen) { + c->want_send = FALSE; + if(c->close_pending) + return 1; + else { + logmsg("[x-%d-DOH] switching to recv", c->index); + c->want_recv = TRUE; + } + } + return 0; +} + +static const char *http_status_descr(int http_status) +{ + switch(http_status) { + case 200: + return "Ok"; + case 400: + return "Bad Request"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + default: + if(http_status < 100) + return "This is wrong"; + if(http_status < 200) + return "Just Kidding"; + if(http_status < 300) + return "Lgtm"; + if(http_status < 400) + return "Follow Me For More Requests"; + if(http_status < 500) + return "You were wrong"; + return "We did something wrong"; + } +} + +static int doh_conn_send_err(struct doh_conn *c, int http_status) +{ + c->inblen = 0; + c->close_pending = TRUE; + c->want_recv = FALSE; + if(c->outblen) { + logmsg("[%d-%d-DOH] error sending response with %zu bytes still outgoing", + c->query_id, c->index, c->outblen); + return 1; + } + + memset(c->outbuf, 0, sizeof(c->outbuf)); + snprintf(c->outbuf, sizeof(c->outbuf) - 1, + "HTTP/1.1 %d %s\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n", + http_status, http_status_descr(http_status)); + c->outblen = strlen(c->outbuf); + c->want_send = TRUE; + logmsg("[%d-%d-DOH] sending HTTP response %d", + c->query_id, c->index, http_status); + return 0; +} + +static int doh_conn_send_answer(struct doh_conn *c, struct blob *body, + timediff_t delay_ms) +{ + if(c->outblen) { /* Should not happen */ + logmsg("[%d-%d-DOH] trying to send an answer with outbuf still having " + "%zu bytes", c->query_id, c->index, c->outblen); + return 1; + } + memset(c->outbuf, 0, sizeof(c->outbuf)); + snprintf(c->outbuf, sizeof(c->outbuf) - 1, + "HTTP/1.1 200 %s\r\n" + "Server: curl/test-dnsd\r\n" + "Date: Thu, 06 Aug 2026 08:42:00 GMT\r\n" + "Content-Type: application/dns-message\r\n" + "Content-Length: %ld\r\n" + "\r\n", + http_status_descr(200), (long)body->dlen); + c->outblen = strlen(c->outbuf); + if((c->outblen + body->dlen) > sizeof(c->outbuf)) { + logmsg("[%d-%d-DOH] response size of %zu too large for outbuf", + c->query_id, c->index, c->outblen + body->dlen); + c->outblen = 0; + return 1; + } + memcpy(c->outbuf + c->outblen, body->data, body->dlen); + c->outblen += body->dlen; + c->send_ts = now_plus(delay_ms); + if(delay_ms > 0) + logmsg("[%d-%d-DOH] delay response by %" FMT_TIMEDIFF_T "ms", + c->query_id, c->index, delay_ms); + else + c->want_send = TRUE; + c->want_recv = FALSE; + logmsg("[%d-%d-DOH] sending HTTP response 200", + c->query_id, c->index); + return 0; +} + +static int doh_req_parse_headers(const char **pstr, + size_t *pcontent_length, + bool *pcomplete) +{ + static const struct Curl_str HD_content_length = { + STRCONST("Content-Length:") + }; + static const struct Curl_str HD_content_type = { + STRCONST("Content-Type:") + }; + static const struct Curl_str HD_wanted_type = { + STRCONST("application/dns-message") + }; + const char *p = *pstr; + bool ct_ok = FALSE; + bool cl_ok = FALSE; + bool eoh = FALSE; + + *pcomplete = FALSE; + *pcontent_length = 0; + while(p[0]) { + const char *nl, *start = p; + struct Curl_str hd_name, hd_val; + + if((p[0] == '\r') && (p[1] == '\n')) { + p += 2; + eoh = TRUE; + break; + } + nl = strchr(p, '\n'); + if(!nl) /* incomplete */ + break; + if(curlx_str_word(&p, &hd_name, 1024) || + curlx_str_singlespace(&p) || + curlx_str_untilnl(&p, &hd_val, 1024) || + curlx_str_newline(&p) || + curlx_str_newline(&p)) { + logmsg("unrecognized request header '%.*s'", + (int)(nl - start), start); + return 1; + } + if(curlx_str_case_equal(&HD_content_type, &hd_name)) { + if(!curlx_str_case_equal(&HD_wanted_type, &hd_val)) { + logmsg("wrong content-type: '%.*s'", (int)hd_val.len, hd_val.str); + return 1; + } + ct_ok = TRUE; + } + else if(curlx_str_case_equal(&HD_content_length, &hd_name)) { + const char *s = hd_val.str; + curl_off_t offt; + if(curlx_str_number(&s, &offt, 4096)) { + logmsg("wrong content-length: '%.*s'", (int)hd_val.len, hd_val.str); + return 1; + } + *pcontent_length = (size_t)offt; + cl_ok = TRUE; + } + else { + /* ignore this header */ + } + } + + *pstr = p; + if(!eoh) + return 0; /* need more */ + if(!ct_ok) { + logmsg("request missing Content-Type"); + return 1; + } + if(!cl_ok) { + logmsg("request missing Content-Length"); + return 1; + } + *pcomplete = TRUE; + return 0; +} + +static int doh_conn_do_req(struct doh_conn *c, bool eos) +{ + static const struct Curl_str DOH_PROTO = { STRCONST("HTTP/1.1") }; + static const struct Curl_str DOH_METHOD = { STRCONST("POST") }; + static const struct Curl_str DOH_PATH = { STRCONST("/") }; + const char *first_nl; + bool complete = FALSE; + + if(!c->inblen && eos) + return 1; + + if(!c->inbody_offset) { + first_nl = strchr(c->inbuf, '\n'); + if(first_nl) { + /* This is a poor man's HTTP/1.1 parser and we should rather have + * one in curlx that we can share. */ + const char *p = c->inbuf; + struct Curl_str method, path, proto; + + if(curlx_str_word(&p, &method, 1024) || curlx_str_singlespace(&p)) { + logmsg("[x-%d-DOH] unrecognized first request line method '%.*s'", + c->index, (int)(first_nl - c->inbuf), c->inbuf); + return 1; + } + if(curlx_str_word(&p, &path, 1024) || curlx_str_singlespace(&p)) { + logmsg("[x-%d-DOH] unrecognized first request line path '%.*s'", + c->index, (int)(first_nl - c->inbuf), c->inbuf); + return 1; + } + if(curlx_str_untilnl(&p, &proto, 1024) || + curlx_str_newline(&p) || + curlx_str_newline(&p)) { + logmsg("[x-%d-DOH] unrecognized first request line proto '%.*s'", + c->index, (int)(first_nl - c->inbuf), c->inbuf); + return 1; + } + if(!curlx_str_case_equal(&DOH_PROTO, &proto)) { + logmsg("[x-%d-DOH] unrecognized request protocol '%.*s'", + c->index, (int)proto.len, proto.str); + return 1; + } + if(!curlx_str_case_equal(&DOH_METHOD, &method)) { + logmsg("[x-%d-DOH] unsupported request method '%.*s'", + c->index, (int)method.len, method.str); + return doh_conn_send_err(c, 405); + } + if(!curlx_str_case_equal(&DOH_PATH, &path)) { + logmsg("[x-%d-DOH] request path not fond '%.*s'", + c->index, (int)path.len, path.str); + return doh_conn_send_err(c, 404); + } + if(doh_req_parse_headers(&p, &c->inbody_len, &complete)) { + return doh_conn_send_err(c, 400); + } + /* Looks ok, remember the start of the body bytes */ + c->inbody_offset = (p - c->inbuf); + } + if(!complete) + return eos ? 1 : 0; /* want more, error if client close */ + } + + if(c->inblen >= (c->inbody_offset + c->inbody_len)) { + /* We have all bytes for processing the request */ + uint8_t qbuf[256]; /* query storage */ + size_t qlen = 0; /* query size */ + size_t rlen = c->inbody_offset + c->inbody_len; /* request size */ + uint16_t qtype = 0, id; + struct blob blob; + timediff_t delay_ms; + + c->query_id = ++last_query_id; + if(store_incoming("DoH", c->query_id, (const uint8_t *)c->inbuf + + c->inbody_offset, c->inbody_len, + qbuf, sizeof(qbuf), &qlen, &qtype, &id)) { + logmsg("[%d-%d-DOH] error storing incoming request", + c->query_id, c->index); + return doh_conn_send_err(c, 400); + } + + /* remove handled request from inbuf */ + if(rlen >= c->inblen) + c->inblen = 0; + else { + memmove(c->inbuf, c->inbuf + rlen, c->inblen - rlen); + c->inblen -= rlen; + } + c->inbody_len = c->inbody_offset = 0; + + memset(&blob, 0, sizeof(blob)); + if(dnsd_make_answer(&blob, c->query_id, qbuf, qlen, qtype, id, + &delay_ms)) { + return doh_conn_send_err(c, 500); + } + return doh_conn_send_answer(c, &blob, delay_ms); + } + return 0; +} + +static int doh_conn_recv(struct doh_conn *c) +{ + char errbuf[STRERROR_LEN]; + ssize_t n; + int sockerr; + + n = sread(c->sock, c->inbuf + c->inblen, sizeof(c->inbuf) - 1 - c->inblen); + if(n < 0) { + sockerr = SOCKERRNO; + if((sockerr == SOCKEINPROGRESS) || SOCK_EAGAIN(sockerr)) + return 0; + sockerr = SOCKERRNO; + logmsg("[x-%d-DOH] sread() failed with error (%d) %s", c->index, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + return 1; + } + else if(n == 0) { + logmsg("[x-%d-DOH] sread() == 0, client closed connection", c->index); + return doh_conn_do_req(c, TRUE); + } + else { + c->inblen += (size_t)n; + c->inbuf[c->inblen] = 0; + logmsg("[x-%d-DOH] sread() == %zu, processing", c->index, (size_t)n); + return doh_conn_do_req(c, FALSE); + } +} + +static int doh_conns_serve(fd_set *readfds, fd_set *writefds) +{ + struct curltime now; + size_t i; + + for(i = 0; i < MAX_DOH_CONNS; ++i) { + struct doh_conn *c = &doh_conns[i]; + if(c->sock != CURL_SOCKET_BAD) { + if(c->want_send && FD_ISSET(c->sock, writefds)) { + now = curlx_now(); + if(doh_conn_send(c, &now)) { + doh_conn_close(i); + continue; + } + } + if(c->want_recv && FD_ISSET(c->sock, readfds)) { + if(doh_conn_recv(c)) { + doh_conn_close(i); + continue; + } + } + } + } + return 0; +} + +static int test_dnsd(int argc, const char **argv) +{ + int arg = 1; + curl_socket_t sock_udp = CURL_SOCKET_BAD; + curl_socket_t sock_tcp_listen = CURL_SOCKET_BAD; + char errbuf[STRERROR_LEN]; + int rc, sockerr; + int result = 0; + + pidname = ".dnsd.pid"; + serverlogfile = "log/dnsd.log"; + serverlogslocked = 0; + server_port = 9123; /* UDP */ + socket_domain = AF_INET; + + while(argc > arg) { + const char *opt; + curl_off_t num; + if(!strcmp("--verbose", argv[arg])) { + arg++; + /* nothing yet */ + } + else if(!strcmp("--version", argv[arg])) { + printf("dnsd IPv4%s\n", +#ifdef USE_IPV6 + "/IPv6" +#else + "" +#endif + ); return 0; } else if(!strcmp("--pidfile", argv[arg])) { @@ -870,97 +1427,14 @@ static int test_dnsd(int argc, const char **argv) install_signal_handlers(FALSE); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - sock = socket(AF_INET6, SOCK_DGRAM, 0); - else -#endif - sock = socket(AF_INET, SOCK_DGRAM, 0); - - if(sock == CURL_SOCKET_BAD) { - sockerr = SOCKERRNO; - logmsg("Error creating socket (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; + result = open_stream_sock(&sock_tcp_listen, &server_port); + if(result) goto dnsd_cleanup; - } + doh_conns_init(); - flag = 1; - if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - sockerr = SOCKERRNO; - logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; + result = open_udp_sock(&sock_udp, &server_port); + if(result) goto dnsd_cleanup; - } - -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) { - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); - } - else -#endif - { - memset(&me.sa4, 0, sizeof(me.sa4)); - me.sa4.sin_family = AF_INET; - me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa4)); - } - if(rc) { - sockerr = SOCKERRNO; - logmsg("Error binding socket on port %hu (%d) %s", server_port, - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; - goto dnsd_cleanup; - } - - if(!server_port) { - /* The system was supposed to choose a port number, figure out which - port we actually got and update the listener port value with it. */ - curl_socklen_t la_size; - srvr_sockaddr_union_t localaddr; - memset(&localaddr, 0, sizeof(localaddr)); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - la_size = sizeof(localaddr.sa6); - else -#endif - la_size = sizeof(localaddr.sa4); - - if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - sockerr = SOCKERRNO; - logmsg("getsockname() failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - sclose(sock); - goto dnsd_cleanup; - } - switch(localaddr.sa.sa_family) { - case AF_INET: - server_port = ntohs(localaddr.sa4.sin_port); - break; -#ifdef USE_IPV6 - case AF_INET6: - server_port = ntohs(localaddr.sa6.sin6_port); - break; -#endif - default: - break; - } - if(!server_port) { - /* Real failure, listener port shall not be zero beyond this point. */ - logmsg("Apparently getsockname() succeeded, with listener port zero."); - logmsg("A valid reason for this failure is a binary built without"); - logmsg("proper network library linkage. This might not be the only"); - logmsg("reason, but double check it before anything else."); - result = 2; - goto dnsd_cleanup; - } - } dnsd_wrotepidfile = write_pidfile(pidname); if(!dnsd_wrotepidfile) { @@ -976,75 +1450,76 @@ static int test_dnsd(int argc, const char **argv) } } - logmsg("Running %s version on port UDP/%d", socket_type, (int)server_port); - curlx_nonblock(sock, TRUE); + /* start accepting connections */ + if(listen(sock_tcp_listen, 50)) { + sockerr = SOCKERRNO; + logmsg("listen() failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto dnsd_cleanup; + } + + logmsg("Running %s on port UDP+TCP/%u", socket_type, server_port); + curlx_nonblock(sock_udp, TRUE); + curlx_nonblock(sock_tcp_listen, TRUE); for(;;) { - uint16_t id = 0; - uint8_t inbuffer[1500]; - srvr_sockaddr_union_t from; - curl_socklen_t fromlen; - uint8_t qbuf[256]; /* query storage */ - size_t qlen = 0; /* query size */ - uint16_t qtype = 0; timediff_t timeout_ms = 0; - fromlen = sizeof(from); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - fromlen = sizeof(from.sa6); - else -#endif - fromlen = sizeof(from.sa4); + fd_set readfds, writefds; + struct timeval tv; + int maxfd = 0; + struct curltime now = curlx_now(); - timeout_ms = send_resp_queue(sock); + FD_ZERO(&readfds); + fdset_add_sock(&readfds, sock_udp, &maxfd); + fdset_add_sock(&readfds, sock_tcp_listen, &maxfd); - { - fd_set readfds; - struct timeval tv; - int maxfd = (int)sock; + FD_ZERO(&writefds); + timeout_ms = queue_udp_next_ms(&now); + if(!timeout_ms) + fdset_add_sock(&writefds, sock_udp, &maxfd); - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - if(!timeout_ms || (timeout_ms > 100)) - timeout_ms = 100; + doh_conns_fdsets(&readfds, &writefds, &now, &maxfd, &timeout_ms); - rc = select(maxfd + 1, &readfds, NULL, NULL, - curlx_mstotv(&tv, timeout_ms)); + if(!timeout_ms || (timeout_ms > 100)) + timeout_ms = 100; - if(rc == -1) { - logmsg("error %d returned by select()", SOCKERRNO); - } - else if(!rc) { /* timeout */ - continue; - } + rc = select(maxfd + 1, &readfds, &writefds, NULL, + curlx_mstotv(&tv, timeout_ms)); + + if(rc == -1) { + logmsg("error %d returned by select()", SOCKERRNO); } - n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0, - &from.sa, &fromlen); - if(got_exit_signal) - break; - if(n < 0) { - logmsg("recvfrom"); - result = 3; - break; + else if(!rc) { /* timeout */ + continue; } - /* read once per incoming query, which is probably more than one - per test case */ - read_instructions(); - - ++query_id; - store_incoming(query_id, inbuffer, n, - qbuf, sizeof(qbuf), &qlen, &qtype, &id); + if(FD_ISSET(sock_udp, &writefds)) { + now = curlx_now(); + queue_udp_send(sock_udp, &now); + } + if(FD_ISSET(sock_udp, &readfds)) { + result = udp_recv_req(sock_udp); + if(result) + break; + } - set_advisor_read_lock(loglockfile); - serverlogslocked = 1; + result = doh_conns_serve(&readfds, &writefds); + if(result) + break; - resp = create_resp(query_id, &from.sa, fromlen, qbuf, - qlen, qtype, id); - if(!resp) - logmsg("error creating response"); - else - queue_resp(resp); + if(FD_ISSET(sock_tcp_listen, &readfds)) { + /* Service all queued connections */ + curl_socket_t sock_conn; + while(TRUE) { + sock_conn = accept_connection(sock_tcp_listen); + if(!sock_conn) /* no more connections to accept */ + break; + if(sock_conn == CURL_SOCKET_BAD) + goto dnsd_cleanup; + doh_conns_add(sock_conn); + } + } if(got_exit_signal) break; @@ -1056,14 +1531,12 @@ static int test_dnsd(int argc, const char **argv) } dnsd_cleanup: + if(sock_udp != CURL_SOCKET_BAD) + sclose(sock_udp); + if(sock_tcp_listen != CURL_SOCKET_BAD) + sclose(sock_tcp_listen); -#if 0 - if((peer != sock) && (peer != CURL_SOCKET_BAD)) - sclose(peer); -#endif - - if(sock != CURL_SOCKET_BAD) - sclose(sock); + doh_conns_close_all(); if(got_exit_signal) logmsg("signalled to die"); @@ -1078,7 +1551,7 @@ static int test_dnsd(int argc, const char **argv) clear_advisor_read_lock(loglockfile); } - clear_resp_queue(); + queue_udp_clear(); restore_signal_handlers(FALSE); diff --git a/tests/server/first.h b/tests/server/first.h index aa81a5798eb9..1de9af74b793 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -159,6 +159,14 @@ extern curl_socket_t sockdaemon(curl_socket_t sock, uint16_t *listenport, const char *unix_socket, bool bind_only); +extern int open_udp_sock(curl_socket_t *psock, uint16_t *pport); +extern int open_stream_sock(curl_socket_t *psock, uint16_t *pport); +extern curl_socket_t accept_connection(curl_socket_t listen_sock); +extern bool curlx_str_case_equal(const struct Curl_str *s1, + const struct Curl_str *s2); + +/* returns true if the current socket is an IP one */ +extern bool socket_domain_is_ip(void); /* global variables */ static const char *srcpath = "."; /* pointing to the test directory */ diff --git a/tests/server/sws.c b/tests/server/sws.c index 6c18b9a3995b..5d7087927d9b 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -189,23 +189,6 @@ static char *data_to_hex(const char *data, size_t len) /* work around for handling trailing headers */ static int already_recv_zeroed_chunk = FALSE; -#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) -/* returns true if the current socket is an IP one */ -static bool socket_domain_is_ip(void) -{ - switch(socket_domain) { - case AF_INET: -#ifdef USE_IPV6 - case AF_INET6: -#endif - return TRUE; - default: - /* case AF_UNIX: */ - return FALSE; - } -} -#endif - /* parse the file on disk that might have a test number for us */ static int parse_cmdfile(struct sws_httprequest *req) { @@ -1749,92 +1732,6 @@ static void http_upgrade(struct sws_httprequest *req) /* left to implement */ } -/* returns a socket handle, or 0 if there are no more waiting sockets, - or < 0 if there was an error */ -static curl_socket_t accept_connection(curl_socket_t sock) -{ - curl_socket_t msgsock = CURL_SOCKET_BAD; - int sockerr; - char errbuf[STRERROR_LEN]; - int flag = 1; - - if(MAX_SOCKETS == num_sockets) { - logmsg("Too many open sockets!"); - return CURL_SOCKET_BAD; - } - - msgsock = accept(sock, NULL, NULL); - - if(got_exit_signal) { - if(msgsock != CURL_SOCKET_BAD) - sclose(msgsock); - return CURL_SOCKET_BAD; - } - - if(msgsock == CURL_SOCKET_BAD) { - sockerr = SOCKERRNO; - if(SOCK_EAGAIN(sockerr)) { - /* nothing to accept */ - return 0; - } - logmsg("MAJOR ERROR, accept() failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - return CURL_SOCKET_BAD; - } - - if(curlx_nonblock(msgsock, TRUE)) { - sockerr = SOCKERRNO; - logmsg("curlx_nonblock failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - sclose(msgsock); - return CURL_SOCKET_BAD; - } - -#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) - if(socket_domain != AF_UNIX) { -#endif - if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, - (void *)&flag, sizeof(flag))) { - sockerr = SOCKERRNO; - logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - sclose(msgsock); - return CURL_SOCKET_BAD; - } -#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) - } -#endif - - /* - * As soon as this server accepts a connection from the test harness it - * must set the server logs advisor read lock to indicate that server - * logs should not be read until this lock is removed by this server. - */ - - if(!serverlogslocked) - set_advisor_read_lock(loglockfile); - serverlogslocked += 1; - - logmsg("====> Client connect"); - - all_sockets[num_sockets] = msgsock; - num_sockets += 1; - -#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) - if(socket_domain_is_ip()) { - /* - * Disable the Nagle algorithm to make it easier to send out a large - * response in many small segments to torture the clients more. - */ - if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, - (void *)&flag, sizeof(flag))) - logmsg("====> TCP_NODELAY failed"); - } -#endif - - return msgsock; -} - /* returns 1 if the connection should be serviced again immediately, 0 if there is no data waiting, or < 0 if it should be closed */ static int service_connection(curl_socket_t *msgsock, @@ -1914,11 +1811,9 @@ static int service_connection(curl_socket_t *msgsock, static int test_sws(int argc, const char *argv[]) { - srvr_sockaddr_union_t me; curl_socket_t sock = CURL_SOCKET_BAD; int wrotepidfile = 0; int wroteportfile = 0; - int flag; #ifdef USE_UNIX_SOCKETS bool unlink_socket = FALSE; #endif @@ -1932,6 +1827,7 @@ static int test_sws(int argc, const char *argv[]) const char *location_str = port_str; int keepalive_secs = 5; const char *protocol_type = "HTTP"; + int result = 0; /* a default CONNECT port is pointless, but still ... */ size_t socket_idx; @@ -2005,6 +1901,7 @@ static int test_sws(int argc, const char *argv[]) arg++; if(argc > arg) { #ifdef USE_UNIX_SOCKETS + srvr_sockaddr_union_t me; server_unix_socket = argv[arg]; if(strlen(server_unix_socket) >= sizeof(me.sau.sun_path)) { fprintf(stderr, @@ -2091,115 +1988,13 @@ static int test_sws(int argc, const char *argv[]) if(!req) goto sws_cleanup; - sock = socket(socket_domain, SOCK_STREAM, 0); + result = open_stream_sock(&sock, &server_port); + if(result) + goto sws_cleanup; all_sockets[0] = sock; num_sockets = 1; - if(sock == CURL_SOCKET_BAD) { - sockerr = SOCKERRNO; - logmsg("Error creating socket (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - goto sws_cleanup; - } - -#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) - if(socket_domain != AF_UNIX) { -#endif - flag = 1; - if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (void *)&flag, sizeof(flag))) { - sockerr = SOCKERRNO; - logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - goto sws_cleanup; - } -#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) - } -#endif - if(curlx_nonblock(sock, TRUE)) { - sockerr = SOCKERRNO; - logmsg("curlx_nonblock failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - goto sws_cleanup; - } - - switch(socket_domain) { - case AF_INET: - memset(&me.sa4, 0, sizeof(me.sa4)); - me.sa4.sin_family = AF_INET; - me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa4)); - break; -#ifdef USE_IPV6 - case AF_INET6: - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); - break; -#endif /* USE_IPV6 */ -#ifdef USE_UNIX_SOCKETS - case AF_UNIX: - rc = bind_unix_socket(sock, server_unix_socket, &me.sau); -#endif /* USE_UNIX_SOCKETS */ - } - if(rc) { - sockerr = SOCKERRNO; -#ifdef USE_UNIX_SOCKETS - if(socket_domain == AF_UNIX) - logmsg("Error binding socket on path %s (%d) %s", server_unix_socket, - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - else -#endif - logmsg("Error binding socket on port %hu (%d) %s", server_port, - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - goto sws_cleanup; - } - - if(!server_port) { - /* The system was supposed to choose a port number, figure out which - port we actually got and update the listener port value with it. */ - curl_socklen_t la_size; - srvr_sockaddr_union_t localaddr; - memset(&localaddr, 0, sizeof(localaddr)); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - la_size = sizeof(localaddr.sa6); - else -#endif - la_size = sizeof(localaddr.sa4); - if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - sockerr = SOCKERRNO; - logmsg("getsockname() failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - sclose(sock); - goto sws_cleanup; - } - switch(localaddr.sa.sa_family) { - case AF_INET: - server_port = ntohs(localaddr.sa4.sin_port); - break; -#ifdef USE_IPV6 - case AF_INET6: - server_port = ntohs(localaddr.sa6.sin6_port); - break; -#endif - default: - break; - } - if(!server_port) { - /* Real failure, listener port shall not be zero beyond this point. */ - logmsg("Apparently getsockname() succeeded, with listener port zero."); - logmsg("A valid reason for this failure is a binary built without"); - logmsg("proper network library linkage. This might not be the only"); - logmsg("reason, but double check it before anything else."); - sclose(sock); - goto sws_cleanup; - } - } #ifdef USE_UNIX_SOCKETS if(socket_domain != AF_UNIX) #endif @@ -2302,11 +2097,19 @@ static int test_sws(int argc, const char *argv[]) /* Service all queued connections */ curl_socket_t msgsock; do { + if(MAX_SOCKETS == num_sockets) { + logmsg("Too many open sockets!"); + goto sws_cleanup; + } msgsock = accept_connection(sock); + if(!msgsock) + break; logmsg("accept_connection %ld returned %ld", (long)sock, (long)msgsock); if(msgsock == CURL_SOCKET_BAD) goto sws_cleanup; + all_sockets[num_sockets] = msgsock; + num_sockets += 1; if(req->delay) curlx_wait_ms(req->delay); } while(msgsock > 0); @@ -2414,5 +2217,5 @@ static int test_sws(int argc, const char *argv[]) restore_signal_handlers(FALSE); - return 0; + return result; } diff --git a/tests/server/util.c b/tests/server/util.c index 34d9a6858726..c2e1d52f60bb 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -886,3 +886,342 @@ curl_socket_t sockdaemon(curl_socket_t sock, return sock; } + +int open_udp_sock(curl_socket_t *psock, uint16_t *pport) +{ + srvr_sockaddr_union_t me; + curl_socket_t sock = CURL_SOCKET_BAD; + uint16_t port = *pport; + int sockerr, flag, rc; + char errbuf[STRERROR_LEN]; + int result = 0; + + sock = socket(socket_domain, SOCK_DGRAM, 0); + + if(sock == CURL_SOCKET_BAD) { + sockerr = SOCKERRNO; + logmsg("Error creating socket (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + + flag = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { + sockerr = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + +#ifdef USE_IPV6 + if(socket_domain == AF_INET6) { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } + else +#endif + { + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_addr.s_addr = INADDR_ANY; + me.sa4.sin_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa4)); + } + if(rc) { + sockerr = SOCKERRNO; + logmsg("Error binding socket on port %hu (%d) %s", port, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + + if(!port) { + /* The system was supposed to choose a port number, figure out which + port we actually got and update the listener port value with it. */ + curl_socklen_t la_size; + srvr_sockaddr_union_t localaddr; + memset(&localaddr, 0, sizeof(localaddr)); +#ifdef USE_IPV6 + if(socket_domain == AF_INET6) + la_size = sizeof(localaddr.sa6); + else +#endif + la_size = sizeof(localaddr.sa4); + if(getsockname(sock, &localaddr.sa, &la_size) < 0) { + sockerr = SOCKERRNO; + logmsg("getsockname() failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + switch(localaddr.sa.sa_family) { + case AF_INET: + port = ntohs(localaddr.sa4.sin_port); + break; +#ifdef USE_IPV6 + case AF_INET6: + port = ntohs(localaddr.sa6.sin6_port); + break; +#endif + default: + break; + } + if(!port) { + /* Real failure, listener port shall not be zero beyond this point. */ + logmsg("Apparently getsockname() succeeded, with listener port zero."); + logmsg("A valid reason for this failure is a binary built without"); + logmsg("proper network library linkage. This might not be the only"); + logmsg("reason, but double check it before anything else."); + result = 2; + goto out; + } + } + +out: + if(result) { + if(sock != CURL_SOCKET_BAD) + sclose(sock); + sock = CURL_SOCKET_BAD; + port = 0; + } + *psock = sock; + *pport = port; + return result; +} + +bool socket_domain_is_ip(void) +{ + switch(socket_domain) { + case AF_INET: +#ifdef USE_IPV6 + case AF_INET6: +#endif + return TRUE; + default: + /* case AF_UNIX: */ + return FALSE; + } +} + +int open_stream_sock(curl_socket_t *psock, uint16_t *pport) +{ + srvr_sockaddr_union_t me; + char errbuf[STRERROR_LEN]; + int flag, sockerr, rc = 0; + curl_socket_t sock = CURL_SOCKET_BAD; + uint16_t port = *pport; + int result = 0; + + sock = socket(socket_domain, SOCK_STREAM, 0); + + if(sock == CURL_SOCKET_BAD) { + sockerr = SOCKERRNO; + logmsg("Error creating socket (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + +#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) + if(socket_domain != AF_UNIX) { +#endif + flag = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flag, sizeof(flag))) { + sockerr = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } +#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) + } +#endif + if(curlx_nonblock(sock, TRUE)) { + sockerr = SOCKERRNO; + logmsg("curlx_nonblock failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + + switch(socket_domain) { + case AF_INET: + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_addr.s_addr = INADDR_ANY; + me.sa4.sin_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa4)); + break; +#ifdef USE_IPV6 + case AF_INET6: + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + break; +#endif /* USE_IPV6 */ +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + rc = bind_unix_socket(sock, server_unix_socket, &me.sau); +#endif /* USE_UNIX_SOCKETS */ + } + if(rc) { + sockerr = SOCKERRNO; +#ifdef USE_UNIX_SOCKETS + if(socket_domain == AF_UNIX) + logmsg("Error binding socket on path %s (%d) %s", server_unix_socket, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + else +#endif + logmsg("Error binding socket on port %hu (%d) %s", port, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + + if(!port) { + /* The system was supposed to choose a port number, figure out which + port we actually got and update the listener port value with it. */ + curl_socklen_t la_size; + srvr_sockaddr_union_t localaddr; + memset(&localaddr, 0, sizeof(localaddr)); +#ifdef USE_IPV6 + if(socket_domain != AF_INET6) +#endif + la_size = sizeof(localaddr.sa4); +#ifdef USE_IPV6 + else + la_size = sizeof(localaddr.sa6); +#endif + if(getsockname(sock, &localaddr.sa, &la_size) < 0) { + sockerr = SOCKERRNO; + logmsg("getsockname() failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + result = 1; + goto out; + } + switch(localaddr.sa.sa_family) { + case AF_INET: + port = ntohs(localaddr.sa4.sin_port); + break; +#ifdef USE_IPV6 + case AF_INET6: + port = ntohs(localaddr.sa6.sin6_port); + break; +#endif + default: + break; + } + if(!port) { + /* Real failure, listener port shall not be zero beyond this point. */ + logmsg("Apparently getsockname() succeeded, with listener port zero."); + logmsg("A valid reason for this failure is a binary built without"); + logmsg("proper network library linkage. This might not be the only"); + logmsg("reason, but double check it before anything else."); + result = 1; + goto out; + } + } +out: + if(result) { + if(sock != CURL_SOCKET_BAD) + sclose(sock); + sock = CURL_SOCKET_BAD; + port = 0; + } + *psock = sock; + *pport = port; + return result; +} + +/* returns a socket handle, or 0 if there are no more waiting sockets, + or < 0 if there was an error */ +curl_socket_t accept_connection(curl_socket_t listen_sock) +{ + curl_socket_t msgsock = CURL_SOCKET_BAD; + int sockerr; + char errbuf[STRERROR_LEN]; + int flag = 1; + + msgsock = accept(listen_sock, NULL, NULL); + + if(got_exit_signal) { + if(msgsock != CURL_SOCKET_BAD) + sclose(msgsock); + return CURL_SOCKET_BAD; + } + + if(msgsock == CURL_SOCKET_BAD) { + sockerr = SOCKERRNO; + if(SOCK_EAGAIN(sockerr)) { + /* nothing to accept */ + return 0; + } + logmsg("MAJOR ERROR, accept() failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + return CURL_SOCKET_BAD; + } + + if(curlx_nonblock(msgsock, TRUE)) { + sockerr = SOCKERRNO; + logmsg("curlx_nonblock failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + sclose(msgsock); + return CURL_SOCKET_BAD; + } + +#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) + if(socket_domain != AF_UNIX) { +#endif + if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, + (void *)&flag, sizeof(flag))) { + sockerr = SOCKERRNO; + logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s", + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + sclose(msgsock); + return CURL_SOCKET_BAD; + } +#if defined(_WIN32) && defined(USE_UNIX_SOCKETS) + } +#endif + + /* + * As soon as this server accepts a connection from the test harness it + * must set the server logs advisor read lock to indicate that server + * logs should not be read until this lock is removed by this server. + */ + + if(!serverlogslocked) + set_advisor_read_lock(loglockfile); + serverlogslocked += 1; + + logmsg("====> Client connect"); + +#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) + if(socket_domain_is_ip()) { + /* + * Disable the Nagle algorithm to make it easier to send out a large + * response in many small segments to torture the clients more. + */ + if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, + (void *)&flag, sizeof(flag))) + logmsg("====> TCP_NODELAY failed"); + } +#endif + + return msgsock; +} + +bool curlx_str_case_equal(const struct Curl_str *s1, + const struct Curl_str *s2) +{ + return ((s1->len == s2->len) && + !CURL_STRNICMP(s1->str, s2->str, s1->len)); +} From ff4bab0a68fcb1d3ffc31b61b71c877473dc04f4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 23:14:25 +0200 Subject: [PATCH 0832/1062] ldap_do: refactor error handling and simplify show_vals function - (much) less code repetition - simplifies ldap_do somewhat Closes #22510 --- lib/ldap.c | 152 ++++++++++++++++++----------------------------------- 1 file changed, 51 insertions(+), 101 deletions(-) diff --git a/lib/ldap.c b/lib/ldap.c index c8830b3edcd5..d7e871863d77 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -262,6 +262,52 @@ static BOOLEAN bypass_cert_verify(PLDAP Connection, } #endif +static CURLcode show_vals(struct Curl_easy *data, BerValue **vals, + const char *attr) +{ + CURLcode result = CURLE_OK; + int i; + size_t attr_len = strlen(attr); + + for(i = 0; vals[i] && !result; i++) { + result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, ":", 1); + + if(result) + break; + + if(ldap_value_needs_base64(attr, attr_len, vals[i])) { + char *val_b64 = NULL; + size_t val_b64_sz = 0; + + /* Binary attribute, encode to base64. */ + if(vals[i]->bv_len) + result = curlx_base64_encode((uint8_t *)vals[i]->bv_val, + vals[i]->bv_len, + &val_b64, &val_b64_sz); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); + if(!result && val_b64_sz) + result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + curlx_free(val_b64); + } + else { + result = Curl_client_write(data, CLIENTWRITE_BODY, " ", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + vals[i]->bv_val, vals[i]->bv_len); + } + + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + } + return result; +} + static CURLcode ldap_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -273,7 +319,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) int num = 0; struct connectdata *conn = data->conn; int ldap_proto = LDAP_VERSION3; - int ldap_ssl = 0; + bool ldap_ssl = FALSE; #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval ldap_timeout = { 10, 0 }; /* 10s connection/search timeout */ #endif @@ -309,8 +355,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; /* Get the URL scheme (either ldap or ldaps) */ - if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) - ldap_ssl = 1; + ldap_ssl = Curl_conn_is_ssl(conn, FIRSTSOCKET); infof(data, "LDAP local: trying to establish %s connection", ldap_ssl ? "encrypted" : "cleartext"); @@ -471,7 +516,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *attribute; #endif - int i; /* Get the DN and write it to the client */ { @@ -505,7 +549,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) attribute; attribute = ldap_next_attribute(server, entryIterator, ber)) { BerValue **vals; - size_t attr_len; #ifdef USE_WIN32_LDAP char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { @@ -516,103 +559,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *attr = attribute; #endif - attr_len = strlen(attr); - vals = ldap_get_values_len(server, entryIterator, attribute); if(vals) { - for(i = 0; vals[i]; i++) { - result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, ":", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - - if(ldap_value_needs_base64(attr, attr_len, vals[i])) { - char *val_b64 = NULL; - size_t val_b64_sz = 0; - - /* Binary attribute, encode to base64. */ - if(vals[i]->bv_len) { - result = curlx_base64_encode((uint8_t *)vals[i]->bv_val, - vals[i]->bv_len, - &val_b64, &val_b64_sz); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); - if(result) { - curlx_free(val_b64); - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - - if(val_b64_sz) { - result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, - val_b64_sz); - if(result) { - curlx_free(val_b64); - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - } - - curlx_free(val_b64); - } - else { - result = Curl_client_write(data, CLIENTWRITE_BODY, " ", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, - vals[i]->bv_val, vals[i]->bv_len); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - goto quit; - } - } - + result = show_vals(data, vals, attr); /* Free memory used to store values */ ldap_value_free_len(vals); } @@ -621,7 +570,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); if(result) goto quit; } From 2a5d6a5502135a0dc8cc2c074a3913ba34c84440 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 7 Aug 2026 11:41:16 +0200 Subject: [PATCH 0833/1062] curl_url_set.md: expand the CURLU_NO_AUTHORITY description Closes #22515 --- docs/libcurl/curl_url_set.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/libcurl/curl_url_set.md b/docs/libcurl/curl_url_set.md index 2fa31123f15e..35ed109fcd53 100644 --- a/docs/libcurl/curl_url_set.md +++ b/docs/libcurl/curl_url_set.md @@ -224,6 +224,10 @@ host part (normally the only mandatory part of the authority), but libcurl cannot know whether this is permitted for custom schemes. Specifying the flag permits empty authority sections, similar to how file scheme is handled. +This option also makes libcurl accept and parse URLs with known schemes even +when they have no hostnames. URLs such as `https://` and `ftp://` otherwise +cause parse errors by default. + ## CURLU_PATH_AS_IS When set for **CURLUPART_URL**, this skips the normalization of the From a15cf1ea07963ab9d92803e23a96ac5541c6ba0f Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 6 Aug 2026 23:30:50 -0400 Subject: [PATCH 0834/1062] configure: clarify --enable-debug option - Change --enable-debug help text to say it is for developing curl. - Warn when --enable-debug is used. This change copies the help text and warning from cmake ENABLE_DEBUG. Also, it shortens the "for debugging curl itself" to just say "for debugging curl". Now it looks like this: --enable-debug Enable curl debug features (for developing curl) --disable-debug Disable curl debug features Ref: https://github.com/curl/curl/issues/22481#issuecomment-5207220359 Reported-by: Viktor Szakats Closes https://github.com/curl/curl/pull/22512 --- CMakeLists.txt | 2 +- configure.ac | 4 ++++ docs/INSTALL.md | 4 ++-- m4/curl-confopts.m4 | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 868234926516..55f63a678ca6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,7 @@ if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "G list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") # Apply to all feature checks endif() -option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OFF) +option(ENABLE_DEBUG "Enable curl debug features (for developing curl)" OFF) if(ENABLE_DEBUG) message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.") endif() diff --git a/configure.ac b/configure.ac index 6ae2e7b57ead..d894dd48d06a 100644 --- a/configure.ac +++ b/configure.ac @@ -5619,6 +5619,10 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: Features: ${SUPPORT_FEATURES} ]) +if test "$want_debug" = "yes"; then + AC_MSG_WARN([This curl build is Debug-enabled and insecure, do not use in production.]) +fi + if test -n "$experimental"; then for a in $experimental; do AC_MSG_WARN([$a is enabled but marked EXPERIMENTAL. Use with caution!]) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 546f3edf1aed..a2f233b5d091 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -146,8 +146,8 @@ Building statically is not for the faint of heart. ## Debug -If you are a curl developer and use gcc, you might want to enable more debug -options with the `--enable-debug` option. +If you are a curl developer and use gcc, you might want to enable curl debug +features (for developing curl) with the `--enable-debug` option. curl can be built to use a whole range of libraries to provide various useful services, and configure tries to auto-detect a decent default. If you want to diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index 146e2e308ea4..20e5ab372262 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -113,8 +113,8 @@ AC_DEFUN([CURL_CHECK_OPTION_DEBUG], [ AC_MSG_CHECKING([whether to enable debug build options]) OPT_DEBUG_BUILD="default" AC_ARG_ENABLE(debug, -AS_HELP_STRING([--enable-debug],[Enable debug build options]) -AS_HELP_STRING([--disable-debug],[Disable debug build options]), +AS_HELP_STRING([--enable-debug],[Enable curl debug features (for developing curl)]) +AS_HELP_STRING([--disable-debug],[Disable curl debug features]), OPT_DEBUG_BUILD=$enableval) case "$OPT_DEBUG_BUILD" in no) From 817d75e25bb61b33f8976279c040ffa17f563639 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 7 Aug 2026 17:22:46 +0200 Subject: [PATCH 0835/1062] tests/server/tftpd: replace duplicate code with `open_udp_sock()` call Follow-up to 7f6a75664f9fb45390193e492ee3361fb795d098 #22506 Closes #22517 --- tests/server/tftpd.c | 96 +------------------------------------------- 1 file changed, 2 insertions(+), 94 deletions(-) diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 86d42f6f558c..0ca6aca83e7d 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -1005,15 +1005,10 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) static int test_tftpd(int argc, const char **argv) { - srvr_sockaddr_union_t me; struct tftphdr *tp; ssize_t n = 0; int arg = 1; curl_socket_t sock = CURL_SOCKET_BAD; - int flag; - int rc; - int sockerr; - char errbuf[STRERROR_LEN]; struct testcase test; int result = 0; srvr_sockaddr_union_t from; @@ -1107,96 +1102,9 @@ static int test_tftpd(int argc, const char **argv) install_signal_handlers(TRUE); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - sock = socket(AF_INET6, SOCK_DGRAM, 0); - else -#endif - sock = socket(AF_INET, SOCK_DGRAM, 0); - - if(sock == CURL_SOCKET_BAD) { - sockerr = SOCKERRNO; - logmsg("Error creating socket (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; - goto tftpd_cleanup; - } - - flag = 1; - if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - sockerr = SOCKERRNO; - logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; - goto tftpd_cleanup; - } - -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) { - memset(&me.sa6, 0, sizeof(me.sa6)); - me.sa6.sin6_family = AF_INET6; - me.sa6.sin6_addr = in6addr_any; - me.sa6.sin6_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa6)); - } - else -#endif - { - memset(&me.sa4, 0, sizeof(me.sa4)); - me.sa4.sin_family = AF_INET; - me.sa4.sin_addr.s_addr = INADDR_ANY; - me.sa4.sin_port = htons(server_port); - rc = bind(sock, &me.sa, sizeof(me.sa4)); - } - if(rc) { - sockerr = SOCKERRNO; - logmsg("Error binding socket on port %hu (%d) %s", server_port, - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - result = 1; + result = open_udp_sock(&sock, &server_port); + if(result) goto tftpd_cleanup; - } - - if(!server_port) { - /* The system was supposed to choose a port number, figure out which - port we actually got and update the listener port value with it. */ - curl_socklen_t la_size; - srvr_sockaddr_union_t localaddr; - memset(&localaddr, 0, sizeof(localaddr)); -#ifdef USE_IPV6 - if(socket_domain == AF_INET6) - la_size = sizeof(localaddr.sa6); - else -#endif - la_size = sizeof(localaddr.sa4); - if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - sockerr = SOCKERRNO; - logmsg("getsockname() failed with error (%d) %s", - sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); - sclose(sock); - goto tftpd_cleanup; - } - switch(localaddr.sa.sa_family) { - case AF_INET: - server_port = ntohs(localaddr.sa4.sin_port); - break; -#ifdef USE_IPV6 - case AF_INET6: - server_port = ntohs(localaddr.sa6.sin6_port); - break; -#endif - default: - break; - } - if(!server_port) { - /* Real failure, listener port shall not be zero beyond this point. */ - logmsg("Apparently getsockname() succeeded, with listener port zero."); - logmsg("A valid reason for this failure is a binary built without"); - logmsg("proper network library linkage. This might not be the only"); - logmsg("reason, but double check it before anything else."); - result = 2; - goto tftpd_cleanup; - } - } tftpd_wrotepidfile = write_pidfile(pidname); if(!tftpd_wrotepidfile) { From 725fbeaad9532c42acfc9d8e66b48f38425a7697 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 7 Aug 2026 22:32:02 -0700 Subject: [PATCH 0836/1062] tests: fix typo in assert message in http test Found by pyrefly. --- tests/http/test_01_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/test_01_basic.py b/tests/http/test_01_basic.py index 24e03d19b824..3b7719fcce26 100644 --- a/tests/http/test_01_basic.py +++ b/tests/http/test_01_basic.py @@ -132,7 +132,7 @@ def test_01_09_h2_prior_knowledge(self, env: Env, httpd): r = curl.http_get(url=url, extra_args=['--http2-prior-knowledge']) r.check_exit_code(0) assert len(r.responses) == 1, f'{r.responses}' - assert r.response['status'] == 200, f'{r.responsw}' + assert r.response['status'] == 200, f'{r.response}' assert r.response['protocol'] == 'HTTP/2', f'{r.response}' assert r.json['server'] == env.domain1 From b24346fe31ad45a91de7d9be0698c8b2c0954d3d Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 6 Aug 2026 22:00:42 -0400 Subject: [PATCH 0837/1062] api: fix printf format specifier - Use %hu for unsigned short instead of %u. Prior to this change some compilers could warn of an argument type mismatch. C argument promotion rules promote the unsigned short argument to an int, which does not match %u (unsigned int) but does match %hu (unsigned short). Assisted-by: Viktor Szakats Closes https://github.com/curl/curl/pull/22511 --- lib/api.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/api.c b/lib/api.c index 930350297833..39147417baa9 100644 --- a/lib/api.c +++ b/lib/api.c @@ -225,7 +225,7 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, if(data->callstack.count) { #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "EAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + "EAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, data->callstack.calls[data->callstack.count-1])); #endif result = CURLE_RECURSIVE_API_CALL; @@ -235,7 +235,8 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "EAPI guard: calling %u with multi call to %u ongoing\n", (uint16_t)fn, + "EAPI guard: calling %hu with multi call to %u ongoing\n", + (uint16_t)fn, data->multi->callstack.calls[data->multi->callstack.count-1])); #endif result = CURLE_RECURSIVE_API_CALL; @@ -247,7 +248,7 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, /* Not allowed to be invoked while an event cb is ongoing */ #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "EAPI guard: calling %u while event callback ongoing\n", + "EAPI guard: calling %hu while event callback ongoing\n", (uint16_t)fn)); #endif result = CURLE_RECURSIVE_API_CALL; @@ -259,7 +260,7 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, Curl_ssl_scache_is_locked_by_current_thread(data)) { #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "EAPI guard: calling %u while vtls_scache is locked by " + "EAPI guard: calling %hu while vtls_scache is locked by " "current thread\n", (uint16_t)fn)); #endif result = CURLE_RECURSIVE_API_CALL; @@ -330,7 +331,7 @@ bool Curl_mapi_enter(struct Curl_mapi_guard *guard, else if(!fn_props->recurse && multi->callstack.count) { #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "MAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + "MAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, multi->callstack.calls[multi->callstack.count-1])); #endif mresult = CURLM_RECURSIVE_API_CALL; @@ -342,7 +343,7 @@ bool Curl_mapi_enter(struct Curl_mapi_guard *guard, Curl_ssl_scache_is_locked_by_current_thread(multi->admin)) { #ifdef CURLVERBOSE DEBUGF(curl_mfprintf(stderr, - "MAPI guard: calling %u while its vtls_scache is locked by " + "MAPI guard: calling %hu while its vtls_scache is locked by " "current thread\n", (uint16_t)fn)); #endif mresult = CURLM_RECURSIVE_API_CALL; From 04ff9df3e815c33dd5ecf7b891b32202858fa039 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 8 Aug 2026 23:05:31 +0200 Subject: [PATCH 0838/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 907e2afc4d3a..d04d10fc1c8e 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -5,7 +5,7 @@ curl and libcurl 8.22.0 curl_easy_setopt() options: 312 Public functions in libcurl: 100 Authors: 1503 - Contributors: 3759 + Contributors: 3761 This release includes the following changes: @@ -28,6 +28,7 @@ This release includes the following bugfixes: o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: de-duplicate `ngtcp2_conn_client_new()` call code [156] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] + o cf-socket: disable TCP SYN retransmissions for localhost on Windows [164] o cfilters: fix event-based connection shutdown [91] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] @@ -36,6 +37,7 @@ This release includes the following bugfixes: o cmake: replace `remove` command with `rm` and pass arg safely [11] o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] + o configure: clarify --enable-debug option [133] o configure: fix misleading error messages [42] o configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows [79] o configure: only check in the watt library if WATT_ROOT is set [120] @@ -46,16 +48,19 @@ This release includes the following bugfixes: o conncache: connection alive checks intervals [20] o connect: connection close tweaks [112] o content_encoding: give a clear error on multi-member gzip [46] + o cookie: refuse to load cookies set against a PSL domain [139] o CREDENTIALS.md: remove comment about empty user/pass [50] o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] o curl: help category cleanups [169] o curl_gssapi: document/update feature availability [145] o curl_trc: remove unused expire timers [147] + o curl_url_set.md: expand the CURLU_NO_AUTHORITY description [134] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] + o DEPRECATE.md: HTTP/2 Server Push gets removed in March 2027 [174] o dnsd: fix bounds check in `read_https_alpn_part()` [143] o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] o FTP: fix TLS session reuse on the data connection [80] @@ -100,8 +105,10 @@ This release includes the following bugfixes: o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] + o psl: update a comment to understandable English [162] o pytest: update two H3 tests for nghttp3 1.18.0+ [158] o quiche: set the max field section size [100] + o rtsp: refactor method handling and improve error checks [161] o runtests: allow comments in `setenv` section, merge sections in test433 [89] o runtests: fix `mode="warn"` tests passing unconditionally, fix test 1752 [66] o runtests: flush cached test parts when (re)loading a file [95] @@ -134,6 +141,7 @@ This release includes the following bugfixes: o tests: convert unit test 1396 and 1398 into libtests [146] o tests: fix the FTP check for unexpected RST [117] o tests: fix type promotion on 32-bit arches in http test code [88] + o tests: fix typo in assert message in http test o tests: improve exception handling in Python test code o tests: remove test1701 [58] o tests: simplify by removing unneeded Python code @@ -187,6 +195,7 @@ For all changes ever done in curl: Planned upcoming removals include: + o HTTP/2 Server Push o local crypto implementations o NTLM o SMB @@ -196,21 +205,21 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 11soda11, AlanKingPL, Alb3e3, Alhuda Khan, Bartel Sielski, - Bigtang on hackerone, Bill Mill, Bryan Henderson, + 11soda11, 1rhino2 on hackerone, AlanKingPL, Alb3e3, Alhuda Khan, + Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, Christoph Reiter, claudex on github, Collin Funk, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, Laurent Sabourin, - Matthew John Cheetham, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, - pszemus on github, Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, - RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, - Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, - Stephan Zeisberg, stze on hackerone, Viktor Szakats, xmoezzz on github, - Yoshiro Yoneya - (54 contributors) + Marcel Jamin, Matthew John Cheetham, Memduh Çelik, Patrick Monnerat, + Pavel Sobolev, pszemus on github, Ramesh Adhikari, Ray Satiro, renovate[bot], + Rito Rhymes, RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, + Sam James, Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, + Stefan Eissing, Stephan Zeisberg, stze on hackerone, Viktor Szakats, + xmoezzz on github, Yoshiro Yoneya + (56 contributors) References to bug reports and discussions on issues: @@ -344,8 +353,11 @@ References to bug reports and discussions on issues: [130] = https://curl.se/bug/?i=21467 [131] = https://curl.se/bug/?i=22037 [132] = https://curl.se/bug/?i=22418 + [133] = https://curl.se/bug/?i=22512 + [134] = https://curl.se/bug/?i=22515 [135] = https://curl.se/bug/?i=22413 [137] = https://curl.se/bug/?i=22341 + [139] = https://curl.se/bug/?i=22500 [141] = https://curl.se/bug/?i=22422 [142] = https://curl.se/bug/?i=22421 [143] = https://curl.se/bug/?i=22420 @@ -365,9 +377,13 @@ References to bug reports and discussions on issues: [158] = https://curl.se/bug/?i=22397 [159] = https://curl.se/bug/?i=22400 [160] = https://curl.se/bug/?i=22460 + [161] = https://curl.se/bug/?i=22505 + [162] = https://curl.se/bug/?i=22502 + [164] = https://curl.se/bug/?i=22494 [166] = https://curl.se/bug/?i=22448 [167] = https://curl.se/bug/?i=22449 [169] = https://curl.se/bug/?i=22447 [170] = https://curl.se/bug/?i=22445 [171] = https://curl.se/bug/?i=22444 [172] = https://curl.se/bug/?i=22443 + [174] = https://curl.se/bug/?i=22490 From 3f1c033afe008123680306663cc01279e831ac2d Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 7 Aug 2026 21:16:52 -0700 Subject: [PATCH 0839/1062] libcurl.pc: add the Link.ABI and Source tags Link.ABI is a feature of pkgconf 3.0 and indicates the ABI against which a consumer must link the package. For libcurl, this is hard-coded as C. Source is a URL from which to download the package tarball. Closes #22519 --- libcurl.pc.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libcurl.pc.in b/libcurl.pc.in index c0ba5244a839..000d42745645 100644 --- a/libcurl.pc.in +++ b/libcurl.pc.in @@ -33,9 +33,11 @@ Name: libcurl URL: https://curl.se/ Description: Library to transfer files with HTTP, FTP, etc. Version: @CURLVERSION@ +Source: https://curl.se/download/curl-@CURLVERSION@.tar.gz Requires: @LIBCURL_PC_REQUIRES@ Requires.private: @LIBCURL_PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lcurl @LIBCURL_PC_LIBS@ Libs.private: @LIBCURL_PC_LDFLAGS_PRIVATE@ @LIBCURL_PC_LIBS_PRIVATE@ Cflags: -I${includedir} @LIBCURL_PC_CFLAGS@ Cflags.private: @LIBCURL_PC_CFLAGS_PRIVATE@ +Link.ABI: c From 1ea07d6e0a1263aaa549a8253db619e87af1da34 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 22:52:45 +0200 Subject: [PATCH 0840/1062] CI: filter all .[ch] files before doing typo checks The filter only leaves comments and strings in all files using .c or .h extensions. It is important that the filter runs after all the other checks are done on these files as this process destroys the files. Fixes #22508 Closes #22509 --- .github/scripts/c-comments | 191 +++++++++++++++++++++++++++++++++ .github/scripts/c-strip | 7 ++ .github/workflows/checksrc.yml | 32 +++--- 3 files changed, 216 insertions(+), 14 deletions(-) create mode 100755 .github/scripts/c-comments create mode 100755 .github/scripts/c-strip diff --git a/.github/scripts/c-comments b/.github/scripts/c-comments new file mode 100755 index 000000000000..f8c30cf0f974 --- /dev/null +++ b/.github/scripts/c-comments @@ -0,0 +1,191 @@ +#!/usr/bin/env perl +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +# Output all the C comments and double-quoted strings in the given source +# files. All other contents should be blanked out. Output the text at the same +# horizontal position as in the original file. +# +# Ignores strings for the preprocessor. +# +## States +# +# 0 - default, initial state +# 1 - there was a slash +# 2 - quoted string +# 3 - // comment +# 4 - /* comment +# 5 - asterisk found within a /* comment +# 6 - #include line +# 7 - backslash in a string +# 8 - backslash in plain code +# 9 - single quote in plain code +# +## Flags +# +# 1 - include preprocessor line, ignore strings + +sub scanline { + my ($col, $state, $flags, $l) = @_; + my $line; + + if($state == 3) { + # // ended on the prev line, go back to init + $state = 0; + } + + if(($state == 0) && ($l =~ /^ *\# *include/)) { + # preprocessor include line + $flags |= 1; + } + else { + # not preprocessor + $flags &= ~1; + } + + my @c = split(//, $l); + + # state machine this line + for my $c (@c) { + if($state == 1) { + # we had a slash + if($c eq "/") { + # // confirmed, the rest of the line is a comment + $line .= "//"; + $state = 3; + } + elsif($c eq "*") { + # /* confirmed + $state = 4; + $line .= "/*"; + } + else { + # back to normal + $line .= " "; + $state = 0; + } + } + elsif($state == 2) { + # a string + if($c eq "\\") { + $line .= "\\"; + $state = 7; + } + elsif($c eq "\"") { + # end of the string + $line .= "\""; + $state = 0; + } + else { + $line .= $c; + } + } + elsif($state == 3) { + # a // comment + $line .= $c; + } + elsif($state == 4) { + # an ongoing /* comment + if($c eq "*") { + # could a comment close + $state = 5; + } + else { + $line .= $c; + } + } + elsif($state == 5) { + if($c eq "/") { + # a /* */ comment ended here */ + $line .= "*/"; + $state = 0; + } + else { + # the /* comment continues + $line .= "*$c"; + $state = 4; + } + } + elsif($state == 7) { + # the prev was a backslash in a string + $line .= $c; + # switch back to normal string + $state = 2; + } + elsif($state == 8) { + # the prev was a backslash in code + if($c eq "\n") { + $line .= $c; + } + else { + #$line .= " "; + } + # switch back to plain code + $state = 0; + } + elsif($state == 9) { + # the prev was a single quote in code + if($c eq "\n") { + $line .= $c; + # switch back to plain code + $state = 0; + } + elsif($c eq "\\") { + # a backslash followed the quote + $line .= " "; + $state = 8; + } + else { + # switch back to plain code + $state = 0; + } + } + else { + if($c eq "\\") { + # got a backslash + $line .= " "; + $state = 8 + } + elsif($c eq "\'") { + # got a single quote + $line .= " "; + $state = 9 + } + if($c eq "/") { + $state = 1; # got a slash + } + elsif(($c eq "\"") && !($flags & 1)) { + # start of a string, not within a preprocessor line + $line .= "\""; + $state = 2; + } + elsif($c eq "\n") { + $line .= "\n"; + } + else { + $line .= " "; + } + } + } + # strip trailing space + $line =~ s/( +)\n/\n/; + return $state, $flags, $line; +} + +sub strip { + my ($f) = @_; + my $state = 0; + my $flags = 0; + open(F, "<$f") || die "can't open $f"; + while() { + my $l = $_; + ($state, $flags, $line) = scanline(0, $state, $flags, $l); + print "$line"; + } + close(F); +} + +for my $f (@ARGV) { + strip($f); +} diff --git a/.github/scripts/c-strip b/.github/scripts/c-strip new file mode 100755 index 000000000000..6b5cfbf86306 --- /dev/null +++ b/.github/scripts/c-strip @@ -0,0 +1,7 @@ +#!/bin/sh +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +./.github/scripts/c-comments "$1" > temp +mv temp "$1" diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index a5d18356db82..f6a2ec84e52b 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -66,20 +66,6 @@ jobs: source ~/venv/bin/activate reuse lint - - name: 'codespell' - run: | - source ~/venv/bin/activate - codespell --version - .github/scripts/codespell.sh - - - name: 'typos' - timeout-minutes: 2 - run: | - /home/linuxbrew/.linuxbrew/bin/brew install typos-cli - eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - typos --version - .github/scripts/typos.sh - - name: 'cmakelint' run: | source ~/venv/bin/activate @@ -96,6 +82,24 @@ jobs: source ~/venv/bin/activate scripts/pythonlint.sh + - name: filter off code from all C and H files + run: | + git ls-files -z '**.[ch]' | xargs -0 -r -n1 ./.github/scripts/c-strip + + - name: 'codespell' + run: | + source ~/venv/bin/activate + codespell --version + .github/scripts/codespell.sh + + - name: 'typos' + timeout-minutes: 2 + run: | + /home/linuxbrew/.linuxbrew/bin/brew install typos-cli + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + typos --version + .github/scripts/typos.sh + pytype: name: 'pytype' runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12 From 26fdb92c0a48337a9f696b79a8922e504243a823 Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Mon, 3 Aug 2026 20:51:52 +0530 Subject: [PATCH 0841/1062] mbedtls: enforce verifyhost when verifypeer is disabled Verify in test 2118 Closes #22475 --- lib/vtls/mbedtls.c | 6 ++-- tests/data/Makefile.am | 1 + tests/data/test2118 | 43 +++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 2 +- tests/libtest/lib2118.c | 60 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 tests/data/test2118 create mode 100644 tests/libtest/lib2118.c diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index f0d2f802307b..664d1f97a2a1 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -453,9 +453,11 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, mbed_extract_certinfo(data, crt); } + /* `verifypeer` and `verifyhost` are independent, so clear the flags of a + disabled check only. The name mismatch belongs to `verifyhost`. */ if(!conn_config->verifypeer) - *flags = 0; - else if(!conn_config->verifyhost) + *flags &= MBEDTLS_X509_BADCERT_CN_MISMATCH; + if(!conn_config->verifyhost) *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; if(*flags) { diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 4993864a5a36..43adb287416a 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -255,6 +255,7 @@ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 test2093 test2094 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \ +test2118 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2118 b/tests/data/test2118 new file mode 100644 index 000000000000..a3a18e750198 --- /dev/null +++ b/tests/data/test2118 @@ -0,0 +1,43 @@ + + + + +HTTPS +HTTP GET +PEM certificate + + + +# Server-side + + + +# Client-side + + +SSL +local-http +!wolfssl +!rustls + + +https test-localhost.nn.pem + + +lib%TESTNUMBER + + +VERIFYHOST with VERIFYPEER disabled rejects a mismatching certificate + + +https://localhost:%HTTPSPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +60 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 0787bfc91166..6311fe0b5363 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -117,7 +117,7 @@ TESTS_C = \ lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \ lib1964.c lib1965.c lib1967.c lib1970.c \ lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \ - lib2023.c lib2032.c lib2082.c \ + lib2023.c lib2032.c lib2082.c lib2118.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ lib2402.c lib2404.c lib2405.c \ lib2412.c lib2414.c \ diff --git a/tests/libtest/lib2118.c b/tests/libtest/lib2118.c new file mode 100644 index 000000000000..052ade015918 --- /dev/null +++ b/tests/libtest/lib2118.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +/* CURLOPT_SSL_VERIFYHOST is independent of CURLOPT_SSL_VERIFYPEER, so a + certificate issued for another name must be rejected even when the peer is + not verified. The server presents a certificate for "localhost.nn". */ + +static CURLcode test_lib2118(const char *URL) +{ + CURLcode result; + CURL *curl; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); + + result = curl_easy_perform(curl); + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return result; +} From b5ab518ea56f1872db597b115f7d3234d512dbc0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 01:28:00 +0000 Subject: [PATCH 0842/1062] GHA: Update awslabs/aws-lc to v5.5.0 Closes #22526 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index d880b1688508..f08013950bcc 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -37,7 +37,7 @@ env: CURL_TEST_MIN: 1850 DO_NOT_TRACK: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 5.1.0 + AWSLC_VERSION: 5.5.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1f1d9b6dc1f5..d4eb7c0bcae7 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -36,7 +36,7 @@ env: DO_NOT_TRACK: '1' HOMEBREW_NO_INSTALL_CLEANUP: '1' # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 5.1.0 + AWSLC_VERSION: 5.5.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com From be29bb4b6c690c24c463f0c556d1c9bb79f30359 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 01:27:54 +0000 Subject: [PATCH 0843/1062] GHA: update ruff to v0.16.2 Closes #22525 --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index d068818e57d3..a130d9147d29 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -5,4 +5,4 @@ cmakelang==0.6.13 codespell==2.4.3 reuse==6.2.0 -ruff==0.16.0 +ruff==0.16.2 From 8c525c081d72148b3d3ed51d89ca9a6200ca0c36 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 9 Aug 2026 20:51:01 +0000 Subject: [PATCH 0844/1062] GHA: update dependency pizlonator/fil-c to v0.683 Closes #22523 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d4eb7c0bcae7..d3aca8d4dcb8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,7 +40,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.682 + FIL_C_VERSION: 0.683 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com From 791b6883edc1c02c4b253e8b47237ec13ac66d06 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 9 Aug 2026 11:07:27 +0200 Subject: [PATCH 0845/1062] build: replace `assert()` with `DEBUGASSERT()`, where missing Follow-up to 9a2663322c330ff11275abafd612e9c99407a94a #17572 Follow-up to c96f982166fbc671b77f84a1730e9b6d11357e8f #10451 Closes #22521 --- lib/vquic/vquic.c | 2 +- src/tool_doswin.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 4975b8794d1f..2d2484957b89 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -283,7 +283,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, * does not seem to like a msg_control of length 0. */ memset(msg_ctrl, 0, sizeof(msg_ctrl)); msg.msg_control = msg_ctrl; - assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int))); + DEBUGASSERT(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int))); msg.msg_controllen = CMSG_SPACE(sizeof(int)); cm = CMSG_FIRSTHDR(&msg); cm->cmsg_level = SOL_UDP; diff --git a/src/tool_doswin.c b/src/tool_doswin.c index aacab5f75d1d..36ef53243901 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -799,10 +799,10 @@ curl_socket_t win32_stdin_read_thread(void) static curl_socket_t socket_r = CURL_SOCKET_BAD; if(socket_r != CURL_SOCKET_BAD) { - assert(stdin_thread); + DEBUGASSERT(stdin_thread); return socket_r; } - assert(!stdin_thread); + DEBUGASSERT(!stdin_thread); do { curl_socklen_t socksize = 0; @@ -944,7 +944,7 @@ curl_socket_t win32_stdin_read_thread(void) return CURL_SOCKET_BAD; } - assert(socket_r != CURL_SOCKET_BAD); + DEBUGASSERT(socket_r != CURL_SOCKET_BAD); return socket_r; } #endif /* USE_WINSOCK */ From 2d30fd26a060e7c3de3393503fb5ba7e8f3840f8 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 8 Aug 2026 13:20:15 +0200 Subject: [PATCH 0846/1062] DoH: improvements - decode results when individual requests are done - makes happy eyeballing start asap - remove doh_resp structures as no longer needed - remove CURL_DNS_TYPE_NS, CURL_DNS_TYPE_CNAME and CURL_DNS_TYPE_DNAME from DoH - DoH: do not set PIPEWAIT and SSL OPTS when url starts with http: - mark Doh master handle as dirty after every sub-request, not only the last - Doh: start probe on AAAA before A (was the other way). cf-dns: set EXPIRE_HAPPY_EYEBALLS timer when waiting 50ms on AAAA result or progress will not be triggered in time. Add debug env var CURL_DBG_HE_AAAA_AWAIT_MS to override the default 50ms on waiting for the AAAA result to arrive. test2100: set CURL_DBG_HE_AAAA_AWAIT_MS to 60 seconds to provide enough time for slow CI runs to sent all DoH requests. closes #22514 --- docs/libcurl/libcurl-env-dbg.md | 5 + lib/vdns/cf-dns.c | 51 ++- lib/vdns/doh.c | 665 ++++++++++++++------------------ lib/vdns/doh.h | 18 +- tests/data/test2100 | 6 + tests/http/test_21_resolve.py | 23 +- tests/http/testenv/dnsd.py | 23 +- tests/server/dnsd.c | 10 +- tests/unit/unit1650.c | 12 +- 9 files changed, 382 insertions(+), 431 deletions(-) diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index 1387df4badbf..a039cebc5cd2 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -174,6 +174,11 @@ a multi handle is destroyed. This implicitly triggers for easy handles that are run via easy_perform. The value of the environment variable gives the shutdown timeout in milliseconds. +## `CURL_DBG_HE_AAAA_AWAIT_MS` + +Overrides the time delaying a connect for AAAA resolve results to arrive +before continuing with Happy Eyeballing. + ## `CURL_DBG_RESOLV_MAX_THREADS` Overrides the maximum number of threads for resolver. diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index f02b37f126ff..51d4791ecce6 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -34,12 +34,15 @@ #include "vdns/cf-dns.h" #include "vdns/dnscache.h" #include "vdns/httpsrr.h" +#include "curlx/strparse.h" +#define CURL_HE_AAAA_AWAIT_MS 50 struct cf_dns_ctx { struct Curl_dns_entry *dns; struct Curl_peer *peer; CURLcode resolv_result; + timediff_t he_aaaa_await_ms; uint32_t resolv_id; uint8_t dns_queries; uint8_t transport; @@ -64,6 +67,18 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, ctx->dns_queries = dns_queries; ctx->transport = transport; ctx->for_proxy = for_proxy; + ctx->he_aaaa_await_ms = CURL_HE_AAAA_AWAIT_MS; +#ifdef DEBUGBUILD + { + const char *p = getenv("CURL_DBG_HE_AAAA_AWAIT_MS"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + ctx->he_aaaa_await_ms = (uint32_t)l; + } + } + } +#endif CURL_TRC_DNS(data, "[%s] created DNS filter for %s:%u, transport=%x", Curl_resolv_query_str(ctx->dns_queries), @@ -227,8 +242,6 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } } -#define CURL_HEV3_RESOLVE_DELAY_MS 50 - static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -239,19 +252,27 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, else if(ctx->dns) return TRUE; #ifdef USE_CURL_ASYNC - else { - /* We want AAAA answer as we prefer IPv6. If a sub-filter desires - * HTTPS-RR, we check for that query as well. */ - uint8_t wanted_answers = CURL_DNSQ_AAAA; - - /* Note: if a query was never started, it is considered to have + else if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + timediff_t remain_ms; + /* For Happy Eyeballing, we can start on either A or AAAA resolves, + * but AAAA is preferred. We enforce a small delay for missing + * AAAA to arrive, then we let the connect continue. + * Note: if AAAA was never started (-4), it is considered to have * an answer (e.g. a negative one). */ - if(Curl_resolv_has_answers(data, ctx->resolv_id, wanted_answers)) + if(Curl_resolv_has_answers(data, ctx->resolv_id, CURL_DNSQ_AAAA)) + return TRUE; + remain_ms = ctx->he_aaaa_await_ms - + Curl_resolv_elapsed_ms(data, ctx->resolv_id); + if(remain_ms <= 0) return TRUE; - /* If the wanted answers are not available after a delay, - * we let the connect attempts start anyway. */ - return Curl_resolv_elapsed_ms(data, ctx->resolv_id) >= - CURL_HEV3_RESOLVE_DELAY_MS; + CURL_TRC_CF(data, cf, "[%s] still waiting %" FMT_TIMEDIFF_T + "ms for AAAA result", + Curl_resolv_query_str(ctx->dns_queries), remain_ms); + Curl_expire(data, remain_ms, EXPIRE_HAPPY_EYEBALLS); + return FALSE; + } + else { + return TRUE; } #else (void)data; @@ -285,7 +306,9 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, if(ctx->resolv_result && ip_query) { /* failing A|AAAA resolves is a hard failure. */ - CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); + CURL_TRC_CF(data, cf, "[%s] error resolving: %d", + Curl_resolv_query_str(ctx->dns_queries), + (int)ctx->resolv_result); return ctx->resolv_result; } diff --git a/lib/vdns/doh.c b/lib/vdns/doh.c index ccf763dc54d8..fe6f9a890735 100644 --- a/lib/vdns/doh.c +++ b/lib/vdns/doh.c @@ -44,7 +44,7 @@ static void doh_close(struct Curl_easy *data, struct Curl_resolv_async *async); #ifdef CURLVERBOSE -static const char * const errors[] = { +static const char * const doh_code_str[] = { "", "Bad label", "Out of range", @@ -59,16 +59,34 @@ static const char * const errors[] = { "No content", "Bad ID", "Name too long", - "No such name" + "No such name", + "Transport failed", + "Out Of Memory" }; static const char *doh_strerror(DOHcode code) { - if((code >= DOH_OK) && (code <= DOH_DNS_NXDOMAIN)) - return errors[code]; + if((size_t)code < CURL_ARRAYSIZE(doh_code_str)) + return doh_code_str[code]; return "bad error code"; } +static const char *doh_type2name(DNStype dnstype) +{ + switch(dnstype) { + case CURL_DNS_TYPE_A: + return "A"; + case CURL_DNS_TYPE_AAAA: + return "AAAA"; +#ifdef USE_HTTPSRR + case CURL_DNS_TYPE_HTTPS: + return "HTTPS"; +#endif + default: + return "unknown"; + } +} + #endif /* CURLVERBOSE */ /* @unittest 1655 @@ -190,89 +208,8 @@ static size_t doh_probe_write_cb(char *contents, size_t size, size_t nmemb, return realsize; } -#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) && defined(CURLVERBOSE) - -/* doh_print_buf truncates if the hex string will be more than this */ -#define LOCAL_PB_HEXMAX 400 - -static void doh_print_buf(struct Curl_easy *data, - const char *prefix, - unsigned char *buf, size_t len) -{ - unsigned char hexstr[LOCAL_PB_HEXMAX]; - size_t hlen = LOCAL_PB_HEXMAX; - bool truncated = FALSE; - - if(len > (LOCAL_PB_HEXMAX / 2)) - truncated = TRUE; - Curl_hexencode(buf, len, hexstr, hlen); - if(!truncated) - infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr); - else - infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr); -} -#endif - -/* called from multi when a sub transfer, e.g. doh probe, is done. - * This looks up the probe response at its meta CURL_EZM_DOH_PROBE - * and copies the response body over to the struct at the master's - * meta at CURL_EZM_DOH_MASTER. */ static void doh_probe_done(struct Curl_easy *doh, - struct Curl_easy *master, CURLcode result) -{ - struct Curl_resolv_async *async = NULL; - struct doh_probes *dohp = NULL; - struct doh_request *doh_req = NULL; - int i; - - doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); - if(!doh_req) { - /* transfer `doh` is not a DoH probe. */ - DEBUGASSERT(0); - return; - } - - async = Curl_async_get(master, doh_req->resolv_id); - if(!async) { - CURL_TRC_DNS(master, "[%u] ignoring outdated DoH response", - doh_req->resolv_id); - return; - } - dohp = async->doh; - - for(i = 0; i < DOH_SLOT_COUNT; ++i) { - if(dohp->probe_resp[i].probe_mid == doh->mid) - break; - } - /* We really should have found the slot where to store the response */ - if(i >= DOH_SLOT_COUNT) { - DEBUGASSERT(0); - failf(master, "DoH: unknown sub request done"); - return; - } - - async->queries_ongoing--; - infof(doh, "a DoH request is completed, %u to go", async->queries_ongoing); - dohp->probe_resp[i].result = result; - /* We expect either the meta data still to exist or the sub request - * to have already failed. */ - if(!result) { - dohp->probe_resp[i].dnstype = doh_req->dnstype; - result = curlx_dyn_addn(&dohp->probe_resp[i].body, - curlx_dyn_ptr(&doh_req->resp_body), - curlx_dyn_len(&doh_req->resp_body)); - } - Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); - - if(result) - infof(doh, "DoH request %s", curl_easy_strerror(result)); - - if(!async->queries_ongoing) { - /* DoH completed, run master to act on results */ - Curl_multi_mark_dirty(master); - } -} - + struct Curl_easy *master, CURLcode result); static void doh_probe_dtor(void *key, size_t klen, void *e) { (void)key; @@ -306,6 +243,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, timediff_t timeout_ms; struct doh_request *doh_req; DOHcode d; + bool maybe_https = !curl_strnequal(url, STRCONST("http:")); *pmid = UINT32_MAX; @@ -354,8 +292,10 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)doh_req->req_body_len); ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, doh_req->req_hds); #ifdef USE_HTTP2 - ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); - ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); + if(maybe_https) { + ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); + } #endif #ifndef DEBUGBUILD /* enforce HTTPS if not debug */ @@ -372,55 +312,57 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); if(data->set.no_signal) ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); - - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, - data->set.doh_verifyhost ? 2L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, - data->set.doh_verifypeer ? 1L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, - data->set.doh_verifystatus ? 1L : 0L); - - /* Inherit *some* SSL options from the user's transfer. This is a - best-guess as to which options are needed for compatibility. #3661 - - Note DoH does not inherit the user's proxy server so proxy SSL settings - have no effect and are not inherited. If that changes then two new - options should be added to check doh proxy insecure separately, - CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. - */ - doh->set.ssl.custom_cafile = data->set.ssl.custom_cafile; - doh->set.ssl.custom_capath = data->set.ssl.custom_capath; - doh->set.ssl.custom_cablob = data->set.ssl.custom_cablob; - if(data->set.str[STRING_SSL_CAFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); - } - if(data->set.blobs[BLOB_CAINFO]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, data->set.blobs[BLOB_CAINFO]); - } - if(data->set.str[STRING_SSL_CAPATH]) { - ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH]); - } - if(data->set.str[STRING_SSL_CRLFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, data->set.str[STRING_SSL_CRLFILE]); - } - if(data->set.ssl.certinfo) - ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); - if(data->set.ssl.fsslctx) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); - if(data->set.ssl.fsslctxp) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); if(data->set.fdebug) ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug); if(data->set.debugdata) ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata); - if(data->set.str[STRING_SSL_EC_CURVES]) { - ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, - data->set.str[STRING_SSL_EC_CURVES]); - } - (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, - ((long)data->set.ssl.primary.ssl_options & - ~CURLSSLOPT_AUTO_CLIENT_CERT)); + if(maybe_https) { + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, + data->set.doh_verifyhost ? 2L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, + data->set.doh_verifypeer ? 1L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, + data->set.doh_verifystatus ? 1L : 0L); + + /* Inherit *some* SSL options from the user's transfer. This is a + best-guess as to which options are needed for compatibility. #3661 + + Note DoH does not inherit the user's proxy server so proxy SSL settings + have no effect and are not inherited. If that changes then two new + options should be added to check doh proxy insecure separately, + CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. + */ + doh->set.ssl.custom_cafile = data->set.ssl.custom_cafile; + doh->set.ssl.custom_capath = data->set.ssl.custom_capath; + doh->set.ssl.custom_cablob = data->set.ssl.custom_cablob; + if(data->set.str[STRING_SSL_CAFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); + } + if(data->set.blobs[BLOB_CAINFO]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, data->set.blobs[BLOB_CAINFO]); + } + if(data->set.str[STRING_SSL_CAPATH]) { + ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH]); + } + if(data->set.str[STRING_SSL_CRLFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, data->set.str[STRING_SSL_CRLFILE]); + } + if(data->set.ssl.certinfo) + ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); + if(data->set.ssl.fsslctx) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); + if(data->set.ssl.fsslctxp) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); + if(data->set.str[STRING_SSL_EC_CURVES]) { + ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, + data->set.str[STRING_SSL_EC_CURVES]); + } + + (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, + ((long)data->set.ssl.primary.ssl_options & + ~CURLSSLOPT_AUTO_CLIENT_CERT)); + } doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ @@ -469,39 +411,56 @@ CURLcode Curl_doh(struct Curl_easy *data, Curl_doh_cleanup(data, async); } + if(!async->dns_queries) + return CURLE_BAD_FUNCTION_ARGUMENT; +#ifdef USE_HTTPSRR + if(CURL_DNSQ_IS_ADDR(async->dns_queries) && + (async->dns_queries & CURL_DNSQ_HTTPS)) { + /* Can't mix those in the same async resolve */ + DEBUGASSERT(0); + return CURLE_BAD_FUNCTION_ARGUMENT; + } +#else + if(async->dns_queries & CURL_DNSQ_HTTPS) { + DEBUGASSERT(0); + return CURLE_NOT_BUILT_IN; + } +#endif + /* start clean, consider allocating this struct on demand */ async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); if(!dohp) return CURLE_OUT_OF_MEMORY; for(i = 0; i < DOH_SLOT_COUNT; ++i) { - dohp->probe_resp[i].probe_mid = UINT32_MAX; - curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE); + dohp->probe_rc[i] = DOH_OK; + dohp->probe_mid[i] = UINT32_MAX; } - /* create IPv4 DoH request */ - if(async->dns_queries & CURL_DNSQ_A) { - result = doh_probe_run(data, CURL_DNS_TYPE_A, +#ifdef USE_IPV6 + /* AAAA results have preference in happy eyeballing, trigger first */ + if(async->dns_queries & CURL_DNSQ_AAAA) { + /* create IPv6 DoH request */ + result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, - &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); + &dohp->probe_mid[DOH_SLOT_IPV6]); if(result) goto error; async->queries_ongoing++; } +#endif -#ifdef USE_IPV6 - if(async->dns_queries & CURL_DNSQ_AAAA) { - /* create IPv6 DoH request */ - result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, + /* create IPv4 DoH request */ + if(async->dns_queries & CURL_DNSQ_A) { + result = doh_probe_run(data, CURL_DNS_TYPE_A, async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, - &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); + &dohp->probe_mid[DOH_SLOT_IPV4]); if(result) goto error; async->queries_ongoing++; } -#endif #ifdef USE_HTTPSRR if(async->dns_queries & CURL_DNSQ_HTTPS) { @@ -516,7 +475,7 @@ CURLcode Curl_doh(struct Curl_easy *data, qname ? qname : async->peer->hostname, data->set.str[STRING_DOH], data->multi, async->id, - &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid); + &dohp->probe_mid[DOH_SLOT_HTTPS_RR]); curlx_free(qname); if(result) goto error; @@ -614,58 +573,7 @@ static DOHcode doh_store_https(const unsigned char *doh, int index, } #endif -static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen, - unsigned int index, struct dohentry *d) -{ - struct dynbuf *c; - unsigned int loop = 128; /* a valid DNS name can never loop this much */ - unsigned char length; - - if(d->numcname == DOH_MAX_CNAME) - return DOH_OK; /* skip! */ - - c = &d->cname[d->numcname++]; - do { - if(index >= dohlen) - return DOH_DNS_OUT_OF_RANGE; - length = doh[index]; - if((length & 0xc0) == 0xc0) { - int newpos; - /* name pointer, get the new offset (14 bits) */ - if((index + 1) >= dohlen) - return DOH_DNS_OUT_OF_RANGE; - - /* move to the new index */ - newpos = (length & 0x3f) << 8 | doh[index + 1]; - index = (unsigned int)newpos; - continue; - } - else if(length & 0xc0) - return DOH_DNS_BAD_LABEL; /* bad input */ - else - index++; - - if(length) { - if(curlx_dyn_len(c)) { - if(curlx_dyn_addn(c, STRCONST("."))) - return DOH_OUT_OF_MEM; - } - if((index + length) > dohlen) - return DOH_DNS_BAD_LABEL; - - if(curlx_dyn_addn(c, &doh[index], length)) - return DOH_OUT_OF_MEM; - index += length; - } - } while(length && --loop); - - if(!loop) - return DOH_DNS_LABEL_LOOP; - return DOH_OK; -} - static DOHcode doh_rdata(const unsigned char *doh, - size_t dohlen, unsigned short rdlength, unsigned short type, int index, @@ -674,10 +582,7 @@ static DOHcode doh_rdata(const unsigned char *doh, /* RDATA - A (TYPE 1): 4 bytes - AAAA (TYPE 28): 16 bytes - - NS (TYPE 2): N bytes - HTTPS (TYPE 65): N bytes */ - DOHcode rc; - switch(type) { case CURL_DNS_TYPE_A: if(rdlength != 4) @@ -690,22 +595,15 @@ static DOHcode doh_rdata(const unsigned char *doh, doh_store_aaaa(doh, index, d); break; #ifdef USE_HTTPSRR - case CURL_DNS_TYPE_HTTPS: - rc = doh_store_https(doh, index, d, rdlength); + case CURL_DNS_TYPE_HTTPS: { + DOHcode rc = doh_store_https(doh, index, d, rdlength); if(rc) return rc; break; + } #endif - case CURL_DNS_TYPE_CNAME: - rc = doh_store_cname(doh, dohlen, (unsigned int)index, d); - if(rc) - return rc; - break; - case CURL_DNS_TYPE_DNAME: - /* explicit for clarity; skip; rely on synthesized CNAME */ - break; default: - /* unsupported type, skip it */ + /* unsupported type, or type we do not store, skip it */ break; } return DOH_OK; @@ -715,11 +613,8 @@ static DOHcode doh_rdata(const unsigned char *doh, UNITTEST void de_init(struct dohentry *de); UNITTEST void de_init(struct dohentry *de) { - int i; memset(de, 0, sizeof(*de)); de->ttl = INT_MAX; - for(i = 0; i < DOH_MAX_CNAME; i++) - curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } /* TTL value cap */ @@ -781,7 +676,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if((type != CURL_DNS_TYPE_CNAME) && /* may be synthesized from DNAME */ (type != CURL_DNS_TYPE_DNAME) && /* if present, accept and ignore */ (type != dnstype)) - /* Not the same type as was asked for nor CNAME nor DNAME */ + /* Not the same type as was asked for, nor CNAME nor DNAME */ return DOH_DNS_UNEXPECTED_TYPE; index += 2; @@ -810,9 +705,9 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if(dohlen < (index + rdlength)) return DOH_DNS_OUT_OF_RANGE; - rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d); + rc = doh_rdata(doh, rdlength, type, (int)index, d); if(rc) - return rc; /* bad doh_rdata */ + return rc; index += rdlength; ancount--; } @@ -864,69 +759,10 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if(index != dohlen) return DOH_DNS_MALFORMAT; /* something is wrong */ -#ifdef USE_HTTPSRR - if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr && - !d->numhttps_rrs) -#else - if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr) -#endif - /* nothing stored! */ - return DOH_NO_CONTENT; - return DOH_OK; /* ok */ } -#ifdef CURLVERBOSE -static void doh_show(struct Curl_easy *data, - const struct dohentry *d) -{ - int i; - infof(data, "[DoH] TTL: %u seconds", d->ttl); - for(i = 0; i < d->numaddr; i++) { - const struct dohaddr *a = &d->addr[i]; - if(a->type == CURL_DNS_TYPE_A) { - infof(data, "[DoH] A: %u.%u.%u.%u", - a->ip.v4[0], a->ip.v4[1], - a->ip.v4[2], a->ip.v4[3]); - } - else if(a->type == CURL_DNS_TYPE_AAAA) { - int j; - char buffer[128] = "[DoH] AAAA: "; - size_t len = strlen(buffer); - char *ptr = &buffer[len]; - len = sizeof(buffer) - len; - for(j = 0; j < 16; j += 2) { - size_t l; - curl_msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", - d->addr[i].ip.v6[j], - d->addr[i].ip.v6[j + 1]); - l = strlen(ptr); - len -= l; - ptr += l; - } - infof(data, "%s", buffer); - } - } -#ifdef USE_HTTPSRR - for(i = 0; i < d->numhttps_rrs; i++) { -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - doh_print_buf(data, "DoH HTTPS", d->https_rrs[i].val, d->https_rrs[i].len); -#else - infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); -#endif - } -#endif /* USE_HTTPSRR */ - for(i = 0; i < d->numcname; i++) { - infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i])); - } -} -#else -#define doh_show(x, y) -#endif - /* - * doh2ai() - * * This function returns a pointer to the first element of a newly allocated * Curl_addrinfo struct linked list filled with the data from a set of DoH * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for @@ -936,7 +772,6 @@ static void doh_show(struct Curl_easy *data, * Curl_freeaddrinfo(). For each successful call to this function there * must be an associated call later to Curl_freeaddrinfo(). */ - static CURLcode doh2ai(const struct dohentry *de, const char *hostname, int port, struct Curl_addrinfo **aip) { @@ -947,14 +782,9 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, #ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif + size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ CURLcode result = CURLE_OK; int i; - size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ - - DEBUGASSERT(de); - - if(!de->numaddr) - return CURLE_COULDNT_RESOLVE_HOST; for(i = 0; i < de->numaddr; i++) { size_t ss_size; @@ -1032,35 +862,16 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, return result; } -#ifdef CURLVERBOSE -static const char *doh_type2name(DNStype dnstype) -{ - switch(dnstype) { - case CURL_DNS_TYPE_A: - return "A"; - case CURL_DNS_TYPE_AAAA: - return "AAAA"; -#ifdef USE_HTTPSRR - case CURL_DNS_TYPE_HTTPS: - return "HTTPS"; -#endif - default: - return "unknown"; - } -} -#endif - /* @unittest 1655 */ UNITTEST void de_cleanup(struct dohentry *d); UNITTEST void de_cleanup(struct dohentry *d) { - int i = 0; - for(i = 0; i < d->numcname; i++) { - curlx_dyn_free(&d->cname[i]); - } #ifdef USE_HTTPSRR + int i = 0; for(i = 0; i < d->numhttps_rrs; i++) curlx_safefree(d->https_rrs[i].val); +#else + (void)d; #endif } @@ -1192,115 +1003,207 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, #endif /* USE_HTTPSRR */ +/* called from multi when a sub transfer, e.g. doh probe, is done. + * Parse the response and set the results in the `async` context + * of master, using the id from the probe's CURL_EZM_DOH_PROBE + * meta data. */ +static void doh_probe_done(struct Curl_easy *doh, + struct Curl_easy *master, CURLcode result) +{ + struct Curl_resolv_async *async = NULL; + struct doh_probes *dohp = NULL; + struct doh_request *doh_req = NULL; + struct Curl_addrinfo **pdest_ai; + struct dohentry de; + int slot, httpcode; + + de_init(&de); + doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); + if(!doh_req) { + /* transfer `doh` is not a DoH probe. */ + DEBUGASSERT(0); + goto out; + } + + async = Curl_async_get(master, doh_req->resolv_id); + if(!async) { + CURL_TRC_DNS(master, "[%u] ignoring outdated DoH response", + doh_req->resolv_id); + goto out; + } + dohp = async->doh; + + for(slot = 0; slot < DOH_SLOT_COUNT; ++slot) { + if(dohp->probe_mid[slot] == doh->mid) + break; + } + /* We really should have found the slot where to store the response */ + if(slot >= DOH_SLOT_COUNT) { + failf(master, "DoH: unknown sub request done"); + DEBUGASSERT(0); + goto out; + } + + async->queries_ongoing--; + dohp = async->doh; + httpcode = doh->info.httpcode; + switch(slot) { + case DOH_SLOT_IPV4: + async->dns_responses |= CURL_DNSQ_A; + break; +#ifdef USE_IPV6 + case DOH_SLOT_IPV6: + async->dns_responses |= CURL_DNSQ_AAAA; + break; +#endif +#ifdef USE_HTTPSRR + case DOH_SLOT_HTTPS_RR: + async->dns_responses |= CURL_DNSQ_HTTPS; + break; +#endif + default: + DEBUGASSERT(0); + break; + } + + if(result) { + dohp->probe_rc[slot] = DOH_HTTP_FAILED; + infof(doh, "[DoH] [%s] error: %s", + doh_type2name(doh_req->dnstype), curl_easy_strerror(result)); + goto out; + } + else if((httpcode < 200) || (httpcode >= 300)) { + dohp->probe_rc[slot] = DOH_HTTP_FAILED; + infof(doh, "[DoH] [%s] error: HTTP status %d", + doh_type2name(doh_req->dnstype), httpcode); + goto out; + } + + dohp->probe_rc[slot] = doh_resp_decode(curlx_dyn_uptr(&doh_req->resp_body), + curlx_dyn_len(&doh_req->resp_body), + doh_req->dnstype, &de); + if(dohp->probe_rc[slot]) { +#ifdef USE_HTTPSRR + if((dohp->probe_rc[slot] == DOH_NO_CONTENT) && + (doh_req->dnstype == CURL_DNS_TYPE_HTTPS)) { + dohp->probe_rc[slot] = DOH_DNS_NXDOMAIN; + } +#endif + infof(doh, "[DoH] [%s] error decoding response: %s", + doh_type2name(doh_req->dnstype), + doh_strerror(dohp->probe_rc[slot])); + goto out; + } + + if(doh_req->dnstype == CURL_DNS_TYPE_A) + pdest_ai = &async->ai_A; + else if(doh_req->dnstype == CURL_DNS_TYPE_AAAA) + pdest_ai = &async->ai_AAAA; + else + pdest_ai = NULL; + + if(pdest_ai && de.numaddr) { + if(*pdest_ai) { + Curl_freeaddrinfo(*pdest_ai); + *pdest_ai = NULL; + } + result = doh2ai(&de, async->peer->hostname, async->peer->port, pdest_ai); + if(result) { /* hard failure on our side, fail completely */ + infof(doh, "[DoH] [%s] error creating addrinfo: %s", + doh_type2name(doh_req->dnstype), curl_easy_strerror(result)); + dohp->probe_rc[slot] = DOH_OOM; + async->result = result; + } + } +#ifdef USE_HTTPSRR + else if((doh_req->dnstype == CURL_DNS_TYPE_HTTPS) && de.numhttps_rrs) { + CURL_TRC_DNS(doh, "[HTTPS] got %d records", de.numhttps_rrs); + result = doh_resp_decode_httpsrr(doh, de.https_rrs->val, + de.https_rrs->len, &async->httpsrr); + if(result) { + dohp->probe_rc[slot] = DOH_HTTP_FAILED; + infof(doh, "[DoH] error decoding HTTPS RR: %s", + curl_easy_strerror(result)); + goto out; + } + } +#endif /* USE_HTTPSRR */ + + /* DoH request complete, run master to act on results */ + infof(doh, "DoH request complete, %u to go", async->queries_ongoing); + +out: + Curl_multi_mark_dirty(master); + de_cleanup(&de); + Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); +} + CURLcode Curl_doh_take_result(struct Curl_easy *data, struct Curl_resolv_async *async, struct Curl_dns_entry **pdns) { struct doh_probes *dohp = async->doh; CURLcode result = CURLE_OK; - struct dohentry de; *pdns = NULL; /* defaults to no response */ if(!dohp) return CURLE_OUT_OF_MEMORY; + async->negative_answer = FALSE; + if(async->result) { + result = async->result; + goto out; + } + if(CURL_DNSQ_IS_ADDR(async->dns_queries) && - dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && - dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { + dohp->probe_mid[DOH_SLOT_IPV4] == UINT32_MAX && + dohp->probe_mid[DOH_SLOT_IPV6] == UINT32_MAX) { failf(data, "Could not DoH-resolve: %s", async->peer->hostname); return async->for_proxy ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!async->queries_ongoing) { struct Curl_dns_entry *dns = NULL; - DOHcode rc[DOH_SLOT_COUNT]; bool negative = TRUE; int slot; - memset(rc, 0, sizeof(rc)); /* remove DoH handles from multi handle and close them */ doh_close(data, async); /* parse the responses, create the struct and return it! */ - de_init(&de); for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { - struct doh_response *p = &dohp->probe_resp[slot]; - if(!p->dnstype) - continue; - rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body), - curlx_dyn_len(&p->body), - p->dnstype, &de); /* Failing without an NXDOMAIN answer - a SERVFAIL-class rcode or an undecodable response - says nothing about the name. Such a failure must not be cached as a negative entry. */ - if(rc[slot] && (rc[slot] != DOH_DNS_NXDOMAIN)) + if(dohp->probe_rc[slot] && (dohp->probe_rc[slot] != DOH_DNS_NXDOMAIN)) negative = FALSE; - if(rc[slot]) { - CURL_TRC_DNS(data, "[%s] [DoH] error: %s of type %s for %s", - Curl_resolv_query_str(async->dns_queries), - doh_strerror(rc[slot]), - doh_type2name(p->dnstype), async->peer->hostname); - } } /* next slot */ - if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { - if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { - /* we have an address, of one kind or other */ - struct Curl_addrinfo *ai; - - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_doh)) { - CURL_TRC_DNS(data, "hostname: %s", async->peer->hostname); - doh_show(data, &de); - } - - result = doh2ai(&de, async->peer->hostname, async->peer->port, &ai); - if(result) { - /* a decoded response without any usable address, e.g. only - CNAME records, is an authoritative "no data" answer */ - if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) - async->negative_answer = TRUE; - goto error; - } - - /* we got a response, create a dns entry. */ - dns = Curl_dnsc_mk_addr(data, async->dns_queries, &ai, async->peer); - if(!dns) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } /* address processing done */ - else { - /* every query failed. Only NXDOMAIN answers for all of them - make this a negative answer, eligible for caching. */ - async->negative_answer = negative; - result = async->for_proxy ? - CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; + if(async->ai_A || async->ai_AAAA) { + dns = Curl_dnsc_mk_addr2( + data, async->dns_queries, &async->ai_A, &async->ai_AAAA, async->peer); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } } - #ifdef USE_HTTPSRR - if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { - /* Now add and HTTPSRR information if we have */ - struct Curl_https_rrinfo *hrr = NULL; - - CURL_TRC_DNS(data, "[HTTPS] got %d records", de.numhttps_rrs); - if(de.numhttps_rrs > 0 && result == CURLE_OK) { - result = doh_resp_decode_httpsrr(data, de.https_rrs->val, - de.https_rrs->len, &hrr); - if(result) { - infof(data, "Failed to decode HTTPS RR"); - Curl_dns_entry_unlink(data, &dns); - goto error; - } - infof(data, "Some HTTPS RR to process"); - } - Curl_httpsrr_trace(data, hrr); - dns = Curl_dnsc_mk_https(data, &hrr, async->peer); + else if((async->dns_queries & CURL_DNSQ_HTTPS) && + !dohp->probe_rc[DOH_SLOT_HTTPS_RR]) { + Curl_httpsrr_trace(data, async->httpsrr); + dns = Curl_dnsc_mk_https(data, &async->httpsrr, async->peer); if(!dns) { result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } } #endif /* USE_HTTPSRR */ + else { + /* every query failed. Only NXDOMAIN answers for all of them + make this a negative answer, eligible for caching. */ + async->negative_answer = negative; + result = async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; + } /* and add the entry to the cache */ if(dns) @@ -1311,8 +1214,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, /* wait for pending DoH transactions to complete */ return CURLE_AGAIN; -error: - de_cleanup(&de); +out: Curl_doh_cleanup(data, async); return result; } @@ -1326,16 +1228,15 @@ static void doh_close(struct Curl_easy *data, uint32_t mid; size_t slot; for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { - mid = doh->probe_resp[slot].probe_mid; + mid = doh->probe_mid[slot]; if(mid == UINT32_MAX) continue; - doh->probe_resp[slot].probe_mid = UINT32_MAX; + doh->probe_mid[slot] = UINT32_MAX; /* should have been called before data is removed from multi handle */ DEBUGASSERT(data->multi); probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) : NULL; if(!probe_data) { - DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!", - doh->probe_resp[slot].probe_mid)); + DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!", mid)); continue; } probe_data->sub_xfer_done = NULL; /* No longer interested in result */ @@ -1352,11 +1253,7 @@ void Curl_doh_cleanup(struct Curl_easy *data, { struct doh_probes *dohp = async->doh; if(dohp) { - int i; doh_close(data, async); - for(i = 0; i < DOH_SLOT_COUNT; ++i) { - curlx_dyn_free(&dohp->probe_resp[i].body); - } curlx_safefree(async->doh); } } diff --git a/lib/vdns/doh.h b/lib/vdns/doh.h index 1e4271796fe5..a6358027a7c4 100644 --- a/lib/vdns/doh.h +++ b/lib/vdns/doh.h @@ -45,7 +45,10 @@ typedef enum { DOH_NO_CONTENT, /* 11 */ DOH_DNS_BAD_ID, /* 12 */ DOH_DNS_NAME_TOO_LONG, /* 13 */ - DOH_DNS_NXDOMAIN /* 14 - no such name */ + DOH_DNS_NXDOMAIN, /* 14 - no such name */ + DOH_HTTP_FAILED, /* failure at the HTTP level */ + DOH_OOM, /* out of memory */ + DOH_CODE_LAST /* Not used, limit */ } DOHcode; typedef enum { @@ -98,17 +101,11 @@ struct doh_request { DNStype dnstype; }; -struct doh_response { - uint32_t probe_mid; - struct dynbuf body; - DNStype dnstype; - CURLcode result; -}; - /* each transfer firing off DoH requests has this * as easy meta for CURL_EZM_DOH_MASTER */ struct doh_probes { - struct doh_response probe_resp[DOH_SLOT_COUNT]; + uint32_t probe_mid[DOH_SLOT_COUNT]; + DOHcode probe_rc[DOH_SLOT_COUNT]; }; /* @@ -123,7 +120,6 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, struct Curl_dns_entry **pdns); #define DOH_MAX_ADDR 24 -#define DOH_MAX_CNAME 4 #define DOH_MAX_HTTPS 4 struct dohaddr { @@ -150,11 +146,9 @@ struct dohhttps_rr { #endif struct dohentry { - struct dynbuf cname[DOH_MAX_CNAME]; struct dohaddr addr[DOH_MAX_ADDR]; int numaddr; unsigned int ttl; - int numcname; #ifdef USE_HTTPSRR struct dohhttps_rr https_rrs[DOH_MAX_HTTPS]; int numhttps_rrs; diff --git a/tests/data/test2100 b/tests/data/test2100 index 1bb5361e15f7..57dc8c6579c0 100644 --- a/tests/data/test2100 +++ b/tests/data/test2100 @@ -55,6 +55,12 @@ IPv6 HTTP GET using DoH (with HTTPS RR) +# Make Happy Eyeballing wait longer on AAAA results to arrive +# On slow runs (valgrind) the connect otherwise might succeed before +# all DoH requests are sent off. + +CURL_DBG_HE_AAAA_AWAIT_MS=60000 + https://foo.example.com:%HTTPSPORT/%TESTNUMBER --insecure --doh-insecure --doh-url https://%HOSTIP:%HTTPSPORT/%TESTNUMBER0001 diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index 685a9792aec7..28634f14614e 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -124,14 +124,14 @@ def test_21_05_resolv_single_thread(self, env: Env, httpd, nghttpx): r.check_stats(count=count, http_status=0, exitcode=6) assert r.duration > timedelta(milliseconds=count * delay_ms), f'{r}' - def dns_settings(self, dns_method, dnsd): + def dns_settings(self, dns_method, dnsd, path="/"): xargs = [] run_env = os.environ.copy() - run_env['CURL_DEBUG'] = 'dns,doh' + run_env['CURL_DEBUG'] = 'all' if dns_method == 'DoH': if not Env.curl_can_doh(): pytest.skip(reason="curl built without DoH") - xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}/'] + xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}{path}'] else: if not Env.curl_override_dns(): pytest.skip(reason="no DNS override") @@ -178,8 +178,6 @@ def test_21_08_dnsd_aaaa(self, env: Env, httpd, dnsd, dns_method): # dnsd with one answer for A, delayed one for AAAA @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd, dns_method): - if dns_method == 'DoH': - pytest.skip(reason='DoH does not handle partial responses') dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'], delay_aaaa_ms=env.test_timeout * 1000) run_env, xargs = self.dns_settings(dns_method, dnsd) @@ -194,8 +192,6 @@ def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd, dns_method): @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") @pytest.mark.parametrize("dns_method", ["DNS", "DoH"]) def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd, dns_method): - if dns_method == 'DoH': - pytest.skip(reason='DoH does not handle partial responses') dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'], delay_a_ms=env.test_timeout * 1000) run_env, xargs = self.dns_settings(dns_method, dnsd) @@ -317,6 +313,19 @@ def test_21_16_dnsd_link_local(self, env: Env, httpd, dnsd): re.match(r'.* \* IPv6: fe80::1', line)] assert len(aaaa_resolves) == 1, f'{r.dump_logs()}' + # dnsd+DoH, handling HTTP response failure + def test_21_17_dnsd_http_fails(self, env: Env, httpd, dnsd): + count = 2 + dnsd.set_answers(rcode_a=2, rcode_aaaa=3) + run_env, xargs = self.dns_settings('DoH', dnsd, path='/notfound') + curl = CurlClient(env=env, run_env=run_env, force_resolv=False) + urls = [f'https://test-sf.http.curl.invalid/?id={i}' for i in range(count)] + r = curl.http_download(urls=urls, with_stats=True, extra_args=xargs) + r.check_exit_code(6) + r.check_stats(count=count, http_status=0, exitcode=6) + if env.curl_is_verbose(): + assert not [t for t in r.trace_lines if 'Negative DNS entry' in t], f'{r}' + def _clean_files(self, files): for file in files: if os.path.exists(file): diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index d80405e9e835..4411b07391ae 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -30,6 +30,7 @@ from datetime import datetime, timedelta, timezone from typing import Dict, List, Optional +from . import CurlClient from .env import Env from .ports import alloc_ports_and_do @@ -50,6 +51,7 @@ def __init__(self, env: Env): self._dnsd_dir = os.path.join(env.gen_dir, self.name) self._log_dir = self._dnsd_dir self._lock_dir = os.path.join(self._dnsd_dir, 'lock') + self._tmp_dir = os.path.join(self._dnsd_dir, 'tmp') self._log_file = os.path.join(self._log_dir, 'dnsd.log') self._conf_file = os.path.join(self._log_dir, 'dnsd.cmd') self._pid_file = os.path.join(self._log_dir, 'dnsd.pid') @@ -87,12 +89,14 @@ def start_if_needed(self): return True def stop(self, wait_dead=True): + result = True if self._process: self._process.terminate() self._process.wait(timeout=2) self._process = None + result = self.wait_dead(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) self.close_log() - return True + return result def restart(self): self.stop() @@ -100,6 +104,7 @@ def restart(self): def initial_start(self): self._mkpath(self._lock_dir) + self._mkpath(self._tmp_dir) def startup(ports: Dict[str, int]) -> bool: self._port = ports[self._port_skey] @@ -132,10 +137,24 @@ def start(self, wait_live=True): return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) + def wait_dead(self, timeout: timedelta): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + try_until = datetime.now(timezone.utc) + timeout + while datetime.now(timezone.utc) < try_until: + r = curl.http_get(url=f'http://127.0.0.1:{self._port}/') + if r.exit_code != 0: + return True + time.sleep(.1) + log.debug(f"Server still responding after {timeout}") + return False + def wait_live(self, timeout: timedelta): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir, + timeout=timeout.total_seconds()) try_until = datetime.now(timezone.utc) + timeout while datetime.now(timezone.utc) < try_until: - if os.path.exists(self._log_file): + r = curl.http_get(url=f'http://127.0.0.1:{self._port}/') + if r.exit_code == 0: return True time.sleep(.1) log.error(f"Server still not responding after {timeout}") diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 4c512a3701b0..03bfef87dad2 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -188,7 +188,7 @@ static const char *type2string(uint16_t qtype) * Return query (qname + type + class), type and id. */ static int store_incoming(const char *source, int query_id, - const unsigned char *data, size_t size, + const unsigned char *data, size_t datalen, unsigned char *qbuf, size_t qbuflen, size_t *qlen, uint16_t *qtype, uint16_t *idp) { @@ -200,12 +200,18 @@ static int store_incoming(const char *source, int query_id, uint16_t qd; const uint8_t *qptr; char name[256]; - size_t qsize; + size_t qsize, size; *qlen = 0; *qtype = 0; *idp = 0; + size = datalen; + if(datalen < 16) { + logmsg("query data size is too small: %ld", (long)datalen); + return -1; + } + snprintf(dumpfile, sizeof(dumpfile), "%s/dnsd.input", logdir); /* Open request dump file. */ diff --git a/tests/unit/unit1650.c b/tests/unit/unit1650.c index 1e6217176d94..890027b19353 100644 --- a/tests/unit/unit1650.c +++ b/tests/unit/unit1650.c @@ -106,7 +106,7 @@ static CURLcode test_unit1650(const char *arg) "\x6c\x04\x63\x75\x72\x6c\x00\x00\x05\x00\x01\xc0\x0c\x00\x05\x00" "\x01\x00\x00\x00\x37\x00\x11\x08\x61\x6e\x79\x77\x68\x65\x72\x65" "\x06\x72\x65\x61\x6c\x6c\x79\x00", 56, - CURL_DNS_TYPE_A, DOH_OK, "anywhere.really (55)"}, + CURL_DNS_TYPE_A, DOH_OK, NULL}, {DNS_FOO_EXAMPLE_COM, 49, CURL_DNS_TYPE_A, DOH_OK, "127.0.0.1 (55)"}, @@ -121,7 +121,7 @@ static CURLcode test_unit1650(const char *arg) "\x6c\x04\x63\x75\x72\x6c\x00\x00\x05\x00\x01\xc0\x0c\x00\x05\x00" "\x01\x00\x00\x00\x37\x00" "\x07\x03\x61\x6e\x79\xc0\x27\x00", 46, - CURL_DNS_TYPE_A, DOH_DNS_LABEL_LOOP, NULL}, + CURL_DNS_TYPE_A, DOH_OK, NULL}, /* packet with NSCOUNT == 1 */ {"\x00\x00\x01\x00\x00\x01\x00\x01\x00\x01\x00\x00\x04\x61\x61\x61" @@ -219,13 +219,6 @@ static CURLcode test_unit1650(const char *arg) ptr++; } } - for(u = 0; u < d.numcname; u++) { - size_t o; - curl_msnprintf(ptr, len, "%s ", curlx_dyn_ptr(&d.cname[u])); - o = strlen(ptr); - len -= o; - ptr += o; - } curl_msnprintf(ptr, len, "(%u)", d.ttl); de_cleanup(&d); if(resp[i].out && strcmp((const char *)buffer, resp[i].out)) { @@ -281,7 +274,6 @@ static CURLcode test_unit1650(const char *arg) (int)rc); abort_if(rc || strcmp((const char *)buffer, "127.0.0.1"), "bad address"); } - fail_if(d.numcname, "bad cname counter"); } #endif From 5050196dcc74b8369bcfad35d3b0926d0ff96180 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 4 Aug 2026 16:52:46 +0200 Subject: [PATCH 0847/1062] build: require `!NDEBUG` for debug-enabled (aka development) builds To prevent creating a curl-development (aka debug-enabled, aka `-DENABLE_DEBUG=ON`, `--enable-debug` or `DEBUGBUILD`) build without `assert()`s. Since it may break expectations by missing to catch error cases. Also to make it less likely to build an inherently insecure development build by accident. - in CMake, for non-Multi-Config builds, show this error unless Debug configuration is set at configure-time: ``` CMake Error at CMakeLists.txt:263 (message): Debug-enabled (aka development mode) curl requires the Debug configuration ``` Ref: https://github.com/curl/curl/actions/runs/31159065911/job/92805128331?pr=22513#step:6:56 The other 3 predefined configs (`Release`, `RelWithDebInfo`, `MinSizeRel`) set `NDEBUG` automatically. - or this message in other cases, e.g. when using CMake Multi-Config with non-Debug, e.g. `--config Release`, or passed `NDEBUG` manually with either build tools: ``` curl_setup.h(1062): fatal error C1189: #error: "Debug-enabled builds cannot be combined with NDEBUG" ``` Ref: https://github.com/curl/curl/actions/runs/31159628749/job/92807878479#step:9:28 Also: - INSTALL-CMAKE.md: document. - GHA/windows: adjust CI jobs to meet the above requirement, where missing. Reported-by Stefan Eissing Bug: https://github.com/curl/curl/pull/22484#discussion_r3730045572 Ref: ea984456804ab3ff1656a668fabdf734a32e379e #13592 Fixes #22481 Fixes #22484 Closes #22513 --- .github/workflows/windows.yml | 12 ++++++------ CMakeLists.txt | 3 +++ docs/INSTALL-CMAKE.md | 2 ++ lib/curl_setup.h | 4 ++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 28645b2303a5..71921ea40028 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -299,13 +299,13 @@ jobs: build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_DROP_UNUSED=ON', install: 'mingw-w64-clang-aarch64-libssh2' } - - { name: 'openssl', type: 'Release', chkprefill: '_chkprefill', + - { name: 'openssl', type: 'Debug', chkprefill: '_chkprefill', build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DUSE_PROXY_HTTP3=ON', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2' } - { name: 'openssl', type: 'Release', test: 'uwp', build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', - config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', + config: '-DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', install: 'mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-libssh2' } # { name: 'schannel', type: 'Release', test: 'uwp', # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', @@ -582,7 +582,7 @@ jobs: url: 'https://github.com/skeeto/w64devkit/releases/download/v2.8.0/w64devkit-x64-2.8.0.7z.exe' SHA256: 6252bf34fe2231a55ac7f03d482b36d2c7c58697990551bba508102cfb3f342e config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON' - type: 'Release' + type: 'Debug' - name: 'schannel' # mingw-w64 10.0 sys: 'mingw64' dir: 'mingw64' @@ -590,7 +590,7 @@ jobs: ver: '9.5.0' url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/9.5.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-9.5.0-mingw-w64msvcrt-10.0.0-r1.7z' SHA256: 41637132ea7dc36a7f86a1961eaa334c380b5a3423d36aecb481cabcd006e3fe - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCURL_DISABLE_VERBOSE_STRINGS=ON' + config: '-DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCURL_DISABLE_VERBOSE_STRINGS=ON' type: 'Release' tflags: 'skiprun' - name: 'schannel mbedtls U' # mingw-w64 6.0 @@ -602,7 +602,7 @@ jobs: SHA256: 9dc08c9c2bdd5d8173f87791bed644f6e290624f739de474f117b590dfd8a721 config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_USE_MBEDTLS=ON -DCURL_TARGET_WINDOWS_VERSION=0x0600' install: mingw-w64-x86_64-mbedtls - type: 'Release' + type: 'Debug' tflags: 'skiprun' - name: 'schannel !unity' # mingw-w64 5.0 sys: 'mingw32' @@ -611,7 +611,7 @@ jobs: ver: '6.4.0' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/6.4.0/threads-win32/dwarf/i686-6.4.0-release-win32-dwarf-rt_v5-rev0.7z' SHA256: 12d2c62ad4527ec8a52275ea8485678dcbe20bec4716a3c7ba274f225d696085 - config: '-DENABLE_DEBUG=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBSSH=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_TARGET_WINDOWS_VERSION=0x0600' + config: '-DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBSSH=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_TARGET_WINDOWS_VERSION=0x0600' install: mingw-w64-i686-libssh type: 'Debug' tflags: 'skiprun' diff --git a/CMakeLists.txt b/CMakeLists.txt index 55f63a678ca6..ea82301d9d44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,9 @@ endif() option(ENABLE_DEBUG "Enable curl debug features (for developing curl)" OFF) if(ENABLE_DEBUG) + if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(FATAL_ERROR "Debug-enabled (aka development mode) curl requires the Debug configuration") + endif() message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.") endif() diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index f240d9c52e9d..664df7361322 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -222,6 +222,8 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_WERROR`: Turn compiler warnings into errors. Default: `OFF` - `ENABLE_CURL_MANUAL`: Build the man page for curl and enable its `-M`/`--manual` option. Default: `ON` - `ENABLE_DEBUG`: Enable curl debug features (for developing curl itself). Default: `OFF` + This also requires Debug configuration enabled/selected at the same time, + via `CMAKE_BUILD_TYPE=Debug`, or `--config Debug` for multi-config generators. - `IMPORT_LIB_SUFFIX`: Import library suffix. Default: `_imp` for MSVC-like toolchains, otherwise empty. - `LIBCURL_OUTPUT_NAME`: Basename of the curl library. Default: `libcurl` - `PICKY_COMPILER`: Enable picky compiler options. Default: `ON` diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 9d18875b9d4f..38b4706cb97c 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1058,6 +1058,10 @@ typedef unsigned int curl_bit; #include "curl_ctype.h" +#if defined(DEBUGBUILD) && defined(NDEBUG) +#error "Debug-enabled builds cannot be combined with NDEBUG" +#endif + /* * Macro used to include code only in debug builds. */ From 5ef4e1cabca04ed60159597e8d32d7349195648b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 10 Aug 2026 08:02:37 +0200 Subject: [PATCH 0848/1062] checksrc: check for `assert`, replace in tests where missing, drop from examples Follow-up to 791b6883edc1c02c4b253e8b47237ec13ac66d06 #22521 Closes #22527 --- docs/examples/sslbackend.c | 5 +---- docs/internals/CODE_STYLE.md | 1 + scripts/checksrc.pl | 1 + tests/libtest/cli_h2_pausing.c | 2 +- tests/libtest/cli_hx_download.c | 22 +++++++++--------- tests/libtest/lib2700.c | 40 ++++++++++++++++----------------- tests/libtest/lib530.c | 6 ++++- tests/libtest/lib582.c | 3 ++- tests/libtest/lib583.c | 3 ++- 9 files changed, 44 insertions(+), 39 deletions(-) diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index 20002c4f816a..39c042a67773 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -49,8 +49,7 @@ int main(int argc, const char **argv) const curl_ssl_backend **list; int i; - result = curl_global_sslset(CURLSSLBACKEND_NONE, NULL, &list); - assert(result == CURLSSLSET_UNKNOWN_BACKEND); + (void)curl_global_sslset(CURLSSLBACKEND_NONE, NULL, &list); for(i = 0; list[i]; i++) printf("SSL backend #%d: '%s' (ID: %d)\n", @@ -71,8 +70,6 @@ int main(int argc, const char **argv) return 1; } - assert(result == CURLSSLSET_OK); - printf("Version with SSL backend '%s':\n\n\t%s\n", name, curl_version()); return 0; diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index 5feb52f3dca6..af8d916b36fa 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -353,6 +353,7 @@ This is the full list of functions generally banned. accept4 access aprintf + assert atoi atol calloc diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index ea71ee7ecb88..5d3230c1e497 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -70,6 +70,7 @@ "accept4" => 1, "access" => 1, "aprintf" => 1, + "assert" => 1, "atoi" => 1, "atol" => 1, "calloc" => 1, diff --git a/tests/libtest/cli_h2_pausing.c b/tests/libtest/cli_h2_pausing.c index bf98d03ecae2..19a2760245dc 100644 --- a/tests/libtest/cli_h2_pausing.c +++ b/tests/libtest/cli_h2_pausing.c @@ -64,7 +64,7 @@ static size_t cb(char *data, size_t size, size_t nmemb, void *clientp) ++handle->paused; curl_mfprintf(stderr, "INFO: [%zu] write, PAUSING %d time on %zu bytes\n", handle->idx, handle->paused, realsize); - assert(handle->paused == 1); + DEBUGASSERT(handle->paused == 1); return CURL_WRITEFUNC_PAUSE; } if(handle->fail_write) { diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index 6ad18e124a94..6afa53914282 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -148,15 +148,15 @@ static int my_progress_d_cb(void *userdata, if(result) { curl_mfprintf(stderr, "[t-%zu] info CURLINFO_TLS_SSL_PTR failed: %d\n", t->idx, (int)result); - assert(0); + DEBUGASSERT(0); } else { switch(tls->backend) { #ifdef USE_OPENSSL case CURLSSLBACKEND_OPENSSL: { const char *version = SSL_get_version((SSL *)tls->internals); - assert(version); - assert(strcmp(version, "unknown")); + DEBUGASSERT(version); + DEBUGASSERT(strcmp(version, "unknown")); curl_mfprintf(stderr, "[t-%zu] info OpenSSL using %s\n", t->idx, version); break; @@ -165,8 +165,8 @@ static int my_progress_d_cb(void *userdata, #ifdef USE_WOLFSSL case CURLSSLBACKEND_WOLFSSL: { const char *version = wolfSSL_get_version((WOLFSSL *)tls->internals); - assert(version); - assert(strcmp(version, "unknown")); + DEBUGASSERT(version); + DEBUGASSERT(strcmp(version, "unknown")); curl_mfprintf(stderr, "[t-%zu] info wolfSSL using %s\n", t->idx, version); break; @@ -176,7 +176,7 @@ static int my_progress_d_cb(void *userdata, case CURLSSLBACKEND_GNUTLS: { gnutls_protocol_t v = gnutls_protocol_get_version( (gnutls_session_t)tls->internals); - assert(v); + DEBUGASSERT(v); curl_mfprintf(stderr, "[t-%zu] info GnuTLS using %s\n", t->idx, gnutls_protocol_get_name(v)); break; @@ -186,8 +186,8 @@ static int my_progress_d_cb(void *userdata, case CURLSSLBACKEND_MBEDTLS: { const char *version = mbedtls_ssl_get_version((mbedtls_ssl_context *)tls->internals); - assert(version); - assert(strcmp(version, "unknown")); + DEBUGASSERT(version); + DEBUGASSERT(strcmp(version, "unknown")); curl_mfprintf(stderr, "[t-%zu] info mbedTLS using %s\n", t->idx, version); break; @@ -197,7 +197,7 @@ static int my_progress_d_cb(void *userdata, case CURLSSLBACKEND_RUSTLS: { int v = rustls_connection_get_protocol_version( (struct rustls_connection *)tls->internals); - assert(v); + DEBUGASSERT(v); curl_mfprintf(stderr, "[t-%zu] info rustls TLS version 0x%x\n", t->idx, (unsigned int)v); break; @@ -211,7 +211,7 @@ static int my_progress_d_cb(void *userdata, sspi_status = QueryContextAttributes(ctxt_handle, SECPKG_ATTR_CONNECTION_INFO, &info); - assert(sspi_status == SEC_E_OK); + DEBUGASSERT(sspi_status == SEC_E_OK); (void)sspi_status; curl_mfprintf(stderr, "[t-%zu] info Schannel TLS version 0x%08lx\n", t->idx, (unsigned long)info.dwProtocol); @@ -592,7 +592,7 @@ static CURLcode test_cli_hx_download(const char *URL) if(t->result) result = t->result; else /* on success we expect SSL to have been checked */ - assert(t->checked_ssl); + DEBUGASSERT(t->checked_ssl); } curlx_free(transfer_d); } diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index c2bb39e22196..6d5a7ffc0806 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -39,7 +39,7 @@ static const char *descr_flags(int flags) return "pong"; if(flags & CURLWS_CLOSE) return "close"; - assert(FALSE); + DEBUGASSERT(FALSE); return ""; } @@ -52,17 +52,17 @@ static CURLcode send_header(CURL *curl, int flags, size_t size) result = curl_ws_send(curl, NULL, 0, &nsent, (curl_off_t)size, flags | CURLWS_OFFSET); if(result == CURLE_AGAIN) { - assert(nsent == 0); + DEBUGASSERT(nsent == 0); goto retry; } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); - assert(nsent == 0); + DEBUGASSERT(nsent == 0); return result; } - assert(nsent == 0); + DEBUGASSERT(nsent == 0); return CURLE_OK; } @@ -81,20 +81,20 @@ static CURLcode recv_header(CURL *curl, int *flags, curl_off_t *offset, retry: result = curl_ws_recv(curl, NULL, 0, &nread, &meta); if(result == CURLE_AGAIN) { - assert(nread == 0); + DEBUGASSERT(nread == 0); goto retry; } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); - assert(nread == 0); + DEBUGASSERT(nread == 0); return result; } - assert(nread == 0); - assert(meta); - assert(meta->flags); - assert(meta->offset == 0); + DEBUGASSERT(nread == 0); + DEBUGASSERT(meta); + DEBUGASSERT(meta->flags); + DEBUGASSERT(meta->offset == 0); *flags = meta->flags; *offset = meta->offset; @@ -123,17 +123,17 @@ static CURLcode send_chunk(CURL *curl, int flags, const char *buffer, result = curl_ws_send(curl, buffer + *offset, size - *offset, &nsent, 0, flags); if(result == CURLE_AGAIN) { - assert(nsent == 0); + DEBUGASSERT(nsent == 0); goto retry; } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); - assert(nsent == 0); + DEBUGASSERT(nsent == 0); return result; } - assert(nsent <= size - *offset); + DEBUGASSERT(nsent <= size - *offset); *offset += nsent; @@ -152,21 +152,21 @@ static CURLcode recv_chunk(CURL *curl, int flags, curl_off_t *offset, retry: result = curl_ws_recv(curl, buffer, sizeof(buffer), &nread, &meta); if(result == CURLE_AGAIN) { - assert(nread == 0); + DEBUGASSERT(nread == 0); goto retry; } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); - assert(nread == 0); + DEBUGASSERT(nread == 0); return result; } - assert(nread <= sizeof(buffer)); - assert(meta); - assert(meta->flags == flags); - assert(meta->offset == *offset); - assert(meta->bytesleft == (*bytesleft - (curl_off_t)nread)); + DEBUGASSERT(nread <= sizeof(buffer)); + DEBUGASSERT(meta); + DEBUGASSERT(meta->flags == flags); + DEBUGASSERT(meta->offset == *offset); + DEBUGASSERT(meta->bytesleft == (*bytesleft - (curl_off_t)nread)); *offset += nread; *bytesleft -= nread; diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index bddb857be0f6..94ecb7adf297 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -341,7 +341,11 @@ static CURLcode testone(const char *URL, int timer_fail_at, int socket_fail_at) tv.tv_usec = 100000; } - assert(maxFd); + if(!maxFd) { + t530_msg("maxFd == 0"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } select_test((int)maxFd, &readSet, &writeSet, NULL, &tv); /* Check the sockets for reading / writing */ diff --git a/tests/libtest/lib582.c b/tests/libtest/lib582.c index 4b3f042852ec..50d683398c5e 100644 --- a/tests/libtest/lib582.c +++ b/tests/libtest/lib582.c @@ -227,7 +227,8 @@ static CURLcode test_lib582(const char *URL) struct curltime timeout = { 0 }; timeout.tv_sec = (time_t)-1; - assert(test_argc >= 5); + if(test_argc < 5) + return TEST_ERR_MAJOR_BAD; start_test_timing(); diff --git a/tests/libtest/lib583.c b/tests/libtest/lib583.c index 8f366003f483..934c54b1cdd0 100644 --- a/tests/libtest/lib583.c +++ b/tests/libtest/lib583.c @@ -36,7 +36,8 @@ static CURLcode test_lib583(const char *URL) CURLcode result = CURLE_OK; CURLMcode mresult; - assert(test_argc >= 4); + if(test_argc < 4) + return TEST_ERR_MAJOR_BAD; global_init(CURL_GLOBAL_ALL); From a833c83a872f00396d017cc83e368bdaf44fcfbd Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 10 Aug 2026 12:08:52 +0200 Subject: [PATCH 0849/1062] build: minor debug option message fixes/improvements - CMAKE_INSTALL.md: sync up option desc with `CMakeLists.txt`. - sync `--enable-debug` configure text with help description. - say `--disable-debug` is the default in help description. Follow-up to a15cf1ea07963ab9d92803e23a96ac5541c6ba0f #22512 Closes #22532 --- docs/INSTALL-CMAKE.md | 2 +- m4/curl-confopts.m4 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 664df7361322..2d1390373259 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -221,7 +221,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_TARGET_WINDOWS_VERSION`: Minimum target Windows version as hex string, e.g. `0x0a00` for Windows 10. - `CURL_WERROR`: Turn compiler warnings into errors. Default: `OFF` - `ENABLE_CURL_MANUAL`: Build the man page for curl and enable its `-M`/`--manual` option. Default: `ON` -- `ENABLE_DEBUG`: Enable curl debug features (for developing curl itself). Default: `OFF` +- `ENABLE_DEBUG`: Enable curl debug features (for developing curl). Default: `OFF` This also requires Debug configuration enabled/selected at the same time, via `CMAKE_BUILD_TYPE=Debug`, or `--config Debug` for multi-config generators. - `IMPORT_LIB_SUFFIX`: Import library suffix. Default: `_imp` for MSVC-like toolchains, otherwise empty. diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index 20e5ab372262..133ee219cf32 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -110,11 +110,11 @@ dnl variable want_debug value as appropriate. AC_DEFUN([CURL_CHECK_OPTION_DEBUG], [ AC_BEFORE([$0],[CURL_CHECK_OPTION_WARNINGS]) AC_BEFORE([$0],[XC_CHECK_PROG_CC]) - AC_MSG_CHECKING([whether to enable debug build options]) + AC_MSG_CHECKING([whether to enable curl debug features (for developing curl)]) OPT_DEBUG_BUILD="default" AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[Enable curl debug features (for developing curl)]) -AS_HELP_STRING([--disable-debug],[Disable curl debug features]), +AS_HELP_STRING([--disable-debug],[Disable curl debug features (default)]), OPT_DEBUG_BUILD=$enableval) case "$OPT_DEBUG_BUILD" in no) From 43680a50cf289778cba1f20f752ba6a9cb81fd66 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 10 Aug 2026 19:26:15 +0200 Subject: [PATCH 0850/1062] appveyor: sync Debug options in two jobs Also drop `!DEBUGBUILD` from job names, it's redundant now. Follow-up to 5050196dcc74b8369bcfad35d3b0926d0ff96180 #22513 Closes #22535 --- appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9335da3892c0..a08285efc523 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -39,9 +39,9 @@ environment: - job_name: 'CM VS2022, Release, x64, OpenSSL 3.5, Shared, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' CMAKE_GENERATOR: 'Visual Studio 17 2022' - CMAKE_GENERATE: '-A x64 -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON' + CMAKE_GENERATE: '-A x64 -DENABLE_DEBUG=OFF -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON' - - job_name: 'CM VS2026, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' + - job_name: 'CM VS2026, Release, arm64, Schannel, Static, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' CMAKE_VERSION: 4.2.1 CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 @@ -60,7 +60,7 @@ environment: CMAKE_VERSION: 3.21.7 CMAKE_SHA256: 4c4840e2dc2bf82e8a16081ff506bba54f3a228b91ce36317129fed4035ef2e3 CMAKE_GENERATOR: 'Visual Studio 11 2012' - CMAKE_GENERATE: '-A Win32' + CMAKE_GENERATE: '-A Win32 -DENABLE_DEBUG=OFF' - job_name: 'CM VS2013, Debug, x64, Schannel, Shared' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' @@ -95,7 +95,7 @@ environment: CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A x64 -T ClangCl -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=ON' - - job_name: 'CM VS2022, Release, x64, Schannel, Shared, Unicode, !DEBUGBUILD, Build-tests' + - job_name: 'CM VS2022, Release, x64, Schannel, Shared, Unicode, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' CMAKE_GENERATOR: 'Visual Studio 17 2022' ENABLE_UNICODE: 'ON' From 1cf6be5a0b4a1db8c501dcda8ac98e09970fd372 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 10 Aug 2026 19:09:17 +0200 Subject: [PATCH 0851/1062] GHA/linux: replace raw C option with `CMAKE_BUILD_TYPE=Debug` in tsan job Ref: #22533 Follow-up to 2a46df31fdb91851895bc46d81f0065e6cafc80b #18274 Closes #22534 --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d3aca8d4dcb8..20b0ab78a225 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -414,9 +414,9 @@ jobs: install_packages: clang-22 libtsan2 install_steps: pytest openssl-tsan CC: clang-22 - CFLAGS: -fsanitize=thread -g + CFLAGS: -fsanitize=thread LDFLAGS: -fsanitize=thread - generate: -DOPENSSL_ROOT_DIR=/home/runner/openssl -DUSE_ECH=ON -DENABLE_DEBUG=ON + generate: -DCMAKE_BUILD_TYPE=Debug -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/home/runner/openssl -DUSE_ECH=ON - name: 'memory-sanitizer' image: ubuntu-26.04 From 874a54de5dcb5b68e22e896787d8b9bd9cda8aaa Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 3 Aug 2026 16:12:03 +0200 Subject: [PATCH 0852/1062] multi: use index list for expire timeouts Replace the `timeoutlist` and time nodes for expire timers with `struct expire_timers` that keeps a time and next array. There is only ever one timeout per timer id and a fully dynamic list is not needed. Do the sorting by keeping an array of 'next' indiced and a 'first' starting point. Added tracking of splay tree status. Fixed missing splay removal when last timeout was removed via Curl_expire_done(). Closes #22473 --- lib/curl_trc.c | 15 ++- lib/multi.c | 266 ++++++++++++++++++++++++++++--------------------- lib/multiif.h | 2 +- lib/urldata.h | 18 ++-- 4 files changed, 165 insertions(+), 136 deletions(-) diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 44eeef88f412..4e4204a07b41 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -323,7 +323,7 @@ static const char *trc_timer_name(int tid) { if((tid >= 0) && ((size_t)tid < CURL_ARRAYSIZE(Curl_trc_timer_names))) return Curl_trc_timer_names[(size_t)tid]; - return "UNKNOWN?"; + return "TIMER-???"; } void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...) @@ -341,14 +341,13 @@ void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...) void Curl_trc_easy_timers(struct Curl_easy *data) { if(CURL_TRC_TIMER_is_verbose(data)) { - struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); - if(e) { + if(data->state.timeouts.first < EXPIRE_LAST) { + struct expire_timers *timeouts = &data->state.timeouts; const struct curltime *pnow = Curl_pgrs_now(data); - while(e) { - struct time_node *n = Curl_node_elem(e); - e = Curl_node_next(e); - CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "us", - curlx_ptimediff_us(&n->time, pnow)); + expire_id eid = data->state.timeouts.first; + for(; eid < EXPIRE_LAST; eid = timeouts->next[eid]) { + CURL_TRC_TIMER(data, eid, "expires in %" FMT_TIMEDIFF_T "us", + curlx_ptimediff_us(&timeouts->time[eid], pnow)); } } } diff --git a/lib/multi.c b/lib/multi.c index 72a6524f8faa..500885668af4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -74,7 +74,7 @@ static void move_pending_to_connect(struct Curl_multi *multi, struct Curl_easy *data); static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_multi *multi, - struct Curl_easy *d); + struct Curl_easy *data); static void multi_timeout(struct Curl_multi *multi, struct curltime *expire_time, long *timeout_ms); @@ -220,6 +220,8 @@ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) Curl_llist_append(&multi->msglist, msg, &msg->list); } +static void multi_timeouts_init(struct Curl_easy *data); + struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, size_t ev_hashsize, /* event hash */ size_t chashsize, /* connection hash */ @@ -271,7 +273,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, /* Initialize admin handle to operate inside this multi */ multi->admin->multi = multi; multi->admin->state.internal = TRUE; - Curl_llist_init(&multi->admin->state.timeoutlist, NULL); + multi_timeouts_init(multi->admin); #ifdef DEBUGBUILD if(getenv("CURL_DEBUG")) @@ -494,8 +496,8 @@ CURLMcode Curl_multi_add_handle(struct Curl_multi *multi, if(multi_xfers_add(multi, data)) return CURLM_OUT_OF_MEMORY; - /* Initialize timeout list for this handle */ - Curl_llist_init(&data->state.timeoutlist, NULL); + /* Initialize timeouts for this handle */ + multi_timeouts_init(data); /* * No failure allowed in this function beyond this point. No modification @@ -795,8 +797,8 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, (void)multi_done(data, data->result, premature); } - /* The timer must be shut down before data->multi is set to NULL, else the - timenode will remain in the splay tree after curl_easy_cleanup is + /* The timer must be shut down before data->multi is set to NULL, else + data's splaynode would remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ Curl_expire_clear(data); @@ -1113,6 +1115,17 @@ static CURLcode mstate_perform_pollset(struct Curl_easy *data, return result; } +#ifdef CURLVERBOSE +static size_t multi_timeouts_count(struct expire_timers *timeouts) +{ + size_t n = 0; + expire_id eid = timeouts->first; + for(; eid < EXPIRE_LAST; eid = timeouts->next[eid]) + ++n; + return n; +} +#endif + /* Initializes `poll_set` with the current socket poll actions needed * for transfer `data`. */ CURLMcode Curl_multi_pollset(struct Curl_easy *data, @@ -1193,7 +1206,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, #ifdef CURLVERBOSE if(CURL_TRC_M_is_verbose(data)) { - size_t timeout_count = Curl_llist_count(&data->state.timeoutlist); + size_t timeout_count = multi_timeouts_count(&data->state.timeouts); switch(ps->n) { case 0: CURL_TRC_M(data, "pollset[], timeouts=%zu, paused %d/%d (r/w)", @@ -2915,6 +2928,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi, if(t) { /* the removed may have another timeout in queue */ struct Curl_easy *data = Curl_splayget(t); + data->state.timeouts.registered = FALSE; (void)add_next_timeout(&start, multi, data); if(data->mstate == MSTATE_PENDING) { bool stream_unused; @@ -3105,6 +3119,12 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) } } +static void multi_timeouts_init(struct Curl_easy *data) +{ + data->state.timeouts.first = EXPIRE_LAST; + data->state.timeouts.registered = FALSE; +} + /* * add_next_timeout() * @@ -3119,43 +3139,26 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) */ static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_multi *multi, - struct Curl_easy *d) + struct Curl_easy *data) { - struct curltime *tv = &d->state.expiretime; - struct Curl_llist *list = &d->state.timeoutlist; - struct Curl_llist_node *e; - - /* move over the timeout list for this specific handle and remove all - timeouts that are now passed tense and store the next pending - timeout in *tv */ - for(e = Curl_llist_head(list); e;) { - struct Curl_llist_node *n = Curl_node_next(e); - struct time_node *node = Curl_node_elem(e); - timediff_t diff = curlx_ptimediff_us(&node->time, pnow); - if(diff <= 0) - /* remove outdated entry */ - Curl_node_remove(e); - else - /* the list is sorted so get out on the first mismatch */ + struct expire_timers *timeouts = &data->state.timeouts; + + DEBUGASSERT(!timeouts->registered); + while(timeouts->first < EXPIRE_LAST) { + timediff_t us = curlx_ptimediff_us(&timeouts->time[timeouts->first], pnow); + if(us <= 0) /* remove already expired timer */ + timeouts->first = timeouts->next[timeouts->first]; + else /* timeouts are sorted, first is first in the future now */ break; - e = n; } - e = Curl_llist_head(list); - if(!e) { - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; - } - else { - struct time_node *node = Curl_node_elem(e); - /* copy the first entry to 'tv' */ - memcpy(tv, &node->time, sizeof(*tv)); + if(timeouts->first < EXPIRE_LAST) { /* Insert this node again into the splay. Keep the timer in the list in case we need to recompute future timers. */ - multi->timetree = Curl_splayinsert(tv, multi->timetree, - &d->state.timenode); + Curl_splayset(&timeouts->splaynode, data); + multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], + multi->timetree, &timeouts->splaynode); + timeouts->registered = TRUE; } return CURLM_OK; } @@ -3181,12 +3184,11 @@ static void multi_mark_expired_as_dirty(struct Curl_multi *multi, data = Curl_splayget(t); /* assign this for next loop */ if(!data) continue; + data->state.timeouts.registered = FALSE; #ifdef CURLVERBOSE if(CURL_TRC_TIMER_is_verbose(data)) { - struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); - if(e) { - struct time_node *n = Curl_node_elem(e); - CURL_TRC_TIMER(data, n->eid, "has expired"); + if(data->state.timeouts.first < EXPIRE_LAST) { + CURL_TRC_TIMER(data, data->state.timeouts.first, "has expired"); } } #endif @@ -3553,13 +3555,10 @@ static void multi_timeout(struct Curl_multi *multi, } #ifdef CURLVERBOSE - if(CURL_TRC_TIMER_is_verbose(data)) { - struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); - if(e) { - struct time_node *n = Curl_node_elem(e); - CURL_TRC_TIMER(data, n->eid, "gives multi timeout in %ldms", - *timeout_ms); - } + if(CURL_TRC_TIMER_is_verbose(data) && + (data->state.timeouts.first < EXPIRE_LAST)) { + CURL_TRC_TIMER(data, data->state.timeouts.first, + "gives multi timeout in %ldms", *timeout_ms); } #endif } @@ -3638,6 +3637,33 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) return CURLM_OK; } +#ifdef DEBUGBUILD +static bool multi_timeouts_check(struct Curl_easy *data) +{ + struct expire_timers *timeouts = &data->state.timeouts; + expire_id eid; + int i = 0; + for(eid = timeouts->first; eid < EXPIRE_LAST; eid = timeouts->next[eid]) { + if(++i >= EXPIRE_LAST) { + failf(data, "expire timeouts looped: %d iterations and no end", i); + return FALSE; + } + if(eid == timeouts->next[eid]) { + failf(data, "expire timeouts wrong: %d points to itself", (int)eid); + return FALSE; + } + if((timeouts->next[eid] < EXPIRE_LAST) && + (curlx_ptimediff_ms(&timeouts->time[eid], + &timeouts->time[timeouts->next[eid]]) > 0)) { + failf(data, "expire timeouts not sorted: %d happens after %d but " + "is listed before", (int)eid, (int)timeouts->next[eid]); + return FALSE; + } + } + return TRUE; +} +#endif + /* * multi_deltimeout() * @@ -3645,15 +3671,40 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) */ static void multi_deltimeout(struct Curl_easy *data, expire_id eid) { - struct Curl_llist_node *e; - struct Curl_llist *timeoutlist = &data->state.timeoutlist; - /* find and remove the specific node from the list */ - for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) { - struct time_node *n = Curl_node_elem(e); - if(n->eid == eid) { - Curl_node_remove(e); + struct expire_timers *timeouts = &data->state.timeouts; + expire_id orig_first = timeouts->first; + expire_id *anchor = &timeouts->first; + + while(*anchor < EXPIRE_LAST) { + if(*anchor == eid) { + *anchor = timeouts->next[eid]; + break; + } + anchor = &timeouts->next[*anchor]; + } + DEBUGASSERT(multi_timeouts_check(data)); + if(timeouts->registered) { + struct Curl_multi *multi = data->multi; + int rc; + + if(!multi) { + DEBUGASSERT(0); return; } + if((timeouts->first >= EXPIRE_LAST) || /* no more timeouts */ + (timeouts->first != orig_first)) { /* active timeout changed */ + rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, + &multi->timetree); + if(rc) + infof(data, "Internal error removing splay node = %d", rc); + timeouts->registered = FALSE; + } + if((timeouts->first < EXPIRE_LAST) && !timeouts->registered) { + multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], + multi->timetree, + &timeouts->splaynode); + timeouts->registered = TRUE; + } } } @@ -3668,51 +3719,44 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data, struct curltime *stamp, expire_id eid) { - struct Curl_llist_node *e; - struct time_node *node; - struct Curl_llist_node *prev = NULL; - size_t n; - struct Curl_llist *timeoutlist = &data->state.timeoutlist; - - node = &data->state.expires[eid]; - - /* copy the timestamp and id */ - memcpy(&node->time, stamp, sizeof(*stamp)); - node->eid = eid; /* also marks it as in use */ - - n = Curl_llist_count(timeoutlist); - if(n) { - /* find the correct spot in the list */ - for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) { - struct time_node *check = Curl_node_elem(e); - timediff_t diff = curlx_ptimediff_ms(&check->time, &node->time); - if(diff > 0) - break; - prev = e; - } - } - /* else - this is the first timeout on the list */ + struct expire_timers *timeouts = &data->state.timeouts; + expire_id *anchor = &timeouts->first; - Curl_llist_insert_next(timeoutlist, prev, node, &node->list); + if(eid >= EXPIRE_LAST) { + DEBUGASSERT(0); + return CURLM_BAD_FUNCTION_ARGUMENT; + } + /* remove from list, store time and re-insert */ + multi_deltimeout(data, eid); + memcpy(&timeouts->time[eid], stamp, sizeof(*stamp)); + while(*anchor < EXPIRE_LAST) { + timediff_t ms = curlx_ptimediff_ms(&timeouts->time[*anchor], stamp); + if(ms > 0) /* *anchor's time is after eid's time */ + break; + anchor = &timeouts->next[*anchor]; + } + timeouts->next[eid] = *anchor; + timeouts->next[eid] = *anchor; + *anchor = eid; + DEBUGASSERT(multi_timeouts_check(data)); CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "us", - curlx_ptimediff_us(&node->time, Curl_pgrs_now(data))); + curlx_ptimediff_us(stamp, Curl_pgrs_now(data))); return CURLM_OK; } void Curl_expire_ex(struct Curl_easy *data, - timediff_t milli, expire_id id) + timediff_t milli, expire_id eid) { struct Curl_multi *multi = data->multi; - struct curltime *curr_expire = &data->state.expiretime; + struct expire_timers *timeouts = &data->state.timeouts; + expire_id prev_id = timeouts->first; struct curltime set; /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) return; - - DEBUGASSERT(id < EXPIRE_LAST); + DEBUGASSERT(eid < EXPIRE_LAST); set = *Curl_pgrs_now(data); set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */ @@ -3723,29 +3767,22 @@ void Curl_expire_ex(struct Curl_easy *data, set.tv_usec -= 1000000; } - /* Remove any timer with the same id */ - multi_deltimeout(data, id); - - /* Add it to the timer list. It must stay in the list until it has expired - in case we need to recompute the minimum timer later. */ - multi_addtimeout(data, &set, id); + /* Add the timeout, will replace any previous value for this timer. */ + multi_addtimeout(data, &set, eid); + DEBUGASSERT(timeouts->first < EXPIRE_LAST); - if(curr_expire->tv_sec || curr_expire->tv_usec) { - /* This means that the struct is added as a node in the splay tree. - Compare if the new time is earlier, and only remove-old/add-new if it - is. */ - timediff_t diff = curlx_ptimediff_ms(&set, curr_expire); + if(timeouts->registered) { int rc; - - if(diff > 0) { - /* The current splay tree entry is sooner than this new expiry time. - We do not need to update our splay tree entry. */ + /* timeouts->splaynode is in splay tree already. If the first timer + * was NOT the one we just set AND is still the first one, + * nothing changed from the splay tree's point of view. The + * set timer triggers after the one already in the tree. Leave. */ + if((prev_id != eid) && (prev_id == timeouts->first)) return; - } /* Since this is an updated time, we must remove the previous entry from the splay tree first and then re-add the new value */ - rc = Curl_splayremove(multi->timetree, &data->state.timenode, + rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, &multi->timetree); if(rc) infof(data, "Internal error removing splay node = %d", rc); @@ -3753,10 +3790,10 @@ void Curl_expire_ex(struct Curl_easy *data, /* Indicate that we are in the splay tree and insert the new timer expiry value since it is our local minimum. */ - *curr_expire = set; - Curl_splayset(&data->state.timenode, data); - multi->timetree = Curl_splayinsert(curr_expire, multi->timetree, - &data->state.timenode); + Curl_splayset(&timeouts->splaynode, data); + multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], + multi->timetree, &timeouts->splaynode); + timeouts->registered = TRUE; } /* @@ -3796,31 +3833,28 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id) void Curl_expire_clear(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; - struct curltime *nowp = &data->state.expiretime; + struct expire_timers *timeouts = &data->state.timeouts; /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) return; - if(nowp->tv_sec || nowp->tv_usec) { + if(timeouts->registered) { /* Since this is an cleared time, we must remove the previous entry from the splay tree */ - struct Curl_llist *list = &data->state.timeoutlist; int rc; - rc = Curl_splayremove(multi->timetree, &data->state.timenode, + rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, &multi->timetree); if(rc) infof(data, "Internal error clearing splay node = %d", rc); - /* clear the timeout list too */ - Curl_llist_destroy(list, NULL); + /* clear the timeouts */ + multi_timeouts_init(data); if(data->id >= 0) CURL_TRC_M(data, "[TIMEOUT] all cleared"); - nowp->tv_sec = 0; - nowp->tv_usec = 0; } } diff --git a/lib/multiif.h b/lib/multiif.h index aaac33e94267..45a2e7d3159e 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -29,7 +29,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id); void Curl_expire_ex(struct Curl_easy *data, - timediff_t milli, expire_id id); + timediff_t milli, expire_id eid); void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; diff --git a/lib/urldata.h b/lib/urldata.h index 4108f287d705..231a698517ea 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -521,13 +521,12 @@ typedef enum { EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; -/* - * One instance for each timeout an easy handle can set. - */ -struct time_node { - struct Curl_llist_node list; - struct curltime time; - expire_id eid; +struct expire_timers { + struct Curl_tree splaynode; /* for the splay stuff */ + struct curltime time[EXPIRE_LAST]; + expire_id next[EXPIRE_LAST]; + expire_id first; + BIT(registered); /* timeout node is registered in splay tree */ }; /* individual pieces of the URL */ @@ -589,10 +588,7 @@ struct UrlState { BIT(provider_loaded); #endif /* USE_OPENSSL */ - struct curltime expiretime; /* set this with Curl_expire() only */ - struct Curl_tree timenode; /* for the splay stuff */ - struct Curl_llist timeoutlist; /* list of pending timeouts */ - struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */ + struct expire_timers timeouts; /* expire timeouts */ /* a place to store the most recently set (S)FTP entrypath */ char *most_recent_ftp_entrypath; From a61f2f9219bc121eea41a870e181e93a79623743 Mon Sep 17 00:00:00 2001 From: anupamme Date: Mon, 10 Aug 2026 00:31:24 +0000 Subject: [PATCH 0853/1062] ldap: reject control characters in URL-decoded filter values Closes #22524 --- lib/ldap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ldap.c b/lib/ldap.c index d7e871863d77..350bd1737746 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -840,7 +840,7 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, LDAP_TRACE(("filter '%s'\n", filter)); /* Unescape the filter */ - result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO); + result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_CTRL); if(result) { rc = LDAP_NO_MEMORY; From 419f6404d46d8f5e404988149a0881d015113bb9 Mon Sep 17 00:00:00 2001 From: hunterinvariants <217359725+hunterinvariants@users.noreply.github.com> Date: Tue, 11 Aug 2026 06:43:06 +0200 Subject: [PATCH 0854/1062] schannel: reuse the send buffer - Reuse a buffer for schannel_send instead of allocating and freeing a buffer for every send. Closes https://github.com/curl/curl/pull/22540 --- lib/vtls/schannel.c | 19 +++++++++++++------ lib/vtls/schannel_int.h | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 88d7ccec88f0..aca109bfdc52 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1996,13 +1996,18 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, len = backend->stream_sizes.cbMaximumMessage; } - /* calculate the complete message length and allocate a buffer for it */ + /* calculate the complete message length and prepare the send buffer */ data_len = backend->stream_sizes.cbHeader + len + backend->stream_sizes.cbTrailer; - ptr = curlx_malloc(data_len); - if(!ptr) { - return CURLE_OUT_OF_MEMORY; + if(data_len > backend->send_buffer_len) { + ptr = curlx_realloc(backend->send_buffer, data_len); + if(!ptr) + return CURLE_OUT_OF_MEMORY; + backend->send_buffer = ptr; + backend->send_buffer_len = data_len; } + else + ptr = backend->send_buffer; /* setup output buffers (header, data, trailer, empty) */ InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, @@ -2091,8 +2096,6 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = CURLE_SEND_ERROR; } - curlx_safefree(ptr); - if(len == *pnwritten) /* Encrypted message including header, data and trailer entirely sent. The return value is the number of unencrypted bytes that were sent. */ @@ -2567,6 +2570,10 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) backend->cred = NULL; } + /* free the buffer used to encrypt outgoing data */ + curlx_safefree(backend->send_buffer); + backend->send_buffer_len = 0; + /* free internal buffer for received encrypted data */ if(backend->encdata.buffer) { curlx_safefree(backend->encdata.buffer); diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index 242f6a0f4496..ccbb63d7e20c 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -112,6 +112,8 @@ struct sbuffer { }; struct schannel_ssl_backend_data { + unsigned char *send_buffer; + size_t send_buffer_len; struct sbuffer encdata; struct sbuffer decdata; struct Curl_schannel_cred *cred; From 6c7993d4d9f8722a869207f86a31f20fa9e8780d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 10 Aug 2026 22:49:53 +0200 Subject: [PATCH 0855/1062] build: strip trailing spaces from `libcurl.pc` Fixing: ``` $ pccritic libcurl.pc [...] [info ] file has trailing whitespace on one or more lines (style/PC062) ``` Closes #22536 --- CMakeLists.txt | 4 ++++ configure.ac | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea82301d9d44..a32b657ef8b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2362,6 +2362,10 @@ if(NOT CURL_DISABLE_INSTALL) configure_file( "${PROJECT_SOURCE_DIR}/libcurl.pc.in" "${PROJECT_BINARY_DIR}/libcurl.pc" @ONLY) + # Strip trailing spaces + file(READ "${PROJECT_BINARY_DIR}/libcurl.pc" _libcurl_pc) + string(REGEX REPLACE " +\n" "\n" _libcurl_pc "${_libcurl_pc}") + file(WRITE "${PROJECT_BINARY_DIR}/libcurl.pc" "${_libcurl_pc}") install(FILES "${PROJECT_BINARY_DIR}/libcurl.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/configure.ac b/configure.ac index d894dd48d06a..bf40500b9919 100644 --- a/configure.ac +++ b/configure.ac @@ -5555,10 +5555,13 @@ AC_CONFIG_FILES([\ tests/http/config.ini \ tests/http/Makefile \ projects/Makefile \ - projects/vms/Makefile \ - libcurl.pc + projects/vms/Makefile ]) AC_CONFIG_FILES([curl-config], [chmod +x curl-config]) +AC_CONFIG_FILES([libcurl.pc], [ + dnl strip trailing spaces + "$SED" 's/ *$//g' libcurl.pc > libcurl.pc.tmp && mv libcurl.pc.tmp libcurl.pc +], [SED="$SED"]) AC_OUTPUT SUPPORT_PROTOCOLS_LOWER=`echo "$SUPPORT_PROTOCOLS" | tr A-Z a-z` From 45a84dfe0658ccab05ff1a9eee3afc3206fe0374 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 11 Aug 2026 10:38:48 +0200 Subject: [PATCH 0856/1062] scorecard: report 'rss-max' In addition report the maximum of the sampple RSS values. Increase the sampling frequency for accuracy. Closes #22542 --- tests/http/scorecard.py | 2 +- tests/http/testenv/curl.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 08986ba1d628..27f062f71c06 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -217,7 +217,7 @@ def print_score_table(cls, score): if col_has_stats[idx]: if 'stats' in cell: s = f'[{cell["stats"]["cpu"]:>.1f}%' \ - f'/{Card.fmt_size(cell["stats"]["rss"])}]' + f'/{Card.fmt_size(cell["stats"]["rss-max"])}]' else: s = '' print(f' {s:<{statw}}', end='') diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 64b089ba819f..918c66092d34 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -56,6 +56,8 @@ def AverageStats(cls, profiles: List['RunProfile']): for key in cls.STAT_KEYS: vals = [s[key] for s in stats] avg[key] = mean(vals) if len(vals) else 0.0 + vals = [s['rss-max'] for s in [p.stats for p in profiles]] + avg['rss-max'] = mean(vals) if len(vals) else 0 return avg def __init__(self, pid: int, started_at: datetime, run_dir): @@ -98,6 +100,7 @@ def finish(self): self._stats = {} for key in self.STAT_KEYS: self._stats[key] = fmean([s[key] for s in self._samples], weights) + self._stats['rss-max'] = max([s['rss'] for s in self._samples]) else: self._stats = None self._psu = None @@ -1055,7 +1058,7 @@ def _run(self, args, intext='', with_stats: bool = False, p.kill() raise subprocess.TimeoutExpired(cmd=args, timeout=self._timeout) from e profile.sample() - ptimeout = 0.01 + ptimeout = 0.001 exitcode = p.returncode profile.finish() log.info(f'done: exit={exitcode}, profile={profile}') From 3b0cb22eb6246b0da2b944e713f801339443303e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 6 Aug 2026 14:07:15 +0200 Subject: [PATCH 0857/1062] servers: add option to build without window handler (Windows) curl already built like this in UWP mode; add a macro to allow building regular Win32 targets too, meaning without the hidden GUI window, window handler, and thus without support for shutting down `servers.exe` gracefully. To test the server in CI as a plain console app and remove one component/variable from the system, in an effort chasing flaky runs. The graceful option was used by `taskkill` (without `-f`) in runtests, and it has been disabled (in favor of always `-f` (forced) kill), in an attempt to avoid taskkill hanging. Those resolved, but general CI flakiness remained. We see `taskkill` invoked seldom per job, and in most of these cases it doesn't find the PID requested. It means the graceful option is not actually used since 2025-04, and rather rarely before that. Also just today, the callback and main loop received a few updates to avoid potentially unsafe calls and interaction with the ctrl handler. Not expecting anything breakthrough here. Also graceful shutdown is a useful feature to cleanup properly and to be in sync with other platforms. Also: GHA/windows: enable in Windows CI jobs. Ref: https://learn.microsoft.com/windows/console/registering-a-control-handler-function#listen-with-hidden-window-example Follow-up to 7db9947fcf5d3deebe98e7992ebedc9a2f624bf5 #22045 Follow-up to 9ea48811fed455d4a869eb100b2216f039f8677a #22487 Follow-up to b11e0026f71aa33b47d3b31f7c6a2aee4f929b12 #22495 Follow-up to 208b87744e8ce326e2569c2efd735da43ce74a0b #21039 Follow-up to f450f3801b6b9dff0ea280f5fb4bf28203f7b313 #19897 Follow-up to 2701ac6a4d16a62130dad05be1c484903b8545c7 #19421 Follow-up to 4e203f65a1e5e448daf1da2b8c3aaa33654dd080 #17054 Closes #22496 --- .github/workflows/windows.yml | 6 ++++-- tests/server/util.c | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 71921ea40028..b7fc7c4c9fa1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -368,6 +368,7 @@ jobs: MATRIX_TYPE: '${{ matrix.type }}' TFLAGS: '${{ matrix.tflags }}' run: | + CPPFLAGS+=' -DCURL_DEBUG_NO_WIN32_WND' if [ "${MATRIX_TEST}" = 'uwp' ]; then CPPFLAGS+=' -DWINSTORECOMPAT -DWINAPI_FAMILY=WINAPI_FAMILY_APP -D_WIN32_WINNT=0x0a00' if [[ "${MATRIX_ENV}" != 'clang'* ]]; then @@ -581,7 +582,7 @@ jobs: ver: '16.1.0' url: 'https://github.com/skeeto/w64devkit/releases/download/v2.8.0/w64devkit-x64-2.8.0.7z.exe' SHA256: 6252bf34fe2231a55ac7f03d482b36d2c7c58697990551bba508102cfb3f342e - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON' + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON -DCMAKE_C_FLAGS=-DCURL_DEBUG_NO_WIN32_WND' type: 'Debug' - name: 'schannel' # mingw-w64 10.0 sys: 'mingw64' @@ -590,7 +591,7 @@ jobs: ver: '9.5.0' url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/9.5.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-9.5.0-mingw-w64msvcrt-10.0.0-r1.7z' SHA256: 41637132ea7dc36a7f86a1961eaa334c380b5a3423d36aecb481cabcd006e3fe - config: '-DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCURL_DISABLE_VERBOSE_STRINGS=ON' + config: '-DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCURL_DISABLE_VERBOSE_STRINGS=ON -DCMAKE_C_FLAGS=-DCURL_DEBUG_NO_WIN32_WND' type: 'Release' tflags: 'skiprun' - name: 'schannel mbedtls U' # mingw-w64 6.0 @@ -1013,6 +1014,7 @@ jobs: cflags='' rcflags='' ldflags='' + cflags+=' -DCURL_DEBUG_NO_WIN32_WND' if [ "${MATRIX_PLAT}" = 'uwp' ]; then options+=' -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0' cflags+=' -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP' diff --git a/tests/server/util.c b/tests/server/util.c index c2e1d52f60bb..b2e4d4535f9e 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -33,6 +33,10 @@ #include #endif +#if defined(CURL_WINDOWS_UWP) && !defined(CURL_DEBUG_NO_WIN32_WND) +#define CURL_DEBUG_NO_WIN32_WND +#endif + void loghex(const unsigned char *buffer, ssize_t len) { char data[12000]; @@ -450,7 +454,7 @@ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) /* stay signal-safe */ return TRUE; } -#ifndef CURL_WINDOWS_UWP +#ifndef CURL_DEBUG_NO_WIN32_WND static DWORD thread_main_id = 0; static HANDLE thread_main_window = NULL; static HWND hidden_main_window = NULL; @@ -538,7 +542,7 @@ static DWORD WINAPI main_window_loop(void *lpParameter) hidden_main_window = NULL; return (DWORD)msg.wParam; } -#endif /* CURL_WINDOWS_UWP */ +#endif /* !CURL_DEBUG_NO_WIN32_WND */ #endif /* !_WIN32 */ void install_signal_handlers(bool keep_sigalrm) @@ -592,7 +596,7 @@ void install_signal_handlers(bool keep_sigalrm) if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE)) logmsg("cannot install CTRL event handler"); -#ifndef CURL_WINDOWS_UWP +#ifndef CURL_DEBUG_NO_WIN32_WND thread_main_window = CreateThread(NULL, 0, &main_window_loop, GetModuleHandle(NULL), 0, &thread_main_id); if(!thread_main_window || !thread_main_id) @@ -629,7 +633,7 @@ void restore_signal_handlers(bool keep_sigalrm) #endif #else /* _WIN32 */ (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); -#ifndef CURL_WINDOWS_UWP +#ifndef CURL_DEBUG_NO_WIN32_WND if(thread_main_window && thread_main_id) { if(PostThreadMessage(thread_main_id, WM_APP, 0, 0)) { if(WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE)) { From 2c22d3069aef507d6a6876a6d20616fe5e50c6a3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 11 Aug 2026 11:19:28 +0200 Subject: [PATCH 0858/1062] build: strip duplicate spaces after `Libs.private:` in `libcurl.pc` When `LIBCURL_PC_LDFLAGS_PRIVATE` is empty and `LIBCURL_PC_LIBS_PRIVATE` is not. To keep it tidy. Closes #22544 --- CMakeLists.txt | 3 ++- configure.ac | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a32b657ef8b6..d2e2951823d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2362,9 +2362,10 @@ if(NOT CURL_DISABLE_INSTALL) configure_file( "${PROJECT_SOURCE_DIR}/libcurl.pc.in" "${PROJECT_BINARY_DIR}/libcurl.pc" @ONLY) - # Strip trailing spaces + # Strip trailing spaces, and duplicate spaces after colon file(READ "${PROJECT_BINARY_DIR}/libcurl.pc" _libcurl_pc) string(REGEX REPLACE " +\n" "\n" _libcurl_pc "${_libcurl_pc}") + string(REGEX REPLACE "\nLibs\.private: +" "\nLibs.private: " _libcurl_pc "${_libcurl_pc}") file(WRITE "${PROJECT_BINARY_DIR}/libcurl.pc" "${_libcurl_pc}") install(FILES "${PROJECT_BINARY_DIR}/libcurl.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/configure.ac b/configure.ac index bf40500b9919..8e991dda54c6 100644 --- a/configure.ac +++ b/configure.ac @@ -5559,8 +5559,8 @@ AC_CONFIG_FILES([\ ]) AC_CONFIG_FILES([curl-config], [chmod +x curl-config]) AC_CONFIG_FILES([libcurl.pc], [ - dnl strip trailing spaces - "$SED" 's/ *$//g' libcurl.pc > libcurl.pc.tmp && mv libcurl.pc.tmp libcurl.pc + dnl strip trailing spaces, and duplicate spaces after colon + "$SED" -e 's/ *$//g' -e 's/^Libs\.private: */Libs.private: /g' libcurl.pc > libcurl.pc.tmp && mv libcurl.pc.tmp libcurl.pc ], [SED="$SED"]) AC_OUTPUT From 4d9ba9fef48e43903815e0c0e96f917eaf9f185c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 11 Aug 2026 11:03:46 +0200 Subject: [PATCH 0859/1062] libcurl.pc: add `License` tag Also to improve `pccritic` score. Idea-by: Dan Fandrich Follow-up to 3f1c033afe008123680306663cc01279e831ac2d #22519 Cherry-picked from #22543 Closes #22545 --- libcurl.pc.in | 1 + 1 file changed, 1 insertion(+) diff --git a/libcurl.pc.in b/libcurl.pc.in index 000d42745645..e13b64782893 100644 --- a/libcurl.pc.in +++ b/libcurl.pc.in @@ -32,6 +32,7 @@ supported_features="@SUPPORT_FEATURES@" Name: libcurl URL: https://curl.se/ Description: Library to transfer files with HTTP, FTP, etc. +License: curl Version: @CURLVERSION@ Source: https://curl.se/download/curl-@CURLVERSION@.tar.gz Requires: @LIBCURL_PC_REQUIRES@ From f5378b88a974e565f767f2a041972aa942a69c5d Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Tue, 11 Aug 2026 11:42:14 +0530 Subject: [PATCH 0860/1062] content_encoding: exact-match the identity transfer-coding token Verified by test 2119 Closes #22541 --- lib/content_encoding.c | 3 ++- tests/data/Makefile.am | 2 +- tests/data/test2119 | 54 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2119 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index e23850783619..88446cfa0c08 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -783,7 +783,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, * Exception is "chunked" transfer-encoding which always must happen */ if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) || (!is_transfer && data->set.http_ce_skip)) { - bool is_identity = curl_strnequal(name, "identity", 8); + bool is_identity = (namelen == 8) && + curl_strnequal(name, "identity", 8); /* not requested, ignore */ CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s", (int)namelen, name); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 43adb287416a..17ae5ca610ec 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -255,7 +255,7 @@ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ test2088 test2089 test2090 test2091 test2092 test2093 test2094 \ test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \ -test2118 \ +test2118 test2119 \ \ test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ test2208 \ diff --git a/tests/data/test2119 b/tests/data/test2119 new file mode 100644 index 000000000000..8f9c2ecaed25 --- /dev/null +++ b/tests/data/test2119 @@ -0,0 +1,54 @@ + + + + +HTTP +Transfer-Encoding + + + +# Server-side + + +HTTP/1.1 200 swsclose OK +Content-Length: 6 +Transfer-Encoding: identityx + +-foo- + + + +# Client-side + + +http + + +Reject unsolicited Transfer-Encoding token with an identity prefix + +# no --tr-encoding, so any transfer-coding other than chunked/identity is +# unsolicited and must be rejected + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -sS + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# CURLE_BAD_CONTENT_ENCODING is 61 + +61 + + +curl: (61) Unsolicited Transfer-Encoding (identityx) found + + + From b9254da6175360102176303ecf9dbb5519b0ffcf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 11 Aug 2026 16:32:07 +0200 Subject: [PATCH 0861/1062] build: clear `Require.private` for static-only builds in `libcurl.pc` Before this patch static-only libcurl builds stored the list of module dependencies in both `Require` and `Require.private`. The new `pccritic` tool flagged this as an issue. Having (or not) a duplicate in `Require.private` does not change the output created by `pkgconf` and `pkg-config`, as tested with both with and without `--static` option. Thus, fix by deleting the private copy. Fixing: ``` $ pccritic libcurl.pc bld/libcurl.pc score: 63/100 grade: D (0 critical, 0 major, 9 minor, 1 info) [minor ] 'libssh2' appears in both Requires and Requires.private (requires/PC051) [minor ] 'libidn2' appears in both Requires and Requires.private (requires/PC051) [minor ] 'openssl' appears in both Requires and Requires.private (requires/PC051) [...] ``` Ref: https://github.com/curl/curl/actions/runs/31495008715/job/93790403026#step:10:210 (macOS, CM clang OpenSSL torture) Ref: https://github.com/curl/curl/actions/runs/31495008825/job/93790433646#step:9:216 (mingw, AM ucrt-x86_64 wolfssl c-ares U) Follow-up to f057de5a1a950a90d1920021db152a4b695f1a8a #13911 Follow-up to 98e5904165859679cd78825bcccb52306ee3bb66 #5373 Cherry-picked from #22543 Closes #22548 --- CMakeLists.txt | 1 + configure.ac | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2e2951823d1..f85f44fe8381 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2301,6 +2301,7 @@ if(NOT CURL_DISABLE_INSTALL) set(LIBCURL_PC_REQUIRES "${LIBCURL_PC_REQUIRES_PRIVATE}") set(LIBCURL_PC_LIBS "${LIBCURL_PC_LIBS_PRIVATE}") set(LIBCURL_PC_CFLAGS "${LIBCURL_PC_CFLAGS_PRIVATE}") + set(LIBCURL_PC_REQUIRES_PRIVATE "") endif() if(BUILD_STATIC_LIBS) set(ENABLE_STATIC "yes") diff --git a/configure.ac b/configure.ac index 8e991dda54c6..96c515a31343 100644 --- a/configure.ac +++ b/configure.ac @@ -5209,6 +5209,7 @@ dnl Merge pkg-config private fields into public ones when static-only if test "$enable_shared" = "no"; then LIBCURL_PC_REQUIRES=$LIBCURL_PC_REQUIRES_PRIVATE LIBCURL_PC_LIBS=$LIBCURL_PC_LIBS_PRIVATE + LIBCURL_PC_REQUIRES_PRIVATE= else LIBCURL_PC_REQUIRES= LIBCURL_PC_LIBS= From cd238bb7addf5b1f174df47ccc61651a545c4876 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 10:36:20 +0200 Subject: [PATCH 0862/1062] tool_operate: only check for schannel if on windows It can never be true elsewhere. Reported-by: Max Dymond Closes #22552 --- src/tool_operate.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tool_operate.c b/src/tool_operate.c index 213f20b88ff9..25b7973d2c05 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2168,6 +2168,7 @@ static CURLcode serial_transfers(CURLSH *share) return result; } +#ifdef _WIN32 static CURLcode is_using_schannel(int *pusing) { CURLcode result = CURLE_OK; @@ -2195,6 +2196,14 @@ static CURLcode is_using_schannel(int *pusing) *pusing = using_schannel; return result; } +#else +/* on non-Windows it can never use Schannel */ +static CURLcode is_using_schannel(int *pusing) +{ + *pusing = 0; /* never */ + return CURLE_OK; +} +#endif /* Set the CA cert locations specified in the environment. For Windows if no * environment-specified filename is found then check for CA bundle default From acd9411b01d2fff0bb6aa7c82c1d44c280751d1e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 12 Aug 2026 11:02:20 +0200 Subject: [PATCH 0863/1062] configure: replace stray raw `grep` with `$GREP` Follow-up to 7cc2e8b349df28d55d5f40bfae323485df9f0cf2 Follow-up to bfaab6ed6bb3e8865cd8dd60375fbdc39203a862 Cherry-picked from #22543 Closes #22554 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 96c515a31343..c06f0c52a523 100644 --- a/configure.ac +++ b/configure.ac @@ -2814,7 +2814,7 @@ AS_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar *) AC_MSG_RESULT(yes) AC_MSG_CHECKING([if libraries can be versioned]) - GLD=`$LD --help < /dev/null 2>/dev/null | grep version-script` + GLD=`$LD --help < /dev/null 2>/dev/null | $GREP version-script` if test -z "$GLD"; then AC_MSG_RESULT(no) AC_MSG_WARN([You need an ld version supporting the --version-script option]) From 852f5d34da772de65d057897278aa1e75dd060d1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 12 Aug 2026 10:52:42 +0200 Subject: [PATCH 0864/1062] build: strip empty properties from `libcurl.pc` To keep it tidy and to support properties in the template that only appear in the generated `libcurl.pc` when filled. E.g. `Required.private` may remain empty after #22548. Also: - GHA/windows: dump `libcurl.pc` to log in every job - tidy up a pre-existing regex in the updated command. 2c22d3069aef507d6a6876a6d20616fe5e50c6a3 #22544 Ref: b9254da6175360102176303ecf9dbb5519b0ffcf #22548 Cherry-picked from #22543 Closes #22553 --- .github/workflows/windows.yml | 30 ++++++++++++++++++++++++++++++ CMakeLists.txt | 3 ++- configure.ac | 4 ++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b7fc7c4c9fa1..660479cdef1d 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -156,6 +156,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' timeout-minutes: 10 run: | @@ -419,6 +425,12 @@ jobs: grep -F '#define' bld/lib/curl_config.h | sort || true cat bld/cmake_install.cmake || true + - name: 'libcurl.pc, curl-config, cmake_install.cmake' + run: | + for f in libcurl.pc curl-config cmake_install.cmake; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' timeout-minutes: 10 run: | @@ -703,6 +715,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' timeout-minutes: 5 run: | @@ -844,6 +862,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -1108,6 +1132,12 @@ jobs: timeout-minutes: 5 run: cmake --build bld --config "${MATRIX_TYPE}" --parallel 5 + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'curl -V' timeout-minutes: 1 run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index f85f44fe8381..5805391bc3bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2363,10 +2363,11 @@ if(NOT CURL_DISABLE_INSTALL) configure_file( "${PROJECT_SOURCE_DIR}/libcurl.pc.in" "${PROJECT_BINARY_DIR}/libcurl.pc" @ONLY) - # Strip trailing spaces, and duplicate spaces after colon + # Strip trailing spaces, duplicate spaces after colon, empty properties file(READ "${PROJECT_BINARY_DIR}/libcurl.pc" _libcurl_pc) string(REGEX REPLACE " +\n" "\n" _libcurl_pc "${_libcurl_pc}") string(REGEX REPLACE "\nLibs\.private: +" "\nLibs.private: " _libcurl_pc "${_libcurl_pc}") + string(REGEX REPLACE "\n([A-Za-z.]+:\n)+" "\n" _libcurl_pc "${_libcurl_pc}") file(WRITE "${PROJECT_BINARY_DIR}/libcurl.pc" "${_libcurl_pc}") install(FILES "${PROJECT_BINARY_DIR}/libcurl.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/configure.ac b/configure.ac index c06f0c52a523..b0394ddecb49 100644 --- a/configure.ac +++ b/configure.ac @@ -5560,8 +5560,8 @@ AC_CONFIG_FILES([\ ]) AC_CONFIG_FILES([curl-config], [chmod +x curl-config]) AC_CONFIG_FILES([libcurl.pc], [ - dnl strip trailing spaces, and duplicate spaces after colon - "$SED" -e 's/ *$//g' -e 's/^Libs\.private: */Libs.private: /g' libcurl.pc > libcurl.pc.tmp && mv libcurl.pc.tmp libcurl.pc + dnl strip trailing spaces, duplicate spaces after colon, empty properties + "$SED" -e 's/ *$//g' -e 's/^Libs\.private: */Libs.private: /' -e '/^@<:@A-Za-z.@:>@*:$/d' libcurl.pc > libcurl.pc.tmp && mv libcurl.pc.tmp libcurl.pc ], [SED="$SED"]) AC_OUTPUT From 943ac04fc4d7d554d9064c7cd03fbe1830ff25d4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 11 Aug 2026 10:31:55 +0200 Subject: [PATCH 0865/1062] CI: dump `libcurl.pc` (and `curl-config`) to log, verify `.pc` with `pccritic` tool `pccritic` is available on the latest macOS runner revision (not every job runs get it yet), Cygwin, MSYS2/mingw-w64 (one job, others will follow on the next msys2/setup-msys2 Action bump). It will be automatically used on BSDs, once they start offering it. Missing from Linux jobs. Also: - GHA/windows: convert `libcurl.pc` to Unix newlines to bump score 96 to 100 in mingw-w64 jobs. CMake-specific fix pending in: #22556 - GHA: verify with minimum `pccritic` score 94 to 100 (depending on CI job). - build: fill `Maintainer:` `libcurl.pc` property when run curl CI. Issues remaining: macOS: ``` [info ] Cflags adds an include path already on the default search path (pkgconf strips it): -I/usr/local/include (cflags/PC031) [info ] Libs adds a library path already on the default search path (pkgconf strips it): -L/usr/local/lib (libs/PC040) [minor ] Libs.private contains an unexpected flag: /Applications/Xcode_26.6.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.5.sdk/System/Library/Frameworks/GSS.framework (libs/PC041) ``` Windows: ``` [minor ] file uses CRLF line endings (style/PC061) ``` Ref: #22556 Follow-up to b9254da6175360102176303ecf9dbb5519b0ffcf #22548 Follow-up to 852f5d34da772de65d057897278aa1e75dd060d1 #22553 Follow-up to 4d9ba9fef48e43903815e0c0e96f917eaf9f185c #22545 Follow-up to 6c7993d4d9f8722a869207f86a31f20fa9e8780d #22536 Follow-up to 3f1c033afe008123680306663cc01279e831ac2d #22519 Closes #22543 --- .github/workflows/configure-vs-cmake.yml | 10 ++++--- .github/workflows/http3-linux.yml | 6 +++++ .github/workflows/linux.yml | 6 +++++ .github/workflows/macos.yml | 30 +++++++++++++++++++++ .github/workflows/non-native.yml | 34 +++++++++++++++++++----- .github/workflows/windows.yml | 16 +++++++++-- CMakeLists.txt | 4 +++ appveyor.sh | 1 + configure.ac | 5 ++++ libcurl.pc.in | 1 + 10 files changed, 102 insertions(+), 11 deletions(-) diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 9f296c1c9f18..3c569301fb42 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -37,6 +37,7 @@ concurrency: permissions: {} env: + CURL_CI: github DO_NOT_TRACK: '1' HOMEBREW_NO_INSTALL_CLEANUP: '1' @@ -65,7 +66,7 @@ jobs: - name: 'cmake log' run: cat bld-cm/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true - - name: 'dump generated files' + - name: 'generated libcurl.pc files' run: | for f in libcurl.pc curl-config; do echo "::group::AM ${f}"; grep -v '^#' bld-am/"${f}" || true; echo '::endgroup::' @@ -127,12 +128,15 @@ jobs: - name: 'cmake log' run: cat bld-cm/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true - - name: 'dump generated files' + - name: 'generated libcurl.pc files' run: | for f in libcurl.pc curl-config; do echo "::group::AM ${f}"; grep -v '^#' bld-am/"${f}" || true; echo '::endgroup::' echo "::group::CM ${f}"; grep -v '^#' bld-cm/"${f}" || true; echo '::endgroup::' done + pccritic --version + pccritic --min-score 98 bld-am/libcurl.pc + pccritic --min-score 98 bld-cm/libcurl.pc - name: 'compare generated curl_config.h files' run: ./.github/scripts/cmp-config.pl bld-am/lib/curl_config.h bld-cm/lib/curl_config.h @@ -181,7 +185,7 @@ jobs: - name: 'cmake log' run: cat bld-cm/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true - - name: 'dump generated files' + - name: 'generated libcurl.pc files' run: | for f in libcurl.pc curl-config; do echo "::group::AM ${f}"; grep -v '^#' bld-am/"${f}" || true; echo '::endgroup::' diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index f08013950bcc..0f16d1c006a9 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -815,6 +815,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'test configs' run: grep -H -v '^#' bld/tests/config bld/tests/http/config.ini || true diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 20b0ab78a225..8a05b7105ef4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -886,6 +886,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'test configs' run: grep -H -v '^#' bld/tests/config bld/tests/http/config.ini || true diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index bd7ca4a2661f..838590a331e7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -181,6 +181,16 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + if command -v pccritic >/dev/null 2>&1; then + pccritic --version + pccritic --min-score 98 bld/libcurl.pc + fi + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -511,6 +521,16 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + if command -v pccritic >/dev/null 2>&1; then + pccritic --version + pccritic --min-score 96 bld/libcurl.pc + fi + - name: 'test configs' run: grep -H -v '^#' bld/tests/config bld/tests/http/config.ini || true @@ -774,6 +794,16 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + if command -v pccritic >/dev/null 2>&1; then + pccritic --version + pccritic --min-score 94 bld/libcurl.pc + fi + - name: 'build / ${{ matrix.build }}' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 1ab4a8804420..29663c726e9f 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -183,6 +183,16 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + if command -v pccritic >/dev/null 2>&1; then + pccritic --version + pccritic --min-score 98 bld/libcurl.pc + fi + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -315,6 +325,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -407,17 +423,17 @@ jobs: if: ${{ !cancelled() }} run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true - - name: 'dump config files' - run: | - for f in libcurl.pc curl-config; do - echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' - done - - name: 'curl_config.h' run: | echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -533,6 +549,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 660479cdef1d..c3ed0c42da24 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -105,7 +105,7 @@ jobs: # https://cygwin.com/cgi-bin2/package-grep.cgi packages: >- ${{ matrix.build == 'autotools' && 'autoconf automake libtool make' || 'cmake ninja' }} - gcc-core binutils perl + gcc-core binutils perl pkgconf openssh libpsl-devel zlib-devel @@ -161,6 +161,8 @@ jobs: for f in libcurl.pc curl-config; do echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' done + pccritic --version + pccritic --min-score 100 bld/libcurl.pc - name: 'build' timeout-minutes: 10 @@ -423,13 +425,17 @@ jobs: run: | echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true - cat bld/cmake_install.cmake || true - name: 'libcurl.pc, curl-config, cmake_install.cmake' run: | for f in libcurl.pc curl-config cmake_install.cmake; do echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' done + if command -v pccritic >/dev/null 2>&1; then + sed -i.bak 's/\x0d//g' bld/libcurl.pc # to suppress pccritic warning: style/PC061 + pccritic --version + pccritic --min-score 100 bld/libcurl.pc + fi - name: 'build' timeout-minutes: 10 @@ -1128,6 +1134,12 @@ jobs: echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'libcurl.pc, curl-config' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'build' timeout-minutes: 5 run: cmake --build bld --config "${MATRIX_TYPE}" --parallel 5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5805391bc3bd..6ca969539a90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2341,6 +2341,7 @@ if(NOT CURL_DISABLE_INSTALL) # Generate a pkg-config file matching this config. # Consumed variables: + # CURL_PACKAGE_MAINTAINER # CURLVERSION # exec_prefix # includedir @@ -2360,6 +2361,9 @@ if(NOT CURL_DISABLE_INSTALL) # https://manpages.debian.org/unstable/pkgconf/pkg-config.1.en.html # https://manpages.debian.org/unstable/pkg-config/pkg-config.1.en.html # https://www.msys2.org/docs/pkgconfig/ + if(NOT "$ENV{CURL_CI}" STREQUAL "") + set(CURL_PACKAGE_MAINTAINER "https://curl.se/") + endif() configure_file( "${PROJECT_SOURCE_DIR}/libcurl.pc.in" "${PROJECT_BINARY_DIR}/libcurl.pc" @ONLY) diff --git a/appveyor.sh b/appveyor.sh index 0a40dd281236..64e8b6d13182 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -89,6 +89,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then false fi echo 'curl_config.h'; grep -F '#define' _bld/lib/curl_config.h | sort || true + echo 'libcurl.pc'; grep -v '^#' '_bld/libcurl.pc' || true time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 [[ "${CMAKE_GENERATE:-}" != *'-DBUILD_SHARED_LIBS=OFF'* ]] && PATH="$(pwd)/_bld/lib/${PRJ_CFG}:$PATH" [[ "${CMAKE_GENERATE:-}" = *'-DCURL_USE_OPENSSL=ON'* ]] && { PATH="${openssl_root}:$PATH"; cp "${openssl_root}"/*.dll "_bld/src/${PRJ_CFG}"; } diff --git a/configure.ac b/configure.ac index b0394ddecb49..0c085a85e83f 100644 --- a/configure.ac +++ b/configure.ac @@ -5217,6 +5217,11 @@ fi AC_SUBST(LIBCURL_PC_REQUIRES) AC_SUBST(LIBCURL_PC_LIBS) +if test -n "$CURL_CI"; then + CURL_PACKAGE_MAINTAINER=https://curl.se/ + AC_SUBST(CURL_PACKAGE_MAINTAINER) +fi + rm "$compilersh" dnl diff --git a/libcurl.pc.in b/libcurl.pc.in index e13b64782893..66296f5e576f 100644 --- a/libcurl.pc.in +++ b/libcurl.pc.in @@ -33,6 +33,7 @@ Name: libcurl URL: https://curl.se/ Description: Library to transfer files with HTTP, FTP, etc. License: curl +Maintainer: @CURL_PACKAGE_MAINTAINER@ Version: @CURLVERSION@ Source: https://curl.se/download/curl-@CURLVERSION@.tar.gz Requires: @LIBCURL_PC_REQUIRES@ From da04aa96abc0b9b5700e1146edb613d8ceb1b6e0 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 30 Jul 2026 18:34:18 +0200 Subject: [PATCH 0866/1062] tidy-up: comments and formatting Closes #22558 --- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 5 +++-- lib/url.c | 2 -- lib/vauth/spnego_sspi.c | 2 +- tests/server/dnsd.c | 12 ++++++------ tests/server/util.c | 7 ++++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 838590a331e7..0033dc02908f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -37,7 +37,7 @@ permissions: {} # # - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) # - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) -# - 10.10 Yosemite (2014) - SYS_recvmsg_x, SYS_sendmsg_x (runtime) +# - 10.10 Yosemite (2014) - SYS_recvmsg_x, SYS_sendmsg_x (build-time) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) # - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime), GSS Framework diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c3ed0c42da24..b07d005978e6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1064,8 +1064,9 @@ jobs: # Officially this requires the vcvarsall.bat MS-DOS batch file (as of # VS2022). Since it integrates badly with CI steps and shell scripts, # reproduce the necessary build configuration manually, without envs. - # The option is likely called: IgnoreStandardErrorWarningFormat=true, - # but there seems to be no option to pass it via CMake or CLI. + # The option to disable the MSBuild heuristics is likely called: + # IgnoreStandardErrorWarningFormat=true, but there seems to be no + # facility to pass it via CMake or CLI. MSVC_EDITION='2022/Enterprise/vc/tools/msvc' [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]] && MSVC_EDITION='18/Enterprise/vc/tools/msvc' [[ "$(uname -s)" = *'ARM64'* ]] && MSVC_HOST='arm64' || MSVC_HOST='x64' # x86 diff --git a/lib/url.c b/lib/url.c index 8272a85e4e85..5998cd69bc7f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -199,7 +199,6 @@ static void up_free(struct Curl_easy *data) * * We ignore SIGPIPE when this is called from curl_easy_cleanup. */ - CURLcode Curl_close(struct Curl_easy **datap) { struct Curl_easy *data; @@ -457,7 +456,6 @@ static void easy_meta_freeentry(void *p) * function. * @return CURLcode */ - CURLcode Curl_open(struct Curl_easy **curl) { struct Curl_easy *data; diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index da9d7b023b95..3f6c93f0bf29 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -163,7 +163,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } /* Use the special name "!ntlm" to prevent NTLM from being used: - * https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa + * https://learn.microsoft.com/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa */ #ifdef UNICODE nego->identity.PackageList = diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 03bfef87dad2..62cbb8d9cfa7 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -788,11 +788,11 @@ static void queue_udp_clear(void) } /* this is an answer to a question */ -static struct udp_resp * -udp_resp_create(int query_id, - const struct sockaddr *addr, curl_socklen_t addrlen, - const unsigned char *qbuf, size_t qlen, - uint16_t qtype, uint16_t id) +static struct udp_resp *udp_resp_create(int query_id, + const struct sockaddr *addr, + curl_socklen_t addrlen, + const unsigned char *qbuf, size_t qlen, + uint16_t qtype, uint16_t id) { struct udp_resp *resp; timediff_t delay_ms = 0; @@ -803,7 +803,7 @@ udp_resp_create(int query_id, resp->query_id = query_id; /* on some platforms `curl_socklen_t` is an `int`. Casting might - * wrap this, but then it still has to fit our record size. */ + * wrap this, but then it still has to fit our record size. */ if((size_t)addrlen > sizeof(resp->addr)) { logmsg("[%d-UDP] unable to handle addrlen of %zu", query_id, (size_t)addrlen); diff --git a/tests/server/util.c b/tests/server/util.c index b2e4d4535f9e..c4ea55985599 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -326,7 +326,7 @@ void storerequest(const char *reqbuf, size_t totalsize, const char *filename) errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } -static bool initiate_exit(int signum) /* stay signal-safe */ +static bool initiate_exit(int signum) /* keep signal-safe */ { if(got_exit_signal == 0) { got_exit_signal = 1; @@ -371,7 +371,7 @@ static SIGHANDLER_T old_sigterm_handler = SIG_ERR; * the POSIX specification: * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 */ -static void exit_signal_handler(int signum) /* stay signal-safe */ +static void exit_signal_handler(int signum) /* keep signal-safe */ { int old_errno = errno; exit_msg = "exit_signal_handler(): triggered"; @@ -421,7 +421,7 @@ static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, int norestart) * application, such as one in UNIX, to become multi-threaded and cause * unexpected behavior. */ -static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) /* stay signal-safe */ +static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) /* keep signal-safe */ { static const char msgU[] = "ctrl_event_handler(): unhandled\n"; static const char msgH[] = "ctrl_event_handler(): handled\n"; @@ -1111,6 +1111,7 @@ int open_stream_sock(curl_socket_t *psock, uint16_t *pport) result = 1; goto out; } + switch(localaddr.sa.sa_family) { case AF_INET: port = ntohs(localaddr.sa4.sin_port); From eae88a7473475511220767b7f2ed792da82a79d6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 12:11:37 +0200 Subject: [PATCH 0867/1062] urlapi: avoid dedotdotify() if possible The dedotdotify() function that removes ./ and ../ sequences from paths juggles memory and is slow. Now needs_dedotdot() is called first to determine if the removal process is necessary and otherwise avoids doing it. Avoids unnecessary memory operations. Adjusted unit test 1395 accordingly because now a lot of input strings return NULL for "no change necessary". Suggested-by: Max Dymond Closes #22557 --- lib/urlapi.c | 29 ++++++++++++++++++--- tests/unit/unit1395.c | 60 +++++++++++++++++++++---------------------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 1e61fbcae57d..1b498e95d0b1 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -750,6 +750,29 @@ static bool is_dot(const char **str, size_t *clen) #define ISSLASH(x) ((x) == '/') +/* prescan the string to see if it needs work */ +static bool needs_dedotdot(const char *p, size_t pn) +{ + /* a single byte path cannot be cleaned up */ + if(pn < 2) + return FALSE; + while(pn) { + if(is_dot(&p, &pn)) { + /* "./" or dot before end of string */ + if(!pn || ISSLASH(*p)) + return TRUE; + /* "../" or ".." before end of string */ + else if(is_dot(&p, &pn) && (!pn || ISSLASH(*p))) + return TRUE; + } + else { + p++; + pn--; + } + } + return FALSE; +} + /* * dedotdotify() * @@ -761,7 +784,8 @@ static bool is_dot(const char **str, size_t *clen) * * RETURNS * - * Zero for success and 'out' set to an allocated dedotdotified string. + * Zero for success and 'out' set to an allocated string (or NULL if there's + * nothing to do). * * @unittest 1395 */ @@ -776,8 +800,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) size_t dlen = clen; *outp = NULL; - /* a single byte path cannot be cleaned up */ - if(clen < 2) + if(!needs_dedotdot(input, clen)) return 0; curlx_dyn_init(&out, clen + 1); diff --git a/tests/unit/unit1395.c b/tests/unit/unit1395.c index a28630397720..58945b59a2b1 100644 --- a/tests/unit/unit1395.c +++ b/tests/unit/unit1395.c @@ -39,33 +39,33 @@ static CURLcode test_unit1395(const char *arg) { "/%2f%2e%2e%2f/../a", "/a" }, { "/%2f%2e%2e%2f/../", "/" }, { "/%2f%2e%2e%2f/.", "/%2f%2e%2e%2f/" }, - { "/%2f%2e%2e%2f/", "/%2f%2e%2e%2f/" }, - { "/%2f%2e%2e%2f", "/%2f%2e%2e%2f" }, - { "/%2f%2e%2e%2", "/%2f%2e%2e%2" }, - { "/%2f%2e%2e%", "/%2f%2e%2e%" }, + { "/%2f%2e%2e%2f/", NULL }, + { "/%2f%2e%2e%2f", NULL }, + { "/%2f%2e%2e%2", NULL }, + { "/%2f%2e%2e%", NULL }, { "/%2f%2e%2e", "/%2f%2e%2e" }, - { "/%2f%2e%2", "/%2f%2e%2" }, - { "/%2f%2e%", "/%2f%2e%" }, + { "/%2f%2e%2", NULL }, + { "/%2f%2e%", NULL }, { "/%2f%2e", "/%2f%2e" }, - { "/%2f%2", "/%2f%2" }, - { "/%2f%", "/%2f%" }, - { "/%2f", "/%2f" }, - { "/%2", "/%2" }, + { "/%2f%2", NULL }, + { "/%2f%", NULL }, + { "/%2f", NULL }, + { "/%2", NULL }, { "%2f%2e%2e%2f/../a", "%2f%2e%2e%2f/a" }, { "%2f%2e%2e%2f/../", "%2f%2e%2e%2f/" }, { "%2f%2e%2e%2f/.", "%2f%2e%2e%2f/" }, - { "%2f%2e%2e%2f/", "%2f%2e%2e%2f/" }, - { "%2f%2e%2e%2f", "%2f%2e%2e%2f" }, - { "%2f%2e%2e%2", "%2f%2e%2e%2" }, - { "%2f%2e%2e%", "%2f%2e%2e%" }, + { "%2f%2e%2e%2f/", NULL }, + { "%2f%2e%2e%2f", NULL }, + { "%2f%2e%2e%2", NULL }, + { "%2f%2e%2e%", NULL }, { "%2f%2e%2e", "%2f%2e%2e" }, - { "%2f%2e%2", "%2f%2e%2" }, - { "%2f%2e%", "%2f%2e%" }, + { "%2f%2e%2", NULL }, + { "%2f%2e%", NULL }, { "%2f%2e", "%2f%2e" }, - { "%2f%2", "%2f%2" }, - { "%2f%", "%2f%" }, - { "%2f", "%2f" }, - { "%2", "%2" }, + { "%2f%2", NULL }, + { "%2f%", NULL }, + { "%2f", NULL }, + { "%2", NULL }, { "%", NULL }, { "2", NULL }, { "e", NULL }, @@ -108,12 +108,12 @@ static CURLcode test_unit1395(const char *arg) { "/1/./%2e.", "/" }, { "/1/./../2", "/2" }, { "/hello/1/./../2", "/hello/2" }, - { "test/this", "test/this" }, + { "test/this", NULL }, { "test/this/../now", "test/now" }, { "/1../moo../foo", "/1../moo../foo" }, { "/../../moo", "/moo" }, { "/../../moo?", "/moo?" }, - { "/123?", "/123?" }, + { "/123?", NULL }, { "/", NULL }, { "", NULL }, { "/.../", "/.../" }, @@ -131,16 +131,16 @@ static CURLcode test_unit1395(const char *arg) { "/a/%2E%2e/b", "/b" }, { "/a/%2e./b", "/b" }, { "/a/.%2e/b", "/b" }, - { "/%2f..%2f", "/%2f..%2f" }, + { "/%2f..%2f", NULL }, { "/a/b/.", "/a/b/" }, { "/a/b/..", "/a/" }, - { "well-known", "well-known" }, - { ".well-known", ".well-known" }, - { "..well-known", "..well-known" }, - { "...well-known", "...well-known" }, - { "....well-known", "....well-known" }, - { "%2ewell-known", "%2ewell-known" }, - { "%2Ewell-known", "%2Ewell-known" }, + { "well-known", NULL }, + { ".well-known", NULL }, + { "..well-known", NULL }, + { "...well-known", NULL }, + { "....well-known", NULL }, + { "%2ewell-known", NULL }, + { "%2Ewell-known", NULL }, { "../.well-known", ".well-known" }, }; From d064bc2adbf2aa0762c89ea4166cecd55693bb62 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 11:44:04 +0200 Subject: [PATCH 0868/1062] thrdqueue: drop name strdups from Curl_thrdq_create The only usage of the thread queue and pool is for "DNS" which is a static string. No point in strdup'ing it (twice). Future users are *likely* to also used fixed strings, otherwise we revert this change when dynamic names are introduced. Reported-by: Max Dymond Closes #22555 --- lib/thrdpool.c | 9 ++++----- lib/thrdqueue.c | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/thrdpool.c b/lib/thrdpool.c index 51fabf04a665..900fd1cfc09d 100644 --- a/lib/thrdpool.c +++ b/lib/thrdpool.c @@ -50,7 +50,7 @@ struct thrdslot { }; struct curl_thrdpool { - char *name; + const char *name; uint64_t refcount; curl_mutex_t lock; curl_cond_t await; @@ -236,7 +236,6 @@ static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked) thrdpool_join_zombies(tpool); if(locked) Curl_mutex_release(&tpool->lock); - curlx_free(tpool->name); Curl_cond_destroy(&tpool->await); Curl_mutex_destroy(&tpool->lock); curlx_free(tpool); @@ -312,6 +311,7 @@ CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, { struct curl_thrdpool *tpool; CURLcode result = CURLE_OUT_OF_MEMORY; + DEBUGASSERT(name); tpool = curlx_calloc(1, sizeof(*tpool)); if(!tpool) @@ -327,9 +327,8 @@ CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, tpool->fn_return = fn_return; tpool->fn_user_data = user_data; - tpool->name = curlx_strdup(name); - if(!tpool->name) - goto out; + /* a const string that remains */ + tpool->name = name; #ifdef DEBUGBUILD { diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c index d1b5bcd646a2..b0094059052e 100644 --- a/lib/thrdqueue.c +++ b/lib/thrdqueue.c @@ -37,7 +37,7 @@ struct curl_thrdq { - char *name; + const char *name; curl_mutex_t lock; curl_cond_t await; struct Curl_llist sendq; @@ -187,7 +187,6 @@ static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join) Curl_llist_destroy(&tqueue->sendq, NULL); Curl_llist_destroy(&tqueue->recvq, NULL); - curlx_free(tqueue->name); Curl_cond_destroy(&tqueue->await); if(locked) Curl_mutex_release(&tqueue->lock); @@ -207,6 +206,7 @@ CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, { struct curl_thrdq *tqueue; CURLcode result = CURLE_OUT_OF_MEMORY; + DEBUGASSERT(name); tqueue = curlx_calloc(1, sizeof(*tqueue)); if(!tqueue) @@ -221,9 +221,8 @@ CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, tqueue->fn_event = fn_event; tqueue->fn_user_data = user_data; - tqueue->name = curlx_strdup(name); - if(!tqueue->name) - goto out; + /* a const string that remains */ + tqueue->name = name; result = Curl_thrdpool_create(&tqueue->tpool, name, min_threads, max_threads, idle_time_ms, From fbfb2499f03567611d2dc649bfadafe2b3341ef7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 14:04:48 +0200 Subject: [PATCH 0869/1062] keylog: add a random size argument to Curl_tls_keylog_write() To allow the function to verify that the buffer is large enough. Avoids possible future internal mishaps. Closes #22560 --- lib/vtls/gtls.c | 3 ++- lib/vtls/keylog.c | 15 ++++++++------- lib/vtls/keylog.h | 8 ++++---- lib/vtls/openssl.c | 1 + lib/vtls/rustls.c | 4 ++-- lib/vtls/wolfssl.c | 5 +++-- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 92607b1135cd..b49148982bb2 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1043,7 +1043,8 @@ static int keylog_callback(gnutls_session_t session, const char *label, return -1; } - Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + Curl_tls_keylog_write(label, crandom.data, crandom.size, + secret->data, secret->size); return 0; } diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 094bf69db728..ac9649a41f78 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -102,19 +102,20 @@ bool Curl_tls_keylog_write_line(const char *line) return TRUE; } -bool Curl_tls_keylog_write( - const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen) +bool Curl_tls_keylog_write(const char *label, + const unsigned char *client_random, + size_t random_size, + const unsigned char *secret, size_t secretlen) { size_t pos, i; unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + (2 * CLIENT_RANDOM_SIZE) + 1 + (2 * SECRET_MAXLEN) + 1 + 1]; - - if(!keylog_file_fp) { + DEBUGASSERT(random_size >= CLIENT_RANDOM_SIZE); + if(random_size < CLIENT_RANDOM_SIZE) + return FALSE; + if(!keylog_file_fp) return FALSE; - } pos = strlen(label); if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) { diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h index f95dfeae2d53..d489eb6360a1 100644 --- a/lib/vtls/keylog.h +++ b/lib/vtls/keylog.h @@ -61,10 +61,10 @@ const char *Curl_tls_keylog_file_name(void); * Appends a key log file entry. * Returns true iff the key log file is open and a valid entry was provided. */ -bool Curl_tls_keylog_write( - const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen); +bool Curl_tls_keylog_write(const char *label, + const unsigned char *client_random, + size_t random_size, + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF. diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 8c98d1541243..c25938c71601 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -700,6 +700,7 @@ static void ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) *keylog_done = TRUE; Curl_tls_keylog_write("CLIENT_RANDOM", client_random, + sizeof(client_random), master_key, master_key_length); } #endif /* !HAVE_KEYLOG_CALLBACK */ diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 6590adc40724..ef2ace26460a 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -517,11 +517,11 @@ static void cr_keylog_log_cb(struct rustls_str label, size_t secret_len) { char clabel[KEYLOG_LABEL_MAXLEN]; - (void)client_random_len; DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE); /* Turning a "rustls_str" into a null delimited "c" string */ curl_msnprintf(clabel, sizeof(clabel), "%.*s", (int)label.len, label.data); - Curl_tls_keylog_write(clabel, client_random, secret, secret_len); + Curl_tls_keylog_write(clabel, client_random, client_random_len, + secret, secret_len); } static CURLcode diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index d1642f77ccab..05d89ea1eac6 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -162,7 +162,8 @@ static int wssl_tls13_secret_callback(SSL *ssl, int id, return 0; } - Curl_tls_keylog_write(label, client_random, secret, secretSz); + Curl_tls_keylog_write(label, client_random, sizeof(client_random), + secret, secretSz); return 0; } #endif /* HAVE_SECRET_CALLBACK && WOLFSSL_TLS13 */ @@ -203,7 +204,7 @@ static void wssl_log_tls12_secret(WOLFSSL *ssl) return; } - Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen); + Curl_tls_keylog_write("CLIENT_RANDOM", cr, crLen, ms, msLen); } #endif /* OPENSSL_EXTRA */ From 35843dd635bab1627902b08a605e2a6334cc2fc7 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 11 Aug 2026 17:09:26 +0200 Subject: [PATCH 0870/1062] progress: cleanup, less memory Keep only a single `start` time and calculate the rest in delta microseconds since then. Separate timestamps and durations into separate sub-structures for clarity. remove progress.t_acceptdata It was recorded in FTP accept but never used anywhere. Closes #22547 --- lib/cf-ip-happy.c | 6 +-- lib/connect.c | 4 +- lib/ftp.c | 1 - lib/getinfo.c | 44 ++++++++--------- lib/multi.c | 16 +++--- lib/progress.c | 110 +++++++++++++++++++++++++----------------- lib/progress.h | 7 ++- lib/tftp.c | 2 - lib/transfer.c | 8 ++- lib/urldata.h | 42 ++++++++-------- lib/vdns/cf-dns.c | 3 +- tests/unit/unit1303.c | 8 +-- tests/unit/unit1399.c | 34 ++++++------- 13 files changed, 144 insertions(+), 141 deletions(-) diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index c9c65bacb717..6371e3ebe5b6 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -568,8 +568,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, next_expire_ms = Curl_timeleft_ms(data); if(next_expire_ms < 0) { failf(data, "Connection timeout after %" FMT_OFF_T " ms", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); + Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE)); return CURLE_OPERATION_TIMEDOUT; } @@ -741,8 +740,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf, proxy_peer ? "over proxy " : "", proxy_peer ? proxy_peer->hostname : "", proxy_peer ? " " : "", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle), + Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE), curl_easy_strerror(result)); #ifdef SOCKETIMEDOUT diff --git a/lib/connect.c b/lib/connect.c index 2a6c4f767741..3140881f27cc 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -81,7 +81,7 @@ UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; ctimeleft_ms = ctimeout_ms - - curlx_ptimediff_ms(pnow, &data->progress.t_startsingle); + Curl_pgrs_since_ms(data, pnow, TIMER_STARTSINGLE); if(!ctimeleft_ms) ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ } @@ -91,7 +91,7 @@ UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, if(data->set.timeout) { timeleft_ms = data->set.timeout - - curlx_ptimediff_ms(pnow, &data->progress.t_startop); + Curl_pgrs_since_ms(data, pnow, TIMER_STARTOP); if(!timeleft_ms) timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ } diff --git a/lib/ftp.c b/lib/ftp.c index c1b05eeccb98..0b4b0eda7360 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1398,7 +1398,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, conn, SECONDARYSOCKET); } conn->bits.do_more = FALSE; - Curl_pgrsTime(data, TIMER_STARTACCEPT); Curl_expire(data, (data->set.accepttimeout > 0) ? data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, EXPIRE_FTP_ACCEPT); diff --git a/lib/getinfo.c b/lib/getinfo.c index fde4aa4ef2d3..d9873531d58e 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -43,15 +43,9 @@ void Curl_initinfo(struct Curl_easy *data) struct Progress *pro = &data->progress; struct PureInfo *info = &data->info; - pro->t_nslookup = 0; - pro->t_connect = 0; - pro->t_appconnect = 0; - pro->t_pretransfer = 0; - pro->t_posttransfer = 0; - pro->t_starttransfer = 0; - pro->timespent = 0; - pro->t_redirect = 0; - pro->is_t_startransfer_set = FALSE; + memset(&pro->delta, 0, sizeof(pro->delta)); + memset(&pro->total, 0, sizeof(pro->total)); + pro->startransfer_added = FALSE; info->httpcode = 0; info->httpproxycode = 0; @@ -436,31 +430,31 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, data->progress.ul.total_size : -1; break; case CURLINFO_TOTAL_TIME_T: - *param_offt = data->progress.timespent; + *param_offt = data->progress.total.spent_us; break; case CURLINFO_NAMELOOKUP_TIME_T: - *param_offt = data->progress.t_nslookup; + *param_offt = data->progress.total.nslookup_us; break; case CURLINFO_CONNECT_TIME_T: - *param_offt = data->progress.t_connect; + *param_offt = data->progress.total.connect_us; break; case CURLINFO_APPCONNECT_TIME_T: - *param_offt = data->progress.t_appconnect; + *param_offt = data->progress.total.appconnect_us; break; case CURLINFO_PRETRANSFER_TIME_T: - *param_offt = data->progress.t_pretransfer; + *param_offt = data->progress.total.pretransfer_us; break; case CURLINFO_POSTTRANSFER_TIME_T: - *param_offt = data->progress.t_posttransfer; + *param_offt = data->progress.total.posttransfer_us; break; case CURLINFO_STARTTRANSFER_TIME_T: - *param_offt = data->progress.t_starttransfer; + *param_offt = data->progress.total.starttransfer_us; break; case CURLINFO_QUEUE_TIME_T: - *param_offt = data->progress.t_postqueue; + *param_offt = data->progress.total.queued_us; break; case CURLINFO_REDIRECT_TIME_T: - *param_offt = data->progress.t_redirect; + *param_offt = data->progress.delta.startredirect_us; break; case CURLINFO_RETRY_AFTER: *param_offt = data->info.retry_after; @@ -512,22 +506,22 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, #endif switch(info) { case CURLINFO_TOTAL_TIME: - *param_doublep = DOUBLE_SECS(data->progress.timespent); + *param_doublep = DOUBLE_SECS(data->progress.total.spent_us); break; case CURLINFO_NAMELOOKUP_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_nslookup); + *param_doublep = DOUBLE_SECS(data->progress.total.nslookup_us); break; case CURLINFO_CONNECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_connect); + *param_doublep = DOUBLE_SECS(data->progress.total.connect_us); break; case CURLINFO_APPCONNECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_appconnect); + *param_doublep = DOUBLE_SECS(data->progress.total.appconnect_us); break; case CURLINFO_PRETRANSFER_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer); + *param_doublep = DOUBLE_SECS(data->progress.total.pretransfer_us); break; case CURLINFO_STARTTRANSFER_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer); + *param_doublep = DOUBLE_SECS(data->progress.total.starttransfer_us); break; case CURLINFO_SIZE_UPLOAD: *param_doublep = (double)data->progress.ul.cur_size; @@ -550,7 +544,7 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, (double)data->progress.ul.total_size : -1; break; case CURLINFO_REDIRECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_redirect); + *param_doublep = DOUBLE_SECS(data->progress.delta.startredirect_us); break; default: diff --git a/lib/multi.c b/lib/multi.c index 500885668af4..4a6f431f6daa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1826,29 +1826,25 @@ static bool multi_handle_timeout(struct Curl_easy *data, timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* Handle timed out */ - struct curltime since; - if(Curl_is_connecting(data)) - since = data->progress.t_startsingle; - else - since = data->progress.t_startop; + timerid base_timer = Curl_is_connecting(data) ? + TIMER_STARTSINGLE : TIMER_STARTOP; + timediff_t elapsed_ms = Curl_pgrs_since_ms(data, NULL, base_timer); 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)); + elapsed_ms); else { struct SingleRequest *k = &data->req; if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" FMT_OFF_T " bytes received", - curlx_ptimediff_ms(Curl_pgrs_now(data), &since), - k->bytecount, k->size); + elapsed_ms, k->bytecount, k->size); } else { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " bytes received", - curlx_ptimediff_ms(Curl_pgrs_now(data), &since), - k->bytecount); + elapsed_ms, k->bytecount); } } *result = CURLE_OPERATION_TIMEDOUT; diff --git a/lib/progress.c b/lib/progress.c index b6322461d544..915f55258fff 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -188,7 +188,7 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) int Curl_pgrsDone(struct Curl_easy *data) { int rc; - data->progress.lastshow = 0; + data->progress.delta.lastshow_us = -1; rc = Curl_pgrsUpdate(data); /* the final (forced) update */ if(rc) return rc; @@ -247,7 +247,6 @@ static const char * const pgrs_timer_names[] = { "PGRS-PRETRANSFER", "PGRS-STARTTRANSFER", "PGRS-POSTRANSFER", - "PGRS-STARTACCEPT", "PGRS-REDIRECT", }; @@ -274,34 +273,33 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, break; case TIMER_STARTOP: /* This is set at the start of a transfer */ - data->progress.t_startop = timestamp; - data->progress.t_startqueue = timestamp; - data->progress.t_postqueue = 0; + data->progress.delta.startop_us = + curlx_ptimediff_us(×tamp, &data->progress.start); + data->progress.delta.startqueue_us = data->progress.delta.startop_us; break; case TIMER_STARTSINGLE: /* This is set at the start of each single transfer */ - data->progress.t_startsingle = timestamp; - data->progress.is_t_startransfer_set = FALSE; + data->progress.delta.startsingle_us = + curlx_ptimediff_us(×tamp, &data->progress.start); + data->progress.startransfer_added = FALSE; break; case TIMER_POSTQUEUE: /* Queue time is accumulative from all involved redirects */ - data->progress.t_postqueue += - curlx_ptimediff_us(×tamp, &data->progress.t_startqueue); - break; - case TIMER_STARTACCEPT: - data->progress.t_acceptdata = timestamp; + data->progress.total.queued_us += + curlx_ptimediff_us(×tamp, &data->progress.start) - + data->progress.delta.startqueue_us; break; case TIMER_NAMELOOKUP: - delta = &data->progress.t_nslookup; + delta = &data->progress.total.nslookup_us; break; case TIMER_CONNECT: - delta = &data->progress.t_connect; + delta = &data->progress.total.connect_us; break; case TIMER_APPCONNECT: - delta = &data->progress.t_appconnect; + delta = &data->progress.total.appconnect_us; break; case TIMER_PRETRANSFER: - delta = &data->progress.t_pretransfer; + delta = &data->progress.total.pretransfer_us; break; case TIMER_STARTTRANSFER: /* prevent updating t_starttransfer unless: @@ -310,25 +308,27 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, * This prevents repeated invocations of the function from incorrectly * changing the t_starttransfer time. */ - if(data->progress.is_t_startransfer_set) { + if(data->progress.startransfer_added) { CURL_TRC_M(data, "[%s] ignored", pgrs_timer_name(timer)); return; } - data->progress.is_t_startransfer_set = TRUE; - delta = &data->progress.t_starttransfer; + data->progress.startransfer_added = TRUE; + delta = &data->progress.total.starttransfer_us; break; case TIMER_POSTRANSFER: - delta = &data->progress.t_posttransfer; + delta = &data->progress.total.posttransfer_us; break; case TIMER_REDIRECT: - data->progress.t_redirect = curlx_ptimediff_us(×tamp, - &data->progress.start); - data->progress.t_startqueue = timestamp; + data->progress.delta.startredirect_us = + curlx_ptimediff_us(×tamp, &data->progress.start); + /* transfer starts queueing again */ + data->progress.delta.startqueue_us = + data->progress.delta.startredirect_us; break; } if(delta) { - timediff_t us = curlx_ptimediff_us(×tamp, - &data->progress.t_startsingle); + timediff_t us = curlx_ptimediff_us(×tamp, &data->progress.start) - + data->progress.delta.startsingle_us; if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; @@ -350,13 +350,17 @@ void Curl_pgrsTime(struct Curl_easy *data, timerid timer) Curl_pgrsTimeWas(data, timer, *Curl_pgrs_now(data)); } -void Curl_pgrsStartNow(struct Curl_easy *data) +void Curl_pgrsStart(struct Curl_easy *data, const struct curltime *pnow) { struct Progress *p = &data->progress; + if(!pnow) + pnow = Curl_pgrs_now(data); p->speeder_c = 0; /* reset the progress meter display */ - p->start = *Curl_pgrs_now(data); - p->is_t_startransfer_set = FALSE; + p->start = *pnow; + memset(&p->delta, 0, sizeof(p->delta)); + memset(&p->total, 0, sizeof(p->total)); + p->startransfer_added = FALSE; p->dl.cur_size = 0; p->ul.cur_size = 0; /* the sizes are unknown at start */ @@ -456,21 +460,22 @@ static bool progress_calc(struct Curl_easy *data, { struct Progress * const p = &data->progress; int i_next, i_oldest, i_latest; - timediff_t duration_us; + timediff_t duration_us, elapsed_us; curl_off_t amount; /* The time spent so far (from the start) in microseconds */ - p->timespent = curlx_ptimediff_us(pnow, &p->start); - p->dl.speed = trspeed(p->dl.cur_size, p->timespent); - p->ul.speed = trspeed(p->ul.cur_size, p->timespent); + elapsed_us = curlx_ptimediff_us(pnow, &p->start); + p->total.spent_us = elapsed_us; + p->dl.speed = trspeed(p->dl.cur_size, p->total.spent_us); + p->ul.speed = trspeed(p->ul.cur_size, p->total.spent_us); if(!p->speeder_c) { /* no previous record exists */ p->speed_amount[0] = p->dl.cur_size + p->ul.cur_size; - p->speed_time[0] = *pnow; + p->speed_time[0] = elapsed_us; p->speeder_c++; /* use the overall average at the start */ p->current_speed = p->ul.speed + p->dl.speed; - p->lastshow = pnow->tv_sec; + p->delta.lastshow_us = elapsed_us; return TRUE; } /* We have at least one record now. Where to put the next and @@ -480,11 +485,11 @@ static bool progress_calc(struct Curl_easy *data, /* Make a new record only when some time has passed. * Too frequent calls otherwise ruin the history. */ - if(curlx_ptimediff_ms(pnow, &p->speed_time[i_latest]) >= 1000) { + if((elapsed_us - p->speed_time[i_latest]) >= (1000 * 1000)) { p->speeder_c++; i_latest = i_next; p->speed_amount[i_latest] = p->dl.cur_size + p->ul.cur_size; - p->speed_time[i_latest] = *pnow; + p->speed_time[i_latest] = elapsed_us; } else if(data->req.done) { /* When a transfer is done, and we did not have a current speed @@ -493,7 +498,7 @@ static bool progress_calc(struct Curl_easy *data, * reported speed since it no longer measures a full second. */ if(!p->current_speed) { p->speed_amount[i_latest] = p->dl.cur_size + p->ul.cur_size; - p->speed_time[i_latest] = *pnow; + p->speed_time[i_latest] = elapsed_us; } } else { @@ -507,8 +512,7 @@ static bool progress_calc(struct Curl_easy *data, /* How much we transferred between oldest and current records */ amount = p->speed_amount[i_latest] - p->speed_amount[i_oldest]; /* How long this took */ - duration_us = curlx_ptimediff_us(&p->speed_time[i_latest], - &p->speed_time[i_oldest]); + duration_us = p->speed_time[i_latest] - p->speed_time[i_oldest]; if(duration_us <= 0) duration_us = 1; @@ -522,9 +526,10 @@ static bool progress_calc(struct Curl_easy *data, p->current_speed = amount * 1000000 / duration_us; } - if((p->lastshow == pnow->tv_sec) && !data->req.done) + if((p->delta.lastshow_us >= 0) && !data->req.done && + ((elapsed_us - p->delta.lastshow_us) >= (1000 * 1000))) return FALSE; - p->lastshow = pnow->tv_sec; + p->delta.lastshow_us = elapsed_us; return TRUE; } @@ -569,7 +574,7 @@ static void progress_meter(struct Curl_easy *data) char time_left[8]; char time_total[8]; char time_spent[8]; - curl_off_t cur_secs = (curl_off_t)p->timespent / 1000000; /* seconds */ + curl_off_t cur_secs = (curl_off_t)p->total.spent_us / 1000000; if(!p->headers_out) { if(data->state.resume_from) { @@ -731,5 +736,24 @@ void Curl_pgrsUpdate_nometer(struct Curl_easy *data) void Curl_pgrsCompleted(struct Curl_easy *data) { struct Progress * const p = &data->progress; - p->timespent = curlx_ptimediff_us(Curl_pgrs_now(data), &p->start); + p->total.spent_us = curlx_ptimediff_us(Curl_pgrs_now(data), &p->start); +} + +timediff_t Curl_pgrs_since_ms(struct Curl_easy *data, + const struct curltime *pnow, + timerid timer) +{ + if(!pnow) + pnow = Curl_pgrs_now(data); + switch(timer) { + case TIMER_STARTOP: + return (curlx_ptimediff_us(pnow, &data->progress.start) - + data->progress.delta.startop_us) / 1000; + case TIMER_STARTSINGLE: + return (curlx_ptimediff_us(pnow, &data->progress.start) - + data->progress.delta.startsingle_us) / 1000; + default: + DEBUGASSERT(0); + return 0; + } } diff --git a/lib/progress.h b/lib/progress.h index 1f2a4e71c305..a4649ae83e98 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -38,7 +38,6 @@ typedef enum { TIMER_PRETRANSFER, TIMER_STARTTRANSFER, TIMER_POSTRANSFER, - TIMER_STARTACCEPT, TIMER_REDIRECT, TIMER_LAST /* must be last */ } timerid; @@ -47,7 +46,7 @@ typedef enum { const struct curltime *Curl_pgrs_now(struct Curl_easy *data); int Curl_pgrsDone(struct Curl_easy *data); -void Curl_pgrsStartNow(struct Curl_easy *data); +void Curl_pgrsStart(struct Curl_easy *data, const struct curltime *pnow); void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta); @@ -85,4 +84,8 @@ void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); void Curl_pgrsCompleted(struct Curl_easy *data); +timediff_t Curl_pgrs_since_ms(struct Curl_easy *data, + const struct curltime *pnow, + timerid timer); + #endif /* HEADER_CURL_PROGRESS_H */ diff --git a/lib/tftp.c b/lib/tftp.c index c101edebedba..af9e9752f715 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -988,8 +988,6 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) conn->bits.bound = TRUE; } - Curl_pgrsStartNow(data); - *done = TRUE; return CURLE_OK; diff --git a/lib/transfer.c b/lib/transfer.c index 673d79837da2..17312e41ef12 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -386,15 +386,13 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" FMT_OFF_T " bytes received", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle), + Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE), k->bytecount, k->size); } else { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " bytes received", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle), + Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE), k->bytecount); } result = CURLE_OPERATION_TIMEDOUT; @@ -555,7 +553,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) Curl_initinfo(data); /* reset session-specific information "variables" */ Curl_pgrsResetTransferSizes(data); - Curl_pgrsStartNow(data); + Curl_pgrsStart(data, NULL); /* In case the handle is reused and an authentication method was picked in the session we need to make sure we only use the one(s) we now diff --git a/lib/urldata.h b/lib/urldata.h index 231a698517ea..b2798eb4f2b9 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -438,43 +438,43 @@ struct pgrs_dir { struct Progress { struct curltime now; /* current time of processing */ - time_t lastshow; /* time() of the last displayed progress meter or NULL to - force redraw at next call */ + struct curltime start; /* when transfer was initialized, set once */ + struct pgrs_dir ul; struct pgrs_dir dl; curl_off_t deliver; /* amount of data delivered to application */ - curl_off_t current_speed; /* uses the currently fastest transfer */ curl_off_t earlydata_sent; - timediff_t timespent; - - timediff_t t_postqueue; - timediff_t t_nslookup; - timediff_t t_connect; - timediff_t t_appconnect; - timediff_t t_pretransfer; - timediff_t t_posttransfer; - timediff_t t_starttransfer; - timediff_t t_redirect; - - struct curltime start; - struct curltime t_startsingle; - struct curltime t_startop; - struct curltime t_startqueue; - struct curltime t_acceptdata; + struct { + timediff_t startop_us; /* since start when operations started */ + timediff_t startsingle_us; /* since start when last request started */ + timediff_t startqueue_us; /* since start when last entered queueing */ + timediff_t startredirect_us; /* since start when last redirected */ + timediff_t lastshow_us; /* since start when last progress shown */ + } delta; + struct { + timediff_t spent_us; /* all time spent since start */ + timediff_t queued_us; /* time spent since startsingle's for queueing */ + timediff_t nslookup_us; /* same for name resolves */ + timediff_t connect_us; /* same for connects */ + timediff_t appconnect_us; /* same for application connects, e.g. TLS */ + timediff_t pretransfer_us; /* same until requests were sent */ + timediff_t starttransfer_us; /* same until responses started */ + timediff_t posttransfer_us; /* same until responses ended */ + } total; #define CURL_SPEED_RECORDS (5 + 1) /* 6 entries for 5 seconds */ curl_off_t speed_amount[CURL_SPEED_RECORDS]; - struct curltime speed_time[CURL_SPEED_RECORDS]; + timediff_t speed_time[CURL_SPEED_RECORDS]; uint32_t speeder_c; BIT(hide); BIT(ul_size_known); BIT(dl_size_known); BIT(headers_out); /* when the headers have been written */ BIT(callback); /* set when progress callback is used */ - BIT(is_t_startransfer_set); + BIT(startransfer_added); }; struct auth { diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index 51d4791ecce6..a533ba7c2769 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -230,8 +230,7 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ failf(data, "Failed to resolve '%s' with timeout after %" FMT_TIMEDIFF_T " ms", ctx->peer->hostname, - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); + Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE)); return CURLE_OPERATION_TIMEDOUT; } else { diff --git a/tests/unit/unit1303.c b/tests/unit/unit1303.c index 52a7ea9c4331..acc31c39f179 100644 --- a/tests/unit/unit1303.c +++ b/tests/unit/unit1303.c @@ -139,10 +139,10 @@ static CURLcode test_unit1303(const char *arg) }; /* this is the pretended start time of the transfer */ - easy->progress.t_startsingle.tv_sec = BASE; - easy->progress.t_startsingle.tv_usec = 0; - easy->progress.t_startop.tv_sec = BASE; - easy->progress.t_startop.tv_usec = 0; + easy->progress.start.tv_sec = BASE; + easy->progress.start.tv_usec = 0; + easy->progress.delta.startsingle_us = 0; + easy->progress.delta.startop_us = 0; for(i = 0; i < CURL_ARRAYSIZE(run); i++) { timediff_t timeout; diff --git a/tests/unit/unit1399.c b/tests/unit/unit1399.c index 909b13e8b2cf..a2fb21acec69 100644 --- a/tests/unit/unit1399.c +++ b/tests/unit/unit1399.c @@ -46,16 +46,14 @@ static void t1399_stop(struct Curl_easy *easy) /* * Invoke Curl_pgrsTime for TIMER_STARTSINGLE to trigger the behavior that - * manages is_t_startransfer_set, but fake the t_startsingle time for purposes + * manages startransfer, but fake the startsingle_us time for purposes * of the test. */ static void fake_t_startsingle_time(struct Curl_easy *data, - struct curltime fake_now, int seconds_offset) { Curl_pgrsTime(data, TIMER_STARTSINGLE); - data->progress.t_startsingle.tv_sec = fake_now.tv_sec + seconds_offset; - data->progress.t_startsingle.tv_usec = fake_now.tv_usec; + data->progress.delta.startsingle_us = (seconds_offset * 1000 * 1000); } static bool usec_matches_seconds(timediff_t time_usec, int expected_seconds) @@ -72,16 +70,15 @@ static bool usec_matches_seconds(timediff_t time_usec, int expected_seconds) static void expect_timer_seconds(struct Curl_easy *data, int seconds) { + struct Progress *p = &data->progress; char msg[64]; curl_msnprintf(msg, sizeof(msg), "about %d seconds should have passed", seconds); - fail_unless(usec_matches_seconds(data->progress.t_nslookup, seconds), msg); - fail_unless(usec_matches_seconds(data->progress.t_connect, seconds), msg); - fail_unless(usec_matches_seconds(data->progress.t_appconnect, seconds), msg); - fail_unless(usec_matches_seconds(data->progress.t_pretransfer, seconds), - msg); - fail_unless(usec_matches_seconds(data->progress.t_starttransfer, seconds), - msg); + fail_unless(usec_matches_seconds(p->total.nslookup_us, seconds), msg); + fail_unless(usec_matches_seconds(p->total.connect_us, seconds), msg); + fail_unless(usec_matches_seconds(p->total.appconnect_us, seconds), msg); + fail_unless(usec_matches_seconds(p->total.pretransfer_us, seconds), msg); + fail_unless(usec_matches_seconds(p->total.starttransfer_us, seconds), msg); } /* Scenario: simulate a redirect. When a redirect occurs, t_nslookup, @@ -98,15 +95,12 @@ static CURLcode test_unit1399(const char *arg) data->multi = NULL; data->progress.now = now; - data->progress.t_nslookup = 0; - data->progress.t_connect = 0; - data->progress.t_appconnect = 0; - data->progress.t_pretransfer = 0; - data->progress.t_starttransfer = 0; - data->progress.t_redirect = 0; + memset(&data->progress.delta, 0, sizeof(data->progress.delta)); + memset(&data->progress.total, 0, sizeof(data->progress.total)); + data->progress.start.tv_sec = now.tv_sec - 2; data->progress.start.tv_usec = now.tv_usec; - fake_t_startsingle_time(data, now, -2); + fake_t_startsingle_time(data, 0); Curl_pgrsTime(data, TIMER_NAMELOOKUP); Curl_pgrsTime(data, TIMER_CONNECT); @@ -117,8 +111,8 @@ static CURLcode test_unit1399(const char *arg) expect_timer_seconds(data, 2); /* now simulate the redirect */ - data->progress.t_redirect = data->progress.t_starttransfer + 1; - fake_t_startsingle_time(data, now, -1); + data->progress.delta.startredirect_us = 1 * 1000 * 1000; + fake_t_startsingle_time(data, 1); Curl_pgrsTime(data, TIMER_NAMELOOKUP); Curl_pgrsTime(data, TIMER_CONNECT); From 8a8ff47b63aff1d72c1796dbdb4955f243beac23 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 3 Aug 2026 02:28:33 +0200 Subject: [PATCH 0871/1062] tidy-up: C and CI - drop redundant casts for `CURL_UNCONST()` pointers (Windows). - GHA/linux: delete redundant/dupe `-g` C flag in memory sanitizer job. Spotted-by: Daniel Stenberg - examples/http2-upload: drop local macro. - examples/http2-upload: drop leading underscore from union name. - examples/http2-upload: drop interim macro. - tests/server/util: reapply patch lost in a rebase. - sync `main()` declarations across the codebase. Closes #22559 --- .github/workflows/linux.yml | 2 +- CMakeLists.txt | 2 +- configure.ac | 2 +- docs/examples/anyauthput.c | 2 +- docs/examples/ftp-wildcard.c | 2 +- docs/examples/htmltidy.c | 2 +- docs/examples/http2-download.c | 2 +- docs/examples/http2-upload.c | 16 +++++++--------- docs/examples/httpput-postfields.c | 2 +- docs/examples/httpput.c | 2 +- docs/examples/multi-event.c | 2 +- docs/examples/multi-uv.c | 2 +- docs/examples/smooth-gtk-thread.c | 2 +- docs/examples/sslbackend.c | 2 +- docs/internals/CODE_STYLE.md | 2 +- docs/libcurl/curl_strequal.md | 2 +- docs/libcurl/curl_strnequal.md | 2 +- docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md | 2 +- docs/libcurl/opts/CURLOPT_PROTOCOLS.md | 2 +- docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md | 2 +- docs/libcurl/opts/CURLOPT_READFUNCTION.md | 2 +- docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md | 2 +- docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md | 2 +- lib/socks_sspi.c | 3 +-- lib/vauth/digest_sspi.c | 10 +++++----- lib/vauth/krb5_sspi.c | 6 +++--- lib/vauth/ntlm_sspi.c | 6 +++--- lib/vauth/spnego_sspi.c | 12 +++++------- lib/vtls/schannel.c | 6 ++---- projects/OS400/curlmain.c | 4 ++-- projects/vms/report_openssl_version.c | 2 +- src/curlinfo.c | 2 +- tests/cmake/test.c | 2 +- tests/cmake/test.cpp | 2 +- tests/libtest/first.c | 2 +- tests/server/first.c | 2 +- tests/server/util.c | 8 +++----- 37 files changed, 59 insertions(+), 68 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8a05b7105ef4..ae0c6924c5b3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -423,7 +423,7 @@ jobs: install_packages: clang-22 install_steps: randcurl CC: clang-22 - CFLAGS: -fsanitize=memory -Wformat -Werror=format-security -Werror=array-bounds -g + CFLAGS: -fsanitize=memory -Wformat -Werror=format-security -Werror=array-bounds LDFLAGS: -fsanitize=memory LIBS: -ldl configure: --without-ssl --without-zlib --without-brotli --without-zstd --without-libpsl --without-nghttp2 --enable-debug diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca969539a90..cdf47d486bc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1806,7 +1806,7 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) # On non-Windows and not cross-compiling, check for writable argv[] include(CheckCSourceRuns) check_c_source_runs(" - int main(int argc, char **argv) + int main(int argc, char *argv[]) { (void)argc; argv[0][0] = ' '; diff --git a/configure.ac b/configure.ac index 0c085a85e83f..5071f8652385 100644 --- a/configure.ac +++ b/configure.ac @@ -1841,7 +1841,7 @@ dnl ********************************************************************** AC_MSG_CHECKING([if argv can be written to]) CURL_RUN_IFELSE([[ -int main(int argc, char **argv) +int main(int argc, char *argv[]) { #ifdef _WIN32 /* on Windows, writing to the argv does not hide the argument in diff --git a/docs/examples/anyauthput.c b/docs/examples/anyauthput.c index b1bd9f0fbf2f..a28ee2c69437 100644 --- a/docs/examples/anyauthput.c +++ b/docs/examples/anyauthput.c @@ -88,7 +88,7 @@ static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) return nread; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURL *curl; CURLcode result; diff --git a/docs/examples/ftp-wildcard.c b/docs/examples/ftp-wildcard.c index abde048e9d8d..d7627aab9887 100644 --- a/docs/examples/ftp-wildcard.c +++ b/docs/examples/ftp-wildcard.c @@ -97,7 +97,7 @@ static size_t write_cb(char *buff, size_t size, size_t nmemb, void *cb_data) return written; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { /* curl easy handle */ CURL *curl; diff --git a/docs/examples/htmltidy.c b/docs/examples/htmltidy.c index 2e2def59c3c4..8a510f49f4cc 100644 --- a/docs/examples/htmltidy.c +++ b/docs/examples/htmltidy.c @@ -74,7 +74,7 @@ static void dumpNode(TidyDoc doc, TidyNode tnod, int indent) } } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURL *curl; char curl_errbuf[CURL_ERROR_SIZE]; diff --git a/docs/examples/http2-download.c b/docs/examples/http2-download.c index 3d612781bbfe..d46c4caead76 100644 --- a/docs/examples/http2-download.c +++ b/docs/examples/http2-download.c @@ -187,7 +187,7 @@ static int setup(struct transfer *t, int num) /* * Download many transfers over HTTP/2, using the same connection! */ -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURLcode result; struct transfer *trans; diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index cad74afc50de..9acd7198f1c4 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -67,20 +67,18 @@ #endif #ifdef _MSC_VER -#define gettimeofday(a, b) my_gettimeofday(a, b) -static int my_gettimeofday(struct timeval *tp, void *tzp) +static int gettimeofday(struct timeval *tp, void *tzp) { (void)tzp; if(tp) { -/* Offset between 1601-01-01 and 1970-01-01 in 100 nanosec units */ -#define WIN32_FT_OFFSET 116444736000000000 union { CURL_TYPEOF_CURL_OFF_T ns100; /* time since 1 Jan 1601 in 100ns units */ FILETIME ft; - } _now; - GetSystemTimeAsFileTime(&_now.ft); - tp->tv_usec = (long)((_now.ns100 / 10) % 1000000); - tp->tv_sec = (long)((_now.ns100 - WIN32_FT_OFFSET) / 10000000); + } now; + GetSystemTimeAsFileTime(&now.ft); + tp->tv_usec = (long)((now.ns100 / 10) % 1000000); + /* subtract offset between 1601-01-01 and 1970-01-01 in 100ns units */ + tp->tv_sec = (long)((now.ns100 - 116444736000000000) / 10000000); } return 0; } @@ -286,7 +284,7 @@ static int setup(struct input *t, int num, const char *upload) /* * Upload all files over HTTP/2, using the same physical connection! */ -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURLcode result; struct input *trans; diff --git a/docs/examples/httpput-postfields.c b/docs/examples/httpput-postfields.c index 602190d9f6c1..d34f387fdb78 100644 --- a/docs/examples/httpput-postfields.c +++ b/docs/examples/httpput-postfields.c @@ -47,7 +47,7 @@ static const char olivertwist[] = * CURLOPT_POSTFIELDS to the URL given as an argument. */ -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURL *curl; CURLcode result; diff --git a/docs/examples/httpput.c b/docs/examples/httpput.c index fb33c8ac406a..90cb81b7de22 100644 --- a/docs/examples/httpput.c +++ b/docs/examples/httpput.c @@ -73,7 +73,7 @@ static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) return retcode; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURL *curl; CURLcode result; diff --git a/docs/examples/multi-event.c b/docs/examples/multi-event.c index b52adefa0cae..d10c93d3e066 100644 --- a/docs/examples/multi-event.c +++ b/docs/examples/multi-event.c @@ -211,7 +211,7 @@ static int handle_socket(CURL *curl, curl_socket_t s, int action, void *userp, return 0; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURLcode result; diff --git a/docs/examples/multi-uv.c b/docs/examples/multi-uv.c index 2671556ba0c3..49155864936b 100644 --- a/docs/examples/multi-uv.c +++ b/docs/examples/multi-uv.c @@ -225,7 +225,7 @@ static int cb_socket(CURL *curl, curl_socket_t s, int action, return 0; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { CURLcode result; struct datauv uv = { 0 }; diff --git a/docs/examples/smooth-gtk-thread.c b/docs/examples/smooth-gtk-thread.c index 82ce1a2256de..cca3be738bc9 100644 --- a/docs/examples/smooth-gtk-thread.c +++ b/docs/examples/smooth-gtk-thread.c @@ -165,7 +165,7 @@ static gboolean cb_delete(GtkWidget *window, gpointer data) return FALSE; } -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { GtkWidget *top_window, *outside_frame, *inside_frame, *progress_bar; diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index 39c042a67773..bbb1ea409479 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -40,7 +40,7 @@ * **** This example only works with libcurl 7.56.0 and later! **** */ -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { const char *name = argc > 1 ? argv[1] : "openssl"; CURLsslset result; diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index af8d916b36fa..bbf23f944b3e 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -104,7 +104,7 @@ if(!x) For functions the opening brace should be on a separate line: ```c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { return 1; } diff --git a/docs/libcurl/curl_strequal.md b/docs/libcurl/curl_strequal.md index e7270d874e14..0d9689acf782 100644 --- a/docs/libcurl/curl_strequal.md +++ b/docs/libcurl/curl_strequal.md @@ -44,7 +44,7 @@ string comparison functions. This function works on all platforms. # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { const char *name = "compare"; if(curl_strequal(name, argv[1])) diff --git a/docs/libcurl/curl_strnequal.md b/docs/libcurl/curl_strnequal.md index db42bcdf8e21..7e0d40461d50 100644 --- a/docs/libcurl/curl_strnequal.md +++ b/docs/libcurl/curl_strnequal.md @@ -47,7 +47,7 @@ string comparison functions. This function works on all platforms. # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { const char *name = "compare"; if(curl_strnequal(name, argv[1], 5)) diff --git a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md index c79b5e7ebd17..aea8ec45e55a 100644 --- a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md +++ b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md @@ -140,7 +140,7 @@ static size_t wf(char *ptr, size_t size, size_t nmemb, void *stream) return size * nmemb; } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { CURLcode result; curl = curl_easy_init(); diff --git a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md index cdd02b5ed8d5..023773315998 100644 --- a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md @@ -82,7 +82,7 @@ All protocols built-in. # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { CURL *curl = curl_easy_init(); if(curl) { diff --git a/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md b/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md index 02a78ae5d647..969bf88ab7dd 100644 --- a/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md +++ b/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md @@ -65,7 +65,7 @@ All protocols built-in # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { CURL *curl = curl_easy_init(); if(curl) { diff --git a/docs/libcurl/opts/CURLOPT_READFUNCTION.md b/docs/libcurl/opts/CURLOPT_READFUNCTION.md index 0046ded635c8..0c8304ce3c77 100644 --- a/docs/libcurl/opts/CURLOPT_READFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_READFUNCTION.md @@ -100,7 +100,7 @@ size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userdata) return retcode; } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { FILE *file = fopen(argv[1], "rb"); CURLcode result; diff --git a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md index 1d860312c01c..0803d587865d 100644 --- a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md @@ -87,7 +87,7 @@ HTTP, HTTPS, FTP and FTPS # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { CURL *curl = curl_easy_init(); if(curl) { diff --git a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md index 8f3c0ef885d6..731b506319d9 100644 --- a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md +++ b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md @@ -71,7 +71,7 @@ HTTP, HTTPS, FTP and FTPS # EXAMPLE ~~~c -int main(int argc, char **argv) +int main(int argc, char *argv[]) { CURL *curl = curl_easy_init(); if(curl) { diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 4474180e7b84..576d6aef5bbc 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -77,8 +77,7 @@ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), + Curl_pSecFn->AcquireCredentialsHandle(NULL, CURL_UNCONST(TEXT("Kerberos")), SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, cred_handle, NULL); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index c9b1432830a1..e3648e37b857 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -54,7 +54,7 @@ bool Curl_auth_is_digest_supported(void) /* Query the security package for Digest */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), + CURL_UNCONST(TEXT(SP_NAME_DIGEST)), &SecurityPackage); /* Release the package buffer as it is not required anymore */ @@ -115,7 +115,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* Query the security package for DigestSSP */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), + CURL_UNCONST(TEXT(SP_NAME_DIGEST)), &SecurityPackage); if(status != SEC_E_OK) { failf(data, "SSPI: could not get auth info"); @@ -158,7 +158,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), + CURL_UNCONST(TEXT(SP_NAME_DIGEST)), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, &credentials, NULL); @@ -402,7 +402,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Query the security package for DigestSSP */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), + CURL_UNCONST(TEXT(SP_NAME_DIGEST)), &SecurityPackage); if(status != SEC_E_OK) { failf(data, "SSPI: could not get auth info"); @@ -502,7 +502,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), + CURL_UNCONST(TEXT(SP_NAME_DIGEST)), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, &credentials, NULL); diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index 543a6fe284bc..24d2421be652 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -46,7 +46,7 @@ bool Curl_auth_is_gssapi_supported(void) /* Query the security package for Kerberos */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), + CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), &SecurityPackage); /* Release the package buffer as it is not required anymore */ @@ -109,7 +109,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->output_token) { /* Query the security package for Kerberos */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), + CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), &SecurityPackage); if(status != SEC_E_OK) { failf(data, "SSPI: could not get auth info"); @@ -150,7 +150,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), + CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, krb5->credentials, NULL); diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 2e98e86e5f3d..4f977865c9d6 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -46,7 +46,7 @@ bool Curl_auth_is_ntlm_supported(void) /* Query the security package for NTLM */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)), + CURL_UNCONST(TEXT(SP_NAME_NTLM)), &SecurityPackage); /* Release the package buffer as it is not required anymore */ @@ -95,7 +95,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* Query the security package for NTLM */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)), + CURL_UNCONST(TEXT(SP_NAME_NTLM)), &SecurityPackage); if(status != SEC_E_OK) { failf(data, "SSPI: could not get auth info"); @@ -135,7 +135,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)), + CURL_UNCONST(TEXT(SP_NAME_NTLM)), SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, ntlm->credentials, NULL); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 3f6c93f0bf29..3a140ad84e25 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -48,7 +48,7 @@ bool Curl_auth_is_spnego_supported(void) /* Query the security package for Negotiate */ status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), + CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), &SecurityPackage); /* Release the package buffer as it is not required anymore */ @@ -114,7 +114,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->output_token) { /* Query the security package for Negotiate */ nego->status = Curl_pSecFn->QuerySecurityPackageInfo( - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), + CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), &SecurityPackage); if(nego->status != SEC_E_OK) { failf(data, "SSPI: could not get auth info"); @@ -166,11 +166,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, * https://learn.microsoft.com/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa */ #ifdef UNICODE - nego->identity.PackageList = - (unsigned short *)CURL_UNCONST(TEXT("!ntlm")); + nego->identity.PackageList = CURL_UNCONST(TEXT("!ntlm")); #else - nego->identity.PackageList = - (unsigned char *)CURL_UNCONST(TEXT("!ntlm")); + nego->identity.PackageList = CURL_UNCONST(TEXT("!ntlm")); #endif nego->identity.PackageListLength = 5; @@ -181,7 +179,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Acquire our credentials handle */ nego->status = Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), + CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, nego->credentials, NULL); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index aca109bfdc52..eea5f3af55e9 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -627,8 +627,7 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, } sspi_status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(UNISP_NAME), + Curl_pSecFn->AcquireCredentialsHandle(NULL, CURL_UNCONST(UNISP_NAME), SECPKG_CRED_OUTBOUND, NULL, &credentials, NULL, NULL, &backend->cred->cred_handle, NULL); @@ -677,8 +676,7 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, } sspi_status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(UNISP_NAME), + Curl_pSecFn->AcquireCredentialsHandle(NULL, CURL_UNCONST(UNISP_NAME), SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, &backend->cred->cred_handle, NULL); diff --git a/projects/OS400/curlmain.c b/projects/OS400/curlmain.c index 690e8c622177..fa2e93d35f47 100644 --- a/projects/OS400/curlmain.c +++ b/projects/OS400/curlmain.c @@ -42,13 +42,13 @@ extern int QadrtFreeEnviron(void); extern char * setlocale_a(int, const char *); /* The ASCII main program. */ -extern int main_a(int argc, char **argv); +extern int main_a(int argc, char *argv[]); /* Global values of original EBCDIC arguments. */ int ebcdic_argc; char ** ebcdic_argv; -int main(int argc, char **argv) +int main(int argc, char *argv[]) { int i; int j; diff --git a/projects/vms/report_openssl_version.c b/projects/vms/report_openssl_version.c index d2b0d367bc49..9a8538e17da4 100644 --- a/projects/vms/report_openssl_version.c +++ b/projects/vms/report_openssl_version.c @@ -39,7 +39,7 @@ unsigned long LIB$SET_SYMBOL(const struct dsc$descriptor_s *symbol, const struct dsc$descriptor_s *value, const unsigned long *table_type); -int main(int argc, char **argv) +int main(int argc, char *argv[]) { void *libptr; const char *(*ssl_version)(int t); diff --git a/src/curlinfo.c b/src/curlinfo.c index 5357a36fa853..1760a95d95c0 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -270,7 +270,7 @@ static const char * const disabled[] = { #endif }; -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { size_t i; diff --git a/tests/cmake/test.c b/tests/cmake/test.c index 75c6bf913e12..dc27c6ce0d7e 100644 --- a/tests/cmake/test.c +++ b/tests/cmake/test.c @@ -24,7 +24,7 @@ #include #include -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { (void)argc; puts("libcurl C test:"); diff --git a/tests/cmake/test.cpp b/tests/cmake/test.cpp index f5fd4ecc2a5c..18a76d8561b6 100644 --- a/tests/cmake/test.cpp +++ b/tests/cmake/test.cpp @@ -34,7 +34,7 @@ class CurlClass { } }; -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { (void)argc; std::cout << "libcurl C++ test:" << std::endl; diff --git a/tests/libtest/first.c b/tests/libtest/first.c index a512cddae975..2968ee611a97 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -211,7 +211,7 @@ void ws_close(CURL *curl) } #endif /* CURL_DISABLE_WEBSOCKETS */ -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { const char *URL = ""; CURLcode result; diff --git a/tests/server/first.c b/tests/server/first.c index c19018bc0ec4..fa233914b76c 100644 --- a/tests/server/first.c +++ b/tests/server/first.c @@ -25,7 +25,7 @@ #include -int main(int argc, const char **argv) +int main(int argc, const char *argv[]) { entry_func_t entry_func; const char *entry_name; diff --git a/tests/server/util.c b/tests/server/util.c index c4ea55985599..54854285ecb2 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -1097,13 +1097,11 @@ int open_stream_sock(curl_socket_t *psock, uint16_t *pport) srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 - if(socket_domain != AF_INET6) -#endif - la_size = sizeof(localaddr.sa4); -#ifdef USE_IPV6 - else + if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); + else #endif + la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", From 574749016d6286c7a075041ef70edef7ee50c4be Mon Sep 17 00:00:00 2001 From: hunterinvariants <217359725+hunterinvariants@users.noreply.github.com> Date: Tue, 11 Aug 2026 01:16:32 +0200 Subject: [PATCH 0872/1062] tests: verify AWS SigV4 redirect auth scope Closes #22538 --- tests/data/Makefile.am | 2 +- tests/data/test5026 | 94 +++++++++++++++++++++++++++++++++++++++++ tests/data/test5027 | 96 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 tests/data/test5026 create mode 100644 tests/data/test5027 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 17ae5ca610ec..5291ccc23219 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -296,7 +296,7 @@ test4000 test4001 \ test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ test5016 test5017 test5018 test5019 test5020 test5021 test5022 test5023 \ -test5024 test5025 +test5024 test5025 test5026 test5027 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ diff --git a/tests/data/test5026 b/tests/data/test5026 new file mode 100644 index 000000000000..72dfc77f6140 --- /dev/null +++ b/tests/data/test5026 @@ -0,0 +1,94 @@ + + + + +HTTP +HTTP proxy +followlocation +aws-sigv4 + + +# Server-side + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +contents + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +# Client-side + + +http + + +AWS SigV4 redirect to a different host + + +http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -u user:secret --aws-sigv4 "aws:amz:us-east-2:es" --location + + +Debug +aws +proxy + + + +# Verify data after the test has been "shot" + + +GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1 +Host: first.host.it.is +Authorization: AWS4-HMAC-SHA256 Credential=user/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=5524f6f960590dc88a36551f11e407031a373e9e2caa3d3f7a3d9280cae367e2 +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1 +Host: goto.second.host.now +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test5027 b/tests/data/test5027 new file mode 100644 index 000000000000..5ac74a08112e --- /dev/null +++ b/tests/data/test5027 @@ -0,0 +1,96 @@ + + + + +HTTP +HTTP proxy +followlocation +aws-sigv4 + + +# Server-side + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +contents + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +# Client-side + + +http + + +AWS SigV4 trusted redirect to a different host + + +http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -u user:secret --aws-sigv4 "aws:amz:us-east-2:es" --location-trusted + + +Debug +aws +proxy + + + +# Verify data after the test has been "shot" + + +GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1 +Host: first.host.it.is +Authorization: AWS4-HMAC-SHA256 Credential=user/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=c540681cd0bba0cd9f6983a4529a45545ca4693192b65f60d8288ea617670f55 +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1 +Host: goto.second.host.now +Authorization: AWS4-HMAC-SHA256 Credential=user/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=f73dad70768d102fbfc307cedf6c77a5ef2ce9da51989647b6240e903340dd26 +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + From eca5ded9f6c6281f46abb9cae073c5132e082df5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 16:23:58 +0200 Subject: [PATCH 0873/1062] mailmap: Ralf Mueller --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index b698ebec8c52..22b0f256b8ce 100644 --- a/.mailmap +++ b/.mailmap @@ -124,3 +124,4 @@ Hamza Bensliman Kaixuan Li Darren Banfi Alhuda Khan +Ralf Mueller <217359725+hunterinvariants@users.noreply.github.com> From 1a17959fc7a80864c9c9b37097856bdb4a465cf8 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 10 Aug 2026 10:17:06 +0200 Subject: [PATCH 0874/1062] url: fix negotiate/ntlm connection reuse Reorder logic when looking for matching connections. Check candidate Negotiate/NTLM state first. Require same "input" parameters when connection is already authenticated. Same for proxy. Deny connection reuse for empty usernames for NTLM/Negotiate using connections. Reported-by: Martin Dukek Closes #22528 --- lib/conncache.c | 2 +- lib/curl_trc.c | 3 +- lib/easy.c | 3 +- lib/multi.c | 2 +- lib/url.c | 124 +++++++++++++++++++++++++++++++----------------- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/lib/conncache.c b/lib/conncache.c index 11ef03343557..cec444731c1b 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -270,7 +270,6 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); - DEBUGASSERT(cpool); if(cpool) { CPOOL_LOCK(cpool, data); /* the identifier inside the connection cache */ @@ -283,6 +282,7 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) } else { /* We should not get here, but in a non-debug build, do something */ + DEBUGASSERT(0); data->id = 0; data->state.lastconnect_id = -1; } diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 4e4204a07b41..b52193270ef3 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -92,8 +92,7 @@ static struct curl_trc_feat Curl_trc_feat_ids = { static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen) { - curl_off_t cid = data->conn ? - data->conn->connection_id : data->state.recent_conn_id; + curl_off_t cid = data->state.recent_conn_id; if(data->id >= 0) { if(cid >= 0) return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid); diff --git a/lib/easy.c b/lib/easy.c index eeb2791706d3..aa2cad22c9a0 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1233,7 +1233,8 @@ static CURLcode easy_connection(struct Curl_easy *data, sfd = Curl_getconnectinfo(data, connp); if(sfd == CURL_SOCKET_BAD) { - failf(data, "Failed to get recent socket"); + failf(data, "Failed to get last socket used for connection #%" FMT_OFF_T, + data->state.lastconnect_id); return CURLE_UNSUPPORTED_PROTOCOL; } diff --git a/lib/multi.c b/lib/multi.c index 4a6f431f6daa..dd4b9bfe8ab7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -642,7 +642,6 @@ static void multi_done_locked(struct connectdata *conn, } data->state.done = TRUE; /* called now! */ - data->state.recent_conn_id = conn->connection_id; Curl_dnscache_prune(data); @@ -952,6 +951,7 @@ void Curl_attach_connection(struct Curl_easy *data, DEBUGASSERT(conn); DEBUGASSERT(conn->attached_xfers < UINT32_MAX); data->conn = conn; + data->state.recent_conn_id = conn->connection_id; conn->attached_xfers++; /* all attached transfers must be from the same multi */ if(!conn->attached_multi) diff --git a/lib/url.c b/lib/url.c index 5998cd69bc7f..5234a534430f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -900,43 +900,75 @@ static bool url_match_ssl_config(struct connectdata *conn, return TRUE; } +#if defined(USE_SPNEGO) || defined(USE_NTLM) +static bool url_allow_sspi_empty_creds(struct Curl_creds *conn_creds, + struct Curl_easy *data, + struct connectdata *conn) +{ +#ifdef USE_WINDOWS_SSPI + /* Empty user: SSPI on Windows can make use of an "ambient" + * user from a "SecurityToken" associated with the current thread or + * process. This token can be switched at any time. We are therefore + * not able to find out reliably what token the connection really + * used, nor what token in the next connect attempt will use. + * To avoid TOCTOU attacks, do not reuse on empty credentials + * UNLESS this connection is the one used by this transfer before. */ + if(!Curl_creds_has_user(conn_creds) && + (data->state.recent_conn_id != conn->connection_id)) + return FALSE; +#else + (void)conn_creds; + (void)data; + (void)conn; +#endif + return TRUE; +} +#endif /* USE_SPNEGO || USE_NTLM */ + #ifdef USE_NTLM static bool url_match_auth_ntlm(struct connectdata *conn, struct url_conn_match *m) { - /* If we are looking for an HTTP+NTLM connection, check if this is - already authenticating with the right credentials. If not, keep - looking so that we can reuse NTLM connections if - possible. (Especially we must not reuse the same connection if - partway through a handshake!) */ - if(m->want_ntlm_http) { + if(conn->http_ntlm_state != NTLMSTATE_NONE) { + /* Connection is using NTLM. We cannot reuse if transfer + * has different Auth input parameters. + * Empty user: Negotiate on Windows can make use of an "ambient" + * user from a "SecurityToken" associated with the current thread or + * process. This token can be switched at any time. We are therefore + * not able to find out reliably what token the connection really + * used, nor what token in the next connect attempt will use. + * To avoid TOCTOU attacks, do not reuse on empty credentials. */ + if(!m->want_ntlm_http || + !Curl_creds_has_user(conn->creds) || + !Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin)) + return FALSE; + if(!url_allow_sspi_empty_creds(conn->creds, m->data, conn)) + return FALSE; + } + else if(m->want_ntlm_http) { + /* Transfer wants NTLM, connection is not using it. + * Do not reuse when connection has credentials and they differ. */ if(conn->creds && (!Curl_creds_same(conn->creds, m->data->state.creds) || - !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) { - /* connection credentials in play and not the same or not for the - * same origin. */ + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) return FALSE; - } - } - else if(conn->http_ntlm_state != NTLMSTATE_NONE) { - /* Connection is using NTLM auth but we do not want NTLM */ - return FALSE; } #ifndef CURL_DISABLE_PROXY /* Same for Proxy NTLM authentication */ - if(m->want_proxy_ntlm_http) { - /* Both conn->http_proxy.user and conn->http_proxy.passwd can be - * NULL */ - if(!conn->http_proxy.creds) + if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { + if(!m->want_proxy_ntlm_http || + !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; - - if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) + if(!url_allow_sspi_empty_creds(m->needle->http_proxy.creds, + m->data, conn)) return FALSE; } - else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { - /* Proxy connection is using NTLM auth but we do not want NTLM */ - return FALSE; + else if(m->want_proxy_ntlm_http) { + if(conn->http_proxy.creds && + !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) + return FALSE; } #endif if(m->want_ntlm_http || m->want_proxy_ntlm_http) { @@ -967,34 +999,39 @@ static bool url_match_auth_ntlm(struct connectdata *conn, static bool url_match_auth_nego(struct connectdata *conn, struct url_conn_match *m) { - /* If we are looking for an HTTP+Negotiate connection, check if this is - already authenticating with the right credentials. If not, keep looking - so that we can reuse Negotiate connections if possible. */ - if(m->want_nego_http) { + if(conn->http_negotiate_state != GSS_AUTHNONE) { + /* Connection is using Negotiate. We cannot reuse if transfer + * has different Auth input parameters. */ + if(!m->want_nego_http || + !Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin)) + return FALSE; + if(!url_allow_sspi_empty_creds(conn->creds, m->data, conn)) + return FALSE; + } + else if(m->want_nego_http) { + /* Transfer wants Negotiate, connection is not using it. + * Do not reuse when connection has credentials and they differ. */ if(conn->creds && (!Curl_creds_same(conn->creds, m->data->state.creds) || !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) return FALSE; } - else if(conn->http_negotiate_state != GSS_AUTHNONE) { - /* Connection is using Negotiate auth but we do not want Negotiate */ - return FALSE; - } #ifndef CURL_DISABLE_PROXY /* Same for Proxy Negotiate authentication */ - if(m->want_proxy_nego_http) { - /* Both conn->http_proxy.user and conn->http_proxy.passwd can be - * NULL */ - if(!conn->http_proxy.creds) + if(conn->proxy_negotiate_state != GSS_AUTHNONE) { + if(!m->want_proxy_nego_http || + !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; - - if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) + if(!url_allow_sspi_empty_creds(m->needle->http_proxy.creds, + m->data, conn)) return FALSE; } - else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { - /* Proxy connection is using Negotiate auth but we do not want Negotiate */ - return FALSE; + else if(m->want_proxy_nego_http) { + if(conn->http_proxy.creds && + !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) + return FALSE; } #endif if(m->want_nego_http || m->want_proxy_nego_http) { @@ -2370,11 +2407,12 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) DEBUGF(curl_mfprintf(stderr, "Error: init connection SSL config\n")); goto out; } - /* attach it and no longer own it */ + + /* Add needle to conn pool, which assigns the connection id. + * Attach regardless of result, for correct handling. */ + result = Curl_cpool_add(data, needle); Curl_attach_connection(data, needle); needle = NULL; - - result = Curl_cpool_add(data, data->conn); if(result) goto out; From 507ee5d79215ee8026db35b03925087c48e03318 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 12 Aug 2026 17:30:40 +0200 Subject: [PATCH 0875/1062] progress: fix progress meter update to run every second again Closes #22562 --- lib/progress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/progress.c b/lib/progress.c index 915f55258fff..82bf5bb83b28 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -527,7 +527,7 @@ static bool progress_calc(struct Curl_easy *data, } if((p->delta.lastshow_us >= 0) && !data->req.done && - ((elapsed_us - p->delta.lastshow_us) >= (1000 * 1000))) + ((elapsed_us - p->delta.lastshow_us) < (1000 * 1000))) return FALSE; p->delta.lastshow_us = elapsed_us; return TRUE; From 743096a4964bf921f7b39cc7b739fd4fab6ad39c Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 12 Aug 2026 15:49:39 +0200 Subject: [PATCH 0876/1062] uint-bset: add slot0 member Add a fixed slot0 member for uint32_bset to have no allocations for sizes < 64. This is a common use case for curl_easy_perform(). Closes #22561 --- lib/multi.c | 2 ++ lib/uint-bset.c | 21 +++++++++++++++------ lib/uint-bset.h | 1 + tests/unit/unit3211.c | 5 +++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index dd4b9bfe8ab7..013aefc63ff2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -432,6 +432,8 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, /* make it a 64 multiple, since our bitsets grow by that and * small (easy_multi) grows to at least 64 on first resize. */ new_size = (((used + min_unused) + 63) / 64) * 64; + if(new_size < 256) /* don't be too shy about it */ + new_size = 256; } } } diff --git a/lib/uint-bset.c b/lib/uint-bset.c index 55aedb234b05..f734f86ffdbc 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -44,14 +44,22 @@ CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax) DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); if(nslots != bset->nslots) { - uint64_t *slots = curlx_calloc(nslots, sizeof(uint64_t)); - if(!slots) - return CURLE_OUT_OF_MEMORY; + uint64_t *slots; + if(nslots > 1) { + slots = curlx_calloc(nslots, sizeof(uint64_t)); + if(!slots) + return CURLE_OUT_OF_MEMORY; + } + else { + bset->slot0 = 0; + slots = &bset->slot0; + } - if(bset->slots) { + if((bset->slots != slots) && nslots && bset->nslots) { memcpy(slots, bset->slots, (CURLMIN(nslots, bset->nslots) * sizeof(uint64_t))); - curlx_free(bset->slots); + if(bset->slots != &bset->slot0) + curlx_free(bset->slots); } bset->slots = slots; bset->nslots = nslots; @@ -63,7 +71,8 @@ CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax) void Curl_uint32_bset_destroy(struct uint32_bset *bset) { DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); - curlx_free(bset->slots); + if(bset->slots != &bset->slot0) + curlx_free(bset->slots); memset(bset, 0, sizeof(*bset)); } diff --git a/lib/uint-bset.h b/lib/uint-bset.h index ce0450c53ca9..d4e2972ad169 100644 --- a/lib/uint-bset.h +++ b/lib/uint-bset.h @@ -39,6 +39,7 @@ struct uint32_bset { uint64_t *slots; + uint64_t slot0; uint32_t nslots; uint32_t first_slot_used; #ifdef DEBUGBUILD diff --git a/tests/unit/unit3211.c b/tests/unit/unit3211.c index 0d1dde200d23..fea99301a1b2 100644 --- a/tests/unit/unit3211.c +++ b/tests/unit/unit3211.c @@ -126,6 +126,10 @@ static CURLcode test_unit3211(const char *arg) { UNITTEST_BEGIN_SIMPLE + static const uint32_t s0[] = { + /* spread numbers, some at slot edges */ + 0, 1, 4, 5, 8, 13, 17, 23, 24, 63, + }; static const uint32_t s1[] = { /* spread numbers, some at slot edges */ 0, 1, 4, 17, 63, 64, 65, 66, 90, 99, @@ -142,6 +146,7 @@ static CURLcode test_unit3211(const char *arg) 120, 121, 122, 123, 124, 125, 126, 127, }; + check_set("s0", 64, s0, CURL_ARRAYSIZE(s1)); check_set("s1", 100, s1, CURL_ARRAYSIZE(s1)); check_set("s2", 1000, s2, CURL_ARRAYSIZE(s2)); From e9b291cf9bd61622f6589d747036d3880eb28b2d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 10 Aug 2026 13:47:47 +0200 Subject: [PATCH 0877/1062] header api: add guards Add API guards for curl_easy_header and curl_easy_nextheader. urldata.h: remove prevhead member as not used Closes #22530 --- lib/api.c | 20 ++++ lib/api.h | 5 + lib/headers.c | 223 +++++++++++++++++++++++----------------- lib/urldata.h | 1 - tests/libtest/lib1940.c | 41 ++++++++ 5 files changed, 192 insertions(+), 98 deletions(-) diff --git a/lib/api.c b/lib/api.c index 39147417baa9..15e3789861ab 100644 --- a/lib/api.c +++ b/lib/api.c @@ -43,6 +43,8 @@ static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { { CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 }, { CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 }, { CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 }, + { CURL_EAPI_FN_easy_header, 0, 1, 0, 0 }, + { CURL_EAPI_FN_easy_nextheader, 0, 1, 0, 0 }, { CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 }, { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 }, { CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 }, @@ -303,6 +305,24 @@ void Curl_eapi_leave(struct Curl_eapi_guard *guard) } } +CURLHcode Curl_eapi_hcode(CURLcode result) +{ + switch(result) { + case CURLE_OK: + return CURLHE_OK; + case CURLE_BAD_FUNCTION_ARGUMENT: + return CURLHE_BAD_ARGUMENT; + case CURLE_OUT_OF_MEMORY: + return CURLHE_OUT_OF_MEMORY; + case CURLE_NOT_BUILT_IN: + return CURLHE_NOT_BUILT_IN; + default: + /* Unfortunately, we cannot convert RECURSIVE_API_CALL, + * but since the header API is reentrant, this should not happen. */ + return CURLHE_BAD_ARGUMENT; + } +} + bool Curl_mapi_enter(struct Curl_mapi_guard *guard, CURLM *m, Curl_mapi_fn fn, diff --git a/lib/api.h b/lib/api.h index c6810a392ebb..642a09879e0f 100644 --- a/lib/api.h +++ b/lib/api.h @@ -57,6 +57,8 @@ typedef enum { CURL_EAPI_FN_easy_cleanup, CURL_EAPI_FN_easy_duphandle, CURL_EAPI_FN_easy_getinfo, + CURL_EAPI_FN_easy_header, + CURL_EAPI_FN_easy_nextheader, CURL_EAPI_FN_easy_pause, CURL_EAPI_FN_easy_perform_ev, CURL_EAPI_FN_easy_perform, @@ -151,6 +153,9 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, CURLcode *presult); void Curl_eapi_leave(struct Curl_eapi_guard *guard); +/* Convert an EAPI failure to a header API result */ +CURLHcode Curl_eapi_hcode(CURLcode result); + /* Curl_eapi_enter() checks for curl being NULL, but windows compiler * analyzers do not realize this. *sigh* */ #define CURL_EAPI_ENTER(g, curl, fn, r) \ diff --git a/lib/headers.c b/lib/headers.c index 3bc9c3aceba0..a6416b0f07b2 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -59,62 +59,84 @@ CURLHcode curl_easy_header(CURL *curl, int request, struct curl_header **hout) { - struct Curl_llist_node *e; - struct Curl_llist_node *e_pick = NULL; - struct Curl_easy *data = curl; - size_t match = 0; - size_t amount = 0; - struct Curl_header_store *hs = NULL; - struct Curl_header_store *pick = NULL; - if(!name || !hout || !data || - (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | - CURLH_PSEUDO)) || !origin || (request < -1)) - return CURLHE_BAD_ARGUMENT; - if(!Curl_llist_count(&data->state.httphdrs)) - return CURLHE_NOHEADERS; /* no headers available */ - if(request > data->state.requests) - return CURLHE_NOREQUEST; - if(request == -1) - request = data->state.requests; - - /* we need a first round to count amount of this header */ - for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { - hs = Curl_node_elem(e); - if(curl_strequal(hs->name, name) && - (hs->type & origin) && - (hs->request == request)) { - amount++; - pick = hs; - e_pick = e; + struct Curl_eapi_guard guard; + CURLHcode hresult = CURLHE_OK; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_header, &result)) { + struct Curl_easy *data = curl; + struct Curl_llist_node *e; + struct Curl_llist_node *e_pick = NULL; + size_t match = 0; + size_t amount = 0; + struct Curl_header_store *hs = NULL; + struct Curl_header_store *pick = NULL; + if(!name || !hout || !data || + (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | + CURLH_PSEUDO)) || !origin || (request < -1)) { + hresult = CURLHE_BAD_ARGUMENT; + goto out; } - } - if(!amount) - return CURLHE_MISSING; - else if(nameindex >= amount) - return CURLHE_BADINDEX; - - if(nameindex == amount - 1) - /* if the last or only occurrence is what's asked for, then we know it */ - hs = pick; - else { + if(!Curl_llist_count(&data->state.httphdrs)) { + hresult = CURLHE_NOHEADERS; /* no headers available */ + goto out; + } + if(request > data->state.requests) { + hresult = CURLHE_NOREQUEST; + goto out; + } + if(request == -1) + request = data->state.requests; + + /* we need a first round to count amount of this header */ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && (hs->type & origin) && - (hs->request == request) && - (match++ == nameindex)) { + (hs->request == request)) { + amount++; + pick = hs; e_pick = e; - break; } } - if(!e) /* this should not happen */ - return CURLHE_MISSING; + if(!amount) + hresult = CURLHE_MISSING; + else if(nameindex >= amount) + hresult = CURLHE_BADINDEX; + if(hresult) + goto out; + + if(nameindex == amount - 1) + /* if the last or only occurrence is what's asked for, then we know it */ + hs = pick; + else { + for(e = Curl_llist_head(&data->state.httphdrs); e; + e = Curl_node_next(e)) { + hs = Curl_node_elem(e); + if(curl_strequal(hs->name, name) && + (hs->type & origin) && + (hs->request == request) && + (match++ == nameindex)) { + e_pick = e; + break; + } + } + if(!e) { /* this should not happen */ + hresult = CURLHE_MISSING; + goto out; + } + } + /* this is the name we want */ + copy_header_external(hs, nameindex, amount, e_pick, + &data->state.headerout[0]); + *hout = &data->state.headerout[0]; + hresult = CURLHE_OK; } - /* this is the name we want */ - copy_header_external(hs, nameindex, amount, e_pick, - &data->state.headerout[0]); - *hout = &data->state.headerout[0]; - return CURLHE_OK; +out: + CURL_EAPI_LEAVE(&guard); + if(result) + hresult = Curl_eapi_hcode(result); + return hresult; } /* public API */ @@ -123,59 +145,68 @@ struct curl_header *curl_easy_nextheader(CURL *curl, int request, struct curl_header *prev) { - struct Curl_easy *data = curl; - struct Curl_llist_node *pick; - struct Curl_llist_node *e; - struct Curl_header_store *hs; - size_t amount = 0; - size_t index = 0; + struct Curl_eapi_guard guard; + struct curl_header *hd = NULL; + CURLcode result; - if(request > data->state.requests) - return NULL; - if(request == -1) - request = data->state.requests; + if(CURL_EAPI_ENTER(&guard, curl, easy_nextheader, &result)) { + struct Curl_easy *data = curl; + struct Curl_llist_node *pick; + struct Curl_llist_node *e; + struct Curl_header_store *hs; + size_t amount = 0; + size_t index = 0; + + if(request > data->state.requests) + goto out; + if(request == -1) + request = data->state.requests; + + if(prev) { + pick = prev->anchor; + if(!pick) + /* something is wrong */ + goto out; + pick = Curl_node_next(pick); + } + else + pick = Curl_llist_head(&data->state.httphdrs); + + if(pick) { + /* make sure it is the next header of the desired type */ + do { + hs = Curl_node_elem(pick); + if((hs->type & origin) && (hs->request == request)) + break; + pick = Curl_node_next(pick); + } while(pick); + } - if(prev) { - pick = prev->anchor; if(!pick) - /* something is wrong */ - return NULL; - pick = Curl_node_next(pick); - } - else - pick = Curl_llist_head(&data->state.httphdrs); - - if(pick) { - /* make sure it is the next header of the desired type */ - do { - hs = Curl_node_elem(pick); - if((hs->type & origin) && (hs->request == request)) - break; - pick = Curl_node_next(pick); - } while(pick); - } + /* no more headers available */ + goto out; - if(!pick) - /* no more headers available */ - return NULL; - - hs = Curl_node_elem(pick); - - /* count number of occurrences of this name within the mask and figure out - the index for the currently selected entry */ - for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { - struct Curl_header_store *check = Curl_node_elem(e); - if(curl_strequal(hs->name, check->name) && - (check->request == request) && - (check->type & origin)) - amount++; - if(e == pick) - index = amount - 1; - } + hs = Curl_node_elem(pick); + + /* count number of occurrences of this name within the mask and figure out + the index for the currently selected entry */ + for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { + struct Curl_header_store *check = Curl_node_elem(e); + if(curl_strequal(hs->name, check->name) && + (check->request == request) && + (check->type & origin)) + amount++; + if(e == pick) + index = amount - 1; + } - copy_header_external(hs, index, amount, pick, - &data->state.headerout[1]); - return &data->state.headerout[1]; + copy_header_external(hs, index, amount, pick, + &data->state.headerout[1]); + hd = &data->state.headerout[1]; + } +out: + CURL_EAPI_LEAVE(&guard); + return hd; } static CURLcode namevalue(char *header, size_t hlen, unsigned int type, @@ -271,7 +302,6 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, /* insert this node into the list of headers */ Curl_llist_append(&data->state.httphdrs, hs, &hs->node); - data->state.prevhead = hs; } else { failf(data, "Invalid response header"); @@ -286,7 +316,6 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, static void headers_reset(struct Curl_easy *data) { Curl_llist_init(&data->state.httphdrs, NULL); - data->state.prevhead = NULL; } struct hds_cw_collect_ctx { diff --git a/lib/urldata.h b/lib/urldata.h index b2798eb4f2b9..6e17adcf433a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -630,7 +630,6 @@ struct UrlState { headers */ struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout[2]; /* for external purposes */ - struct Curl_header_store *prevhead; /* the latest added header */ #endif #ifndef CURL_DISABLE_COOKIES struct curl_slist *cookielist; /* list of cookie files set by diff --git a/tests/libtest/lib1940.c b/tests/libtest/lib1940.c index a0fea9264f5f..8fbafd5fedcd 100644 --- a/tests/libtest/lib1940.c +++ b/tests/libtest/lib1940.c @@ -68,6 +68,45 @@ static void t1940_showem(CURL *curl, int header_request, unsigned int type) } } +static CURLHcode t1940_negative(CURL *curl, int req_index) +{ + struct curl_header *hd1, *hd2; + CURLHcode result; + + result = curl_easy_header(NULL, "x", 0, CURLH_HEADER, -1, &hd1); + if(result != CURLHE_BAD_ARGUMENT) + return result; + + result = curl_easy_header(curl, NULL, 0, CURLH_HEADER, -1, &hd1); + if(result != CURLHE_BAD_ARGUMENT) + return result; + + result = curl_easy_header(curl, "x", 0, CURLH_HEADER, -1, NULL); + if(result != CURLHE_BAD_ARGUMENT) + return result; + + result = curl_easy_header(curl, "x", 0, 0, -1, &hd1); + if(result != CURLHE_BAD_ARGUMENT) + return result; + + result = curl_easy_header(curl, "date", 0, CURLH_HEADER, req_index, &hd1); + if(result != CURLHE_OK) + return result; + if(!hd1) + return CURLHE_NOHEADERS; + + /* Should give the first header */ + hd2 = curl_easy_nextheader(curl, CURLH_HEADER, -1, NULL); + if(!hd2) + return CURLHE_BADINDEX; + + hd2 = curl_easy_nextheader(NULL, CURLH_HEADER, -1, hd1); + if(hd2) /* should not work */ + return CURLHE_BADINDEX; + + return CURLHE_OK; +} + static CURLcode test_lib1940(const char *URL) { CURL *curl = NULL; @@ -106,6 +145,8 @@ static CURLcode test_lib1940(const char *URL) t1940_showem(curl, header_request, CURLH_1XX); t1940_showem(curl, header_request, CURLH_TRAILER); + result = (CURLcode)t1940_negative(curl, header_request); + test_cleanup: curl_easy_cleanup(curl); curl_global_cleanup(); From 3089e7eec8b694ff36a3aa700c4a319d656806e7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 Aug 2026 23:36:38 +0200 Subject: [PATCH 0878/1062] multi: hold timeout values in 'int' instead of 'long' Since this code needs to work (identically) on systems with 32 bit longs anyway, this might just as well always use 32 bit to save space and make the code work the same everywhere. Closes #22564 --- lib/multi.c | 30 ++++++++++++++++-------------- lib/multihandle.h | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 013aefc63ff2..037db1af3927 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -77,7 +77,7 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_easy *data); static void multi_timeout(struct Curl_multi *multi, struct curltime *expire_time, - long *timeout_ms); + int *timeout_ms); static void multi_schedule_pending(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); #ifdef DEBUGBUILD @@ -1538,7 +1538,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, { size_t i; struct curltime expire_time; - long timeout_internal; + int timeout_internal; int nevents = 0; struct easy_pollset ps; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; @@ -1617,11 +1617,11 @@ static CURLMcode multi_wait(struct Curl_multi *multi, * If we are called with `!extrawait` and multi_timeout() reports no * timeouts exist, do not wait. */ multi_timeout(multi, &expire_time, &timeout_internal); - if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) - timeout_ms = (int)timeout_internal; + if((timeout_internal >= 0) && (timeout_internal < timeout_ms)) + timeout_ms = timeout_internal; if(data) - CURL_TRC_M(data, "multi_wait(fds=%u, timeout=%d) tinternal=%ld", + CURL_TRC_M(data, "multi_wait(fds=%u, timeout=%d) tinternal=%d", cpfds.n, timeout_ms, timeout_internal); #ifdef USE_WINSOCK @@ -3504,7 +3504,7 @@ static bool multi_has_dirties(struct Curl_multi *multi) static void multi_timeout(struct Curl_multi *multi, struct curltime *expire_time, - long *timeout_ms) + int *timeout_ms) { static const struct curltime tv_zero = { 0, 0 }; VERBOSE(struct Curl_easy *data = NULL); @@ -3536,9 +3536,9 @@ static void multi_timeout(struct Curl_multi *multi, timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, *pnow); VERBOSE(data = Curl_splayget(multi->timetree)); - /* this should be safe even on 32-bit archs, as we do not use that - overly long timeouts */ - *timeout_ms = (long)diff_ms; + if(diff_ms > INT_MAX) + diff_ms = INT_MAX; + *timeout_ms = (int)diff_ms; } else { if(multi->timetree) @@ -3556,7 +3556,7 @@ static void multi_timeout(struct Curl_multi *multi, if(CURL_TRC_TIMER_is_verbose(data) && (data->state.timeouts.first < EXPIRE_LAST)) { CURL_TRC_TIMER(data, data->state.timeouts.first, - "gives multi timeout in %ldms", *timeout_ms); + "gives multi timeout in %dms", *timeout_ms); } #endif } @@ -3569,8 +3569,10 @@ CURLMcode curl_multi_timeout(CURLM *m, if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) { struct curltime expire_time; + int itimeout_ms; - multi_timeout(m, &expire_time, timeout_ms); + multi_timeout(m, &expire_time, &itimeout_ms); + *timeout_ms = (long)itimeout_ms; mresult = CURLM_OK; } CURL_MAPI_LEAVE(&guard); @@ -3584,7 +3586,7 @@ CURLMcode curl_multi_timeout(CURLM *m, CURLMcode Curl_update_timer(struct Curl_multi *multi) { struct curltime expire_ts = { 0, 0 }; - long timeout_ms; + int timeout_ms; int rc; bool set_value = FALSE; @@ -3602,14 +3604,14 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) set_value = TRUE; } else if(multi->last_timeout_ms < 0) { - CURL_TRC_M(multi->admin, "[TIMER] set %ldms, none before", timeout_ms); + CURL_TRC_M(multi->admin, "[TIMER] set %dms, none before", timeout_ms); set_value = TRUE; } else if(curlx_ptimediff_us(&multi->last_expire_ts, &expire_ts)) { /* We had a timeout before and have one now, the absolute timestamp * differs. The relative timeout_ms may be the same, but the starting * point differs. Let the application restart its timer. */ - CURL_TRC_M(multi->admin, "[TIMER] set %ldms, replace previous", + CURL_TRC_M(multi->admin, "[TIMER] set %dms, replace previous", timeout_ms); set_value = TRUE; } diff --git a/lib/multihandle.h b/lib/multihandle.h index 60d49af1e952..b9a6628f971e 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -169,7 +169,7 @@ struct Curl_multi { /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; void *timer_userp; - long last_timeout_ms; /* the last timeout value set via timer_cb */ + int last_timeout_ms; /* the last timeout value set via timer_cb */ struct curltime last_expire_ts; /* timestamp of last expiry */ #ifdef USE_WINSOCK From 6a6edec2c6ee7c401b6262c385483e0a7f237dd4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 Aug 2026 00:03:27 +0200 Subject: [PATCH 0879/1062] multihandle: move two struct fields To avoid holes on 64-bit architectures. Closes #22565 --- lib/multihandle.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/multihandle.h b/lib/multihandle.h index b9a6628f971e..2e749244f29b 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -93,6 +93,7 @@ struct Curl_multi { not yet reached COMPLETE state */ uint32_t xfers_really_alive; /* amount of added transfers that have passed INIT state but are not COMPLETE yet */ + uint32_t max_concurrent_streams; struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ @@ -160,6 +161,7 @@ struct Curl_multi { struct cshutdn cshutdn; /* connection shutdown handling */ struct cpool cpool; /* connection pool (bundles) */ + struct curltime last_expire_ts; /* timestamp of last expiry */ size_t max_host_connections; /* if >0, a fixed limit of the maximum number of connections per host */ @@ -170,7 +172,6 @@ struct Curl_multi { curl_multi_timer_callback timer_cb; void *timer_userp; int last_timeout_ms; /* the last timeout value set via timer_cb */ - struct curltime last_expire_ts; /* timestamp of last expiry */ #ifdef USE_WINSOCK WSAEVENT wsa_event; /* Winsock event used for waits */ @@ -186,7 +187,6 @@ struct Curl_multi { for write. Used for internal wakeups, e.g. threaded resolver. */ #endif - unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of entries we are allowed to grow the connection cache to */ From dea00a6d7386b91dfcf9b87eb5b2d5c24186aea9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 13 Aug 2026 01:01:07 +0200 Subject: [PATCH 0880/1062] cmake: use built-in variable and target property dump functions with CMake 4.5+ Their output is nearly identical to curl's implementations. Minor differences: it goes to stdout (curl: stderr), marks cache variables, but doesn't mark 'advanced' ones. Also: - indent curl's output to match CMake's. - replace single quotes with double to match CMake output. - alpha-sort target properties to match CMake. - alpha-sort config types to match CMake. - tested OK with cmake-4.4.20260811-g567ed0e and 4.4.20260812-g760e4fb. Refs: https://gitlab.kitware.com/cmake/cmake/-/commit/f723293c7f625b14bd48627042fc04e67b76c513 https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11834 https://gitlab.kitware.com/cmake/cmake/-/work_items/27513 https://cmake.org/cmake/help/git-master/command/cmake_language.html#command:cmake_language Follow-up to 855acb3bb07e0dd06b5722218eb5fded333f7ce0 #17701 Follow-up to f58342ae21fe4a04edddd98f255d8b0c3d66ae3a #15562 Follow-up to cd51bb503ae1779a45c90898b89da6e19c616521 #14439 Closes #22566 --- CMake/Utilities.cmake | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index f86a6aa1e832..319160b0a57f 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -37,24 +37,30 @@ endfunction() # Dump all defined variables with their values function(curl_dumpvars) message("::group::CMake Variable Dump") - get_cmake_property(_vars VARIABLES) - foreach(_var IN ITEMS ${_vars}) - get_property(_var_type CACHE ${_var} PROPERTY TYPE) - get_property(_var_advanced CACHE ${_var} PROPERTY ADVANCED) - if(_var_type) - set(_var_type ":${_var_type}") - endif() - if(_var_advanced) - set(_var_advanced " [adv]") - endif() - message("${_var}${_var_type}${_var_advanced} = '${${_var}}'") - endforeach() + if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.5) + cmake_language(PRINT_VARIABLES ALL) + else() + get_cmake_property(_vars VARIABLES) + foreach(_var IN ITEMS ${_vars}) + get_property(_var_type CACHE ${_var} PROPERTY TYPE) + get_property(_var_advanced CACHE ${_var} PROPERTY ADVANCED) + if(_var_type) + set(_var_type ":${_var_type}") + endif() + if(_var_advanced) + set(_var_advanced " [adv]") + endif() + message(" ${_var}${_var_type}${_var_advanced} = \"${${_var}}\"") + endforeach() + endif() message("::endgroup::") endfunction() # Dump all target properties function(curl_dumptargetprops _target) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND TARGET "${_target}") + if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.5) + cmake_language(PRINT_PROPERTIES TARGETS "${_target}" ALL) + elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND TARGET "${_target}") execute_process(COMMAND "${CMAKE_COMMAND}" "--help-property-list" OUTPUT_VARIABLE _cmake_property_list) string(REPLACE "\n" ";" _cmake_property_list "${_cmake_property_list}") list(REMOVE_DUPLICATES _cmake_property_list) @@ -64,21 +70,22 @@ function(curl_dumptargetprops _target) if(NOT _target_imported) list(REMOVE_ITEM _cmake_property_list "LOCATION" "LOCATION_" "MACOSX_PACKAGE_LOCATION" "VS_DEPLOYMENT_LOCATION") endif() + list(SORT _cmake_property_list) foreach(_prop IN LISTS _cmake_property_list) if(_prop MATCHES "") - foreach(_config IN ITEMS "DEBUG" "RELEASE" "MINSIZEREL" "RELWITHDEBINFO") + foreach(_config IN ITEMS "DEBUG" "MINSIZEREL" "RELEASE" "RELWITHDEBINFO") string(REPLACE "" "${_config}" _propconfig "${_prop}") get_property(_is_set TARGET "${_target}" PROPERTY "${_propconfig}" SET) if(_is_set) get_target_property(_val "${_target}" "${_propconfig}") - message("${_target}.${_propconfig} = '${_val}'") + message(" ${_target}.${_propconfig} = \"${_val}\"") endif() endforeach() else() get_property(_is_set TARGET "${_target}" PROPERTY "${_prop}" SET) if(_is_set) get_target_property(_val "${_target}" "${_prop}") - message("${_target}.${_prop} = '${_val}'") + message(" ${_target}.${_prop} = \"${_val}\"") endif() endif() endforeach() From 9d034539ece1e04f6fb7cb9b7ea02111312f7456 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 13 Aug 2026 10:39:28 +0200 Subject: [PATCH 0881/1062] tool_operate: limit `is_using_schannel()` call to Windows Follow-up to cd238bb7addf5b1f174df47ccc61651a545c4876 #22552 Closes #22568 --- src/tool_operate.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index 25b7973d2c05..585a12dab77f 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2196,13 +2196,6 @@ static CURLcode is_using_schannel(int *pusing) *pusing = using_schannel; return result; } -#else -/* on non-Windows it can never use Schannel */ -static CURLcode is_using_schannel(int *pusing) -{ - *pusing = 0; /* never */ - return CURLE_OK; -} #endif /* Set the CA cert locations specified in the environment. For Windows if no @@ -2220,15 +2213,19 @@ static CURLcode cacertpaths(struct OperationConfig *config) { char *env; CURLcode result; +#ifdef _WIN32 int using_schannel; +#endif if(!feature_ssl || config->cacert || config->capath || (config->insecure_ok && (!config->doh_url || config->doh_insecure_ok))) return CURLE_OK; +#ifdef _WIN32 result = is_using_schannel(&using_schannel); if(result || using_schannel) return result; +#endif env = curl_getenv("CURL_CA_BUNDLE"); if(env) { From ca26e728a62b8f22032f29445c8e4e865cb9e3ad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 Aug 2026 11:34:42 +0200 Subject: [PATCH 0882/1062] conncache: don't assume curl_off_t increment wrap-around In the totally unlikely event that next_easy_id would ever wrap, avoid undefined behavior. It is a signed type. Also: update the related comment in urldata.h that mentioned LONG_MAX but is nowadays CURL_OFF_T_MAX Closes #22569 --- lib/conncache.c | 2 +- lib/urldata.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/conncache.c b/lib/conncache.c index cec444731c1b..d4a71c8a9734 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -274,7 +274,7 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) CPOOL_LOCK(cpool, data); /* the identifier inside the connection cache */ data->id = cpool->next_easy_id++; - if(cpool->next_easy_id <= 0) + if(cpool->next_easy_id == CURL_OFF_T_MAX) cpool->next_easy_id = 0; data->state.lastconnect_id = -1; diff --git a/lib/urldata.h b/lib/urldata.h index 6e17adcf433a..8601a89b8fa2 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1216,9 +1216,9 @@ struct Curl_easy { /* once an easy handle is tied to a connection pool a non-negative number to distinguish this transfer from other using the same pool. For easier - tracking in log output. This may wrap around after LONG_MAX to 0 again, - so it has no uniqueness guarantee for large processings. Note: it has no - uniqueness either IFF more than one connection pool is used by the + tracking in log output. This may wrap around after CURL_OFF_T_MAX to 0 + again, so it has no uniqueness guarantee for large processings. Note: it + has no uniqueness either IFF more than one connection pool is used by the libcurl application. */ curl_off_t id; uint32_t master_mid; /* if set, this transfer belongs to a master */ From 5510e44fcc3a401214cd5f20c415d8af193a7b11 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 Aug 2026 11:51:26 +0200 Subject: [PATCH 0883/1062] tool_operate: refactor schannel checks and move win32 cert checks to function Follow-up to 9d034539ece1e Closes #22570 --- src/tool_operate.c | 100 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index 585a12dab77f..bea8d4509945 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2169,33 +2169,58 @@ static CURLcode serial_transfers(CURLSH *share) } #ifdef _WIN32 -static CURLcode is_using_schannel(int *pusing) +/* returns TRUE if using Schannel or if there is an error, passes back result + in 'resultp' */ +static bool win32_using_schannel(CURLcode *resultp) { - CURLcode result = CURLE_OK; static int using_schannel = -1; /* -1 = not checked 0 = nope 1 = yes */ + *resultp = CURLE_OK; if(using_schannel == -1) { CURL *curltls = curl_easy_init(); /* The TLS backend remains, so keep the info */ const struct curl_tlssessioninfo *tls_backend_info = NULL; if(!curltls) - result = CURLE_OUT_OF_MEMORY; + *resultp = CURLE_OUT_OF_MEMORY; else { - result = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR, - &tls_backend_info); - if(!result) + *resultp = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR, + &tls_backend_info); + if(!*resultp) using_schannel = (tls_backend_info->backend == CURLSSLBACKEND_SCHANNEL); } curl_easy_cleanup(curltls); + if(*resultp) + return TRUE; + } + return using_schannel == 1; +} + +static CURLcode win32_setup_certs(struct OperationConfig *config) +{ + if(!config->capath && !config->cacert) { +#ifdef CURL_CA_SEARCH_SAFE + char *cacert = NULL; + FILE *cafile = tool_execpath("curl-ca-bundle.crt", &cacert); + if(cafile) { + curlx_fclose(cafile); + config->cacert = curlx_strdup(cacert); + if(!config->cacert) + return CURLE_OUT_OF_MEMORY; + } +#elif !defined(CURL_WINDOWS_UWP) && !defined(CURL_DISABLE_CA_SEARCH) + CURLcode result = FindWin32CACert(config, TEXT("curl-ca-bundle.crt")); if(result) return result; +#endif } - *pusing = using_schannel; - return result; + return CURLE_OK; } +#else +#define win32_setup_certs(x) CURLE_OK +#define win32_using_schannel(x) FALSE #endif /* Set the CA cert locations specified in the environment. For Windows if no @@ -2212,74 +2237,47 @@ static CURLcode is_using_schannel(int *pusing) static CURLcode cacertpaths(struct OperationConfig *config) { char *env; - CURLcode result; -#ifdef _WIN32 - int using_schannel; -#endif + CURLcode result = CURLE_OK; if(!feature_ssl || config->cacert || config->capath || (config->insecure_ok && (!config->doh_url || config->doh_insecure_ok))) return CURLE_OK; -#ifdef _WIN32 - result = is_using_schannel(&using_schannel); - if(result || using_schannel) + if(win32_using_schannel(&result)) return result; -#endif env = curl_getenv("CURL_CA_BUNDLE"); if(env) { config->cacert = curlx_strdup(env); curl_free(env); - if(!config->cacert) { + if(!config->cacert) result = CURLE_OUT_OF_MEMORY; - goto fail; - } } else { env = curl_getenv("SSL_CERT_DIR"); if(env) { config->capath = curlx_strdup(env); curl_free(env); - if(!config->capath) { + if(!config->capath) result = CURLE_OUT_OF_MEMORY; - goto fail; - } } - env = curl_getenv("SSL_CERT_FILE"); - if(env) { - config->cacert = curlx_strdup(env); - curl_free(env); - if(!config->cacert) { - result = CURLE_OUT_OF_MEMORY; - goto fail; + if(!result) { + env = curl_getenv("SSL_CERT_FILE"); + if(env) { + config->cacert = curlx_strdup(env); + curl_free(env); + if(!config->cacert) + result = CURLE_OUT_OF_MEMORY; } } } -#ifdef _WIN32 - if(!config->capath && !config->cacert) { -#ifdef CURL_CA_SEARCH_SAFE - char *cacert = NULL; - FILE *cafile = tool_execpath("curl-ca-bundle.crt", &cacert); - if(cafile) { - curlx_fclose(cafile); - config->cacert = curlx_strdup(cacert); - if(!config->cacert) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - } -#elif !defined(CURL_WINDOWS_UWP) && !defined(CURL_DISABLE_CA_SEARCH) - result = FindWin32CACert(config, TEXT("curl-ca-bundle.crt")); - if(result) - goto fail; -#endif + if(!result) + result = win32_setup_certs(config); + if(result) { + curlx_safefree(config->capath); + curlx_safefree(config->cacert); } -#endif - return CURLE_OK; -fail: - curlx_safefree(config->capath); return result; } From bec8012214afddccf72dfe680a755e394eae6312 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Mon, 10 Aug 2026 12:42:40 -0700 Subject: [PATCH 0884/1062] libcurl.pc: Add Copyright tag to the pkgconf file Follow-up to 3f1c033afe008123680306663cc01279e831ac2d #22519 --- libcurl.pc.in | 1 + 1 file changed, 1 insertion(+) diff --git a/libcurl.pc.in b/libcurl.pc.in index 66296f5e576f..8f7e35dd1097 100644 --- a/libcurl.pc.in +++ b/libcurl.pc.in @@ -33,6 +33,7 @@ Name: libcurl URL: https://curl.se/ Description: Library to transfer files with HTTP, FTP, etc. License: curl +Copyright: Copyright (C) Daniel Stenberg, , et al. Maintainer: @CURL_PACKAGE_MAINTAINER@ Version: @CURLVERSION@ Source: https://curl.se/download/curl-@CURLVERSION@.tar.gz From 82b289c9527890a0766b55be2214b3749283307a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 13 Aug 2026 14:02:26 +0200 Subject: [PATCH 0885/1062] test_20_11: more leniency Loosen the RSS increase check slightly to make the test less flaky. Also use 'rss-max' stat. Closes #22574 --- tests/http/test_20_websockets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 70c00797194a..8f8b2efaf07c 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -308,7 +308,7 @@ def srv(): with_profile=True) assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR assert r.profile, f'{r}' - rss1 = r.profile.stats['rss'] / (1024 * 1024) + rss1 = r.profile.stats['rss-max'] / (1024 * 1024) st.clear() send_rounds = 10 @@ -322,8 +322,8 @@ def srv(): with_profile=True) assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR assert r.profile, f'{r}' - rss2 = r.profile.stats['rss'] / (1024 * 1024) - assert (rss1 * 1.1) >= rss2, 'bad memory increase' + rss2 = r.profile.stats['rss-max'] / (1024 * 1024) + assert (rss1 * 1.2) > rss2, 'bad memory increase' # test small frames delivery when pausing def test_20_12_pause_frames_small(self, env: Env, ws_4frames): From 68b5e6013878b1842e70c2af2f23d67427216910 Mon Sep 17 00:00:00 2001 From: Thomas Chauchefoin Date: Thu, 13 Aug 2026 17:27:57 +0200 Subject: [PATCH 0886/1062] dict: avoid busy-loop in sendf() when the socket is not writable Curl_xfer_send() reports CURLE_AGAIN as a successful zero-byte send, so the retry loop spun at 100% CPU and ignored the timeout when the peer stopped draining. Wait for writability and check the remaining time between retries. Closes #22576 --- lib/dict.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/dict.c b/lib/dict.c index 2a0053e9343a..75cdac371453 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -55,6 +55,8 @@ #include "transfer.h" #include "curl_trc.h" +#include "connect.h" +#include "select.h" #include "escape.h" #define DICT_MATCH "/MATCH:" @@ -93,9 +95,12 @@ static CURLcode sendf(struct Curl_easy *data, static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) { + curl_socket_t sockfd = data->conn->sock[FIRSTSOCKET]; size_t bytes_written; size_t write_len; CURLcode result = CURLE_OK; + timediff_t timeout_ms; + int what; char *s; char *sptr; va_list ap; @@ -126,6 +131,29 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) } else break; + + timeout_ms = Curl_timeleft_ms(data); + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + + /* Do not busyloop. The entire loop thing is a workaround as it causes a + BLOCKING behavior which is a NO-NO. This function should rather be + split up in a do and a doing piece where the pieces that are not + possible to send now will be sent in the doing function repeatedly + until the entire request is sent. */ + what = SOCKET_WRITABLE(sockfd, timeout_ms); + if(what < 0) { + result = CURLE_SEND_ERROR; + break; + } + else if(!what) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } } curlx_free(s); /* free the output string */ From e825e5586733af9049d7cff8b4c1b4fd46e7b2f9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 14 Aug 2026 10:36:39 +0200 Subject: [PATCH 0887/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 76 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index d04d10fc1c8e..dc872260e1ea 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1503 - Contributors: 3761 + Authors: 1506 + Contributors: 3765 This release includes the following changes: @@ -22,8 +22,13 @@ This release includes the following bugfixes: o autotools: minor fixes and improvements [33] o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] o build: assume POSIX `select()` is available [166] + o build: clear `Require.private` for static-only builds in `libcurl.pc` [188] o build: drop superfluous `STDC_HEADERS` macro [51] o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] + o build: minor debug option message fixes/improvements [200] + o build: require `!NDEBUG` for debug-enabled (aka development) builds [202] + o build: strip duplicate spaces after `Libs.private:` in `libcurl.pc` [191] + o build: strip trailing spaces from `libcurl.pc` [194] o cd2nroff: fix backslashes for 4-space indent lines [104] o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: de-duplicate `ngtcp2_conn_client_new()` call code [156] @@ -37,6 +42,7 @@ This release includes the following bugfixes: o cmake: replace `remove` command with `rm` and pass arg safely [11] o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] + o cmake: use built-in variable and target property dump functions with CMake 4.5+ [155] o configure: clarify --enable-debug option [133] o configure: fix misleading error messages [42] o configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows [79] @@ -46,7 +52,9 @@ This release includes the following bugfixes: o conncache: apply multi limits to transfers using a shared pool [41] o conncache: conn upkeep/alive: move and enhance [152] o conncache: connection alive checks intervals [20] + o conncache: don't assume curl_off_t increment wrap-around [138] o connect: connection close tweaks [112] + o content_encoding: exact-match the identity transfer-coding token [189] o content_encoding: give a clear error on multi-member gzip [46] o cookie: refuse to load cookies set against a PSL domain [139] o CREDENTIALS.md: remove comment about empty user/pass [50] @@ -61,14 +69,17 @@ This release includes the following bugfixes: o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] o DEPRECATE.md: HTTP/2 Server Push gets removed in March 2027 [174] + o dict: avoid busy-loop in sendf() when the socket is not writable [99] o dnsd: fix bounds check in `read_https_alpn_part()` [143] o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] + o DoH: improvements [203] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gitignore: maintenance updates [170] o gopher: reject CR and LF in the selector [1] o h2: bootstrap max streams from multi handle if in use [132] o h3-proxy: fix NULL deref when non-:status header arrives before :status [167] + o header api: add guards [168] o HISTORY: add when c-ares support was introduced (2004) o hostip: only cache negative resolves for authoritative answers [16] o http2: make server push transfers inherit share from parent [81] @@ -80,6 +91,8 @@ This release includes the following bugfixes: o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] + o keylog: add a random size argument to Curl_tls_keylog_write() [180] + o ldap: reject control characters in URL-decoded filter values [196] o ldap: support empty username and password [106] o ldap: support insecure mode for Windows native LDAP [3] o lib1587: fix gcc `-Wconversion` with LibreSSL on Windows, test in CI [6] @@ -89,6 +102,10 @@ This release includes the following bugfixes: o lib: fix 'ns' -> 'us' in trace messages [57] o lib: ratelimit timestamps [14] o lib: update mentions of the legacy "sessionhandle" [157] + o libcurl.pc: add `License` tag [190] + o libcurl.pc: add Copyright tag to the pkgconf file + o libcurl.pc: add the Link.ABI and Source tags [210] + o mbedtls: enforce verifyhost when verifypeer is disabled [208] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] o md5: replace magic numbers with `MD5_DIGEST_LEN` [122] o mime.c: avoid integer overflow in base64 size calculation [105] @@ -96,7 +113,10 @@ This release includes the following bugfixes: o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] o multi: forbid curl_easy_pause from within multi socket callback [22] + o multi: hold timeout values in 'int' instead of 'long' [165] o multi: remove #if 0'ed code that uses old struct [150] + o multi: use index list for expire timeouts [197] + o multihandle: move two struct fields [163] o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] @@ -105,6 +125,7 @@ This release includes the following bugfixes: o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] + o progress: cleanup, less memory [179] o psl: update a comment to understandable English [162] o pytest: update two H3 tests for nghttp3 1.18.0+ [158] o quiche: set the max field section size [100] @@ -115,6 +136,7 @@ This release includes the following bugfixes: o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] o schannel: fix error check logic in `get_client_cert()` file reader [144] + o schannel: reuse the send buffer [195] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] o scorecard: fix `max_upload` init value in `ul_parallel()` [142] o scripts/badwords.txt: do not recommend using 'will' in rewrites [141] @@ -149,6 +171,7 @@ This release includes the following bugfixes: o tests: target Python 3.8 as the minimum Python version o tests: use simpler constructions in Python code o thrdpool: retry failed thread starts while items wait [62] + o thrdqueue: drop name strdups from Curl_thrdq_create [181] o tidy-up: `TEXT()` vs `_TEXT()` vs `_T()` use (Windows) [102] o tidy-up: comments, messages, formatting [172] o tidy-up: drop redundant includes [110] @@ -163,13 +186,18 @@ This release includes the following bugfixes: o tool_cb_hdr: de-duplicate filename setter [24] o tool_cb_prg: avoid integer overflows [93] o tool_doswin: add stdin relay auth [130] + o tool_operate: limit `is_using_schannel()` call to Windows [140] + o tool_operate: only check for schannel if on windows [187] o tool_operate: remove call to abort() [23] o tool_xattr: add support for Windows alternate data stream [129] o typecheck-gcc: allow passing `char[]` as callback data [153] + o uint-bset: add slot0 member [173] o uint-spbset: reused empty chunks [67] o unit3214: fix to pass on systems with >=128-bit pointers [107] + o url: fix negotiate/ntlm connection reuse [176] o url: reject control codes in credentials set via CURLOPT [70] o urlapi: allow URLs to not have userauth (hostname) [92] + o urlapi: avoid dedotdotify() if possible [182] o urlapi: clear password buffer on error path [121] o urlapi: do not keep an internal port string [31] o urlapi: improved return codes [148] @@ -205,7 +233,7 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 11soda11, 1rhino2 on hackerone, AlanKingPL, Alb3e3, Alhuda Khan, + 11soda11, 1rhino2 on hackerone, AlanKingPL, Alb3e3, Alhuda Khan, anupamme, Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, Christoph Reiter, claudex on github, Collin Funk, Dan Fandrich, @@ -213,13 +241,14 @@ advice from friends like these: ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, Laurent Sabourin, - Marcel Jamin, Matthew John Cheetham, Memduh Çelik, Patrick Monnerat, - Pavel Sobolev, pszemus on github, Ramesh Adhikari, Ray Satiro, renovate[bot], - Rito Rhymes, RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, - Sam James, Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, - Stefan Eissing, Stephan Zeisberg, stze on hackerone, Viktor Szakats, - xmoezzz on github, Yoshiro Yoneya - (56 contributors) + Marcel Jamin, Martin Dukek, Matthew John Cheetham, Max Dymond, Memduh Çelik, + Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, + Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, RMMoreton on github, + Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, Samuel Dainard, + Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, Stephan Zeisberg, + stze on hackerone, Thomas Chauchefoin, Viktor Szakats, xmoezzz on github, + Yoshiro Yoneya + (61 contributors) References to bug reports and discussions on issues: @@ -321,6 +350,7 @@ References to bug reports and discussions on issues: [96] = https://curl.se/bug/?i=22323 [97] = https://curl.se/bug/?i=22318 [98] = https://curl.se/bug/?i=22317 + [99] = https://curl.se/bug/?i=22576 [100] = https://curl.se/bug/?i=22331 [101] = https://curl.se/bug/?i=22333 [102] = https://curl.se/bug/?i=22334 @@ -357,7 +387,9 @@ References to bug reports and discussions on issues: [134] = https://curl.se/bug/?i=22515 [135] = https://curl.se/bug/?i=22413 [137] = https://curl.se/bug/?i=22341 + [138] = https://curl.se/bug/?i=22569 [139] = https://curl.se/bug/?i=22500 + [140] = https://curl.se/bug/?i=22568 [141] = https://curl.se/bug/?i=22422 [142] = https://curl.se/bug/?i=22421 [143] = https://curl.se/bug/?i=22420 @@ -372,6 +404,7 @@ References to bug reports and discussions on issues: [152] = https://curl.se/bug/?i=21806 [153] = https://curl.se/bug/?i=22409 [154] = https://curl.se/bug/?i=22270 + [155] = https://curl.se/bug/?i=22566 [156] = https://curl.se/bug/?i=22401 [157] = https://curl.se/bug/?i=22463 [158] = https://curl.se/bug/?i=22397 @@ -379,11 +412,34 @@ References to bug reports and discussions on issues: [160] = https://curl.se/bug/?i=22460 [161] = https://curl.se/bug/?i=22505 [162] = https://curl.se/bug/?i=22502 + [163] = https://curl.se/bug/?i=22565 [164] = https://curl.se/bug/?i=22494 + [165] = https://curl.se/bug/?i=22564 [166] = https://curl.se/bug/?i=22448 [167] = https://curl.se/bug/?i=22449 + [168] = https://curl.se/bug/?i=22530 [169] = https://curl.se/bug/?i=22447 [170] = https://curl.se/bug/?i=22445 [171] = https://curl.se/bug/?i=22444 [172] = https://curl.se/bug/?i=22443 + [173] = https://curl.se/bug/?i=22561 [174] = https://curl.se/bug/?i=22490 + [176] = https://curl.se/bug/?i=22528 + [179] = https://curl.se/bug/?i=22547 + [180] = https://curl.se/bug/?i=22560 + [181] = https://curl.se/bug/?i=22555 + [182] = https://curl.se/bug/?i=22557 + [187] = https://curl.se/bug/?i=22552 + [188] = https://curl.se/bug/?i=22548 + [189] = https://curl.se/bug/?i=22541 + [190] = https://curl.se/bug/?i=22545 + [191] = https://curl.se/bug/?i=22544 + [194] = https://curl.se/bug/?i=22536 + [195] = https://curl.se/bug/?i=22540 + [196] = https://curl.se/bug/?i=22524 + [197] = https://curl.se/bug/?i=22473 + [200] = https://curl.se/bug/?i=22532 + [202] = https://curl.se/bug/?i=22484 + [203] = https://curl.se/bug/?i=22514 + [208] = https://curl.se/bug/?i=22475 + [210] = https://curl.se/bug/?i=22519 From a3aaca10d9acf4a51d2bf1fb9ea0b933e18e3f33 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 14 Aug 2026 09:48:46 +0200 Subject: [PATCH 0888/1062] multi: cap expire times to INT_MAX internally When the time-out value is passed to the outside world it needs to fit in a signed 32-bit variable (on Windows and 32-bit architectures) anyway. Also, this is 3.5 weeks and we should not knowingly set timeouts that long anyway. The previous cap introduced in 3089e7eec8b694ff was only partial. Closes #22579 --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/multi.c b/lib/multi.c index 037db1af3927..a340dfcae1e7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3757,6 +3757,11 @@ void Curl_expire_ex(struct Curl_easy *data, if(!multi) return; DEBUGASSERT(eid < EXPIRE_LAST); + DEBUGASSERT(milli <= INT_MAX); + if(milli > INT_MAX) + /* Cap ridiculous timeouts, 31-bit ms is still 3.5 weeks. When the time + goes to the user, it must fit in this size. */ + milli = INT_MAX; set = *Curl_pgrs_now(data); set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */ From 554efab7f69dad4391372e9edc2214eff9ab0fe1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 14 Aug 2026 12:49:15 +0200 Subject: [PATCH 0889/1062] multi: drop the assert from large expires Triggers the fuzzers Closes #22582 --- lib/multi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/multi.c b/lib/multi.c index a340dfcae1e7..b990442a924a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -3757,7 +3757,6 @@ void Curl_expire_ex(struct Curl_easy *data, if(!multi) return; DEBUGASSERT(eid < EXPIRE_LAST); - DEBUGASSERT(milli <= INT_MAX); if(milli > INT_MAX) /* Cap ridiculous timeouts, 31-bit ms is still 3.5 weeks. When the time goes to the user, it must fit in this size. */ From 6e9880366fb2d6cccca532262e4c8218860203e4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 14 Aug 2026 11:40:24 +0200 Subject: [PATCH 0890/1062] Revert "uint-bset: add slot0 member" This reverts commit 743096a4964bf921f7b39cc7b739fd4fab6ad39c. Closes #22581 --- lib/uint-bset.c | 21 ++++++--------------- lib/uint-bset.h | 1 - tests/unit/unit3211.c | 5 ----- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/lib/uint-bset.c b/lib/uint-bset.c index f734f86ffdbc..55aedb234b05 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -44,22 +44,14 @@ CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax) DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); if(nslots != bset->nslots) { - uint64_t *slots; - if(nslots > 1) { - slots = curlx_calloc(nslots, sizeof(uint64_t)); - if(!slots) - return CURLE_OUT_OF_MEMORY; - } - else { - bset->slot0 = 0; - slots = &bset->slot0; - } + uint64_t *slots = curlx_calloc(nslots, sizeof(uint64_t)); + if(!slots) + return CURLE_OUT_OF_MEMORY; - if((bset->slots != slots) && nslots && bset->nslots) { + if(bset->slots) { memcpy(slots, bset->slots, (CURLMIN(nslots, bset->nslots) * sizeof(uint64_t))); - if(bset->slots != &bset->slot0) - curlx_free(bset->slots); + curlx_free(bset->slots); } bset->slots = slots; bset->nslots = nslots; @@ -71,8 +63,7 @@ CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax) void Curl_uint32_bset_destroy(struct uint32_bset *bset) { DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); - if(bset->slots != &bset->slot0) - curlx_free(bset->slots); + curlx_free(bset->slots); memset(bset, 0, sizeof(*bset)); } diff --git a/lib/uint-bset.h b/lib/uint-bset.h index d4e2972ad169..ce0450c53ca9 100644 --- a/lib/uint-bset.h +++ b/lib/uint-bset.h @@ -39,7 +39,6 @@ struct uint32_bset { uint64_t *slots; - uint64_t slot0; uint32_t nslots; uint32_t first_slot_used; #ifdef DEBUGBUILD diff --git a/tests/unit/unit3211.c b/tests/unit/unit3211.c index fea99301a1b2..0d1dde200d23 100644 --- a/tests/unit/unit3211.c +++ b/tests/unit/unit3211.c @@ -126,10 +126,6 @@ static CURLcode test_unit3211(const char *arg) { UNITTEST_BEGIN_SIMPLE - static const uint32_t s0[] = { - /* spread numbers, some at slot edges */ - 0, 1, 4, 5, 8, 13, 17, 23, 24, 63, - }; static const uint32_t s1[] = { /* spread numbers, some at slot edges */ 0, 1, 4, 17, 63, 64, 65, 66, 90, 99, @@ -146,7 +142,6 @@ static CURLcode test_unit3211(const char *arg) 120, 121, 122, 123, 124, 125, 126, 127, }; - check_set("s0", 64, s0, CURL_ARRAYSIZE(s1)); check_set("s1", 100, s1, CURL_ARRAYSIZE(s1)); check_set("s2", 1000, s2, CURL_ARRAYSIZE(s2)); From 7103a93b05bc69ea98ed9d05d02fa9eeba533f2f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 13 Aug 2026 13:23:53 +0200 Subject: [PATCH 0891/1062] connections: use admin handles only for maintenance Maintenance tasks like connection upkeep, liveliness checks and cache eviction must only run on an admin handle, not the application transfer from the calling context. Then application transfers will get only attached to the connections they actually work on. This makes `data->state.recent_conn_id` meaningful for checks if a subsequent address is finding the previous connection again. One thing led to another: - add Curl_get_admin(data), to obtain an admin handle for an application handle, inheriting some properties for connection operation. - rename `Curl_easy *data` parameter to `Curl_easy *admin` where only admin handles should be passed. Add DEBUGASSERTs in the called function. - rename `Curl_conn_terminate()` to `Curl_conn_close()` because this makes connections enter the shutdown close where possible. - rename `Curl_cshutdn_terminate()` to `Curl_conn_terminate()` since this definitely kills the connection and does not involve a `cshutdn` instance. - add Curl_share_lock_share(), Curl_share_unlock_share() so it possible to operate on a share without the passed easy handle having the share set. This catches a case where a shared needed to be locked but was not before. Adjust test1554 results. Fixes #22567 Reported-by: cybertron10 on github Closes #22572 --- lib/conncache.c | 315 ++++++++++++++++++++++++-------------------- lib/conncache.h | 25 ++-- lib/cshutdn.c | 174 ++++++++++++------------ lib/cshutdn.h | 29 ++-- lib/curl_share.c | 27 ++-- lib/curl_share.h | 8 ++ lib/curl_trc.c | 2 +- lib/easy.c | 11 +- lib/getinfo.c | 3 +- lib/multi.c | 58 ++++---- lib/multiif.h | 3 +- lib/url.c | 44 +++++-- lib/url.h | 6 + lib/urldata.h | 4 +- tests/data/test1554 | 2 + 15 files changed, 399 insertions(+), 312 deletions(-) diff --git a/lib/conncache.c b/lib/conncache.c index d4a71c8a9734..21b2e61e4143 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -42,7 +42,7 @@ do { \ if(c) { \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ - Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \ + Curl_share_lock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT, \ CURL_LOCK_ACCESS_SINGLE); \ DEBUGASSERT(!(c)->locked); \ (c)->locked = TRUE; \ @@ -55,7 +55,7 @@ DEBUGASSERT((c)->locked); \ (c)->locked = FALSE; \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ - Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \ + Curl_share_unlock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT); \ } \ } while(0) @@ -111,16 +111,12 @@ static void cpool_bundle_free_entry(void *freethis) } void Curl_cpool_init(struct cpool *cpool, - struct Curl_easy *idata, struct Curl_share *share, size_t size) { Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str, curlx_str_key_compare, cpool_bundle_free_entry); - DEBUGASSERT(idata); - - cpool->idata = idata; cpool->share = share; cpool->initialized = TRUE; } @@ -186,6 +182,8 @@ static void cpool_discard_conn(struct cpool *cpool, struct connectdata *conn, bool aborted) { + struct cshutdn *cshutdn; + struct Curl_easy *admin; bool done = FALSE; DEBUGASSERT(data); @@ -193,12 +191,13 @@ static void cpool_discard_conn(struct cpool *cpool, DEBUGASSERT(cpool); DEBUGASSERT(!conn->bits.in_cpool); + admin = Curl_get_admin(data); /* * If this connection is not marked to force-close, leave it open if there * are other users of it */ if(CONN_INUSE(conn) && !aborted) { - CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T + CURL_TRC_M(admin, "[CPOOL] not discarding #%" FMT_OFF_T " still in use by %u transfers", conn->connection_id, conn->attached_xfers); return; @@ -219,35 +218,36 @@ static void cpool_discard_conn(struct cpool *cpool, done = TRUE; if(!done) { /* Attempt to shutdown the connection right away. */ - Curl_cshutdn_run_once(cpool->idata, conn, &done); + Curl_conn_shutdown_once(admin, conn, &done); } - if(done || !data->multi) - Curl_cshutdn_terminate(cpool->idata, conn, FALSE); + cshutdn = Curl_cshutdn_get(data); + if(done || !cshutdn) + Curl_conn_terminate(admin, conn, FALSE); else - Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); + Curl_cshutdn_add(cshutdn, conn, cpool->num_conn); } -void Curl_cpool_destroy(struct cpool *cpool) +void Curl_cpool_destroy(struct cpool *cpool, struct Curl_easy *admin) { - if(cpool && cpool->initialized && cpool->idata) { + if(cpool && cpool->initialized && admin) { struct connectdata *conn; struct Curl_sigpipe_ctx pipe_ctx; - CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections", + CURL_TRC_M(admin, "%s[CPOOL] destroy, %zu connections", cpool->share ? "[SHARE] " : "", cpool->num_conn); /* Move all connections to the shutdown list */ sigpipe_init(&pipe_ctx); - CPOOL_LOCK(cpool, cpool->idata); + CPOOL_LOCK(cpool, admin); conn = cpool_get_first(cpool); if(conn) - sigpipe_apply(cpool->idata, &pipe_ctx); + sigpipe_apply(admin, &pipe_ctx); while(conn) { cpool_remove_conn(cpool, conn); - cpool_discard_conn(cpool, cpool->idata, conn, FALSE); + cpool_discard_conn(cpool, admin, conn, FALSE); conn = cpool_get_first(cpool); } - CPOOL_UNLOCK(cpool, cpool->idata); + CPOOL_UNLOCK(cpool, admin); sigpipe_restore(&pipe_ctx); Curl_hash_destroy(&cpool->dest2bundle); } @@ -255,17 +255,22 @@ void Curl_cpool_destroy(struct cpool *cpool) static struct cpool *cpool_get_instance(struct Curl_easy *data) { - if(data) { - if(CURL_SHARE_KEEP_CONNECT(data->share)) - return &data->share->cpool; - else if(data->multi_easy) - return &data->multi_easy->cpool; - else if(data->multi) - return &data->multi->cpool; - } + /* admin handles do not necessarily find the correct pool */ + DEBUGASSERT(data->mid); + if(CURL_SHARE_KEEP_CONNECT(data->share)) + return &data->share->cpool; + else if(data->multi_easy) + return &data->multi_easy->cpool; + else if(data->multi) + return &data->multi->cpool; return NULL; } +struct cpool *Curl_cpool_get_instance(struct Curl_easy *data) +{ + return cpool_get_instance(data); +} + void Curl_cpool_xfer_init(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); @@ -367,26 +372,90 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, return oldest_idle; } +static void cpool_conn_close(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + struct Curl_easy *admin; + bool do_lock; + + DEBUGASSERT(cpool); + DEBUGASSERT(data && !data->conn); + if(!cpool) + return; + + /* If this connection is not marked to force-close, leave it open if there + * are other users of it */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGASSERT(0); /* does this ever happen? */ + DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); + return; + } + + /* This method may be called while we are under lock, e.g. from a + * user callback in find. */ + admin = Curl_get_admin(data); + do_lock = !CPOOL_IS_LOCKED(cpool); + if(do_lock) + CPOOL_LOCK(cpool, admin); + + if(conn->bits.in_cpool) { + cpool_remove_conn(cpool, conn); + DEBUGASSERT(!conn->bits.in_cpool); + } + + /* treat the connection as aborted in CONNECT_ONLY situations, + * so no graceful shutdown is attempted. */ + if(conn->bits.connect_only) + aborted = TRUE; + + if(data->multi) { + /* Add it to the multi's cpool for shutdown handling */ + infof(data, "%s connection #%" FMT_OFF_T, + aborted ? "closing" : "shutting down", conn->connection_id); + cpool_discard_conn(&data->multi->cpool, data, conn, aborted); + } + else { + /* No multi available, terminate */ + infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); + Curl_conn_terminate(admin, conn, !aborted); + } + + if(do_lock) + CPOOL_UNLOCK(cpool, admin); +} + +void Curl_conn_close(struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + struct cpool *cpool = cpool_get_instance(data); + cpool_conn_close(cpool, data, conn, aborted); +} + /* Evict an idle connection to make room in the pool. A pool owned by * a share has no multi that could perform a controlled shutdown of the * connection; terminate it right away. Otherwise, hand it to the * transfer's multi for shutdown. Expects the pool to be locked. */ static void cpool_evict_conn(struct cpool *cpool, - struct Curl_easy *data, + struct Curl_easy *admin, struct connectdata *conn) { if(cpool->share) { cpool_remove_conn(cpool, conn); - Curl_cshutdn_terminate(cpool->idata, conn, TRUE); + Curl_conn_terminate(admin, conn, TRUE); } else - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); } int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *conn) { struct cpool *cpool = cpool_get_instance(data); + struct cshutdn *cshutdn = Curl_cshutdn_get(data); + struct Curl_easy *admin; struct cpool_bundle *bundle; size_t dest_limit = 0; size_t total_limit = 0; @@ -405,17 +474,18 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!dest_limit && !total_limit) return CPOOL_LIMIT_OK; - CPOOL_LOCK(cpool, cpool->idata); + admin = Curl_get_admin(data); + CPOOL_LOCK(cpool, admin); if(dest_limit) { size_t live; bundle = cpool_find_bundle(cpool, conn); live = bundle ? Curl_llist_count(&bundle->conns) : 0; - shutdowns = Curl_cshutdn_dest_count(data, conn->destination); + shutdowns = Curl_cshutdn_dest_count(cshutdn, conn->destination); while((live + shutdowns) >= dest_limit) { if(shutdowns) { /* close one connection in shutdown right away, if we can */ - if(!Curl_cshutdn_close_oldest(data, conn->destination)) + if(!Curl_cshutdn_close_oldest(cshutdn, conn->destination)) break; } else if(!bundle) @@ -429,17 +499,17 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!oldest_idle) break; /* disconnect the old conn and continue */ - CURL_TRC_M(data, "Discarding connection #%" FMT_OFF_T + CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T " from %zu to reach destination limit of %zu", oldest_idle->connection_id, Curl_llist_count(&bundle->conns), dest_limit); - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); /* in case the bundle was destroyed in disconnect, look it up again */ bundle = cpool_find_bundle(cpool, conn); live = bundle ? Curl_llist_count(&bundle->conns) : 0; } - shutdowns = Curl_cshutdn_dest_count(data, conn->destination); + shutdowns = Curl_cshutdn_dest_count(cshutdn, conn->destination); } if((live + shutdowns) >= dest_limit) { res = CPOOL_LIMIT_DEST; @@ -448,11 +518,11 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } if(total_limit) { - shutdowns = Curl_cshutdn_count(data); + shutdowns = Curl_cshutdn_count(cshutdn); while((cpool->num_conn + shutdowns) >= total_limit) { if(shutdowns) { /* close one connection in shutdown right away, if we can */ - if(!Curl_cshutdn_close_oldest(data, NULL)) + if(!Curl_cshutdn_close_oldest(cshutdn, NULL)) break; } else { @@ -461,13 +531,13 @@ int Curl_cpool_check_limits(struct Curl_easy *data, if(!oldest_idle) break; /* disconnect the old conn and continue */ - CURL_TRC_M(data, "Discarding connection #%" + CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T " from %zu to reach total " "limit of %zu", oldest_idle->connection_id, cpool->num_conn, total_limit); - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); } - shutdowns = Curl_cshutdn_count(data); + shutdowns = Curl_cshutdn_count(cshutdn); } if((cpool->num_conn + shutdowns) >= total_limit) { res = CPOOL_LIMIT_TOTAL; @@ -476,7 +546,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } out: - CPOOL_UNLOCK(cpool, cpool->idata); + CPOOL_UNLOCK(cpool, admin); return res; } @@ -529,7 +599,8 @@ CURLcode Curl_cpool_add(struct Curl_easy *data, static bool cpool_foreach(struct Curl_easy *data, struct cpool *cpool, void *param, - int (*func)(struct Curl_easy *data, + int (*func)(struct cpool *cpool, + struct Curl_easy *data, struct connectdata *conn, void *param)) { struct Curl_hash_iterator iter; @@ -553,7 +624,7 @@ static bool cpool_foreach(struct Curl_easy *data, struct connectdata *conn = Curl_node_elem(curr); curr = Curl_node_next(curr); - if(func(data, conn, param) == 1) { + if(func(cpool, data, conn, param) == 1) { return TRUE; } } @@ -573,6 +644,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, unsigned int maxconnects; struct connectdata *oldest_idle = NULL; struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin; bool kept = TRUE; if(!data || !data->multi) @@ -590,8 +662,10 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, if(cpool && maxconnects) { /* may be called form a callback already under lock */ bool do_lock = !CPOOL_IS_LOCKED(cpool); + + admin = Curl_get_admin(data); if(do_lock) - CPOOL_LOCK(cpool, data); + CPOOL_LOCK(cpool, admin); if(cpool->num_conn > maxconnects) { infof(data, "Connection pool is full, closing the oldest of %zu/%u", cpool->num_conn, maxconnects); @@ -599,11 +673,11 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); kept = (oldest_idle != conn); if(oldest_idle) { - cpool_evict_conn(cpool, data, oldest_idle); + cpool_evict_conn(cpool, admin, oldest_idle); } } if(do_lock) - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } return kept; @@ -649,74 +723,23 @@ bool Curl_cpool_find(struct Curl_easy *data, return found; } -void Curl_conn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool aborted) -{ - struct cpool *cpool = cpool_get_instance(data); - bool do_lock; - - DEBUGASSERT(cpool); - DEBUGASSERT(data && !data->conn); - if(!cpool) - return; - - /* If this connection is not marked to force-close, leave it open if there - * are other users of it */ - if(CONN_INUSE(conn) && !aborted) { - DEBUGASSERT(0); /* does this ever happen? */ - DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); - return; - } - - /* This method may be called while we are under lock, e.g. from a - * user callback in find. */ - do_lock = !CPOOL_IS_LOCKED(cpool); - if(do_lock) - CPOOL_LOCK(cpool, data); - - if(conn->bits.in_cpool) { - cpool_remove_conn(cpool, conn); - DEBUGASSERT(!conn->bits.in_cpool); - } - - /* treat the connection as aborted in CONNECT_ONLY situations, - * so no graceful shutdown is attempted. */ - if(conn->bits.connect_only) - aborted = TRUE; - - if(data->multi) { - /* Add it to the multi's cpool for shutdown handling */ - infof(data, "%s connection #%" FMT_OFF_T, - aborted ? "closing" : "shutting down", conn->connection_id); - cpool_discard_conn(&data->multi->cpool, data, conn, aborted); - } - else { - /* No multi available, terminate */ - infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); - Curl_cshutdn_terminate(cpool->idata, conn, !aborted); - } - - if(do_lock) - CPOOL_UNLOCK(cpool, data); -} - struct cpool_reaper_ctx { size_t reaped; struct curltime now; }; -static int cpool_reap_dead_cb(struct Curl_easy *data, +static int cpool_reap_dead_cb(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { struct cpool_reaper_ctx *reaper = param; if(!CONN_INUSE(conn)) { if(conn->bits.no_reuse || conn->bits.close || - !Curl_cpool_conn_seems_healthy(conn, data, &reaper->now)) { + !Curl_cpool_conn_seems_healthy(conn, admin, &reaper->now)) { /* terminate conn and stop the iteration */ reaper->reaped++; - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } } @@ -730,46 +753,49 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, * * When called, this transfer has no connection attached. */ -void Curl_cpool_prune_dead(struct Curl_easy *data) +void Curl_cpool_prune_dead(struct cpool *cpool, + struct Curl_easy *data) { - struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin; timediff_t elapsed; if(!cpool) return; - CPOOL_LOCK(cpool, data); - elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &cpool->last_cleanup); + admin = Curl_get_admin(data); + CPOOL_LOCK(cpool, admin); + elapsed = curlx_ptimediff_ms(Curl_pgrs_now(admin), &cpool->last_cleanup); if(elapsed >= 1000L) { struct cpool_reaper_ctx reaper; memset(&reaper, 0, sizeof(reaper)); - reaper.now = *Curl_pgrs_now(data); - while(cpool_foreach(data, cpool, &reaper, cpool_reap_dead_cb)) + reaper.now = *Curl_pgrs_now(admin); + while(cpool_foreach(admin, cpool, &reaper, cpool_reap_dead_cb)) ; - cpool->last_cleanup = *Curl_pgrs_now(data); + cpool->last_cleanup = *Curl_pgrs_now(admin); } - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } -static int conn_upkeep(struct Curl_easy *data, +static int conn_upkeep(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { (void)param; - if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) >= - data->set.upkeep_interval_ms) { + if(curlx_ptimediff_ms(Curl_pgrs_now(admin), &conn->keepalive) >= + admin->set.upkeep_interval_ms) { CURLcode result; /* briefly attach for action */ - Curl_attach_connection(data, conn); - result = Curl_conn_keep_alive(data, conn); - conn->keepalive = *Curl_pgrs_now(data); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_keep_alive(admin, conn); + conn->keepalive = *Curl_pgrs_now(admin); + Curl_detach_connection(admin); if(result && !CONN_INUSE(conn)) { - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } } @@ -779,14 +805,15 @@ static int conn_upkeep(struct Curl_easy *data, CURLcode Curl_cpool_upkeep(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); + struct Curl_easy *admin = Curl_get_admin(data); if(!cpool) return CURLE_OK; - CPOOL_LOCK(cpool, data); - while(cpool_foreach(data, cpool, NULL, conn_upkeep)) + CPOOL_LOCK(cpool, admin); + while(cpool_foreach(admin, cpool, NULL, conn_upkeep)) ; - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); return CURLE_OK; } @@ -795,10 +822,12 @@ struct cpool_find_ctx { struct connectdata *conn; }; -static int cpool_find_conn(struct Curl_easy *data, +static int cpool_find_conn(struct cpool *cpool, + struct Curl_easy *data, struct connectdata *conn, void *param) { struct cpool_find_ctx *fctx = param; + (void)cpool; (void)data; if(conn->connection_id == fctx->id) { fctx->conn = conn; @@ -837,36 +866,37 @@ void Curl_cpool_do_locked(struct Curl_easy *data, cb(conn, data, cbdata); } -static int cpool_mark_stale(struct Curl_easy *data, +static int cpool_mark_stale(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { - (void)data; + (void)cpool; + (void)admin; (void)param; conn->bits.no_reuse = TRUE; return 0; } -static int cpool_reap_no_reuse(struct Curl_easy *data, +static int cpool_reap_no_reuse(struct cpool *cpool, + struct Curl_easy *admin, struct connectdata *conn, void *param) { (void)param; if(!CONN_INUSE(conn) && conn->bits.no_reuse) { - Curl_conn_terminate(data, conn, FALSE); + cpool_conn_close(cpool, admin, conn, FALSE); return 1; } return 0; /* continue iteration */ } -void Curl_cpool_nw_changed(struct Curl_easy *data) +void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin) { - struct cpool *cpool = cpool_get_instance(data); - - if(cpool) { - CPOOL_LOCK(cpool, data); - cpool_foreach(data, cpool, NULL, cpool_mark_stale); - while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse)) + if(cpool && admin) { + CPOOL_LOCK(cpool, admin); + cpool_foreach(admin, cpool, NULL, cpool_mark_stale); + while(cpool_foreach(admin, cpool, NULL, cpool_reap_no_reuse)) ; - CPOOL_UNLOCK(cpool, data); + CPOOL_UNLOCK(cpool, admin); } } @@ -907,24 +937,27 @@ bool Curl_cpool_conn_seems_healthy(struct connectdata *conn, struct Curl_easy *data, const struct curltime *pnow) { + struct Curl_easy *admin; bool healthy = TRUE; DEBUGASSERT(!data->conn); if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */ return FALSE; - else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) + if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000) return TRUE; - else if(conn->scheme->run->connection_is_dead) { - Curl_attach_connection(data, conn); - healthy = !conn->scheme->run->connection_is_dead(data, conn); - Curl_detach_connection(data); + + admin = Curl_get_admin(data); + if(conn->scheme->run->connection_is_dead) { + Curl_attach_connection(admin, conn, FALSE); + healthy = !conn->scheme->run->connection_is_dead(admin, conn); + Curl_detach_connection(admin); } else { bool input_pending = FALSE; - Curl_attach_connection(data, conn); - healthy = Curl_conn_is_alive(data, conn, &input_pending); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + healthy = Curl_conn_is_alive(admin, conn, &input_pending); + Curl_detach_connection(admin); if(healthy && input_pending && !CONN_INUSE(conn) && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) { /* Non-multiplexed connections without attached transfers should diff --git a/lib/conncache.h b/lib/conncache.h index 4924c038b36f..f31b5fcf8073 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -34,7 +34,7 @@ struct Curl_multi; struct Curl_share; /** - * Terminate the connection, e.g. close and destroy. + * Close and destroy the connection. * If the connection is in a cpool, remove it. * If a `cshutdn` is available (e.g. data has a multi handle), * pass the connection to that for controlled shutdown. @@ -42,9 +42,9 @@ struct Curl_share; * Takes ownership of `conn`. * `data` should not be attached to a connection. */ -void Curl_conn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool aborted); +void Curl_conn_close(struct Curl_easy *data, + struct connectdata *conn, + bool aborted); struct cpool { /* the pooled connections, bundled per destination */ @@ -53,22 +53,24 @@ struct cpool { curl_off_t next_connection_id; curl_off_t next_easy_id; struct curltime last_cleanup; - struct Curl_easy *idata; /* internal handle for maintenance */ struct Curl_share *share; /* != NULL if pool belongs to share */ BIT(locked); BIT(initialized); }; +/* Get connection pool instance for data or NULL if none exists */ +struct cpool *Curl_cpool_get_instance(struct Curl_easy *data); + /* Init the pool, pass multi only if pool is owned by it. * Cannot fail. */ void Curl_cpool_init(struct cpool *cpool, - struct Curl_easy *idata, struct Curl_share *share, size_t size); /* Destroy all connections and free all members */ -void Curl_cpool_destroy(struct cpool *cpool); +void Curl_cpool_destroy(struct cpool *cpool, + struct Curl_easy *admin); /* Init the transfer to be used within its connection pool. * Assigns `data->id`. */ @@ -126,13 +128,12 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, struct connectdata *conn); /** - * This function scans the data's connection pool for half-open/dead + * Scans the connection pool for half-open/dead * connections, closes and removes them. * The cleanup is done at most once per second. - * - * When called, this transfer has no connection attached. */ -void Curl_cpool_prune_dead(struct Curl_easy *data); +void Curl_cpool_prune_dead(struct cpool *cpool, + struct Curl_easy *data); /** * Perform upkeep actions on connections in the transfer's pool. @@ -154,7 +155,7 @@ void Curl_cpool_do_locked(struct Curl_easy *data, Curl_cpool_conn_do_cb *cb, void *cbdata); /* Close all unused connections, prevent reuse of existing ones. */ -void Curl_cpool_nw_changed(struct Curl_easy *data); +void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin); /* Return TRUE iff the given connection is considered healthy, e.g. * usable for more transfers. */ diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 30cbaa552acb..6ad0f880134a 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -38,6 +38,13 @@ #include "curlx/strparse.h" +struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data) +{ + if(data && data->multi) + return &data->multi->cshutdn; + return NULL; +} + static void cshutdn_run_conn_handler(struct Curl_easy *data, struct connectdata *conn) { @@ -107,22 +114,22 @@ static void cshutdn_run_once(struct Curl_easy *data, conn->bits.shutdown_filters = TRUE; } -void Curl_cshutdn_run_once(struct Curl_easy *data, +void Curl_conn_shutdown_once(struct Curl_easy *admin, struct connectdata *conn, bool *done) { - DEBUGASSERT(!data->conn); - Curl_attach_connection(data, conn); - cshutdn_run_once(data, conn, done); - CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done); - Curl_detach_connection(data); + DEBUGASSERT(!admin->conn); + DEBUGASSERT(!admin->mid); + Curl_attach_connection(admin, conn, FALSE); + cshutdn_run_once(admin, conn, done); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown, done=%d", *done); + Curl_detach_connection(admin); } -void Curl_cshutdn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool do_shutdown) +void Curl_conn_terminate(struct Curl_easy *admin, + struct connectdata *conn, + bool do_shutdown) { - struct Curl_easy *admin = data; bool done; /* there must be a connection to close */ @@ -130,16 +137,10 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, /* it must be removed from the connection pool */ DEBUGASSERT(!conn->bits.in_cpool); /* the transfer must be detached from the connection */ - DEBUGASSERT(data && !data->conn); + DEBUGASSERT(admin && !admin->conn); + DEBUGASSERT(!admin->mid); - /* If we can obtain an internal admin handle, use that to attach - * and terminate the connection. Some protocol will try to mess with - * `data` during shutdown and we do not want that with a `data` from - * the application. */ - if(data->multi && data->multi->admin) - admin = data->multi->admin; - - Curl_attach_connection(admin, conn); + Curl_attach_connection(admin, conn, FALSE); cshutdn_run_conn_handler(admin, conn); if(do_shutdown) { @@ -154,18 +155,17 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); Curl_detach_connection(admin); - if(data->multi) - Curl_multi_ev_conn_done(data->multi, data, conn); + if(admin->multi) + Curl_multi_ev_conn_done(admin->multi, admin, conn); Curl_conn_free(admin, conn); - if(data->multi) { - CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged"); - Curl_multi_connchanged(data->multi); + if(admin->multi) { + CURL_TRC_M(admin, "[SHUTDOWN] trigger multi connchanged"); + Curl_multi_connchanged(admin->multi); } } static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, - struct Curl_easy *data, const char *destination) { struct Curl_llist_node *e; @@ -184,20 +184,19 @@ static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, conn = Curl_node_elem(e); Curl_node_remove(e); sigpipe_init(&sigpipe_ctx); - sigpipe_apply(data, &sigpipe_ctx); - Curl_cshutdn_terminate(data, conn, FALSE); + sigpipe_apply(cshutdn->multi->admin, &sigpipe_ctx); + Curl_conn_terminate(cshutdn->multi->admin, conn, FALSE); sigpipe_restore(&sigpipe_ctx); return TRUE; } return FALSE; } -bool Curl_cshutdn_close_oldest(struct Curl_easy *data, +bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, const char *destination) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; - return cshutdn_destroy_oldest(csd, data, destination); + if(cshutdn) { + return cshutdn_destroy_oldest(cshutdn, destination); } return FALSE; } @@ -205,7 +204,6 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data, #define NUM_POLLS_ON_STACK 10 static CURLcode cshutdn_wait(struct cshutdn *cshutdn, - struct Curl_easy *data, int timeout_ms) { struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; @@ -214,7 +212,7 @@ static CURLcode cshutdn_wait(struct cshutdn *cshutdn, Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); - result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds); + result = Curl_cshutdn_add_pollfds(cshutdn, &cpfds); if(result) goto out; @@ -226,11 +224,11 @@ static CURLcode cshutdn_wait(struct cshutdn *cshutdn, } static void cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); struct Curl_llist_node *enext; + struct Curl_easy *admin = cshutdn->multi->admin; struct connectdata *conn; timediff_t next_expire_ms = 0, ms; bool done; @@ -238,21 +236,21 @@ static void cshutdn_perform(struct cshutdn *cshutdn, if(!e) return; - CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections", + CURL_TRC_M(admin, "[SHUTDOWN] perform on %zu connections", Curl_llist_count(&cshutdn->list)); - sigpipe_apply(data, sigpipe_ctx); + sigpipe_apply(admin, sigpipe_ctx); while(e) { enext = Curl_node_next(e); conn = Curl_node_elem(e); - Curl_cshutdn_run_once(data, conn, &done); + Curl_conn_shutdown_once(admin, conn, &done); if(done) { Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); } else { /* idata has one timer list, but maybe more than one connection. * Set EXPIRE_SHUTDOWN to the smallest time left for all. */ - ms = Curl_conn_shutdown_timeleft(data, conn); + ms = Curl_conn_shutdown_timeleft(admin, conn); if(ms && (!next_expire_ms || (ms < next_expire_ms))) next_expire_ms = ms; } @@ -260,45 +258,42 @@ static void cshutdn_perform(struct cshutdn *cshutdn, } if(next_expire_ms) - Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN); + Curl_expire_ex(admin, next_expire_ms, EXPIRE_SHUTDOWN); } static void cshutdn_terminate_all(struct cshutdn *cshutdn, - struct Curl_easy *data, int timeout_ms) { - struct curltime started = *Curl_pgrs_now(data); + struct Curl_easy *admin = cshutdn->multi->admin; + struct curltime started = *Curl_pgrs_now(admin); struct Curl_llist_node *e; struct Curl_sigpipe_ctx sigpipe_ctx; - DEBUGASSERT(cshutdn); - DEBUGASSERT(data); - - CURL_TRC_M(data, "[SHUTDOWN] shutdown all"); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown all"); sigpipe_init(&sigpipe_ctx); while(Curl_llist_head(&cshutdn->list)) { timediff_t spent_ms; int remain_ms; - cshutdn_perform(cshutdn, data, &sigpipe_ctx); + cshutdn_perform(cshutdn, &sigpipe_ctx); if(!Curl_llist_head(&cshutdn->list)) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly"); + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished cleanly"); break; } /* wait for activity, timeout or "nothing" */ - spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &started); + spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(admin), &started); if(spent_ms >= (timediff_t)timeout_ms) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s", + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, %s", (timeout_ms > 0) ? "timeout" : "best effort done"); break; } remain_ms = timeout_ms - (int)spent_ms; - if(cshutdn_wait(cshutdn, data, remain_ms)) { - CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted"); + if(cshutdn_wait(cshutdn, remain_ms)) { + CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, aborted"); break; } } @@ -308,7 +303,7 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn, while(e) { struct connectdata *conn = Curl_node_elem(e); Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); e = Curl_llist_head(&cshutdn->list); } DEBUGASSERT(!Curl_llist_count(&cshutdn->list)); @@ -327,12 +322,13 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, } void Curl_cshutdn_destroy(struct cshutdn *cshutdn, - struct Curl_easy *data) + struct Curl_easy *admin) { - if(cshutdn->initialized && data) { + if(cshutdn->initialized && admin) { int timeout_ms = 0; /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD + DEBUGASSERT(!admin->mid); { const char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); if(p) { @@ -343,29 +339,28 @@ void Curl_cshutdn_destroy(struct cshutdn *cshutdn, } #endif - CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms", + CURL_TRC_M(admin, "[SHUTDOWN] destroy, %zu connections, timeout=%dms", Curl_llist_count(&cshutdn->list), timeout_ms); - cshutdn_terminate_all(cshutdn, data, timeout_ms); + cshutdn_terminate_all(cshutdn, timeout_ms); } cshutdn->multi = NULL; + cshutdn->initialized = FALSE; } -size_t Curl_cshutdn_count(struct Curl_easy *data) +size_t Curl_cshutdn_count(struct cshutdn *cshutdn) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; - return Curl_llist_count(&csd->list); + if(cshutdn) { + return Curl_llist_count(&cshutdn->list); } return 0; } -size_t Curl_cshutdn_dest_count(struct Curl_easy *data, +size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, const char *destination) { - if(data && data->multi) { - struct cshutdn *csd = &data->multi->cshutdn; + if(cshutdn) { size_t n = 0; - struct Curl_llist_node *e = Curl_llist_head(&csd->list); + struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); while(e) { struct connectdata *conn = Curl_node_elem(e); if(!strcmp(destination, conn->destination)) @@ -378,17 +373,17 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data, } static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, - struct Curl_easy *data, struct connectdata *conn) { + struct Curl_easy *admin = cshutdn->multi->admin; CURLMcode mresult; DEBUGASSERT(cshutdn); DEBUGASSERT(cshutdn->multi->socket_cb); - Curl_attach_connection(data, conn); - mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + mresult = Curl_multi_ev_assess_conn(cshutdn->multi, admin, conn); + Curl_detach_connection(admin); return mresult; } @@ -396,47 +391,46 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn, size_t conns_in_pool) { - struct Curl_easy *data = cshutdn->multi->admin; + struct Curl_easy *admin = cshutdn->multi->admin; size_t max_total = cshutdn->multi->max_total_connections; /* Add the connection to our shutdown list for non-blocking shutdown * during multi processing. */ if(max_total > 0 && (max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { - CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection " + CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection " "due to connection limit of %zu", max_total); - cshutdn_destroy_oldest(cshutdn, data, NULL); + cshutdn_destroy_oldest(cshutdn, NULL); } if(cshutdn->multi->socket_cb) { - if(cshutdn_update_ev(cshutdn, data, conn)) { - CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%" + if(cshutdn_update_ev(cshutdn, conn)) { + CURL_TRC_M(admin, "[SHUTDOWN] update events failed, discarding #%" FMT_OFF_T, conn->connection_id); - Curl_cshutdn_terminate(data, conn, FALSE); + Curl_conn_terminate(admin, conn, FALSE); return; } } Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node); - CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T + CURL_TRC_M(admin, "[SHUTDOWN] added #%" FMT_OFF_T " to shutdowns, now %zu conns in shutdown", conn->connection_id, Curl_llist_count(&cshutdn->list)); } void Curl_cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { - cshutdn_perform(cshutdn, data, sigpipe_ctx); + cshutdn_perform(cshutdn, sigpipe_ctx); } /* return fd_set info about the shutdown connections */ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, - struct Curl_easy *data, fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd) { if(Curl_llist_head(&cshutdn->list)) { + struct Curl_easy *admin = cshutdn->multi->admin; struct Curl_llist_node *e; struct easy_pollset ps; @@ -447,9 +441,9 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, CURLcode result; Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(result) continue; @@ -473,13 +467,13 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, /* return information about the shutdown connections */ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_waitfds *cwfds) { unsigned int need = 0; if(Curl_llist_head(&cshutdn->list)) { struct Curl_llist_node *e; + struct Curl_easy *admin = cshutdn->multi->admin; struct easy_pollset ps; struct connectdata *conn; CURLcode result; @@ -488,9 +482,9 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(!result) need += Curl_waitfds_add_ps(cwfds, &ps); @@ -501,7 +495,6 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, } CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct curl_pollfds *cpfds) { CURLcode result = CURLE_OK; @@ -509,15 +502,16 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, if(Curl_llist_head(&cshutdn->list)) { struct Curl_llist_node *e; struct easy_pollset ps; + struct Curl_easy *admin = cshutdn->multi->admin; struct connectdata *conn; Curl_pollset_init(&ps); for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); - Curl_attach_connection(data, conn); - result = Curl_conn_adjust_pollset(data, conn, &ps); - Curl_detach_connection(data); + Curl_attach_connection(admin, conn, FALSE); + result = Curl_conn_adjust_pollset(admin, conn, &ps); + Curl_detach_connection(admin); if(!result) result = Curl_pollfds_add_ps(cpfds, &ps); diff --git a/lib/cshutdn.h b/lib/cshutdn.h index 8479524993cf..a0338f64882f 100644 --- a/lib/cshutdn.h +++ b/lib/cshutdn.h @@ -33,20 +33,20 @@ struct Curl_share; struct Curl_sigpipe_ctx; /* Run the shutdown of the connection once. - * Shortly attach/detach `data` to `conn` while doing so. + * Shortly attach/detach the admin handle to `conn` while doing so. * `done` will be set TRUE if any error was encountered or if * the connection was shut down completely. */ -void Curl_cshutdn_run_once(struct Curl_easy *data, - struct connectdata *conn, - bool *done); +void Curl_conn_shutdown_once(struct Curl_easy *admin, + struct connectdata *conn, + bool *done); /* Terminates the connection, e.g. closes and destroys it. * If `do_shutdown` is TRUE, the shutdown will be run once before * terminating it. * Takes ownership of `conn`. */ -void Curl_cshutdn_terminate(struct Curl_easy *data, - struct connectdata *conn, - bool do_shutdown); +void Curl_conn_terminate(struct Curl_easy *admin, + struct connectdata *conn, + bool do_shutdown); /* A `cshutdown` is always owned by a multi handle to maintain * the connections to be shut down. It registers timers and @@ -57,25 +57,28 @@ struct cshutdn { BIT(initialized); }; +/* Get the cshutdn instance relevant for `data` or NULL if there is none */ +struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data); + /* Init as part of the given multi handle. */ int Curl_cshutdn_init(struct cshutdn *cshutdn, struct Curl_multi *multi); /* Terminate all remaining connections and free resources. */ void Curl_cshutdn_destroy(struct cshutdn *cshutdn, - struct Curl_easy *data); + struct Curl_easy *admin); /* Number of connections being shut down. */ -size_t Curl_cshutdn_count(struct Curl_easy *data); +size_t Curl_cshutdn_count(struct cshutdn *cshutdn); /* Number of connections to the destination being shut down. */ -size_t Curl_cshutdn_dest_count(struct Curl_easy *data, +size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, const char *destination); /* Close the oldest connection in shutdown to destination or, * when destination is NULL for any destination. * Return TRUE if a connection has been closed. */ -bool Curl_cshutdn_close_oldest(struct Curl_easy *data, +bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, const char *destination); /* Add a connection to have it shut down. Terminate the oldest @@ -86,21 +89,17 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, /* Add sockets and POLLIN/OUT flags for connections being shut down. */ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct curl_pollfds *cpfds); unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_waitfds *cwfds); void Curl_cshutdn_setfds(struct cshutdn *cshutdn, - struct Curl_easy *data, fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd); /* Run maintenance on all connections. */ void Curl_cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx); #endif /* HEADER_CURL_CSHUTDN_H */ diff --git a/lib/curl_share.c b/lib/curl_share.c index b7c331adf7e9..bce6419e8f01 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -38,7 +38,7 @@ static void share_destroy(struct Curl_share *share) return; if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { - Curl_cpool_destroy(&share->cpool); + Curl_cpool_destroy(&share->cpool, share->admin); } Curl_dnscache_destroy(&share->dnscache); @@ -264,7 +264,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_CONNECT: /* It is safe to set this option several times on a share. */ if(!share->cpool.initialized) { - Curl_cpool_init(&share->cpool, share->admin, share, 103); + Curl_cpool_init(&share->cpool, share, 103); } break; @@ -371,11 +371,11 @@ CURLSHcode curl_share_cleanup(CURLSH *sh) return CURLSHE_OK; } -CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, - curl_lock_access accesstype) +CURLSHcode Curl_share_lock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type, + curl_lock_access accesstype) { - struct Curl_share *share = data->share; - if(!share) return CURLSHE_INVALID; @@ -388,10 +388,16 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, return CURLSHE_OK; } -CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype) { - struct Curl_share *share = data->share; + return Curl_share_lock_share(data->share, data, type, accesstype); +} +CURLSHcode Curl_share_unlock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type) +{ if(!share) return CURLSHE_INVALID; @@ -403,6 +409,11 @@ CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) return CURLSHE_OK; } +CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +{ + return Curl_share_unlock_share(data->share, data, type); +} + CURLcode Curl_share_easy_unlink(struct Curl_easy *data) { struct Curl_share *share = data->share; diff --git a/lib/curl_share.h b/lib/curl_share.h index 1d49a8d1dd3c..5c687989e01f 100644 --- a/lib/curl_share.h +++ b/lib/curl_share.h @@ -80,6 +80,14 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, curl_lock_access accesstype); CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); +CURLSHcode Curl_share_lock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type, + curl_lock_access accesstype); +CURLSHcode Curl_share_unlock_share(struct Curl_share *share, + struct Curl_easy *data, + curl_lock_data type); + /* convenience macro to check if this handle is using a shared SSL spool */ #define CURL_SHARE_ssl_scache(data) \ ((data)->share && \ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index b52193270ef3..ccfb5c7994b8 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -92,7 +92,7 @@ static struct curl_trc_feat Curl_trc_feat_ids = { static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen) { - curl_off_t cid = data->state.recent_conn_id; + curl_off_t cid = data->state.lastconnect_id; if(data->id >= 0) { if(cid >= 0) return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid); diff --git a/lib/easy.c b/lib/easy.c index aa2cad22c9a0..10cb72f1137c 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -760,7 +760,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) if(data->conn) { struct connectdata *conn = data->conn; Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); DEBUGASSERT(!data->conn); } @@ -996,7 +996,6 @@ CURL *curl_easy_duphandle(CURL *curl) /* the connection pool is setup on demand */ outcurl->state.lastconnect_id = -1; - outcurl->state.recent_conn_id = -1; outcurl->id = -1; outcurl->mid = UINT32_MAX; outcurl->master_mid = UINT32_MAX; @@ -1130,7 +1129,7 @@ void curl_easy_reset(CURL *curl) data->progress.hide = TRUE; data->state.current_speed = -1; /* init to negative == impossible */ - data->state.recent_conn_id = -1; /* clear remembered connection id */ + data->state.lastconnect_id = -1; /* clear remembered connection id */ /* zero out authentication data: */ memset(&data->state.authhost, 0, sizeof(struct auth)); @@ -1259,7 +1258,7 @@ CURLcode Curl_easy_recv(struct Curl_easy *data, if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); *n = 0; return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n); @@ -1290,7 +1289,7 @@ CURLcode Curl_connect_only_attach(struct Curl_easy *data) if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); return CURLE_OK; } @@ -1316,7 +1315,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ - Curl_attach_connection(data, c); + Curl_attach_connection(data, c, TRUE); sigpipe_ignore(data, &sigpipe_ctx); result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); diff --git a/lib/getinfo.c b/lib/getinfo.c index d9873531d58e..30ee405e9947 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -463,8 +463,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = data->id; break; case CURLINFO_CONN_ID: - *param_offt = data->conn ? - data->conn->connection_id : data->state.recent_conn_id; + *param_offt = data->state.lastconnect_id; break; case CURLINFO_EARLYDATA_SENT_T: *param_offt = data->progress.earlydata_sent; diff --git a/lib/multi.c b/lib/multi.c index b990442a924a..cfa64410f1f4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -285,7 +285,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(Curl_cshutdn_init(&multi->cshutdn, multi)) goto error; - Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize); + Curl_cpool_init(&multi->cpool, NULL, chashsize); #ifdef USE_SSL if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache)) @@ -334,7 +334,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, Curl_multi_ev_cleanup(multi); Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); - Curl_cpool_destroy(&multi->cpool); + Curl_cpool_destroy(&multi->cpool, multi->admin); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); #ifdef USE_SSL Curl_ssl_scache_destroy(multi->ssl_scache); @@ -654,20 +654,19 @@ static void multi_done_locked(struct connectdata *conn, data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); connclose(conn); - Curl_conn_terminate(data, conn, (bool)mdctx->premature); + Curl_conn_close(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" " by server, not reusing", conn->connection_id, conn->destination); connclose(conn); - Curl_conn_terminate(data, conn, (bool)mdctx->premature); + Curl_conn_close(data, conn, (bool)mdctx->premature); } else { /* the connection is no longer in use by any transfer */ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ - data->state.lastconnect_id = conn->connection_id; infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", conn->connection_id, conn->destination); } @@ -845,7 +844,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, struct connectdata *conn; (void)Curl_getconnectinfo(data, &conn); if(conn) - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); } } @@ -943,17 +942,23 @@ void Curl_detach_connection(struct Curl_easy *data) /* * Curl_attach_connection() attaches this transfer to this connection. * - * This is the only function that should assign data->conn + * This is the only function that should assign data->conn. + * `matched == TRUE` means the transfer's properties match this + * connection and it is not a temporary attach for maintenance. */ void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn) + struct connectdata *conn, + bool matched) { DEBUGASSERT(data); DEBUGASSERT(!data->conn); DEBUGASSERT(conn); DEBUGASSERT(conn->attached_xfers < UINT32_MAX); data->conn = conn; - data->state.recent_conn_id = conn->connection_id; + if(matched) + data->state.lastconnect_id = conn->connection_id; + else + DEBUGASSERT(!data->mid); /* admin handle */ conn->attached_xfers++; /* all attached transfers must be from the same multi */ if(!conn->attached_multi) @@ -1288,8 +1293,8 @@ CURLMcode curl_multi_fdset(CURLM *m, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, - read_fd_set, write_fd_set, &this_max_fd); + Curl_cshutdn_setfds(&multi->cshutdn, read_fd_set, write_fd_set, + &this_max_fd); *max_fd = this_max_fd; Curl_pollset_cleanup(&ps); @@ -1337,7 +1342,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); + need += Curl_cshutdn_add_waitfds(&multi->cshutdn, &cwfds); if(need != cwfds.n && ufds) mresult = CURLM_OUT_OF_MEMORY; @@ -1575,7 +1580,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) { + if(Curl_cshutdn_add_pollfds(&multi->cshutdn, &cpfds)) { mresult = CURLM_OUT_OF_MEMORY; goto out; } @@ -1774,7 +1779,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* take this handle to the perform state right away */ multistate(data, MSTATE_PERFORMING); - Curl_attach_connection(data, conn); + Curl_attach_connection(data, conn, TRUE); CURL_REQ_SET_RECV(data); } return mresult; @@ -2391,7 +2396,7 @@ static CURLcode is_finished(struct Curl_multi *multi, We do not have to do this in every case block above where a failure is detected */ Curl_detach_connection(data); - Curl_conn_terminate(data, conn, dead_connection); + Curl_conn_close(data, conn, dead_connection); } } else if(data->mstate == MSTATE_CONNECT) { @@ -2711,7 +2716,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_easy *data, struct Curl_sigpipe_ctx *sigpipe_ctx) { - CURLMcode mresult; + CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; if(multi->dead) { @@ -2738,8 +2743,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); #endif - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx); - return CURLM_OK; + Curl_cshutdn_perform(&multi->cshutdn, sigpipe_ctx); + goto out; } sigpipe_apply(data, sigpipe_ctx); @@ -2758,8 +2763,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->mstate < MSTATE_COMPLETED) { /* Make sure we set the connection's current owner */ DEBUGASSERT(data->conn); - if(!data->conn) - return CURLM_INTERNAL_ERROR; + if(!data->conn) { + mresult = CURLM_INTERNAL_ERROR; + goto out; + } } /* Wait for the connect state as only then is the start time stored, but @@ -2841,7 +2848,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; default: - return CURLM_INTERNAL_ERROR; + mresult = CURLM_INTERNAL_ERROR; + goto out; } if(data->mstate >= MSTATE_CONNECT && @@ -2865,11 +2873,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(MSTATE_COMPLETED == data->mstate) { handle_completed(multi, data, result); - return CURLM_OK; + mresult = CURLM_OK; + goto out; } } while((mresult == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); +out: data->result = result; return mresult; } @@ -3014,7 +3024,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit); #endif - Curl_cpool_destroy(&multi->cpool); + Curl_cpool_destroy(&multi->cpool, multi->admin); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); if(multi->admin) { CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done"); @@ -3393,7 +3403,7 @@ CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) Curl_dnscache_clear(multi->admin); } if(val & CURLMNWC_CLEAR_CONNS) { - Curl_cpool_nw_changed(multi->admin); + Curl_cpool_nw_changed(&multi->cpool, multi->admin); } break; } diff --git a/lib/multiif.h b/lib/multiif.h index 45a2e7d3159e..44ac9bb37880 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -34,7 +34,8 @@ void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn); + struct connectdata *conn, + bool matched); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); CURLcode Curl_preconnect(struct Curl_easy *data); diff --git a/lib/url.c b/lib/url.c index 5234a534430f..f2f74d8b2ec0 100644 --- a/lib/url.c +++ b/lib/url.c @@ -471,7 +471,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->magic = CURLEASY_MAGIC_NUMBER; /* most recent connection is not yet defined */ data->state.lastconnect_id = -1; - data->state.recent_conn_id = -1; /* and not assigned an id yet */ data->id = -1; data->mid = UINT32_MAX; @@ -914,7 +913,7 @@ static bool url_allow_sspi_empty_creds(struct Curl_creds *conn_creds, * To avoid TOCTOU attacks, do not reuse on empty credentials * UNLESS this connection is the one used by this transfer before. */ if(!Curl_creds_has_user(conn_creds) && - (data->state.recent_conn_id != conn->connection_id)) + (data->state.lastconnect_id != conn->connection_id)) return FALSE; #else (void)conn_creds; @@ -1113,7 +1112,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) !Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) { infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating", conn->connection_id); - Curl_conn_terminate(m->data, conn, FALSE); + Curl_conn_close(m->data, conn, FALSE); return FALSE; } @@ -1128,7 +1127,7 @@ static bool url_match_result(void *userdata) if(match->found) { /* Attach it now while still under lock, so the connection does * no longer appear idle and can be reaped. */ - Curl_attach_connection(match->data, match->found); + Curl_attach_connection(match->data, match->found, TRUE); return TRUE; } else if(match->seen_single_use_conn && !match->seen_multiplex_conn) { @@ -1159,11 +1158,11 @@ static bool url_attach_existing(struct Curl_easy *data, struct connectdata *needle, bool *waitpipe) { + struct cpool *cpool = Curl_cpool_get_instance(data); struct url_conn_match match; bool success; DEBUGASSERT(!data->conn); - Curl_cpool_prune_dead(data); memset(&match, 0, sizeof(match)); match.data = data; @@ -1171,6 +1170,8 @@ static bool url_attach_existing(struct Curl_easy *data, match.now = *Curl_pgrs_now(data); match.may_multiplex = xfer_may_multiplex(data, needle); + Curl_cpool_prune_dead(cpool, data); + #ifdef USE_NTLM match.want_ntlm_http = (data->state.authhost.want & CURLAUTH_NTLM) && @@ -2303,9 +2304,9 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) goto out; /* Setup a "faked" transfer that will do nothing */ - Curl_attach_connection(data, needle); + result = Curl_cpool_add(data, needle); + Curl_attach_connection(data, needle, TRUE); needle = NULL; - result = Curl_cpool_add(data, data->conn); if(!result) { /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); @@ -2411,7 +2412,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) /* Add needle to conn pool, which assigns the connection id. * Attach regardless of result, for correct handling. */ result = Curl_cpool_add(data, needle); - Curl_attach_connection(data, needle); + Curl_attach_connection(data, needle, TRUE); needle = NULL; if(result) goto out; @@ -2523,7 +2524,7 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) /* We are not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + Curl_conn_close(data, conn, TRUE); } return result; @@ -2595,6 +2596,31 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key) return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } +struct Curl_easy *Curl_get_admin(struct Curl_easy *data) +{ + struct Curl_easy *admin; + + if(!data->mid) /* already an admin handle */ + admin = data; + else if(data->multi) + admin = data->multi->admin; + else if(data->multi_easy) + admin = data->multi_easy->admin; + else { + DEBUGASSERT(0); /* we do not want this. does it happen? */ + admin = data; + } + if(admin != data) { + admin->set.conn_max_idle_ms = data->set.conn_max_idle_ms; + admin->set.conn_max_age_ms = data->set.conn_max_age_ms; + admin->set.upkeep_interval_ms = data->set.upkeep_interval_ms; + admin->set.timeout = data->set.timeout; + admin->set.server_response_timeout = data->set.server_response_timeout; + admin->set.no_signal = data->set.no_signal; + } + return admin; +} + CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) { if(r1 && (r1 != CURLE_AGAIN)) diff --git a/lib/url.h b/lib/url.h index d481dabb80bc..43ee63cfc597 100644 --- a/lib/url.h +++ b/lib/url.h @@ -67,6 +67,12 @@ CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key, void Curl_conn_meta_remove(struct connectdata *conn, const char *key); void *Curl_conn_meta_get(struct connectdata *conn, const char *key); +/* Get an admin handle for internal operations from the given + * easy handle, if possible. The admin handle inherits certain + * properties from `data`. If no admin handle is available (not multi + * or share attached), the easy handle itself is returned. */ +struct Curl_easy *Curl_get_admin(struct Curl_easy *data); + #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ diff --git a/lib/urldata.h b/lib/urldata.h index 8601a89b8fa2..9aebefafcdd9 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -540,9 +540,7 @@ struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ - curl_off_t lastconnect_id; /* The last connection, -1 if undefined */ - curl_off_t recent_conn_id; /* The most recent connection used, might no - * longer exist */ + curl_off_t lastconnect_id; /* The last assigned connection or -1 */ struct dynbuf headerb; /* buffer to store headers in */ #ifndef CURL_DISABLE_HSTS struct curl_slist *hstslist; /* list of HSTS files set by diff --git a/tests/data/test1554 b/tests/data/test1554 index f76678564775..38ea1feaa387 100644 --- a/tests/data/test1554 +++ b/tests/data/test1554 @@ -63,6 +63,8 @@ run 1: foobar and so on fun! [- Mutex unlock SHARE -] Mutex lock SHARE [- Mutex unlock SHARE +-] Mutex lock CONNECT +[- Mutex unlock CONNECT From fe703df949864cbae594d102d43c4b9ad3e30ef0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 14 Aug 2026 14:35:04 +0200 Subject: [PATCH 0892/1062] lib: optimize struct layouts for reduced memory usage struct SingleRequest tweaks - 'upgr101' from enum upgrade101 => 'unsigned char', saves three bytes - made some 'unsigned char' => uint8_t - moved 'io_flags' to fill a hole on 64-bit arch struct UserDefined tweaks - Sort the fields by size. Larger to smaller. Helps avoding holes. - httpsig_algorithm moved - FTP uint8_t fields moved - new_file_perms moved - rtspreq moved - sort fields on size - urldata: drop 'struct Curl_data_priority' It only had a single struct member 'weight'. Use that directly instead to save indirections and struct alignments. Move field for size order. - urldata: move the RTSP fields in the UrlState struct to be ordered by size struct PureInfo tweaks - make 'pxcode' a uint8_t - move 'conn_protocol' to fill a hole - sort 'struct PureInfo' fields by size Closes #22585 --- lib/http2.c | 17 ++++---- lib/request.h | 8 ++-- lib/setopt.c | 2 +- lib/socks.c | 2 +- lib/url.c | 4 +- lib/urldata.h | 108 ++++++++++++++++++++++++-------------------------- 6 files changed, 67 insertions(+), 74 deletions(-) diff --git a/lib/http2.c b/lib/http2.c index 03f226c92ec3..b4787f156fe9 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -704,7 +704,7 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, if(second) { struct h2_stream_ctx *second_stream; http2_data_setup(cf, second, &second_stream); - second->state.priority.weight = data->state.priority.weight; + second->state.weight = data->state.weight; if(data->share) (void)Curl_share_easy_link(second, data->share); } @@ -1757,15 +1757,15 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, static int sweight_wanted(const struct Curl_easy *data) { /* 0 weight is not set by user and we take the nghttp2 default one */ - return data->set.priority.weight ? - data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; + return data->set.weight ? + data->set.weight : NGHTTP2_DEFAULT_WEIGHT; } static int sweight_in_effect(const struct Curl_easy *data) { /* 0 weight is not set by user and we take the nghttp2 default one */ - return data->state.priority.weight ? - data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; + return data->state.weight ? + data->state.weight : NGHTTP2_DEFAULT_WEIGHT; } /* @@ -1777,10 +1777,9 @@ static int sweight_in_effect(const struct Curl_easy *data) static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { - struct Curl_data_priority *prio = &data->set.priority; - nghttp2_priority_spec_init(pri_spec, 0, - sweight_wanted(data), FALSE); - data->state.priority = *prio; + int prio = data->set.weight; + nghttp2_priority_spec_init(pri_spec, 0, sweight_wanted(data), FALSE); + data->state.weight = prio; } /* diff --git a/lib/request.h b/lib/request.h index ec127cb82e1b..a7c0a93dd3b1 100644 --- a/lib/request.h +++ b/lib/request.h @@ -95,9 +95,10 @@ struct SingleRequest { first one */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ - unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ - unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ - enum upgrade101 upgr101; /* 101 upgrade state */ + uint8_t httpversion_sent; /* Version in request (09, 10, 11, etc.) */ + uint8_t httpversion; /* Version in response (09, 10, 11, etc.) */ + uint8_t upgr101; /* 101 upgrade state */ + uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ /* Client Writer stack, handles transfer- and content-encodings, protocol * checks, pausing by client callbacks. */ @@ -117,7 +118,6 @@ struct SingleRequest { header data */ char *newurl; /* Set to the new URL to use when a redirect or a retry is wanted */ - uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ char *hd_auth; /* Authorization header, full HTTP/1.x line */ #ifndef CURL_DISABLE_PROXY diff --git a/lib/setopt.c b/lib/setopt.c index 924263478b24..d6c641f780a6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1062,7 +1062,7 @@ static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) if((arg >= 1) && (arg <= 256)) - s->priority.weight = (int)arg; + s->weight = (int)arg; break; #else result = CURLE_NOT_BUILT_IN; diff --git a/lib/socks.c b/lib/socks.c index 4f98912a54c4..949f20c13539 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1196,7 +1196,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, if(pxresult) { result = CURLE_PROXY; - data->info.pxcode = pxresult; + data->info.pxcode = (uint8_t)pxresult; goto out; } else if(ctx->state != SOCKS_ST_SUCCESS) diff --git a/lib/url.c b/lib/url.c index f2f74d8b2ec0..60421f688dc5 100644 --- a/lib/url.c +++ b/lib/url.c @@ -429,7 +429,7 @@ void Curl_init_userdefined(struct Curl_easy *data) set->http09_allowed = FALSE; set->httpwant = CURL_HTTP_VERSION_NONE; #if defined(USE_HTTP2) || defined(USE_HTTP3) - memset(&set->priority, 0, sizeof(set->priority)); + set->weight = 0; #endif set->quick_exit = 0L; #ifndef CURL_DISABLE_WEBSOCKETS @@ -2570,7 +2570,7 @@ CURLcode Curl_init_transfer(struct Curl_easy *data, struct connectdata *conn) void Curl_data_priority_clear_state(struct Curl_easy *data) { - memset(&data->state.priority, 0, sizeof(data->state.priority)); + data->state.weight = 0; } #endif /* USE_HTTP2 || USE_HTTP3 */ diff --git a/lib/urldata.h b/lib/urldata.h index 9aebefafcdd9..95ff12a8b250 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -396,22 +396,6 @@ struct connectdata { * All variables in this struct must be initialized/reset in Curl_initinfo(). */ struct PureInfo { - int httpcode; /* Recent HTTP, FTP, RTSP or SMTP response code */ - int httpproxycode; /* response code from proxy when received separate */ - int httpversion; /* the http version number X.Y = X*10+Y */ - time_t filetime; /* If requested, this is might get set. Set to -1 if the - time was unretrievable. */ - curl_off_t request_size; /* the amount of bytes sent in the request(s) */ - curl_off_t numconnects; /* how many new connections libcurl created */ - uint32_t proxyauthavail; /* what proxy auth types were announced */ - uint32_t httpauthavail; /* what host auth types were announced */ - uint32_t proxyauthpicked; /* selected proxy auth type */ - uint32_t httpauthpicked; /* selected host auth type */ - char *contenttype; /* the content type of the object */ - char *wouldredirect; /* URL this would have been redirected to if asked to */ - curl_off_t retry_after; /* info from Retry-After: header */ - uint32_t header_size; /* size of read header(s) in bytes */ - /* PureInfo primary ip_quadruple is copied over from the connectdata struct in order to allow curl_easy_getinfo() to return this information even when the session handle is no longer associated with a connection, @@ -419,11 +403,26 @@ struct PureInfo { session handle without disturbing information which is still alive, and that might be reused, in the connection pool. */ struct ip_quadruple primary; - const char *conn_scheme; - uint32_t conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ - CURLproxycode pxcode; + time_t filetime; /* If requested, this is might get set. Set to -1 if the + time was unretrievable. */ + curl_off_t request_size; /* the amount of bytes sent in the request(s) */ + curl_off_t numconnects; /* how many new connections libcurl created */ + char *contenttype; /* the content type of the object */ + char *wouldredirect; /* URL this would have been redirected to if asked to */ + curl_off_t retry_after; /* info from Retry-After: header */ + const char *conn_scheme; + int httpcode; /* Recent HTTP, FTP, RTSP or SMTP response code */ + int httpproxycode; /* response code from proxy when received separate */ + int httpversion; /* the http version number X.Y = X*10+Y */ + uint32_t conn_protocol; + uint32_t proxyauthavail; /* what proxy auth types were announced */ + uint32_t httpauthavail; /* what host auth types were announced */ + uint32_t proxyauthpicked; /* selected proxy auth type */ + uint32_t httpauthpicked; /* selected host auth type */ + uint32_t header_size; /* size of read header(s) in bytes */ + uint8_t pxcode; /* holds a CURLproxycode */ BIT(timecond); /* set to TRUE if the time condition did not match, which thus made the document NOT get fetched */ BIT(used_proxy); /* the transfer used a proxy */ @@ -496,14 +495,6 @@ struct Curl_data_prio_node { }; #endif -/** - * Priority information for an easy handle in relation to others - * on the same connection. - */ -struct Curl_data_priority { - int weight; -}; - /* Timers */ typedef enum { EXPIRE_100_TIMEOUT, @@ -583,8 +574,6 @@ struct UrlState { void *baseprov; void *libctx; char *propq; /* for a provider */ - - BIT(provider_loaded); #endif /* USE_OPENSSL */ struct expire_timers timeouts; /* expire timeouts */ @@ -593,22 +582,8 @@ struct UrlState { char *range; /* range, if used. See README for detailed specification on this syntax. */ curl_off_t resume_from; /* continue [ftp] transfer from here */ - -#ifndef CURL_DISABLE_RTSP - /* This RTSP state information survives requests and connections */ - uint32_t rtsp_next_client_CSeq; /* the session's next client CSeq */ - uint32_t rtsp_next_server_CSeq; /* the session's next server CSeq */ - uint32_t rtsp_CSeq_recv; /* most recent CSeq received */ - uint8_t rtp_channel_mask[32]; /* for the correctness checking of the - interleaved data */ -#endif - curl_off_t infilesize; /* size of file to upload, -1 means unknown. Copied from set.filesize at start of operation */ -#if defined(USE_HTTP2) || defined(USE_HTTP3) - struct Curl_data_priority priority; /* shallow copy of data->set */ -#endif - curl_read_callback fread_func; /* read callback/function */ void *in; /* CURLOPT_READDATA */ CURLU *uh; /* URL handle for the current parsed URL */ @@ -658,6 +633,17 @@ struct UrlState { } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; +#endif +#ifndef CURL_DISABLE_RTSP + /* This RTSP state information survives requests and connections */ + uint8_t rtp_channel_mask[32]; /* for the correctness checking of the + interleaved data */ + uint32_t rtsp_next_client_CSeq; /* the session's next client CSeq */ + uint32_t rtsp_next_server_CSeq; /* the session's next server CSeq */ + uint32_t rtsp_CSeq_recv; /* most recent CSeq received */ +#endif +#if defined(USE_HTTP2) || defined(USE_HTTP3) + int weight; /* shallow copy of data->set */ #endif uint16_t followlocation; /* redirect counter */ uint8_t retrycount; /* number of retries on a new connection, up to @@ -665,6 +651,9 @@ struct UrlState { uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ +#ifdef USE_OPENSSL + BIT(provider_loaded); +#endif /* USE_OPENSSL */ BIT(really_alive); /* transfer is really alive in multi, passed INIT */ BIT(this_is_a_follow); /* this is a followed Location: request */ BIT(refused_stream); /* this was refused, try again */ @@ -869,7 +858,6 @@ struct UserDefined { void *writeheader; /* write the header to this if non-NULL */ uint32_t httpauth; /* kind of HTTP authentication to use (bitmask) */ uint32_t proxyauth; /* kind of proxy authentication to use (bitmask) */ - uint8_t httpsig_algorithm; /* CURLHTTPSIG_* algorithm for RFC 9421 */ void *postfields; /* if POST, set the fields' values here */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead @@ -949,9 +937,6 @@ struct UserDefined { curl_off_t max_filesize; /* Maximum file size to download */ #ifndef CURL_DISABLE_FTP timediff_t accepttimeout; /* in milliseconds, 0 means no timeout */ - uint8_t ftp_filemethod; /* how to get to a file: curl_ftpfile */ - uint8_t ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ - uint8_t ftp_ccc; /* FTP CCC options: curl_ftpccc */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) struct curl_slist *quote; /* after connection is established */ @@ -968,9 +953,9 @@ struct UserDefined { uint32_t ssh_auth_types; /* allowed SSH auth types */ uint32_t new_directory_perms; /* when creating remote dirs */ #endif - uint32_t new_file_perms; /* when creating remote files */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; + uint32_t new_file_perms; /* when creating remote files */ #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif @@ -978,8 +963,6 @@ struct UserDefined { curl_prot_t redir_protocols; #ifndef CURL_DISABLE_RTSP void *rtp_out; /* write RTP to this if non-NULL */ - /* Common RTSP header options */ - unsigned char rtspreq; /* RTSP request type */ #endif #ifndef CURL_DISABLE_FTP curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer @@ -997,13 +980,6 @@ struct UserDefined { timediff_t happy_eyeballs_timeout; /* ms, 0 is a valid value */ timediff_t server_response_timeout; /* ms, 0 means no timeout */ timediff_t shutdowntimeout; /* ms, 0 means default timeout */ - int tcp_keepidle; /* seconds in idle before sending keepalive probe */ - int tcp_keepintvl; /* seconds between TCP keepalive probes */ - int tcp_keepcnt; /* maximum number of keepalive probes */ - -#if defined(USE_HTTP2) || defined(USE_HTTP3) - struct Curl_data_priority priority; -#endif curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */ void *resolver_start_client; /* pointer to pass to resolver start callback */ @@ -1017,7 +993,16 @@ struct UserDefined { #ifndef CURL_DISABLE_SMTP struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif + int tcp_keepidle; /* seconds in idle before sending keepalive probe */ + int tcp_keepintvl; /* seconds between TCP keepalive probes */ + int tcp_keepcnt; /* maximum number of keepalive probes */ + uint32_t maxconnects; /* Max idle connections in the connection cache */ +#if defined(USE_HTTP2) || defined(USE_HTTP3) + /* Priority information for an easy handle in relation to others on the same + connection. */ + int weight; +#endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ uint16_t expect_100_timeout; /* in milliseconds */ @@ -1032,6 +1017,9 @@ struct UserDefined { #ifndef CURL_DISABLE_TFTP uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif +#ifndef CURL_DISABLE_RTSP + uint8_t rtspreq; /* RTSP request type */ +#endif #ifdef USE_ECH uint8_t tls_ech; /* TLS ECH configuration */ #endif @@ -1045,6 +1033,12 @@ struct UserDefined { */ uint8_t ftp_create_missing_dirs; #endif +#ifndef CURL_DISABLE_FTP + uint8_t ftp_filemethod; /* how to get to a file: curl_ftpfile */ + uint8_t ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ + uint8_t ftp_ccc; /* FTP CCC options: curl_ftpccc */ +#endif + uint8_t httpsig_algorithm; /* CURLHTTPSIG_* algorithm for RFC 9421 */ uint8_t use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! (type: curl_usessl)*/ uint8_t timecondition; /* kind of time comparison: curl_TimeCond */ From d854ab4673c2f9d8048c7f0f6d164b7e4d5e0865 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 14 Aug 2026 11:27:09 +0200 Subject: [PATCH 0893/1062] GHA/windows: install MSYS2 Perl manually on Windows 2025 runner In GitHub runner image windows-2025-vs2026 v20260810.198.2, the default Perl binary changed from MSYS2 to Strawberry. The reason is that the MSYS2 installation not longer has Perl preinstalled. As seen in CMake configuration: ```diff - -- Found Perl: C:/a/_temp/msys64/usr/bin/perl.exe (found version "5.42.2") + -- Found Perl: C:/Strawberry/perl/bin/perl.exe (found version "5.42.0") ``` curl's Perl scripts require an MSYS2 Perl. Fix by installing it manually in this one job. Symptom was `gencert.pl` no longer finding openssl.exe: ``` Missing or broken 'openssl' tool. openssl 1.0.2+ is required. ``` Then the script hanging while trying to trace it. Also: - add distinct error message and list PATH elements if openssl is not found via `gencert.pl`. - tried falling back to Git for Windows Perl, which caused the test step to either hang or run too slowly to fit the time slot. Refs: https://github.com/actions/runner-images/pull/14541 https://github.com/actions/runner-images/releases/tag/win25-vs2026%2F20260810.198 Bug: https://github.com/curl/curl/pull/22577#issuecomment-5286533641 Bug: https://github.com/curl/curl/pull/22577#issuecomment-5291468024 Closes #22580 --- .github/workflows/windows.yml | 1 + tests/certs/genserv.pl | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b07d005978e6..bfff8c210618 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -943,6 +943,7 @@ jobs: - name: 'openssl +examples' install-msys2: >- + perl mingw-w64-ucrt-x86_64-brotli mingw-w64-ucrt-x86_64-zlib mingw-w64-ucrt-x86_64-zstd diff --git a/tests/certs/genserv.pl b/tests/certs/genserv.pl index 6e8ceec315ee..3de4071ccf24 100755 --- a/tests/certs/genserv.pl +++ b/tests/certs/genserv.pl @@ -79,6 +79,7 @@ sub redir { } } if(!$found) { + printf "PATH used: %s\n", join(', ', File::Spec->path()); opensslfail(); } } From 3d6d93a6beb9eeaca98ce20eeed7f64ebb0c9297 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 14 Aug 2026 14:55:49 +0200 Subject: [PATCH 0894/1062] multi: timeout improvements - Move expire timeout code from multi into splay.c - keep a "time_base" timestamp to calculate timediff_t for actual timeout values. Unfortunately this means our timeouts will go wrong after ~500,000 years of continuous operations... - use timediff_t as key in splay instead of curltime - use timediff_t in transfers expire times instead of curltime - re-comment splay.c for better understanding how it works - replace splay nodes double-linked "same" list with a single link, we almost never have duplicate keys - keep transfer `mid` in splay nodes instead of the transfer pointer - keep registered bit in splay node for tracking instead of separate bit in transfer - adapt unit1309.c to changes in timediff_t and mid Closes #22584 --- docs/internals/SPLAY.md | 44 +++-- lib/cf-ip-happy.c | 2 +- lib/connect.c | 2 +- lib/cshutdn.c | 2 +- lib/curl_trc.c | 7 +- lib/curlx/timeval.c | 5 + lib/curlx/timeval.h | 3 + lib/http.c | 4 +- lib/multi.c | 270 ++++++++++------------------ lib/multihandle.h | 8 +- lib/multiif.h | 8 +- lib/splay.c | 386 ++++++++++++++++++++++++---------------- lib/splay.h | 61 +++++-- lib/url.c | 2 +- lib/urldata.h | 4 +- lib/vdns/asyn-thrdd.c | 2 +- tests/data/test1309 | 155 ++++++++++------ tests/unit/unit1309.c | 85 ++++++--- tests/unit/unit3214.c | 2 +- 19 files changed, 597 insertions(+), 455 deletions(-) diff --git a/docs/internals/SPLAY.md b/docs/internals/SPLAY.md index 29cf3858a66a..4a9e49940c5e 100644 --- a/docs/internals/SPLAY.md +++ b/docs/internals/SPLAY.md @@ -17,7 +17,7 @@ it automatically rebalances itself in each operation. ## libcurl use -libcurl adds fixed timeout expiry timestamps to the splay tree, and is meant +libcurl adds fixed timeout expiry `timediff_t` to the splay tree, and is meant to scale up to holding a huge amount of pending timeouts with decent performance. @@ -26,19 +26,25 @@ The splay tree is used to: 1. figure out the next timeout expiry value closest in time 2. iterate over timeouts that already have expired -This splay tree rebalances itself based on the time value. +This splay tree rebalances itself based on the timeout value. -Each node in the splay tree points to a `struct Curl_easy`. Each `Curl_easy` -struct is represented only once in the tree. To still allow each easy handle -to have a large number of timeouts per handle, each handle has a sorted linked -list of pending timeouts. Only the handle's timeout that is closest to expire +Each node in the splay tree carries a `uint32_t id`. This is set to the +`mid`, the unique transfer identifier in a multi handle. Each transfer +is added only once in the tree. To still allow each transfer +to have a large number of timeouts per handle, each handle has a sorted list +of pending timeouts. Only the handle's timeout that is closest to expire is the timestamp used for the splay tree node. When a specific easy handle's timeout expires, the node gets removed from the -splay tree and from the handle's linked list of timeouts. The next timeout for +splay tree and from the handle's list of timeouts. The next timeout for that handle is then first in line and becomes the new timeout value as the node is re-added to the splay. +To convert the `timediff_t` is the splay to the correct timestamps, the +multi handle carries a "base timestamp", set once when created, and the +timeout values are calculated relative to that. This works for about +500,000 years of continued operation of a multi handle. + ## `Curl_splay` ~~~c @@ -50,15 +56,16 @@ Rearranges the tree `t` after the provide time `i`. ## `Curl_splayinsert` ~~~c -struct Curl_tree *Curl_splayinsert(struct curltime key, +struct Curl_tree *Curl_splayinsert(timediff_t key, struct Curl_tree *t, - struct Curl_tree *node); + struct Curl_tree *node, + uint32_t id); ~~~ This function inserts a new `node` in the tree, using the given `key` -timestamp. The `node` struct has a field called `->payload` that can be set to -point to anything. libcurl sets this to the `struct Curl_easy` handle that is -associated with the timeout value set in `key`. +timeout. The `node` struct has a field called `->id` which is set to +the passed `id`. libcurl sets this to the transfer's `mid`, the unique +identifier in a multi handle. The splay insert function does not allocate any memory, it assumes the caller has that arranged. @@ -68,12 +75,12 @@ It returns a pointer to the new tree root. ## `Curl_splaygetbest` ~~~c -struct Curl_tree *Curl_splaygetbest(struct curltime key, +struct Curl_tree *Curl_splaygetbest(timediff_t key, struct Curl_tree *tree, struct Curl_tree **removed); ~~~ -If there is a node in the `tree` that has a time value that is less than the +If there is a node in the `tree` that has a timeout that is less than the provided `key`, this function removes that node from the tree and provides it in the `*removed` pointer (or NULL if there was no match). @@ -95,17 +102,16 @@ Note that a clean tree without any nodes present implies a NULL pointer. ## `Curl_splayset` ~~~c -void Curl_splayset(struct Curl_tree *node, void *payload); +void Curl_splayset(struct Curl_tree *node, uint32_t id); ~~~ -Set a custom pointer to be stored in the splay node. This pointer is not used +Sets the `id` in the splay node. This value is not used by the splay code itself and can be retrieved again with `Curl_splayget`. ## `Curl_splayget` ~~~c -void *Curl_splayget(struct Curl_tree *node); +uint32_t Curl_splayget(struct Curl_tree *node); ~~~ -Get the custom pointer from the splay node that was previously set with -`Curl_splayset`. If no pointer was set before, it returns NULL. +Get the `id` from the splay node that was previously set. diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 6371e3ebe5b6..3f52ee78acc6 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -902,7 +902,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, } #endif cf_ip_happy_ctx_clear(ctx, data); - Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); + Curl_expire_clear(data, EXPIRE_HAPPY_EYEBALLS); /* whatever errors were reported by ballers, clear our errorbuf */ Curl_reset_fail(data); data->info.numconnects++; /* to track the # of connections made */ diff --git a/lib/connect.c b/lib/connect.c index 3140881f27cc..dd401939ffe3 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -121,7 +121,7 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex, data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS); /* Set a timer, unless we operate on the admin handle */ if(data->mid) - Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); + Curl_expire(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); CURL_TRC_M(data, "shutdown start on%s connection", sockindex ? " secondary" : ""); } diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 6ad0f880134a..10cafdd065e3 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -258,7 +258,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn, } if(next_expire_ms) - Curl_expire_ex(admin, next_expire_ms, EXPIRE_SHUTDOWN); + Curl_expire(admin, next_expire_ms, EXPIRE_SHUTDOWN); } static void cshutdn_terminate_all(struct cshutdn *cshutdn, diff --git a/lib/curl_trc.c b/lib/curl_trc.c index ccfb5c7994b8..97f142af1820 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -339,14 +339,15 @@ void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...) void Curl_trc_easy_timers(struct Curl_easy *data) { - if(CURL_TRC_TIMER_is_verbose(data)) { + if(CURL_TRC_TIMER_is_verbose(data) && data->multi) { if(data->state.timeouts.first < EXPIRE_LAST) { struct expire_timers *timeouts = &data->state.timeouts; - const struct curltime *pnow = Curl_pgrs_now(data); + timediff_t base_us = + Curl_timeouts_offset_us(&data->multi->timeouts, Curl_pgrs_now(data)); expire_id eid = data->state.timeouts.first; for(; eid < EXPIRE_LAST; eid = timeouts->next[eid]) { CURL_TRC_TIMER(data, eid, "expires in %" FMT_TIMEDIFF_T "us", - curlx_ptimediff_us(&timeouts->time[eid], pnow)); + timeouts->offset_us[eid] - base_us); } } } diff --git a/lib/curlx/timeval.c b/lib/curlx/timeval.c index 01b20b4da132..a3ced3ff0c9e 100644 --- a/lib/curlx/timeval.c +++ b/lib/curlx/timeval.c @@ -216,6 +216,11 @@ timediff_t curlx_timediff_ceil_ms(struct curltime newer, return (diff * 1000) + ((newer.tv_usec - older.tv_usec + 999) / 1000); } +timediff_t curlx_us_to_ceil_ms(timediff_t us) +{ + return (us / 1000) + ((us > 0) && (us % 1000)); +} + /* * Returns: time difference in number of microseconds. For too large diffs it * returns max value. diff --git a/lib/curlx/timeval.h b/lib/curlx/timeval.h index c01f95d87df0..284c452df021 100644 --- a/lib/curlx/timeval.h +++ b/lib/curlx/timeval.h @@ -59,6 +59,9 @@ timediff_t curlx_ptimediff_ms(const struct curltime *newer, timediff_t curlx_timediff_ceil_ms(struct curltime newer, struct curltime older); +/* Returns milliseconds from microseconds, rounded up. */ +timediff_t curlx_us_to_ceil_ms(timediff_t us); + /* * Make sure that the first argument (newer) is the more recent time and older * is the older time, as otherwise you get a weird negative time-diff back... diff --git a/lib/http.c b/lib/http.c index d3cb5f72b5eb..869458fd3afa 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1486,7 +1486,7 @@ static void http_exp100_continue(struct Curl_easy *data, struct cr_exp100_ctx *ctx = reader->ctx; if(ctx->state > EXP100_SEND_DATA) { ctx->state = EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); + Curl_expire_clear(data, EXPIRE_100_TIMEOUT); } } @@ -1546,7 +1546,7 @@ static void cr_exp100_done(struct Curl_easy *data, { struct cr_exp100_ctx *ctx = reader->ctx; ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); + Curl_expire_clear(data, EXPIRE_100_TIMEOUT); } static const struct Curl_crtype cr_exp100 = { diff --git a/lib/multi.c b/lib/multi.c index cfa64410f1f4..82d830488b9a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -76,7 +76,7 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_multi *multi, struct Curl_easy *data); static void multi_timeout(struct Curl_multi *multi, - struct curltime *expire_time, + timediff_t *pexire_offset_us, int *timeout_ms); static void multi_schedule_pending(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); @@ -143,7 +143,7 @@ static void mstate_enter_completed(struct Curl_easy *data, /* Important: reset the conn pointer so that we do not point to memory that could be freed anytime */ Curl_detach_connection(data); - Curl_expire_clear(data); /* stop all timers */ + Curl_expire_clear_all(data); /* stop all timers */ } /* always use this function to change state, to make debugging easier */ @@ -235,6 +235,8 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, multi->magic = CURLMULTI_MAGIC_NUMBER; + curlx_pnow(&multi->now); + Curl_timeouts_init(&multi->timeouts, &multi->now); Curl_dnscache_init(&multi->dnscache, dnssize); Curl_mntfy_init(multi); Curl_multi_ev_init(multi, ev_hashsize); @@ -800,7 +802,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, /* The timer must be shut down before data->multi is set to NULL, else data's splaynode would remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ - Curl_expire_clear(data); + Curl_expire_clear_all(data); /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) @@ -1542,7 +1544,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, bool extrawait) /* when no socket, wait */ { size_t i; - struct curltime expire_time; int timeout_internal; int nevents = 0; struct easy_pollset ps; @@ -1621,7 +1622,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, * Use the shorter one of the internal and the caller requested timeout. * If we are called with `!extrawait` and multi_timeout() reports no * timeouts exist, do not wait. */ - multi_timeout(multi, &expire_time, &timeout_internal); + multi_timeout(multi, NULL, &timeout_internal); if((timeout_internal >= 0) && (timeout_internal < timeout_ms)) timeout_ms = timeout_internal; @@ -2929,25 +2930,22 @@ static CURLMcode multi_perform(struct Curl_multi *multi, * then and then we risk this loop to remove timers that actually have not * been handled! */ - if(multi->timetree) { - struct Curl_tree *t = NULL; - do { - multi->timetree = Curl_splaygetbest(&start, multi->timetree, &t); - if(t) { - /* the removed may have another timeout in queue */ - struct Curl_easy *data = Curl_splayget(t); - data->state.timeouts.registered = FALSE; - (void)add_next_timeout(&start, multi, data); - if(data->mstate == MSTATE_PENDING) { - bool stream_unused; - CURLcode result_unused; - if(multi_handle_timeout(data, &stream_unused, &result_unused)) { - infof(data, "PENDING handle timeout"); - move_pending_to_connect(multi, data); - } - } + while(Curl_timeouts_remove_expired(&multi->timeouts, &start, &mid)) { + /* the removed may have another timeout in queue */ + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(!data) { + DEBUGASSERT(0); + continue; + } + (void)add_next_timeout(&start, multi, data); + if(data->mstate == MSTATE_PENDING) { + bool stream_unused; + CURLcode result_unused; + if(multi_handle_timeout(data, &stream_unused, &result_unused)) { + infof(data, "PENDING handle timeout"); + move_pending_to_connect(multi, data); } - } while(t); + } } if(running_handles) { @@ -3130,12 +3128,10 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) static void multi_timeouts_init(struct Curl_easy *data) { data->state.timeouts.first = EXPIRE_LAST; - data->state.timeouts.registered = FALSE; + data->state.timeouts.splaynode.registered = FALSE; } /* - * add_next_timeout() - * * Each Curl_easy has a list of timeouts. The add_next_timeout() is called * when it has been removed from the splay tree because the timeout has * expired. This function is then to advance in the list to pick the next @@ -3150,11 +3146,10 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_easy *data) { struct expire_timers *timeouts = &data->state.timeouts; + timediff_t now_us = Curl_timeouts_offset_us(&multi->timeouts, pnow); - DEBUGASSERT(!timeouts->registered); while(timeouts->first < EXPIRE_LAST) { - timediff_t us = curlx_ptimediff_us(&timeouts->time[timeouts->first], pnow); - if(us <= 0) /* remove already expired timer */ + if(timeouts->offset_us[timeouts->first] <= now_us) /* already expired */ timeouts->first = timeouts->next[timeouts->first]; else /* timeouts are sorted, first is first in the future now */ break; @@ -3163,10 +3158,8 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, if(timeouts->first < EXPIRE_LAST) { /* Insert this node again into the splay. Keep the timer in the list in case we need to recompute future timers. */ - Curl_splayset(&timeouts->splaynode, data); - multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], - multi->timetree, &timeouts->splaynode); - timeouts->registered = TRUE; + Curl_timeouts_add(&multi->timeouts, data, + timeouts->offset_us[timeouts->first]); } return CURLM_OK; } @@ -3175,24 +3168,21 @@ static void multi_mark_expired_as_dirty(struct Curl_multi *multi, const struct curltime *ts) { struct Curl_easy *data = NULL; - struct Curl_tree *t = NULL; + uint32_t mid; /* * The loop following here will go on as long as there are expire-times left * to process (compared to `ts`) in the splay and 'data' will be * re-assigned for every expired handle we deal with. */ - while(1) { + while(Curl_timeouts_remove_expired(&multi->timeouts, ts, &mid)) { /* Check if there is one (more) expired timer to deal with! This function extracts a matching node if there is one */ - multi->timetree = Curl_splaygetbest(ts, multi->timetree, &t); - if(!t) - return; - - data = Curl_splayget(t); /* assign this for next loop */ - if(!data) + data = Curl_multi_get_easy(multi, mid); + if(!data) { + DEBUGASSERT(0); continue; - data->state.timeouts.registered = FALSE; + } #ifdef CURLVERBOSE if(CURL_TRC_TIMER_is_verbose(data)) { if(data->state.timeouts.first < EXPIRE_LAST) { @@ -3281,7 +3271,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if the same timeout is still the one to run after this call. That handles the case when the application asks libcurl to run the timeout prematurely. */ - memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); + multi->last_expire_offset_us = 0; /* Applications may set `socket_cb` *after* having added transfers * first. *Then* kick off processing with a @@ -3513,62 +3503,40 @@ static bool multi_has_dirties(struct Curl_multi *multi) } static void multi_timeout(struct Curl_multi *multi, - struct curltime *expire_time, + timediff_t *pexire_offset_us, int *timeout_ms) { - static const struct curltime tv_zero = { 0, 0 }; - VERBOSE(struct Curl_easy *data = NULL); - if(multi->dead) { - *expire_time = tv_zero; + if(pexire_offset_us) + *pexire_offset_us = 0; *timeout_ms = 0; return; } if(multi_has_dirties(multi)) { - *expire_time = *multi_now(multi); + if(pexire_offset_us) + *pexire_offset_us = Curl_timeouts_offset_us(&multi->timeouts, + multi_now(multi)); *timeout_ms = 0; return; } - else if(multi->timetree) { - const struct curltime *pnow = multi_now(multi); - /* splay the lowest to the bottom */ - multi->timetree = Curl_splay(&tv_zero, multi->timetree); - /* this will not return NULL from a non-empty tree, but some compilers - * are not convinced of that. Analyzers are hard. */ - *expire_time = multi->timetree ? multi->timetree->key : tv_zero; - - /* 'multi->timetree' will be non-NULL here but the compilers sometimes - yell at us if we assume so */ - if(multi->timetree && - curlx_ptimediff_us(&multi->timetree->key, pnow) > 0) { - /* some time left before expiration */ - timediff_t diff_ms = - curlx_timediff_ceil_ms(multi->timetree->key, *pnow); - VERBOSE(data = Curl_splayget(multi->timetree)); - if(diff_ms > INT_MAX) - diff_ms = INT_MAX; - *timeout_ms = (int)diff_ms; - } - else { - if(multi->timetree) - VERBOSE(data = Curl_splayget(multi->timetree)); - /* 0 means immediately */ - *timeout_ms = 0; - } - } else { - *expire_time = tv_zero; - *timeout_ms = -1; - } + const struct curltime *pnow = multi_now(multi); + uint32_t mid; + *timeout_ms = Curl_timeouts_next_ms(&multi->timeouts, pnow, + pexire_offset_us, &mid); #ifdef CURLVERBOSE - if(CURL_TRC_TIMER_is_verbose(data) && - (data->state.timeouts.first < EXPIRE_LAST)) { - CURL_TRC_TIMER(data, data->state.timeouts.first, - "gives multi timeout in %dms", *timeout_ms); - } + if(mid != UINT32_MAX) { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(data && CURL_TRC_TIMER_is_verbose(data) && + (data->state.timeouts.first < EXPIRE_LAST)) { + CURL_TRC_TIMER(data, data->state.timeouts.first, + "gives multi timeout in %dms", *timeout_ms); + } + } #endif + } } CURLMcode curl_multi_timeout(CURLM *m, @@ -3578,10 +3546,9 @@ CURLMcode curl_multi_timeout(CURLM *m, CURLMcode mresult; if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) { - struct curltime expire_time; int itimeout_ms; - multi_timeout(m, &expire_time, &itimeout_ms); + multi_timeout(m, NULL, &itimeout_ms); *timeout_ms = (long)itimeout_ms; mresult = CURLM_OK; } @@ -3595,14 +3562,14 @@ CURLMcode curl_multi_timeout(CURLM *m, */ CURLMcode Curl_update_timer(struct Curl_multi *multi) { - struct curltime expire_ts = { 0, 0 }; + timediff_t timeouts_offset_us = 0; int timeout_ms; int rc; bool set_value = FALSE; if(!multi->timer_cb || multi->dead) return CURLM_OK; - multi_timeout(multi, &expire_ts, &timeout_ms); + multi_timeout(multi, &timeouts_offset_us, &timeout_ms); if(timeout_ms < 0 && multi->last_timeout_ms < 0) { /* nothing to do */ @@ -3617,7 +3584,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) CURL_TRC_M(multi->admin, "[TIMER] set %dms, none before", timeout_ms); set_value = TRUE; } - else if(curlx_ptimediff_us(&multi->last_expire_ts, &expire_ts)) { + else if(multi->last_expire_offset_us != timeouts_offset_us) { /* We had a timeout before and have one now, the absolute timestamp * differs. The relative timeout_ms may be the same, but the starting * point differs. Let the application restart its timer. */ @@ -3634,7 +3601,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) if(set_value) { struct Curl_mapi_guard guard; - multi->last_expire_ts = expire_ts; + multi->last_expire_offset_us = timeouts_offset_us; multi->last_timeout_ms = timeout_ms; CURL_CBAPI_MULTI_START(&guard, multi, multi_timer_cb); rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); @@ -3663,8 +3630,7 @@ static bool multi_timeouts_check(struct Curl_easy *data) return FALSE; } if((timeouts->next[eid] < EXPIRE_LAST) && - (curlx_ptimediff_ms(&timeouts->time[eid], - &timeouts->time[timeouts->next[eid]]) > 0)) { + (timeouts->offset_us[eid] > timeouts->offset_us[timeouts->next[eid]])) { failf(data, "expire timeouts not sorted: %d happens after %d but " "is listed before", (int)eid, (int)timeouts->next[eid]); return FALSE; @@ -3675,11 +3641,9 @@ static bool multi_timeouts_check(struct Curl_easy *data) #endif /* - * multi_deltimeout() - * * Remove a given timestamp from the list of timeouts. */ -static void multi_deltimeout(struct Curl_easy *data, expire_id eid) +static void multi_clear_timeout(struct Curl_easy *data, expire_id eid) { struct expire_timers *timeouts = &data->state.timeouts; expire_id orig_first = timeouts->first; @@ -3693,9 +3657,8 @@ static void multi_deltimeout(struct Curl_easy *data, expire_id eid) anchor = &timeouts->next[*anchor]; } DEBUGASSERT(multi_timeouts_check(data)); - if(timeouts->registered) { + if(Curl_timeouts_has(data)) { struct Curl_multi *multi = data->multi; - int rc; if(!multi) { DEBUGASSERT(0); @@ -3703,31 +3666,22 @@ static void multi_deltimeout(struct Curl_easy *data, expire_id eid) } if((timeouts->first >= EXPIRE_LAST) || /* no more timeouts */ (timeouts->first != orig_first)) { /* active timeout changed */ - rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, - &multi->timetree); - if(rc) - infof(data, "Internal error removing splay node = %d", rc); - timeouts->registered = FALSE; + Curl_timeouts_remove(&multi->timeouts, data); } - if((timeouts->first < EXPIRE_LAST) && !timeouts->registered) { - multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], - multi->timetree, - &timeouts->splaynode); - timeouts->registered = TRUE; + if((timeouts->first < EXPIRE_LAST) && !Curl_timeouts_has(data)) { + Curl_timeouts_add(&multi->timeouts, data, + timeouts->offset_us[timeouts->first]); } } } /* - * multi_addtimeout() - * * Add a timestamp to the list of timeouts. Keep the list sorted so that head * of list is always the timeout nearest in time. - * */ -static CURLMcode multi_addtimeout(struct Curl_easy *data, - struct curltime *stamp, - expire_id eid) +static CURLMcode multi_set_timeout(struct Curl_easy *data, + const struct curltime *stamp, + expire_id eid) { struct expire_timers *timeouts = &data->state.timeouts; expire_id *anchor = &timeouts->first; @@ -3737,11 +3691,12 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data, return CURLM_BAD_FUNCTION_ARGUMENT; } /* remove from list, store time and re-insert */ - multi_deltimeout(data, eid); - memcpy(&timeouts->time[eid], stamp, sizeof(*stamp)); + multi_clear_timeout(data, eid); + timeouts->offset_us[eid] = + Curl_timeouts_offset_us(&data->multi->timeouts, stamp); + while(*anchor < EXPIRE_LAST) { - timediff_t ms = curlx_ptimediff_ms(&timeouts->time[*anchor], stamp); - if(ms > 0) /* *anchor's time is after eid's time */ + if(timeouts->offset_us[*anchor] > timeouts->offset_us[eid]) break; anchor = &timeouts->next[*anchor]; } @@ -3754,8 +3709,17 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data, return CURLM_OK; } -void Curl_expire_ex(struct Curl_easy *data, - timediff_t milli, expire_id eid) +/* + * given a number of milliseconds from now to use to set the 'act before + * this'-time for the transfer, to be extracted by curl_multi_timeout() + * + * The timeout will be added to a queue of timeouts if it defines a moment in + * time that is later than the current head of queue. + * + * Expire replaces a former timeout using the same id if already set. + */ +void Curl_expire(struct Curl_easy *data, + timediff_t milli, expire_id eid) { struct Curl_multi *multi = data->multi; struct expire_timers *timeouts = &data->state.timeouts; @@ -3775,96 +3739,58 @@ void Curl_expire_ex(struct Curl_easy *data, set = *Curl_pgrs_now(data); set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */ set.tv_usec += (int)(milli % 1000) * 1000; - if(set.tv_usec >= 1000000) { set.tv_sec++; set.tv_usec -= 1000000; } /* Add the timeout, will replace any previous value for this timer. */ - multi_addtimeout(data, &set, eid); + multi_set_timeout(data, &set, eid); DEBUGASSERT(timeouts->first < EXPIRE_LAST); - if(timeouts->registered) { - int rc; - /* timeouts->splaynode is in splay tree already. If the first timer + if(Curl_timeouts_has(data)) { + /* data has already a timeout registered. If the first timer * was NOT the one we just set AND is still the first one, - * nothing changed from the splay tree's point of view. The - * set timer triggers after the one already in the tree. Leave. */ + * nothing changed from the timeouts point of view. The + * set timer triggers after the one already registered. Leave. */ if((prev_id != eid) && (prev_id == timeouts->first)) return; - /* Since this is an updated time, we must remove the previous entry from - the splay tree first and then re-add the new value */ - rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, - &multi->timetree); - if(rc) - infof(data, "Internal error removing splay node = %d", rc); + /* Since this is an updated time, we must remove data from + * timeouts and then add it again. */ + Curl_timeouts_remove(&multi->timeouts, data); } - /* Indicate that we are in the splay tree and insert the new timer expiry - value since it is our local minimum. */ - Curl_splayset(&timeouts->splaynode, data); - multi->timetree = Curl_splayinsert(&timeouts->time[timeouts->first], - multi->timetree, &timeouts->splaynode); - timeouts->registered = TRUE; + /* Insert the new timer expiry since it is our local minimum. */ + Curl_timeouts_add(&multi->timeouts, data, + timeouts->offset_us[timeouts->first]); } /* - * Curl_expire() - * - * given a number of milliseconds from now to use to set the 'act before - * this'-time for the transfer, to be extracted by curl_multi_timeout() - * - * The timeout will be added to a queue of timeouts if it defines a moment in - * time that is later than the current head of queue. - * - * Expire replaces a former timeout using the same id if already set. - */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) -{ - Curl_expire_ex(data, milli, id); -} - -/* - * Curl_expire_done() - * * Removes the expire timer. Marks it as done. - * */ -void Curl_expire_done(struct Curl_easy *data, expire_id id) +void Curl_expire_clear(struct Curl_easy *data, expire_id id) { /* remove the timer, if there */ - multi_deltimeout(data, id); + multi_clear_timeout(data, id); CURL_TRC_TIMER(data, id, "cleared"); } /* - * Curl_expire_clear() - * * Clear ALL timeout values for this handle. */ -void Curl_expire_clear(struct Curl_easy *data) +void Curl_expire_clear_all(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; - struct expire_timers *timeouts = &data->state.timeouts; /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) return; - if(timeouts->registered) { + if(Curl_timeouts_remove(&multi->timeouts, data)) { /* Since this is an cleared time, we must remove the previous entry from the splay tree */ - int rc; - - rc = Curl_splayremove(multi->timetree, &timeouts->splaynode, - &multi->timetree); - if(rc) - infof(data, "Internal error clearing splay node = %d", rc); - - /* clear the timeouts */ multi_timeouts_init(data); if(data->id >= 0) diff --git a/lib/multihandle.h b/lib/multihandle.h index 2e749244f29b..3f5448f91c7e 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -32,6 +32,7 @@ #include "multi_ntfy.h" #include "psl.h" #include "socketpair.h" +#include "splay.h" #include "uint-bset.h" #include "uint-spbset.h" #include "uint-table.h" @@ -132,9 +133,8 @@ struct Curl_multi { /* current time for transfers running in this multi handle */ struct curltime now; - /* timetree points to the splay-tree of time nodes to figure out expire - times of all currently set timers */ - struct Curl_tree *timetree; + /* expiration times for all attached easy handles */ + struct Curl_timeouts timeouts; /* buffer used for transfer data, lazy initialized */ char *xfer_buf; /* the actual buffer */ @@ -161,7 +161,7 @@ struct Curl_multi { struct cshutdn cshutdn; /* connection shutdown handling */ struct cpool cpool; /* connection pool (bundles) */ - struct curltime last_expire_ts; /* timestamp of last expiry */ + timediff_t last_expire_offset_us; /* times offset of last expiry */ size_t max_host_connections; /* if >0, a fixed limit of the maximum number of connections per host */ diff --git a/lib/multiif.h b/lib/multiif.h index 44ac9bb37880..08d0b11064d2 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -27,11 +27,9 @@ * Prototypes for library-wide functions provided by multi.c */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id); -void Curl_expire_ex(struct Curl_easy *data, - timediff_t milli, expire_id eid); -void Curl_expire_clear(struct Curl_easy *data); -void Curl_expire_done(struct Curl_easy *data, expire_id id); +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id eid); +void Curl_expire_clear(struct Curl_easy *data, expire_id id); +void Curl_expire_clear_all(struct Curl_easy *data); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, struct connectdata *conn, diff --git a/lib/splay.c b/lib/splay.c index ddab7a4d6e2c..788da215ee54 100644 --- a/lib/splay.c +++ b/lib/splay.c @@ -23,173 +23,267 @@ ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #include "splay.h" -/* - * This macro compares two node keys i and j and returns: - * - * negative value: when i is smaller than j - * zero : when i is equal to j - * positive when : when i is larger than j - */ -#define splay_compare(i, j) curlx_ptimediff_us(i, j) + +void Curl_timeouts_init(struct Curl_timeouts *timeouts, + const struct curltime *ptime_base) +{ + timeouts->tree = NULL; + timeouts->time_base = ptime_base ? *ptime_base : curlx_now(); +} + +bool Curl_timeouts_has(struct Curl_easy *data) +{ + struct Curl_tree *node = data ? &data->state.timeouts.splaynode : NULL; + return node && node->registered; +} + +timediff_t Curl_timeouts_offset_us(struct Curl_timeouts *timeouts, + const struct curltime *pts) +{ + return curlx_ptimediff_us(pts, &timeouts->time_base); +} + +int Curl_timeouts_next_ms(struct Curl_timeouts *timeouts, + const struct curltime *pnow, + timediff_t *pexpire_offset_us, + uint32_t *pmid) +{ + if(timeouts->tree) { /* splay the lowest key to the root */ + timeouts->tree = Curl_splay(TIMEDIFF_T_MIN, timeouts->tree); + } + + if(timeouts->tree) { + timediff_t elapsed_us = Curl_timeouts_offset_us(timeouts, pnow); + timediff_t delta_us = timeouts->tree->key - elapsed_us; + if(pmid) + *pmid = timeouts->tree->id; + if(pexpire_offset_us) + *pexpire_offset_us = timeouts->tree->key; + if(delta_us > 0) { /* expires in the future */ + timediff_t ms = curlx_us_to_ceil_ms(delta_us); + return (ms > INT_MAX) ? INT_MAX : (int)ms; + } + else /* has expired */ + return 0; + } + if(pmid) + *pmid = UINT32_MAX; + if(pexpire_offset_us) + *pexpire_offset_us = 0; + return -1; +} + +bool Curl_timeouts_remove_expired(struct Curl_timeouts *timeouts, + const struct curltime *ts, + uint32_t *pmid) +{ + if(timeouts->tree) { + struct Curl_tree *t = NULL; + timediff_t elapsed_us = Curl_timeouts_offset_us(timeouts, ts); + timeouts->tree = Curl_splaygetbest(elapsed_us, timeouts->tree, &t); + if(t) { + *pmid = t->id; + return TRUE; + } + } + *pmid = UINT32_MAX; + return FALSE; +} + +void Curl_timeouts_add(struct Curl_timeouts *timeouts, + struct Curl_easy *data, + timediff_t offset_us) +{ + struct Curl_tree *node = &data->state.timeouts.splaynode; + DEBUGASSERT(!node->registered); + timeouts->tree = Curl_splayinsert(offset_us, timeouts->tree, + node, data->mid); +} + +bool Curl_timeouts_remove(struct Curl_timeouts *timeouts, + struct Curl_easy *data) +{ + struct Curl_tree *node = &data->state.timeouts.splaynode; + if(node->registered) { + int rc = Curl_splayremove(timeouts->tree, node, &timeouts->tree); +#ifdef DEBUGBUILD + if(rc) + curl_mfprintf(stderr, "Internal error removing splay node = %d\n", rc); +#else + (void)rc; +#endif + return TRUE; + } + return FALSE; +} /* - * Splay using the key i (which may or may not be in the tree.) The starting - * root is t. + * Splay using the key i (which may or may not be in the tree). + * This rotates the tree, so: + * - root->smaller has all nodes smaller than `key` + * - root->larger has all nodes larger than `key` + * - root->key may equal `key` or not + * */ -struct Curl_tree *Curl_splay(const struct curltime *pkey, - struct Curl_tree *t) +struct Curl_tree *Curl_splay(timediff_t key, + struct Curl_tree *root) { struct Curl_tree N, *l, *r, *y; - if(!t) + if(!root) return NULL; N.smaller = N.larger = NULL; l = r = &N; for(;;) { - timediff_t comp = splay_compare(pkey, &t->key); - if(comp < 0) { - if(!t->smaller) + if(key < root->key) { + /* key is somewhere in root->smaller branch */ + if(!root->smaller) /* which is empty, done */ break; - if(splay_compare(pkey, &t->smaller->key) < 0) { - y = t->smaller; /* rotate smaller */ - t->smaller = y->larger; - y->larger = t; - t = y; - if(!t->smaller) + if(key < root->smaller->key) { + /* key is somewhere in root->smaller->smaller, make a "Zig step" */ + y = root->smaller; + root->smaller = y->larger; + y->larger = root; + root = y; + if(!root->smaller) break; } - r->smaller = t; /* link smaller */ - r = t; - t = t->smaller; + /* Making root->smaller the new root, the old root is no longer + * referenced. Remember it in the N tree's `r`ight/larger side. + * Everything in old root is smaller than what the right side + * of N already has, so it gets added to r->smaller. */ + r->smaller = root; + r = root; + root = root->smaller; } - else if(comp > 0) { - if(!t->larger) + else if(key > root->key) { + /* key is somewhere in root->larger branch */ + if(!root->larger) /* which is empty, done */ break; - if(splay_compare(pkey, &t->larger->key) > 0) { - y = t->larger; /* rotate larger */ - t->larger = y->smaller; - y->smaller = t; - t = y; - if(!t->larger) + if(key > root->larger->key) { + /* key is somewhere in root->larger->larger, make a "Zig step" */ + y = root->larger; + root->larger = y->smaller; + y->smaller = root; + root = y; + if(!root->larger) break; } - l->larger = t; /* link larger */ - l = t; - t = t->larger; + /* Making root->larger the new root, the old root is no longer + * referenced. Remember it in the N tree's `l`eft/smaller side. + * Everything in old root is larger than what the left side + * of N already has, so it gets added to l->larger. */ + l->larger = root; + l = root; + root = root->larger; } - else + else /* exact match, root is key, done */ break; } - l->larger = t->smaller; /* assemble */ - r->smaller = t->larger; - t->smaller = N.larger; - t->larger = N.smaller; + /* Put it all together again. + * root->smaller has everything larger than current `l`. + * root->larger has everything smaller than current `r`. */ + l->larger = root->smaller; + r->smaller = root->larger; + root->smaller = N.larger; + root->larger = N.smaller; - return t; + return root; } -static const struct curltime SPLAY_SUBNODE = { - ~0, -1 -}; - /* Insert key i into the tree t. Return a pointer to the resulting tree or * NULL if something went wrong. * * @unittest: 1309 */ -struct Curl_tree *Curl_splayinsert(const struct curltime *pkey, - struct Curl_tree *t, - struct Curl_tree *node) +struct Curl_tree *Curl_splayinsert(timediff_t key, + struct Curl_tree *root, + struct Curl_tree *node, + uint32_t id) { DEBUGASSERT(node); - if(t) { - t = Curl_splay(pkey, t); - DEBUGASSERT(t); - if(splay_compare(pkey, &t->key) == 0) { - /* There already exists a node in the tree with the same key. Build a - doubly-linked circular list of nodes. We add the new 'node' struct to - the end of this list. */ - - node->key = SPLAY_SUBNODE; /* identify this node as a subnode */ - node->samen = t; - node->samep = t->samep; - t->samep->samen = node; - t->samep = node; - - return t; /* the root node always stays the same */ + node->key = key; + node->id = id; + node->same = NULL; + node->registered = TRUE; + if(root) { + root = Curl_splay(key, root); + DEBUGASSERT(root); + if(key == root->key) { + /* There already exists a node in the tree with the same key. + Append the new node to the `same` list. */ + struct Curl_tree **panchor = &root->same; + while(*panchor) + panchor = &(*panchor)->same; + *panchor = node; + return root; /* the root node always stays the same */ } } - if(!t) { + /* node becomes the new root. Insert old root as sub-branch. */ + if(!root) { node->smaller = node->larger = NULL; } - else if(splay_compare(pkey, &t->key) < 0) { - node->smaller = t->smaller; - node->larger = t; - t->smaller = NULL; + else if(key < root->key) { + node->smaller = root->smaller; + node->larger = root; + root->smaller = NULL; } else { - node->larger = t->larger; - node->smaller = t; - t->larger = NULL; + node->larger = root->larger; + node->smaller = root; + root->larger = NULL; } - node->key = *pkey; - /* no identical nodes (yet), we are the only one in the list of nodes */ - node->samen = node; - node->samep = node; return node; } /* Finds and deletes the best-fit node from the tree. Return a pointer to the resulting tree. best-fit means the smallest node if it is not larger than the key */ -struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, - struct Curl_tree *t, +struct Curl_tree *Curl_splaygetbest(timediff_t key, + struct Curl_tree *root, struct Curl_tree **removed) { - static const struct curltime tv_zero = { 0, 0 }; struct Curl_tree *x; - if(!t) { + if(!root) { *removed = NULL; /* none removed since there was no root */ return NULL; } /* find smallest */ - t = Curl_splay(&tv_zero, t); - DEBUGASSERT(t); - if(splay_compare(pkey, &t->key) < 0) { + root = Curl_splay(TIMEDIFF_T_MIN, root); + DEBUGASSERT(root); + if(key < root->key) { /* even the smallest is too big */ *removed = NULL; - return t; + return root; } /* FIRST! Check if there is a list with identical keys */ - x = t->samen; - if(x != t) { - /* there is, pick one from the list */ - - /* 'x' is the new root node */ - - x->key = t->key; - x->larger = t->larger; - x->smaller = t->smaller; - x->samep = t->samep; - t->samep->samen = x; - - *removed = t; + if(root->same) { + x = root->same; + DEBUGASSERT(x->key == root->key); + /* 'x' becomes the new root node */ + x->larger = root->larger; + x->smaller = root->smaller; + root->same = NULL; + root->registered = FALSE; + *removed = root; return x; /* new root */ } /* we splayed the tree to the smallest element, there is no smaller */ - x = t->larger; - *removed = t; + x = root->larger; + root->registered = FALSE; + *removed = root; return x; } @@ -205,87 +299,79 @@ struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, * * @unittest: 1309 */ -int Curl_splayremove(struct Curl_tree *t, +int Curl_splayremove(struct Curl_tree *root, struct Curl_tree *removenode, struct Curl_tree **newroot) { struct Curl_tree *x; - if(!t) + if(!root) return 1; DEBUGASSERT(removenode); + if(!removenode->registered) + return 2; - if(splay_compare(&SPLAY_SUBNODE, &removenode->key) == 0) { - /* It is a subnode within a 'same' linked list and thus we can unlink it - easily. */ - DEBUGASSERT(removenode->samen != removenode); - if(removenode->samen == removenode) - /* A non-subnode should never be set to SPLAY_SUBNODE */ - return 3; - - removenode->samep->samen = removenode->samen; - removenode->samen->samep = removenode->samep; - - /* Ensures that double-remove gets caught. */ - removenode->samen = removenode; - - *newroot = t; /* return the same root */ - return 0; - } - - t = Curl_splay(&removenode->key, t); - DEBUGASSERT(t); + root = Curl_splay(removenode->key, root); + DEBUGASSERT(root); - /* First make sure that we got the same root node as the one we want + /* First make sure that we got the same root key as the one we want to remove, as otherwise we might be trying to remove a node that - is not actually in the tree. - - We cannot compare the keys here as a double remove in quick - succession of a node with key != SPLAY_SUBNODE && same != NULL - could return the same key but a different node. */ - DEBUGASSERT(t == removenode); - if(t != removenode) + is not actually in the tree. */ + if(root->key != removenode->key) { + DEBUGASSERT(0); return 2; + } - /* Check if there is a list with identical sizes, as then we are trying to - remove the root node of a list of nodes with identical keys. */ - x = t->samen; - if(x != t) { + if(root != removenode) { + /* Should be in the root->same list then */ + struct Curl_tree **panchor; + for(panchor = &root->same; *panchor; panchor = &(*panchor)->same) { + if(*panchor == removenode) { + *panchor = removenode->same; + removenode->same = NULL; + removenode->registered = FALSE; + *newroot = root; + return 0; + } + } + /* not found in same list, error */ + DEBUGASSERT(0); + return 2; + } + /* removing the root node */ + if(root->same) { /* 'x' is the new root node, we make it use the root node's smaller/larger links */ - - x->key = t->key; - x->larger = t->larger; - x->smaller = t->smaller; - x->samep = t->samep; - t->samep->samen = x; + x = root->same; + x->larger = root->larger; + x->smaller = root->smaller; + root->same = NULL; } else { /* Remove the root node */ - if(!t->smaller) - x = t->larger; + if(!root->smaller) + x = root->larger; else { - x = Curl_splay(&removenode->key, t->smaller); + x = Curl_splay(removenode->key, root->smaller); DEBUGASSERT(x); - x->larger = t->larger; + x->larger = root->larger; } } - - *newroot = x; /* store new root pointer */ - + removenode->registered = FALSE; + *newroot = x; /* return new root */ return 0; } /* set and get the custom payload for this tree node */ -void Curl_splayset(struct Curl_tree *node, void *payload) +void Curl_splayset(struct Curl_tree *node, uint32_t id) { DEBUGASSERT(node); - node->ptr = payload; + node->id = id; } -void *Curl_splayget(struct Curl_tree *node) +uint32_t Curl_splayget(struct Curl_tree *node) { DEBUGASSERT(node); - return node->ptr; + return node->id; } diff --git a/lib/splay.h b/lib/splay.h index c6623f2ef0c4..3f9e66297cd2 100644 --- a/lib/splay.h +++ b/lib/splay.h @@ -27,33 +27,66 @@ #include "curlx/timeval.h" +struct Curl_easy; + /* only use function calls to access this struct */ struct Curl_tree { struct Curl_tree *smaller; /* smaller node */ struct Curl_tree *larger; /* larger node */ - struct Curl_tree *samen; /* points to the next node with identical key */ - struct Curl_tree *samep; /* points to the prev node with identical key */ - struct curltime key; /* this node's "sort" key */ - void *ptr; /* data the splay code does not care about */ + struct Curl_tree *same; /* points to the next node with identical key */ + timediff_t key; /* this node's "sort" key */ + uint32_t id; /* provided id for this node */ + BIT(registered); /* node is registered in splay tree */ +}; + +struct Curl_timeouts { + struct Curl_tree *tree; + struct curltime time_base; }; -struct Curl_tree *Curl_splay(const struct curltime *pkey, - struct Curl_tree *t); +void Curl_timeouts_init(struct Curl_timeouts *timeouts, + const struct curltime *ptime_base); + +bool Curl_timeouts_has(struct Curl_easy *data); + +timediff_t Curl_timeouts_offset_us(struct Curl_timeouts *timeouts, + const struct curltime *pts); + +int Curl_timeouts_next_ms(struct Curl_timeouts *timeouts, + const struct curltime *pnow, + timediff_t *pexpire_offset_us, + uint32_t *pmid); + +bool Curl_timeouts_remove_expired(struct Curl_timeouts *timeouts, + const struct curltime *ts, + uint32_t *pmid); + +void Curl_timeouts_add(struct Curl_timeouts *timeouts, + struct Curl_easy *data, + timediff_t offset_us); + +/* Returns TRUE if data was registered in timeouts before */ +bool Curl_timeouts_remove(struct Curl_timeouts *timeouts, + struct Curl_easy *data); + +struct Curl_tree *Curl_splay(timediff_t key, + struct Curl_tree *root); -struct Curl_tree *Curl_splayinsert(const struct curltime *pkey, - struct Curl_tree *t, - struct Curl_tree *node); +struct Curl_tree *Curl_splayinsert(timediff_t key, + struct Curl_tree *root, + struct Curl_tree *node, + uint32_t id); -struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, - struct Curl_tree *t, +struct Curl_tree *Curl_splaygetbest(timediff_t key, + struct Curl_tree *root, struct Curl_tree **removed); -int Curl_splayremove(struct Curl_tree *t, +int Curl_splayremove(struct Curl_tree *root, struct Curl_tree *removenode, struct Curl_tree **newroot); /* set and get the custom payload for this tree node */ -void Curl_splayset(struct Curl_tree *node, void *payload); -void *Curl_splayget(struct Curl_tree *node); +void Curl_splayset(struct Curl_tree *node, uint32_t id); +uint32_t Curl_splayget(struct Curl_tree *node); #endif /* HEADER_CURL_SPLAY_H */ diff --git a/lib/url.c b/lib/url.c index 60421f688dc5..946ebdd97656 100644 --- a/lib/url.c +++ b/lib/url.c @@ -228,7 +228,7 @@ CURLcode Curl_close(struct Curl_easy **datap) } DEBUGASSERT(!data->conn || data->state.internal); - Curl_expire_clear(data); /* shut off any timers left */ + Curl_expire_clear_all(data); /* shut off any timers left */ if(data->state.rangestringalloc) curlx_free(data->state.range); diff --git a/lib/urldata.h b/lib/urldata.h index 95ff12a8b250..886b48e0a901 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -514,10 +514,10 @@ typedef enum { struct expire_timers { struct Curl_tree splaynode; /* for the splay stuff */ - struct curltime time[EXPIRE_LAST]; + /* microsecond offset from Curl_timeouts base timestamp */ + timediff_t offset_us[EXPIRE_LAST]; expire_id next[EXPIRE_LAST]; expire_id first; - BIT(registered); /* timeout node is registered in splay tree */ }; /* individual pieces of the URL */ diff --git a/lib/vdns/asyn-thrdd.c b/lib/vdns/asyn-thrdd.c index 13c918bd816b..0199e0c0a49a 100644 --- a/lib/vdns/asyn-thrdd.c +++ b/lib/vdns/asyn-thrdd.c @@ -862,7 +862,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(result) return result; - Curl_expire_done(data, EXPIRE_ASYNC_NAME); + Curl_expire_clear(data, EXPIRE_ASYNC_NAME); /* A failure is an authoritative negative answer, eligible for negative caching, only when every A/AAAA query performed came diff --git a/tests/data/test1309 b/tests/data/test1309 index 7e6af3822790..ed38d616e233 100644 --- a/tests/data/test1309 +++ b/tests/data/test1309 @@ -121,7 +121,7 @@ Tree look: 0.49[15] 0.39[14] 0.0[15] -remove pointer 7, payload 718 +remove node 7, payload 718 Tree look: 0.1013[5] 0.1003[4] @@ -172,7 +172,7 @@ Tree look: 0.49[15] 0.39[14] 0.0[15] -remove pointer 8, payload 236 +remove node 8, payload 236 Tree look: 0.1013[7] 0.1003[6] @@ -222,7 +222,7 @@ Tree look: 0.49[7] 0.39[6] 0.0[7] -remove pointer 9, payload 777 +remove node 9, payload 777 Tree look: 0.1013[6] 0.1003[5] @@ -271,7 +271,7 @@ Tree look: 0.49[10] 0.39[9] 0.0[10] -remove pointer 10, payload 295 +remove node 10, payload 295 Tree look: 0.1013[8] 0.1003[7] @@ -319,7 +319,7 @@ Tree look: 0.49[8] 0.39[7] 0.0[8] -remove pointer 11, payload 836 +remove node 11, payload 836 Tree look: 0.1013[5] 0.1003[4] @@ -366,7 +366,7 @@ Tree look: 0.49[11] 0.39[10] 0.0[11] -remove pointer 12, payload 354 +remove node 12, payload 354 Tree look: 0.1013[7] 0.1003[6] @@ -412,7 +412,7 @@ Tree look: 0.49[10] 0.39[9] 0.0[10] -remove pointer 13, payload 895 +remove node 13, payload 895 Tree look: 0.1013[4] 0.1003[3] @@ -457,7 +457,7 @@ Tree look: 0.49[13] 0.39[12] 0.0[13] -remove pointer 14, payload 413 +remove node 14, payload 413 Tree look: 0.1013[6] 0.1003[5] @@ -501,7 +501,7 @@ Tree look: 0.49[11] 0.39[10] 0.0[11] -remove pointer 15, payload 954 +remove node 15, payload 954 Tree look: 0.1013[2] 0.1003[1] @@ -544,7 +544,7 @@ Tree look: 0.49[14] 0.39[13] 0.0[14] -remove pointer 16, payload 472 +remove node 16, payload 472 Tree look: 0.1013[4] 0.1003[3] @@ -586,7 +586,7 @@ Tree look: 0.49[12] 0.39[11] 0.0[12] -remove pointer 17, payload 1013 +remove node 17, payload 1013 Tree look: 0.1003[0] 0.944[2] @@ -627,7 +627,7 @@ Tree look: 0.49[14] 0.39[13] 0.0[14] -remove pointer 18, payload 531 +remove node 18, payload 531 Tree look: 0.1003[2] 0.944[3] @@ -667,7 +667,7 @@ Tree look: 0.49[13] 0.39[12] 0.0[13] -remove pointer 19, payload 49 +remove node 19, payload 49 Tree look: 0.1003[4] 0.944[5] @@ -706,7 +706,7 @@ Tree look: 0.59[6] 0.39[0] 0.0[1] -remove pointer 20, payload 590 +remove node 20, payload 590 Tree look: 0.1003[2] 0.944[3] @@ -744,7 +744,7 @@ Tree look: 0.59[8] 0.39[3] 0.0[4] -remove pointer 21, payload 108 +remove node 21, payload 108 Tree look: 0.1003[4] 0.944[5] @@ -781,7 +781,7 @@ Tree look: 0.59[2] 0.39[1] 0.0[2] -remove pointer 22, payload 649 +remove node 22, payload 649 Tree look: 0.1003[3] 0.944[4] @@ -817,7 +817,7 @@ Tree look: 0.59[5] 0.39[4] 0.0[5] -remove pointer 23, payload 167 +remove node 23, payload 167 Tree look: 0.1003[5] 0.944[6] @@ -852,7 +852,7 @@ Tree look: 0.59[4] 0.39[3] 0.0[4] -remove pointer 24, payload 708 +remove node 24, payload 708 Tree look: 0.1003[3] 0.944[4] @@ -886,7 +886,7 @@ Tree look: 0.59[7] 0.39[6] 0.0[7] -remove pointer 25, payload 226 +remove node 25, payload 226 Tree look: 0.1003[5] 0.944[6] @@ -919,7 +919,7 @@ Tree look: 0.59[5] 0.39[4] 0.0[5] -remove pointer 26, payload 767 +remove node 26, payload 767 Tree look: 0.1003[2] 0.944[3] @@ -951,7 +951,7 @@ Tree look: 0.59[8] 0.39[7] 0.0[8] -remove pointer 27, payload 285 +remove node 27, payload 285 Tree look: 0.1003[4] 0.944[5] @@ -982,7 +982,7 @@ Tree look: 0.59[6] 0.39[5] 0.0[6] -remove pointer 28, payload 826 +remove node 28, payload 826 Tree look: 0.1003[2] 0.944[3] @@ -1012,7 +1012,7 @@ Tree look: 0.59[9] 0.39[8] 0.0[9] -remove pointer 29, payload 344 +remove node 29, payload 344 Tree look: 0.1003[4] 0.944[5] @@ -1041,7 +1041,7 @@ Tree look: 0.59[7] 0.39[6] 0.0[7] -remove pointer 30, payload 885 +remove node 30, payload 885 Tree look: 0.1003[2] 0.944[3] @@ -1069,7 +1069,7 @@ Tree look: 0.59[10] 0.39[9] 0.0[10] -remove pointer 31, payload 403 +remove node 31, payload 403 Tree look: 0.1003[4] 0.944[5] @@ -1096,7 +1096,7 @@ Tree look: 0.59[8] 0.39[7] 0.0[8] -remove pointer 32, payload 944 +remove node 32, payload 944 Tree look: 0.1003[1] 0.934[0] @@ -1122,7 +1122,7 @@ Tree look: 0.59[10] 0.39[9] 0.0[10] -remove pointer 33, payload 462 +remove node 33, payload 462 Tree look: 0.1003[3] 0.934[2] @@ -1147,7 +1147,7 @@ Tree look: 0.59[8] 0.39[7] 0.0[8] -remove pointer 34, payload 1003 +remove node 34, payload 1003 Tree look: 0.934[0] 0.875[2] @@ -1171,7 +1171,7 @@ Tree look: 0.59[10] 0.39[9] 0.0[10] -remove pointer 35, payload 521 +remove node 35, payload 521 Tree look: 0.934[2] 0.875[3] @@ -1194,7 +1194,7 @@ Tree look: 0.59[8] 0.39[7] 0.0[8] -remove pointer 36, payload 39 +remove node 36, payload 39 Tree look: 0.934[4] 0.875[5] @@ -1216,7 +1216,7 @@ Tree look: 0.98[4] 0.59[5] 0.0[0] -remove pointer 37, payload 580 +remove node 37, payload 580 Tree look: 0.934[2] 0.875[3] @@ -1237,7 +1237,7 @@ Tree look: 0.98[6] 0.59[7] 0.0[3] -remove pointer 38, payload 98 +remove node 38, payload 98 Tree look: 0.934[4] 0.875[5] @@ -1257,7 +1257,7 @@ Tree look: 0.118[3] 0.59[0] 0.0[1] -remove pointer 39, payload 639 +remove node 39, payload 639 Tree look: 0.934[3] 0.875[4] @@ -1276,7 +1276,7 @@ Tree look: 0.118[5] 0.59[3] 0.0[4] -remove pointer 40, payload 157 +remove node 40, payload 157 Tree look: 0.934[5] 0.875[6] @@ -1294,7 +1294,7 @@ Tree look: 0.118[0] 0.59[1] 0.0[2] -remove pointer 41, payload 698 +remove node 41, payload 698 Tree look: 0.934[3] 0.875[4] @@ -1311,7 +1311,7 @@ Tree look: 0.118[3] 0.59[4] 0.0[5] -remove pointer 42, payload 216 +remove node 42, payload 216 Tree look: 0.934[5] 0.875[6] @@ -1327,7 +1327,7 @@ Tree look: 0.118[1] 0.59[2] 0.0[3] -remove pointer 43, payload 757 +remove node 43, payload 757 Tree look: 0.934[2] 0.875[3] @@ -1342,7 +1342,7 @@ Tree look: 0.118[3] 0.59[4] 0.0[5] -remove pointer 44, payload 275 +remove node 44, payload 275 Tree look: 0.934[4] 0.875[5] @@ -1356,7 +1356,7 @@ Tree look: 0.118[1] 0.59[2] 0.0[3] -remove pointer 45, payload 816 +remove node 45, payload 816 Tree look: 0.934[1] 0.875[2] @@ -1369,7 +1369,7 @@ Tree look: 0.118[3] 0.59[4] 0.0[5] -remove pointer 46, payload 334 +remove node 46, payload 334 Tree look: 0.934[3] 0.875[4] @@ -1381,7 +1381,7 @@ Tree look: 0.118[1] 0.59[2] 0.0[3] -remove pointer 47, payload 875 +remove node 47, payload 875 Tree look: 0.934[1] 0.659[0] @@ -1392,7 +1392,7 @@ Tree look: 0.118[3] 0.59[4] 0.0[5] -remove pointer 48, payload 393 +remove node 48, payload 393 Tree look: 0.934[3] 0.659[2] @@ -1402,7 +1402,7 @@ Tree look: 0.118[1] 0.59[2] 0.0[3] -remove pointer 49, payload 934 +remove node 49, payload 934 Tree look: 0.659[0] 0.600[1] @@ -1411,7 +1411,7 @@ Tree look: 0.118[3] 0.59[4] 0.0[5] -remove pointer 0, payload 0 +remove node 0, payload 0 Tree look: 0.659[1] 0.600[0] @@ -1419,32 +1419,32 @@ Tree look: 0.177[2] 0.118[1] 0.59[2] -remove pointer 1, payload 541 +remove node 1, payload 541 Tree look: 0.659[2] 0.600[1] 0.177[0] 0.118[1] 0.59[2] -remove pointer 2, payload 59 +remove node 2, payload 59 Tree look: 0.659[3] 0.600[2] 0.177[1] 0.118[0] -remove pointer 3, payload 600 +remove node 3, payload 600 Tree look: 0.659[1] 0.177[0] 0.118[1] -remove pointer 4, payload 118 +remove node 4, payload 118 Tree look: 0.659[1] 0.177[0] -remove pointer 5, payload 659 +remove node 5, payload 659 Tree look: 0.177[0] -remove pointer 6, payload 177 +remove node 6, payload 177 Removing nodes not larger than 0 removed payload 0[0] Removing nodes not larger than 100 @@ -1556,6 +1556,61 @@ removed payload 1003[1] removed payload 1013[0] removed payload 1013[1] removed payload 1013[2] +Removing nodes not larger than 0 +removed payload 0[timeout=0] +Removing nodes not larger than 32 +removed payload 31[timeout=3] +removed payload 9[timeout=5] +removed payload 40[timeout=8] +removed payload 18[timeout=10] +removed payload 49[timeout=13] +removed payload 27[timeout=15] +removed payload 5[timeout=17] +removed payload 36[timeout=20] +removed payload 14[timeout=22] +removed payload 45[timeout=25] +removed payload 23[timeout=27] +removed payload 1[timeout=29] +removed payload 32[timeout=32] +Removing nodes not larger than 64 +removed payload 10[timeout=34] +removed payload 41[timeout=37] +removed payload 19[timeout=39] +removed payload 28[timeout=44] +removed payload 6[timeout=46] +removed payload 37[timeout=49] +removed payload 15[timeout=51] +removed payload 46[timeout=54] +removed payload 24[timeout=56] +removed payload 2[timeout=58] +removed payload 33[timeout=61] +removed payload 11[timeout=63] +Removing nodes not larger than 96 +removed payload 42[timeout=66] +removed payload 20[timeout=68] +removed payload 29[timeout=73] +removed payload 7[timeout=75] +removed payload 38[timeout=78] +removed payload 16[timeout=80] +removed payload 47[timeout=83] +removed payload 25[timeout=85] +removed payload 3[timeout=87] +removed payload 34[timeout=90] +removed payload 12[timeout=92] +removed payload 43[timeout=95] +Removing nodes not larger than 128 +removed payload 21[timeout=97] +removed payload 30[timeout=102] +removed payload 8[timeout=104] +removed payload 39[timeout=107] +removed payload 17[timeout=109] +removed payload 48[timeout=112] +removed payload 26[timeout=114] +removed payload 4[timeout=116] +removed payload 35[timeout=119] +removed payload 13[timeout=121] +removed payload 44[timeout=124] +removed payload 22[timeout=126] diff --git a/tests/unit/unit1309.c b/tests/unit/unit1309.c index 607e49aa88be..388c7d1415f3 100644 --- a/tests/unit/unit1309.c +++ b/tests/unit/unit1309.c @@ -38,10 +38,10 @@ static void splayprint(struct Curl_tree *t, int d, char output) curl_mprintf(" "); if(output) { - curl_mprintf("%ld.%ld[%d]", (long)t->key.tv_sec, (long)t->key.tv_usec, i); + curl_mprintf("0.%ld[%d]", (long)t->key, i); } - for(count = 0, node = t->samen; node != t; node = node->samen, count++) + for(count = 0, node = t->same; node; node = node->same, count++) ; if(output) { @@ -63,67 +63,96 @@ static CURLcode test_unit1309(const char *arg) struct Curl_tree *root, *removed; struct Curl_tree nodes[NUM_NODES * 3]; - size_t storage[NUM_NODES * 3]; int rc; - int i, j; - struct curltime tv_now = { 0, 0 }; + size_t i, j; + timediff_t tv_now = 0, timeout_last; root = NULL; /* the empty tree */ /* add nodes */ for(i = 0; i < NUM_NODES; i++) { - struct curltime key; + timediff_t key; - key.tv_sec = 0; - key.tv_usec = (541 * i) % 1023; - storage[i] = key.tv_usec; - Curl_splayset(&nodes[i], &storage[i]); - root = Curl_splayinsert(&key, root, &nodes[i]); + key = (541 * i) % 1023; + root = Curl_splayinsert(key, root, &nodes[i], (uint32_t)key); + fail_unless(nodes[i].registered, "node should have been registered"); } puts("Result:"); splayprint(root, 0, 1); for(i = 0; i < NUM_NODES; i++) { - int rem = (i + 7) % NUM_NODES; + size_t rem = (i + 7) % NUM_NODES; curl_mprintf("Tree look:\n"); splayprint(root, 0, 1); - curl_mprintf("remove pointer %d, payload %zu\n", rem, - *(size_t *)Curl_splayget(&nodes[rem])); + curl_mprintf("remove node %d, payload %u\n", (int)rem, + Curl_splayget(&nodes[rem])); rc = Curl_splayremove(root, &nodes[rem], &root); if(rc) { /* failed! */ - curl_mprintf("remove %d failed!\n", rem); + curl_mprintf("remove %d failed!\n", (int)rem); fail("remove"); } + fail_unless(!nodes[rem].registered, "node should not be registered"); + rc = Curl_splayremove(root, &nodes[rem], &root); + if(!rc) { + /* failed! */ + curl_mprintf("double remove %d did not fail!\n", (int)rem); + fail("double remove"); + } } fail_unless(!root, "tree not empty after removing all nodes"); /* rebuild tree */ for(i = 0; i < NUM_NODES; i++) { - struct curltime key; + timediff_t key; - key.tv_sec = 0; - key.tv_usec = (541 * i) % 1023; + key = (541 * i) % 1023; /* add some nodes with the same key */ for(j = 0; j <= i % 3; j++) { - storage[(i * 3) + j] = (key.tv_usec * 10) + j; - Curl_splayset(&nodes[(i * 3) + j], &storage[(i * 3) + j]); - root = Curl_splayinsert(&key, root, &nodes[(i * 3) + j]); + root = Curl_splayinsert(key, root, &nodes[(i * 3) + j], + (uint32_t)(key * 10 + j)); } } removed = NULL; for(i = 0; i <= 1100; i += 100) { - curl_mprintf("Removing nodes not larger than %d\n", i); - tv_now.tv_usec = i; - root = Curl_splaygetbest(&tv_now, root, &removed); + curl_mprintf("Removing nodes not larger than %d\n", (int)i); + tv_now = i; + root = Curl_splaygetbest(tv_now, root, &removed); + while(removed) { + curl_mprintf("removed payload %u[%u]\n", + Curl_splayget(removed) / 10, + Curl_splayget(removed) % 10); + root = Curl_splaygetbest(tv_now, root, &removed); + } + } + + fail_unless(!root, "tree not empty when it should be"); + + /* rebuild tree with duplicate values */ + for(i = 0; i < NUM_NODES; i++) { + timediff_t key = (541 * i) % 128; + root = Curl_splayinsert(key, root, &nodes[i], (uint32_t)i); + } + + removed = NULL; + timeout_last = -1; + for(i = 0; i <= 128; i += 32) { + curl_mprintf("Removing nodes not larger than %d\n", (int)i); + root = Curl_splaygetbest(i, root, &removed); while(removed) { - curl_mprintf("removed payload %zu[%zu]\n", - *(size_t *)Curl_splayget(removed) / 10, - *(size_t *)Curl_splayget(removed) % 10); - root = Curl_splaygetbest(&tv_now, root, &removed); + curl_mprintf("removed payload %u[timeout=%d]\n", + Curl_splayget(removed), (int)removed->key); + if(removed->key < timeout_last) { + /* failed! */ + curl_mprintf("remove timeout %d is smaller than last %d!\n", + (int)removed->key, (int)timeout_last); + fail("wrong timeout order"); + } + timeout_last = removed->key; + root = Curl_splaygetbest(i, root, &removed); } } diff --git a/tests/unit/unit3214.c b/tests/unit/unit3214.c index 2697886931dd..833c56c46c6c 100644 --- a/tests/unit/unit3214.c +++ b/tests/unit/unit3214.c @@ -48,7 +48,7 @@ static void checksize(const char *name, size_t size, size_t allowed) These sizes were chosen with platforms with 64-bit pointers in mind. */ #define MAX_CURL_EASY 5370 #define MAX_CONNECTDATA 1300 -#define MAX_CURL_MULTI 920 +#define MAX_CURL_MULTI 928 #define MAX_CURL_HTTPPOST 112 #define MAX_CURL_SLIST 16 #define MAX_CURL_KHKEY 24 From 91323de7f3ddfad2fb6860a695ffa805e9389afe Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 10:32:30 +0200 Subject: [PATCH 0895/1062] GHA/non-native: bump cross-build-actions, NetBSD 11.0, FreeBSD riscv64 New riscv64 job takes 4.5 minutes. Also: - use prefill on riscv64 to save 2 minutes configure time. - disable manuals/perl on riscv64 to save ~1 minute (install, build). - cmake: fix `HAVE_GETHOSTBYNAME_R_6` prefill for FreeBSD. Ref: https://github.com/cross-platform-actions/action/releases/tag/v1.4.0 Closes #22590 --- .github/workflows/non-native.yml | 30 +++++++++++++++++------------- CMake/unix-cache.cmake | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 29663c726e9f..56ef87c8901f 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -57,57 +57,61 @@ jobs: matrix: include: # https://github.com/DragonFlyBSD/DPorts - # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skiprun', + # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skiprun', # install: 'autoconf automake libtool openldap26-client libidn2', # options: '--with-openssl --enable-ldap --enable-ldaps --with-libidn2' } - # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall', + # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skipall', # install: 'cmake ninja' } # https://ports.freebsd.org/ - - { os: 'freebsd' , version: '15.1', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', + - { os: 'freebsd' , version: '15.1', build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'openssl', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity skiprun !examples', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'openssl !unity skiprun !examples', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skiprun !examples', + install: 'cmake-core ninja', + options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF' } + + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } - - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', options: '-DCURL_USE_GSSAPI=ON' } # https://app.midnightbsd.org/ # https://man.midnightbsd.org/cgi-bin/man.cgi/mport - # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skipall !examples', + # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'gnutls skipall !examples', # install: 'autoconf autoconf-archive automake libtool gnutls', # options: '--with-gnutls' } - - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skiprun', + - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'gnutls skiprun', install: 'cmake-core ninja perl5 gnutls openldap26-client libidn2', options: '-DCURL_USE_GNUTLS=ON' } # https://pkgsrc.se/ - - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall !examples', + - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skipall !examples', install: 'autoconf automake libtool mit-krb5', options: '--with-openssl --with-gssapi' } - - { os: 'netbsd' , version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', + - { os: 'netbsd' , version: '11.0' , build: 'cmake' , arch: 'x86_64' , cc: 'gcc' , desc: 'openssl', install: 'cmake ninja-build mit-krb5 openldap-client libidn2', options: '-DCURL_USE_GSSAPI=ON' } # https://openbsd.app/ # https://www.openbsd.org/faq/faq15.html # https://github.com/OpenMPT/openmpt/blob/master/.github/workflows/OpenBSD-Autotools.yml - - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'libressl skipall !examples', + - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'libressl skipall !examples', install: 'autoconf-2.72p0 automake-1.18.1 libtool', # NOTE: also sync these versions with the autoreconf step! options: '--with-openssl' } - - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl', + - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'libressl', install: 'cmake ninja openldap-client-- libidn2' } fail-fast: false @@ -117,7 +121,7 @@ jobs: persist-credentials: false - name: 'setup VM' - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0 with: environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION' operating_system: '${{ matrix.os }}' diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 4ee28f7eb7ac..e6720545e862 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -127,6 +127,7 @@ set(HAVE_GETHOSTBYNAME_R_5 0) set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(HAVE_GETHOSTBYNAME_R_6 1) set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 1) From ebc0fbc0e86d8ef635b9578348bf21bb8a4f480a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 15 Aug 2026 11:20:23 +0200 Subject: [PATCH 0896/1062] connect: only set connect timer on first socket Only collect connect/appconnect timer stats on the first socket. Fixes #22587 Closes #22592 Reported-by: GLaDOS-418 on github --- lib/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connect.c b/lib/connect.c index dd401939ffe3..9d904b4e04b9 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -309,7 +309,7 @@ static CURLcode conn_connect_trace(struct Curl_easy *data, static void conn_report_connect_stats(struct Curl_cfilter *cf, struct Curl_easy *data) { - if(cf) { + if(cf && (cf->sockindex == FIRSTSOCKET)) { struct curltime connected; struct curltime appconnected; From c7c42afab91c46e6baaa0e2801626e13acd6c30a Mon Sep 17 00:00:00 2001 From: hunterinvariants <217359725+hunterinvariants@users.noreply.github.com> Date: Thu, 13 Aug 2026 21:14:02 +0200 Subject: [PATCH 0897/1062] file: support directory listing on Windows Closes #22577 --- docs/TODO.md | 7 --- lib/curlx/fopen.c | 31 ++++++++++++ lib/curlx/fopen.h | 2 + lib/file.c | 113 +++++++++++++++++++++++++++++++++++++++++--- tests/data/test3203 | 14 ++++-- 5 files changed, 150 insertions(+), 17 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 62030569946d..6829910101e8 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -438,13 +438,6 @@ use text frames. An abandoned attempt to add support for this exists in [PR 22093](https://github.com/curl/curl/pull/22093). -# FILE - -## Directory listing on non-POSIX - -Listing the contents of a directory accessed with FILE only works on platforms -with `opendir()`. Support could be added for more systems, like Windows. - # TLS ## `TLS-PSK` with OpenSSL diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 30754b310a6d..09264aeb101a 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -291,6 +291,37 @@ HANDLE curlx_CreateFile(const char *filename, return handle; } + +HANDLE curlx_FindFirstFile(const char *filename, + WIN32_FIND_DATA *find_data) +{ + HANDLE handle = INVALID_HANDLE_VALUE; + +#ifdef UNICODE + TCHAR *filename_t = curlx_convert_UTF8_to_wchar(filename); +#else + const TCHAR *filename_t = filename; +#endif + + if(filename_t) { + TCHAR *fixed = NULL; + const TCHAR *target; + + if(fix_excessive_path(filename_t, &fixed)) + target = fixed; + else + target = filename_t; + + handle = FindFirstFile(target, find_data); + CURLX_FREE(fixed); + +#ifdef UNICODE + curlx_free(filename_t); +#endif + } + + return handle; +} #endif /* !CURL_WINDOWS_UWP */ int curlx_win32_open(const char *filename, int oflag, ...) diff --git a/lib/curlx/fopen.h b/lib/curlx/fopen.h index b64fbf6514c6..469d3c881303 100644 --- a/lib/curlx/fopen.h +++ b/lib/curlx/fopen.h @@ -43,6 +43,8 @@ HANDLE curlx_CreateFile(const char *filename, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); +HANDLE curlx_FindFirstFile(const char *filename, + WIN32_FIND_DATA *find_data); #endif /* !CURL_WINDOWS_UWP */ #define curlx_fstat _fstati64 #define curlx_struct_stat struct _stati64 diff --git a/lib/file.c b/lib/file.c index 2ebfd0c67bcc..d3553b7df2d1 100644 --- a/lib/file.c +++ b/lib/file.c @@ -76,12 +76,14 @@ struct FILEPROTO { char *freepath; /* pointer to the allocated block we must free, this might differ from the 'path' pointer */ int fd; /* open file descriptor to read from! */ + bool is_dir; }; static void file_cleanup(struct FILEPROTO *file) { curlx_safefree(file->freepath); file->path = NULL; + file->is_dir = FALSE; if(file->fd != -1) { curlx_close(file->fd); file->fd = -1; @@ -126,6 +128,38 @@ static CURLcode file_done(struct Curl_easy *data, return CURLE_OK; } +static int file_stat(const char *path, curlx_struct_stat *statbuf) +{ +#ifdef _WIN32 + int result = curlx_stat(path, statbuf); + + if(result) { + size_t pathlen = strlen(path); + + /* MSVCRT's narrow stat() rejects trailing directory separators. */ + if((pathlen > 3) && + ((path[pathlen - 1] == '\\') || (path[pathlen - 1] == '/'))) { + char *trimmed = curlx_strdup(path); + + if(trimmed) { + do { + trimmed[--pathlen] = '\0'; + } while((pathlen > 3) && + ((trimmed[pathlen - 1] == '\\') || + (trimmed[pathlen - 1] == '/'))); + + result = curlx_stat(trimmed, statbuf); + curlx_free(trimmed); + } + } + } + + return result; +#else + return curlx_stat(path, statbuf); +#endif +} + /* * file_connect() gets called from Curl_protocol_connect() to allow us to * do protocol-specific actions at connect-time. We emulate a @@ -135,6 +169,9 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) { char *real_path; struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); +#ifdef _WIN32 + curlx_struct_stat statbuf; +#endif int fd; #ifdef DOS_FILESYSTEM size_t i; @@ -233,7 +270,13 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) file->freepath = real_path; /* free this when done */ file->fd = fd; - if(!data->state.upload && (fd == -1)) { +#ifdef _WIN32 + if(!data->state.upload && (fd == -1) && + !file_stat(file->path, &statbuf) && S_ISDIR(statbuf.st_mode)) + file->is_dir = TRUE; +#endif + + if(!data->state.upload && (fd == -1) && !file->is_dir) { failf(data, "Could not open file %s", data->state.up.path); file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); return CURLE_FILE_COULDNT_READ_FILE; @@ -370,6 +413,58 @@ static CURLcode file_upload(struct Curl_easy *data, return result; } +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) +static CURLcode win32_file_list(struct Curl_easy *data, const char *path) +{ + WIN32_FIND_DATA entry; + HANDLE handle; + char *pattern; + size_t pathlen = strlen(path); + CURLcode result = CURLE_OK; + DWORD error; + + pattern = curl_maprintf("%s%s*", path, + pathlen && + (path[pathlen - 1] == '\\' || + path[pathlen - 1] == '/') ? "" : "\\"); + if(!pattern) + return CURLE_OUT_OF_MEMORY; + + handle = curlx_FindFirstFile(pattern, &entry); + curlx_free(pattern); + if(handle == INVALID_HANDLE_VALUE) + return CURLE_READ_ERROR; + + do { + if(entry.cFileName[0] != TEXT('.')) { + char *name = curlx_convert_tchar_to_UTF8(entry.cFileName); + + if(!name) { + result = CURLE_OUT_OF_MEMORY; + break; + } + + result = Curl_client_write(data, CLIENTWRITE_BODY, + name, strlen(name)); + curlx_free(name); + if(result) + break; + + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + if(result) + break; + } + } while(FindNextFile(handle, &entry)); + + error = GetLastError(); + if(!result && error != ERROR_NO_MORE_FILES) + result = CURLE_READ_ERROR; + + FindClose(handle); + return result; +} +#endif + /* * file_do() is the protocol-specific function for the do-phase, separated * from the connect-phase above. Other protocols merely setup the transfer in @@ -405,8 +500,10 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) fd = file->fd; /* VMS: This only works reliable for STREAMLF files */ - if(curlx_fstat(fd, &statbuf) != -1) { - if(!S_ISDIR(statbuf.st_mode)) + if((file->is_dir ? file_stat(file->path, &statbuf) : + curlx_fstat(fd, &statbuf)) != -1) { + file->is_dir = (fd == -1) || S_ISDIR(statbuf.st_mode); + if(!file->is_dir) expected_size = statbuf.st_size; /* and store the modification time */ data->info.filetime = statbuf.st_mtime; @@ -510,7 +607,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) Curl_pgrsSetDownloadSize(data, expected_size); if(data->state.resume_from) { - if(!S_ISDIR(statbuf.st_mode)) { + if(!file->is_dir) { if(data->state.resume_from != curl_lseek(fd, data->state.resume_from, SEEK_SET)) return CURLE_BAD_DOWNLOAD_RESUME; @@ -524,7 +621,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(result) goto out; - if(!S_ISDIR(statbuf.st_mode)) { + if(!file->is_dir) { while(!result) { ssize_t nread; /* Do not fill a whole buffer if we want less than all data */ @@ -558,7 +655,11 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) } } else { -#ifdef HAVE_OPENDIR +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) + result = win32_file_list(data, file->path); + if(result) + goto out; +#elif defined(HAVE_OPENDIR) DIR *dir = opendir(file->path); struct dirent *entry; diff --git a/tests/data/test3203 b/tests/data/test3203 index 401f69741976..8b52cf41ccc6 100644 --- a/tests/data/test3203 +++ b/tests/data/test3203 @@ -14,16 +14,18 @@ file Get a directory using file:// - - -!win32 - file://localhost%FILE_PWD/%LOGDIR/test%TESTNUMBER.dir/ +%if Unicode + +Contents of file are irrelevant + +%else Contents of file are irrelevant +%endif # Verify data after the test has been "shot" @@ -32,7 +34,11 @@ Contents of file are irrelevant 0 +%if Unicode +%hex[%E6%BC%A2%E5%AD%97%2D%C3%A4]hex%.txt +%else dir-listing-test.txt +%endif From 26dadd9ea3be8c21909bd10e4677890af59011d9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 09:37:03 +0200 Subject: [PATCH 0898/1062] GHA/windows: log preinstalled MSYS2 packages CI jobs rely on defaults, sometimes to reduce log noise (possibly improve performance a bit) or simplify, sometimes by accident. List them to help debug or optimize on changes. Refs: https://github.com/actions/runner-images/issues/14562#issuecomment-5301777198 https://github.com/msys2/MSYS2-packages/commit/636489312cbc55011cb7ec493550598125e7716f https://github.com/msys2/MSYS2-packages/pull/6491 https://github.com/msys2/MSYS2-packages/pull/6479 Follow-up to d854ab4673c2f9d8048c7f0f6d164b7e4d5e0865 #22580 Follow-up to c349bd668c91f2484ae21c0f361ddf497143093c #14097 Closes #22589 --- .github/workflows/windows.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bfff8c210618..bae82e4dfda2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -329,6 +329,10 @@ jobs: install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false steps: + - name: 'preinstalled tools' + shell: bash + run: echo '::group::MSYS2 packages'; /c/msys64/usr/bin/pacman.exe -Q || true; echo '::endgroup::' + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 if: ${{ matrix.sys == 'msys' }} with: @@ -647,6 +651,10 @@ jobs: chkprefill: '' # Set it once to silence actionlint fail-fast: false steps: + - name: 'preinstalled tools' + shell: bash + run: echo '::group::MSYS2 packages'; /c/msys64/usr/bin/pacman.exe -Q || true; echo '::endgroup::' + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: msystem: ${{ matrix.sys }} @@ -1006,6 +1014,10 @@ jobs: fail-fast: false steps: + - name: 'preinstalled tools' + shell: bash + run: echo '::group::MSYS2 packages'; /c/msys64/usr/bin/pacman.exe -Q || true; echo '::endgroup::' + - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: msystem: ${{ matrix.arch == 'arm64' && 'clangarm64' || 'ucrt64' }} From fa21937ab0157325f426de40299f5fc5ac138190 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 20:19:19 +0200 Subject: [PATCH 0899/1062] GHA/non-native: sync BSD test exclusion logic with rest of workflows Stop auto-skipping tests on emulated CPUs, replace it with explicit `skipall` tags instead. To make it easier to enable tests on emulated CPUs and to align with rest of workflows. Closes #22599 --- .github/workflows/non-native.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 56ef87c8901f..f40e3bfb8e00 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -73,15 +73,15 @@ jobs: install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skiprun !examples', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skipall !examples', install: 'cmake-core ninja', options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF' } - - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl skipall !examples', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } - - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl skipall', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', options: '-DCURL_USE_GSSAPI=ON' } @@ -217,7 +217,7 @@ jobs: fi - name: 'build tests' - if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU + if: ${{ !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --target testdeps @@ -226,7 +226,7 @@ jobs: fi - name: 'run tests' - if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') && !contains(matrix.desc, 'skiprun') }} # Slow on emulated CPU + if: ${{ !contains(matrix.desc, 'skipall') && !contains(matrix.desc, 'skiprun') }} # Slow on emulated CPU run: | export TFLAGS='-j8' if [ "${MATRIX_OS}" = 'openbsd' ]; then From 787cbd20330a6a3d9b024d47c2c35691c7b35cab Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 15 Aug 2026 11:07:15 +0200 Subject: [PATCH 0900/1062] multi: shrink expire timer indices The enum `exire_id` fits easily into a uint8_t. Use that type for storing expire timeout list indices. Closes #22591 --- lib/curl_trc.c | 8 ++++---- lib/multi.c | 49 ++++++++++++++++++++++++++++--------------------- lib/multiif.h | 2 +- lib/urldata.h | 4 ++-- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 97f142af1820..2bf81e811964 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -344,10 +344,10 @@ void Curl_trc_easy_timers(struct Curl_easy *data) struct expire_timers *timeouts = &data->state.timeouts; timediff_t base_us = Curl_timeouts_offset_us(&data->multi->timeouts, Curl_pgrs_now(data)); - expire_id eid = data->state.timeouts.first; - for(; eid < EXPIRE_LAST; eid = timeouts->next[eid]) { - CURL_TRC_TIMER(data, eid, "expires in %" FMT_TIMEDIFF_T "us", - timeouts->offset_us[eid] - base_us); + uint8_t id = data->state.timeouts.first; + for(; id < EXPIRE_LAST; id = timeouts->next[id]) { + CURL_TRC_TIMER(data, id, "expires in %" FMT_TIMEDIFF_T "us", + timeouts->offset_us[id] - base_us); } } } diff --git a/lib/multi.c b/lib/multi.c index 82d830488b9a..ceffe9911d84 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1128,7 +1128,7 @@ static CURLcode mstate_perform_pollset(struct Curl_easy *data, static size_t multi_timeouts_count(struct expire_timers *timeouts) { size_t n = 0; - expire_id eid = timeouts->first; + uint8_t eid = timeouts->first; for(; eid < EXPIRE_LAST; eid = timeouts->next[eid]) ++n; return n; @@ -3618,21 +3618,21 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) static bool multi_timeouts_check(struct Curl_easy *data) { struct expire_timers *timeouts = &data->state.timeouts; - expire_id eid; + uint8_t id; int i = 0; - for(eid = timeouts->first; eid < EXPIRE_LAST; eid = timeouts->next[eid]) { + for(id = timeouts->first; id < EXPIRE_LAST; id = timeouts->next[id]) { if(++i >= EXPIRE_LAST) { failf(data, "expire timeouts looped: %d iterations and no end", i); return FALSE; } - if(eid == timeouts->next[eid]) { - failf(data, "expire timeouts wrong: %d points to itself", (int)eid); + if(id == timeouts->next[id]) { + failf(data, "expire timeouts wrong: %d points to itself", (int)id); return FALSE; } - if((timeouts->next[eid] < EXPIRE_LAST) && - (timeouts->offset_us[eid] > timeouts->offset_us[timeouts->next[eid]])) { + if((timeouts->next[id] < EXPIRE_LAST) && + (timeouts->offset_us[id] > timeouts->offset_us[timeouts->next[id]])) { failf(data, "expire timeouts not sorted: %d happens after %d but " - "is listed before", (int)eid, (int)timeouts->next[eid]); + "is listed before", (int)id, (int)timeouts->next[id]); return FALSE; } } @@ -3646,12 +3646,18 @@ static bool multi_timeouts_check(struct Curl_easy *data) static void multi_clear_timeout(struct Curl_easy *data, expire_id eid) { struct expire_timers *timeouts = &data->state.timeouts; - expire_id orig_first = timeouts->first; - expire_id *anchor = &timeouts->first; + uint8_t orig_first = timeouts->first; + uint8_t *anchor = &timeouts->first; + uint8_t id = (uint8_t)eid; + + if((unsigned)eid >= EXPIRE_LAST) { + DEBUGASSERT(0); + return; + } while(*anchor < EXPIRE_LAST) { - if(*anchor == eid) { - *anchor = timeouts->next[eid]; + if(*anchor == id) { + *anchor = timeouts->next[id]; break; } anchor = &timeouts->next[*anchor]; @@ -3684,25 +3690,26 @@ static CURLMcode multi_set_timeout(struct Curl_easy *data, expire_id eid) { struct expire_timers *timeouts = &data->state.timeouts; - expire_id *anchor = &timeouts->first; + uint8_t *anchor = &timeouts->first; + uint8_t id = (uint8_t)eid; - if(eid >= EXPIRE_LAST) { + if((unsigned)eid >= EXPIRE_LAST) { DEBUGASSERT(0); return CURLM_BAD_FUNCTION_ARGUMENT; } /* remove from list, store time and re-insert */ multi_clear_timeout(data, eid); - timeouts->offset_us[eid] = + timeouts->offset_us[id] = Curl_timeouts_offset_us(&data->multi->timeouts, stamp); while(*anchor < EXPIRE_LAST) { - if(timeouts->offset_us[*anchor] > timeouts->offset_us[eid]) + if(timeouts->offset_us[*anchor] > timeouts->offset_us[id]) break; anchor = &timeouts->next[*anchor]; } timeouts->next[eid] = *anchor; timeouts->next[eid] = *anchor; - *anchor = eid; + *anchor = id; DEBUGASSERT(multi_timeouts_check(data)); CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "us", curlx_ptimediff_us(stamp, Curl_pgrs_now(data))); @@ -3723,7 +3730,7 @@ void Curl_expire(struct Curl_easy *data, { struct Curl_multi *multi = data->multi; struct expire_timers *timeouts = &data->state.timeouts; - expire_id prev_id = timeouts->first; + uint8_t prev_id = timeouts->first; struct curltime set; /* this is only interesting while there is still an associated multi struct @@ -3769,11 +3776,11 @@ void Curl_expire(struct Curl_easy *data, /* * Removes the expire timer. Marks it as done. */ -void Curl_expire_clear(struct Curl_easy *data, expire_id id) +void Curl_expire_clear(struct Curl_easy *data, expire_id eid) { /* remove the timer, if there */ - multi_clear_timeout(data, id); - CURL_TRC_TIMER(data, id, "cleared"); + multi_clear_timeout(data, eid); + CURL_TRC_TIMER(data, eid, "cleared"); } /* diff --git a/lib/multiif.h b/lib/multiif.h index 08d0b11064d2..16b9181cf885 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -28,7 +28,7 @@ */ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id eid); -void Curl_expire_clear(struct Curl_easy *data, expire_id id); +void Curl_expire_clear(struct Curl_easy *data, expire_id eid); void Curl_expire_clear_all(struct Curl_easy *data); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, diff --git a/lib/urldata.h b/lib/urldata.h index 886b48e0a901..bc7d9e6d4eb5 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -516,8 +516,8 @@ struct expire_timers { struct Curl_tree splaynode; /* for the splay stuff */ /* microsecond offset from Curl_timeouts base timestamp */ timediff_t offset_us[EXPIRE_LAST]; - expire_id next[EXPIRE_LAST]; - expire_id first; + uint8_t next[EXPIRE_LAST]; + uint8_t first; }; /* individual pieces of the URL */ From 0c72958a67d8640351b1e3656e783e7effd31562 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 23:00:46 +0200 Subject: [PATCH 0901/1062] GHA/windows: bump stunnel to 5.80 Ref: https://www.stunnel.org/NEWS.html Closes #22600 --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bae82e4dfda2..00e088d00bc3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,8 +39,8 @@ env: OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 - STUNNEL_VERSION: '5.79' - STUNNEL_SHA256: 3d946e9759d5a6c6fc5aadb6b56e7de1a81d210ad028235340616e471afeb6f6 + STUNNEL_VERSION: '5.80' + STUNNEL_SHA256: 25947bd268e2e670e1c7f915cefd54585aaa440d09eb20c6109cdbeaf3140bc4 jobs: build-cache: From 1db93bdb7c1943132b13543539aaaf4866f31419 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 16:18:48 +0200 Subject: [PATCH 0902/1062] config-win32.h: limit use to MSVC IDE Project builds This is the only purpose it's maintained for. Drop mingw-w64 and other logic falling outside of VS2010-2013 needs. mingw-w64/MSVC platform differences keep being maintained/documented as part of the CMake pre-fill logic in `CMake/win32-cache.cmake`. Follow-up to ba8752e5566076acc8bdec7ae4ec78901f7050f4 #12224 Closes #22595 --- lib/config-win32.h | 167 +++++++-------------------------------------- 1 file changed, 26 insertions(+), 141 deletions(-) diff --git a/lib/config-win32.h b/lib/config-win32.h index 81f1e51ceed7..30908bd0670a 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -24,55 +24,33 @@ * ***************************************************************************/ -/* ================================================================ */ -/* Hand crafted config file for Windows */ -/* ================================================================ */ +/* ======================================================================== */ +/* Handcrafted config file for building via Visual Studio IDE Project Files */ +/* ======================================================================== */ + +#if !defined(_MSC_VER) || _MSC_VER > 1800 +#error This manual configuration requires MSVC 2010-2013 (IDE Project builds) +#endif /* ---------------------------------------------------------------- */ /* HEADER FILES */ /* ---------------------------------------------------------------- */ -/* Define if you have the header file. */ #define HAVE_FCNTL_H 1 - -/* Define if you have the header file. */ #define HAVE_IO_H 1 - -/* Define if you have the header file. */ #define HAVE_LOCALE_H 1 - -/* Define to 1 if you have the header file. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) +#if _MSC_VER >= 1800 #define HAVE_STDBOOL_H 1 #endif - -/* Define if you have the header file. */ -#ifdef __MINGW32__ -#define HAVE_SYS_PARAM_H 1 -#endif - -/* Define if you have the header file. */ #define HAVE_SYS_TYPES_H 1 - -/* Define if you have the header file. */ #define HAVE_SYS_UTIME_H 1 -/* Define if you have the header file. */ -#ifdef __MINGW32__ -#define HAVE_UNISTD_H 1 -#endif - -/* Define to 1 if you have the header file. */ -#ifdef __MINGW32__ -#define HAVE_LIBGEN_H 1 -#endif - /* ---------------------------------------------------------------- */ /* OTHER HEADER INFO */ /* ---------------------------------------------------------------- */ /* Define to 1 if bool is an available type. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) +#if _MSC_VER >= 1800 #define HAVE_BOOL_T 1 #endif @@ -80,80 +58,30 @@ /* FUNCTIONS */ /* ---------------------------------------------------------------- */ -/* Define if you have the closesocket function. */ #define HAVE_CLOSESOCKET 1 - -/* Define to 1 if you have the `getpeername' function. */ +#define HAVE_FREEADDRINFO 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETADDRINFO_THREADSAFE 1 +#define HAVE_GETHOSTNAME 1 #define HAVE_GETPEERNAME 1 - -/* Define to 1 if you have the getsockname function. */ #define HAVE_GETSOCKNAME 1 - -/* Define if you have the gethostname function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define if you have the gettimeofday function. */ -#ifdef __MINGW32__ -#define HAVE_GETTIMEOFDAY 1 -#endif - -/* Define if you have the ioctlsocket function. */ #define HAVE_IOCTLSOCKET 1 - -/* Define if you have a working ioctlsocket FIONBIO function. */ #define HAVE_IOCTLSOCKET_FIONBIO 1 - -/* Define if you have the setlocale function. */ #define HAVE_SETLOCALE 1 - -/* Define if you have the socket function. */ #define HAVE_SOCKET 1 - -/* Define if you have the utime function. */ #define HAVE_UTIME 1 - -/* Define if you have the recv function. */ #define HAVE_RECV 1 - -/* Define to the type of arg 1 for recv. */ #define RECV_TYPE_ARG1 SOCKET - -/* Define to the type of arg 2 for recv. */ #define RECV_TYPE_ARG2 char * - -/* Define to the type of arg 3 for recv. */ #define RECV_TYPE_ARG3 int - -/* Define to the type of arg 4 for recv. */ #define RECV_TYPE_ARG4 int - -/* Define to the function return type for recv. */ #define RECV_TYPE_RETV int - -/* Define if you have the send function. */ #define HAVE_SEND 1 - -/* Define to the type of arg 1 for send. */ #define SEND_TYPE_ARG1 SOCKET - -/* Define to the type of arg 2 for send. */ #define SEND_TYPE_ARG2 char * - -/* Define to the type of arg 3 for send. */ #define SEND_TYPE_ARG3 int - -/* Define to the type of arg 4 for send. */ #define SEND_TYPE_ARG4 int - -/* Define to the function return type for send. */ #define SEND_TYPE_RETV int - -/* Define to 1 if you have the `basename' function. */ -#ifdef __MINGW32__ -#define HAVE_BASENAME 1 -#endif - -/* Define to 1 if you have the signal function. */ #define HAVE_SIGNAL 1 /* ---------------------------------------------------------------- */ @@ -162,53 +90,33 @@ /* Define if ssize_t is not an available 'typedefed' type. */ #ifndef _SSIZE_T_DEFINED -# ifdef __MINGW32__ -# elif defined(_WIN64) -# define _SSIZE_T_DEFINED -# define ssize_t __int64 -# else -# define _SSIZE_T_DEFINED -# define ssize_t int -# endif +#define _SSIZE_T_DEFINED +#ifdef _WIN64 +# define ssize_t __int64 +#else +# define ssize_t int +#endif #endif /* ---------------------------------------------------------------- */ /* TYPE SIZES */ /* ---------------------------------------------------------------- */ -/* Define to the size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 - -/* Define to the size of `long', as computed by sizeof. */ #define SIZEOF_LONG 4 - -/* Define to the size of `size_t', as computed by sizeof. */ #ifdef _WIN64 # define SIZEOF_SIZE_T 8 #else # define SIZEOF_SIZE_T 4 #endif - -/* Define to the size of `curl_off_t', as computed by sizeof. */ #define SIZEOF_CURL_OFF_T 8 - -/* ---------------------------------------------------------------- */ -/* COMPILER SPECIFIC */ -/* ---------------------------------------------------------------- */ - /* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */ -#if defined(_MSC_VER) || defined(__MINGW32__) -# ifndef _USE_32BIT_TIME_T -# define SIZEOF_TIME_T 8 -# else -# define SIZEOF_TIME_T 4 -# endif +#ifndef _USE_32BIT_TIME_T +# define SIZEOF_TIME_T 8 +#else +# define SIZEOF_TIME_T 4 #endif - -/* Windows XP is required for freeaddrinfo, getaddrinfo */ -#define HAVE_FREEADDRINFO 1 -#define HAVE_GETADDRINFO 1 -#define HAVE_GETADDRINFO_THREADSAFE 1 +#define SIZEOF_OFF_T 4 /* ---------------------------------------------------------------- */ /* STRUCT RELATED */ @@ -223,23 +131,6 @@ /* Define if struct sockaddr_in6 has the sin6_scope_id member. */ #define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 -/* ---------------------------------------------------------------- */ -/* LARGE FILE SUPPORT */ -/* ---------------------------------------------------------------- */ - -/* Number of bits in a file offset, on hosts where this is settable. */ -#ifdef __MINGW32__ -# undef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -#endif - -/* Define to the size of `off_t', as computed by sizeof. */ -#ifdef __MINGW32__ -# define SIZEOF_OFF_T 8 -#else -# define SIZEOF_OFF_T 4 -#endif - /* ---------------------------------------------------------------- */ /* DNS RESOLVER SPECIALTY */ /* ---------------------------------------------------------------- */ @@ -279,16 +170,10 @@ /* Define cpu-machine-OS */ #ifndef CURL_OS -# if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ +# ifdef _M_IX86 /* x86 */ # define CURL_OS "i386-pc-win32" -# elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (VS2005+ or gcc) */ +# elif defined(_M_X64) /* x86_64 */ # define CURL_OS "x86_64-pc-win32" -# elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ -# define CURL_OS "ia64-pc-win32" -# elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 */ -# define CURL_OS "thumbv7a-pc-win32" -# elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ -# define CURL_OS "aarch64-pc-win32" # else # define CURL_OS "unknown-pc-win32" # endif From bf594226d66dc2bbf62a18f3dea1ceabe5b26dcf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 16 Aug 2026 01:57:00 +0200 Subject: [PATCH 0903/1062] GHA/non-native: BSD build improvements - show binary sizes and file types in 'curl -V' step, like rest of workflows. - make `build tests` step verbose. - disable typecheck on emulated CPUs. It results in 3-4x speed-up for riscv64, and ~2x for ARM64 when building tests (in particular the libtests binary.) Ref: 9e6f1c5efb7a70e1f33e467a738f3e3f652f3174 #19637 - enable building tests in an ARM64 job. - move examples build test from ARM64 to riscv64 (to spread job times). Closes #22601 --- .github/workflows/non-native.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index f40e3bfb8e00..7ae7b1e4d124 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -73,17 +73,17 @@ jobs: install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skipall !examples', - install: 'cmake-core ninja', - options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF' } + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skipall', + install: 'cmake-core ninja perl5', + options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl skipall !examples', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', - options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } + options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2 --disable-typecheck' } - - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl skipall', + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl skiprun !examples', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', - options: '-DCURL_USE_GSSAPI=ON' } + options: '-DCURL_USE_GSSAPI=ON -DCURL_DISABLE_TYPECHECK=ON' } # https://app.midnightbsd.org/ # https://man.midnightbsd.org/cgi-bin/man.cgi/mport @@ -206,7 +206,10 @@ jobs: fi - name: 'curl -V' - run: bld/src/curl --disable --version + run: | + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 stat -f '%10z bytes: %N' -- + bld/src/curl --disable --version - name: 'curl install' run: | @@ -220,9 +223,9 @@ jobs: if: ${{ !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then - cmake --build bld --target testdeps + cmake --build bld --verbose --target testdeps else - make -C bld -C tests + make -C bld V=1 -C tests fi - name: 'run tests' From e090cf59ad4c88be9da873fe37dae3487b7ad77e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 09:06:05 +0200 Subject: [PATCH 0904/1062] build: drop `dirent.h` and `opendir()` detections on Windows `opendir()` was used previously with mingw-w64 for `file://` directory listing support. This was replaced by a native Win32 solution on all Windows platforms, leaving `opendir()` no longer used. Drop related feature detections. Follow-up to c7c42afab91c46e6baaa0e2801626e13acd6c30a #22577 Closes #22588 --- CMake/win32-cache.cmake | 4 ---- CMakeLists.txt | 5 +++-- configure.ac | 12 +++++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index 87e0879ab2b7..a1f0d909f208 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -28,10 +28,8 @@ endif() if(MINGW) set(HAVE_BASENAME 1) set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H - set(HAVE_DIRENT_H 1) set(HAVE_GETTIMEOFDAY 1) set(HAVE_LIBGEN_H 1) - set(HAVE_OPENDIR 1) set(HAVE_STDBOOL_H 1) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() @@ -40,10 +38,8 @@ if(MINGW) set(HAVE_UNISTD_H 1) set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() - set(HAVE_DIRENT_H 0) set(HAVE_GETTIMEOFDAY 0) set(HAVE_LIBGEN_H 0) - set(HAVE_OPENDIR 0) set(HAVE_STRINGS_H 0) set(HAVE_SYS_PARAM_H 0) set(HAVE_UTIME_H 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index cdf47d486bc1..be5e0e09ea18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1554,7 +1554,6 @@ check_include_file("sys/un.h" HAVE_SYS_UN_H) check_include_file_concat_curl("sys/utime.h" HAVE_SYS_UTIME_H) # sys/types.h (AmigaOS) check_include_file_concat_curl("arpa/inet.h" HAVE_ARPA_INET_H) -check_include_file("dirent.h" HAVE_DIRENT_H) check_include_file("fcntl.h" HAVE_FCNTL_H) check_include_file_concat_curl("ifaddrs.h" HAVE_IFADDRS_H) check_include_file("io.h" HAVE_IO_H) @@ -1635,7 +1634,6 @@ endif() check_function_exists("accept4" HAVE_ACCEPT4) check_function_exists("fnmatch" HAVE_FNMATCH) check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) # libgen.h unistd.h -check_symbol_exists("opendir" "dirent.h" HAVE_OPENDIR) check_function_exists("poll" HAVE_POLL) # poll.h check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) # winsock2.h sys/socket.h check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) # sys/socket.h @@ -1688,10 +1686,13 @@ else() endif() if(NOT WIN32) + check_include_file("dirent.h" HAVE_DIRENT_H) + check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) + check_symbol_exists("opendir" "dirent.h" HAVE_OPENDIR) check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) diff --git a/configure.ac b/configure.ac index 5071f8652385..25b9e449618d 100644 --- a/configure.ac +++ b/configure.ac @@ -4380,11 +4380,13 @@ elif test "$USE_ARES" = "1"; then curl_res_msg="c-ares" fi -AC_CHECK_HEADER(dirent.h, - [ AC_DEFINE(HAVE_DIRENT_H, 1, [if you have ]) - AC_CHECK_FUNC(opendir, AC_DEFINE(HAVE_OPENDIR, 1, [if you have opendir]) ) - ] -) +if test "$curl_cv_native_windows" != "yes"; then + AC_CHECK_HEADER(dirent.h, + [ AC_DEFINE(HAVE_DIRENT_H, 1, [if you have ]) + AC_CHECK_FUNC(opendir, AC_DEFINE(HAVE_OPENDIR, 1, [if you have opendir]) ) + ] + ) +fi CURL_CONVERT_INCLUDE_TO_ISYSTEM From 5b9946a106df6d923b3dbe37ebf632f756afa0d1 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 12:47:34 +0200 Subject: [PATCH 0905/1062] curl_threads: always use native threads/mutex on Windows Syncing with build systems, which already enforce this. Closes #22593 --- lib/curl_threads.c | 128 ++++++++++++++++++++++----------------------- lib/curl_threads.h | 30 +++++------ 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/lib/curl_threads.c b/lib/curl_threads.c index 16f14a364cf6..a5857b43b9c7 100644 --- a/lib/curl_threads.c +++ b/lib/curl_threads.c @@ -27,7 +27,41 @@ #ifdef USE_THREADS -#ifdef HAVE_THREADS_POSIX +#ifdef _WIN32 + +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) +{ + curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL); + if(!t) { + DWORD gle = GetLastError(); + /* !checksrc! disable ERRNOVAR 1 */ + errno = (gle == ERROR_ACCESS_DENIED || + gle == ERROR_NOT_ENOUGH_MEMORY) ? + EACCES : EINVAL; + return curl_thread_t_null; + } + return t; +} + +void Curl_thread_destroy(curl_thread_t *hnd) +{ + if(*hnd != curl_thread_t_null) { + CloseHandle(*hnd); + *hnd = curl_thread_t_null; + } +} + +int Curl_thread_join(curl_thread_t *hnd) +{ + int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); + + Curl_thread_destroy(hnd); + + return ret; +} + +#elif defined(HAVE_THREADS_POSIX) struct Curl_actual_call { unsigned int (*func)(void *); @@ -95,48 +129,47 @@ int Curl_thread_join(curl_thread_t *hnd) return ret; } -#elif defined(_WIN32) +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_THREADS */ -curl_thread_t Curl_thread_create( - CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) +#ifdef USE_MUTEX + +#ifdef _WIN32 + +void Curl_cond_signal(CONDITION_VARIABLE *c) { - curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL); - if(!t) { - DWORD gle = GetLastError(); - /* !checksrc! disable ERRNOVAR 1 */ - errno = (gle == ERROR_ACCESS_DENIED || - gle == ERROR_NOT_ENOUGH_MEMORY) ? - EACCES : EINVAL; - return curl_thread_t_null; - } - return t; + WakeConditionVariable(c); } -void Curl_thread_destroy(curl_thread_t *hnd) +void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m) { - if(*hnd != curl_thread_t_null) { - CloseHandle(*hnd); - *hnd = curl_thread_t_null; - } + SleepConditionVariableCS(c, m, INFINITE); } -int Curl_thread_join(curl_thread_t *hnd) +CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, + uint32_t timeout_ms) { - int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); - - Curl_thread_destroy(hnd); - - return ret; + if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) { + DWORD err = GetLastError(); + return (err == ERROR_TIMEOUT) ? + CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL; + } + return CURLE_OK; } -#else -#error neither HAVE_THREADS_POSIX nor _WIN32 defined -#endif -#endif /* USE_THREADS */ +curl_thread_id_t Curl_thread_get_current_id(void) +{ + return GetCurrentThreadId(); +} -#ifdef USE_MUTEX +bool Curl_thread_is_current(curl_thread_id_t tid) +{ + return tid == GetCurrentThreadId(); +} -#ifdef HAVE_THREADS_POSIX +#elif defined(HAVE_THREADS_POSIX) void Curl_cond_signal(pthread_cond_t *c) { @@ -196,39 +229,6 @@ bool Curl_thread_is_current(curl_thread_id_t tid) return !!pthread_equal(tid, pthread_self()); } -#elif defined(_WIN32) - -void Curl_cond_signal(CONDITION_VARIABLE *c) -{ - WakeConditionVariable(c); -} - -void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m) -{ - SleepConditionVariableCS(c, m, INFINITE); -} - -CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, - uint32_t timeout_ms) -{ - if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) { - DWORD err = GetLastError(); - return (err == ERROR_TIMEOUT) ? - CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL; - } - return CURLE_OK; -} - -curl_thread_id_t Curl_thread_get_current_id(void) -{ - return GetCurrentThreadId(); -} - -bool Curl_thread_is_current(curl_thread_id_t tid) -{ - return tid == GetCurrentThreadId(); -} - #else #error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif diff --git a/lib/curl_threads.h b/lib/curl_threads.h index 452d5eb44f3e..fc73c1222e9e 100644 --- a/lib/curl_threads.h +++ b/lib/curl_threads.h @@ -26,21 +26,7 @@ #include "curl_setup.h" #ifdef USE_MUTEX -#ifdef HAVE_THREADS_POSIX -# define CURL_THREAD_RETURN_T unsigned int -# define CURL_STDCALL -# define curl_mutex_t pthread_mutex_t -# define curl_thread_t pthread_t * -# define curl_thread_id_t pthread_t -# define curl_thread_t_null (pthread_t *)0 -# define Curl_mutex_init(m) pthread_mutex_init(m, NULL) -# define Curl_mutex_acquire(m) pthread_mutex_lock(m) -# define Curl_mutex_release(m) pthread_mutex_unlock(m) -# define Curl_mutex_destroy(m) pthread_mutex_destroy(m) -# define curl_cond_t pthread_cond_t -# define Curl_cond_init(c) pthread_cond_init(c, NULL) -# define Curl_cond_destroy(c) pthread_cond_destroy(c) -#elif defined(_WIN32) +#ifdef _WIN32 # define CURL_THREAD_RETURN_T DWORD # define CURL_STDCALL WINAPI # define curl_mutex_t CRITICAL_SECTION @@ -54,6 +40,20 @@ # define curl_cond_t CONDITION_VARIABLE # define Curl_cond_init(c) InitializeConditionVariable(c) # define Curl_cond_destroy(c) (void)(c) +#elif defined(HAVE_THREADS_POSIX) +# define CURL_THREAD_RETURN_T unsigned int +# define CURL_STDCALL +# define curl_mutex_t pthread_mutex_t +# define curl_thread_t pthread_t * +# define curl_thread_id_t pthread_t +# define curl_thread_t_null (pthread_t *)0 +# define Curl_mutex_init(m) pthread_mutex_init(m, NULL) +# define Curl_mutex_acquire(m) pthread_mutex_lock(m) +# define Curl_mutex_release(m) pthread_mutex_unlock(m) +# define Curl_mutex_destroy(m) pthread_mutex_destroy(m) +# define curl_cond_t pthread_cond_t +# define Curl_cond_init(c) pthread_cond_init(c, NULL) +# define Curl_cond_destroy(c) pthread_cond_destroy(c) #else #error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif From 4b42956cd1b4ee5c739d9bd73662d21c6b13cae8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 12:56:23 +0200 Subject: [PATCH 0906/1062] build: drop detecting `gettimeofday()` on Windows To remove a build difference between mingw-w64 and MSVC builds (where mingw-w64 has it, while MSVC does not). Also to simplify and save the cost of detection/verification in CI. `gettimeofday()` was used in `curl_threads.c` in codepath practically unreachable in Windows builds, and in `tool_writeout.c` that falls back to `time()` for targets without it. Ref: #22593 Closes #22594 --- CMake/win32-cache.cmake | 2 -- CMakeLists.txt | 2 +- configure.ac | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index a1f0d909f208..6d0cb076e874 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -28,7 +28,6 @@ endif() if(MINGW) set(HAVE_BASENAME 1) set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H - set(HAVE_GETTIMEOFDAY 1) set(HAVE_LIBGEN_H 1) set(HAVE_STDBOOL_H 1) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() @@ -38,7 +37,6 @@ if(MINGW) set(HAVE_UNISTD_H 1) set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() - set(HAVE_GETTIMEOFDAY 0) set(HAVE_LIBGEN_H 0) set(HAVE_STRINGS_H 0) set(HAVE_SYS_PARAM_H 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index be5e0e09ea18..be551fd8e7f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1647,7 +1647,6 @@ check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) check_function_exists("getppid" HAVE_GETPPID) check_function_exists("utimes" HAVE_UTIMES) -check_function_exists("gettimeofday" HAVE_GETTIMEOFDAY) # sys/time.h check_symbol_exists("closesocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET) # winsock2.h check_symbol_exists("sigsetjmp" "setjmp.h" HAVE_SIGSETJMP) check_function_exists("getpass_r" HAVE_GETPASS_R) @@ -1688,6 +1687,7 @@ endif() if(NOT WIN32) check_include_file("dirent.h" HAVE_DIRENT_H) + check_function_exists("gettimeofday" HAVE_GETTIMEOFDAY) # sys/time.h check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) diff --git a/configure.ac b/configure.ac index 25b9e449618d..f130bc558bb2 100644 --- a/configure.ac +++ b/configure.ac @@ -4226,7 +4226,6 @@ AC_CHECK_FUNCS([\ getpwuid \ getpwuid_r \ getrlimit \ - gettimeofday \ mach_absolute_time \ pipe \ poll \ @@ -4249,6 +4248,7 @@ fi if test "$curl_cv_native_windows" != "yes"; then AC_CHECK_FUNCS([\ + gettimeofday \ if_nametoindex \ realpath \ sched_yield \ From 8eb978b9c212068371c39b37be1522904a790811 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 16 Aug 2026 12:40:05 +0200 Subject: [PATCH 0907/1062] GHA/non-native: drop leftover perl5 install from riscv64 job [ci skip] Follow-up to bf594226d66dc2bbf62a18f3dea1ceabe5b26dcf #22601 --- .github/workflows/non-native.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 7ae7b1e4d124..6d232961ecd4 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -74,7 +74,7 @@ jobs: options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skipall', - install: 'cmake-core ninja perl5', + install: 'cmake-core ninja', options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl skipall !examples', From d00000673d05fdf6ceee8941ccd63ad2906257e4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 16 Aug 2026 12:42:20 +0200 Subject: [PATCH 0908/1062] GHA/non-native: sync BSD tflags logic with other workflows To make it easier to customize tests for individual matrix jobs. Also: - tested running riscv64 tests, but it takes too long (8.5-12 minutes, for ~1000 HTTP tests.) Follow-up to bf594226d66dc2bbf62a18f3dea1ceabe5b26dcf #22601 Closes #22602 --- .github/workflows/non-native.yml | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 6d232961ecd4..ca42ba7507fd 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -57,11 +57,11 @@ jobs: matrix: include: # https://github.com/DragonFlyBSD/DPorts - # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skiprun', + # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl', tflags: 'skiprun', # install: 'autoconf automake libtool openldap26-client libidn2', # options: '--with-openssl --enable-ldap --enable-ldaps --with-libidn2' } - # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skipall', + # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64' , cc: 'gcc' , desc: 'openssl', tflags: 'skipall', # install: 'cmake ninja' } # https://ports.freebsd.org/ @@ -69,34 +69,34 @@ jobs: install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'openssl !unity skiprun !examples', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'openssl !unity !examples', tflags: 'skiprun', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl skipall', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl', tflags: 'skipall', install: 'cmake-core ninja', options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } - - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl skipall !examples', + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', tflags: 'skipall', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2 --disable-typecheck' } - - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl skiprun !examples', + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', tflags: 'skiprun', install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', options: '-DCURL_USE_GSSAPI=ON -DCURL_DISABLE_TYPECHECK=ON' } # https://app.midnightbsd.org/ # https://man.midnightbsd.org/cgi-bin/man.cgi/mport - # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'gnutls skipall !examples', + # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'gnutls !examples', tflags: 'skipall', # install: 'autoconf autoconf-archive automake libtool gnutls', # options: '--with-gnutls' } - - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'gnutls skiprun', + - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'gnutls', tflags: 'skiprun', install: 'cmake-core ninja perl5 gnutls openldap26-client libidn2', options: '-DCURL_USE_GNUTLS=ON' } # https://pkgsrc.se/ - - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl skipall !examples', + - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64' , cc: 'gcc' , desc: 'openssl !examples', tflags: 'skipall', install: 'autoconf automake libtool mit-krb5', options: '--with-openssl --with-gssapi' } @@ -107,11 +107,12 @@ jobs: # https://openbsd.app/ # https://www.openbsd.org/faq/faq15.html # https://github.com/OpenMPT/openmpt/blob/master/.github/workflows/OpenBSD-Autotools.yml - - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'libressl skipall !examples', + - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64' , cc: 'clang', desc: 'libressl !examples', tflags: 'skipall', install: 'autoconf-2.72p0 automake-1.18.1 libtool', # NOTE: also sync these versions with the autoreconf step! options: '--with-openssl' } - - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'libressl', + # Skip test 2707 'ws: Peculiar frame sizes' on suspicion of hangs + - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64' , cc: 'clang', desc: 'libressl', tflags: '!2707', install: 'cmake ninja openldap-client-- libidn2' } fail-fast: false @@ -123,7 +124,7 @@ jobs: - name: 'setup VM' uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0 with: - environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION' + environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION TFLAGS' operating_system: '${{ matrix.os }}' version: '${{ matrix.version }}' architecture: '${{ matrix.arch }}' @@ -220,7 +221,7 @@ jobs: fi - name: 'build tests' - if: ${{ !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU + if: ${{ matrix.tflags != 'skipall' }} # Slow on emulated CPU run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target testdeps @@ -229,12 +230,11 @@ jobs: fi - name: 'run tests' - if: ${{ !contains(matrix.desc, 'skipall') && !contains(matrix.desc, 'skiprun') }} # Slow on emulated CPU + if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} # Slow on emulated CPU + env: + TFLAGS: '${{ matrix.tflags }}' run: | - export TFLAGS='-j8' - if [ "${MATRIX_OS}" = 'openbsd' ]; then - TFLAGS="$TFLAGS !2707" # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs - fi + TFLAGS="-j8 ${TFLAGS}" if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target test-ci else From 0c22fcffe3cc83df5d977b2bdeda4f3d894689d5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 17:28:32 +0200 Subject: [PATCH 0909/1062] config-riscos.h: delete handcrafted RISC OS config header, in favor of autotools According to the content of the latest downloadable builds available at (from 2022): https://www.riscos.info/packages/src/Network/, these builds used autotools, not the handcrafted header. Also the handcrafted header did not see an update (that I could find) since 0866c1b52dcff1f872860a42183397b4e8f6dedf (2002-04-08), other than source-wide maintenance updates. Closes #22598 --- lib/Makefile.am | 2 +- lib/config-riscos.h | 138 -------------------------------------------- lib/curl_setup.h | 4 -- 3 files changed, 1 insertion(+), 143 deletions(-) delete mode 100644 lib/config-riscos.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 8a2bd4e68e46..b2e64c788e0c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -28,7 +28,7 @@ include Makefile.inc CMAKE_DIST = CMakeLists.txt curl_config-cmake.h.in -EXTRA_DIST = config-mac.h config-os400.h config-riscos.h config-win32.h \ +EXTRA_DIST = config-mac.h config-os400.h config-win32.h \ curl_config.h.in $(LIB_RCFILES) libcurl.def $(CMAKE_DIST) Makefile.soname \ optiontable.pl diff --git a/lib/config-riscos.h b/lib/config-riscos.h deleted file mode 100644 index 358544fb3cb9..000000000000 --- a/lib/config-riscos.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef HEADER_CURL_CONFIG_RISCOS_H -#define HEADER_CURL_CONFIG_RISCOS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* ================================================================ */ -/* Hand crafted config file for RISC OS */ -/* ================================================================ */ - -/* Define cpu-machine-OS */ -#ifndef CURL_OS -#define CURL_OS "ARM-RISC OS" -#endif - -/* Define if you want the built-in manual */ -#define USE_MANUAL - -/* Define if struct sockaddr_in6 has the sin6_scope_id member */ -#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 - -/* Define if you have the alarm function. */ -#define HAVE_ALARM - -/* Define if you have the header file. */ -#define HAVE_ARPA_INET_H - -/* Define if you have the header file. */ -#define HAVE_FCNTL_H - -/* Define if getaddrinfo exists and works */ -#define HAVE_GETADDRINFO - -/* Define if you have the `gethostname' function. */ -#define HAVE_GETHOSTNAME - -/* Define if you have the `gettimeofday' function. */ -#define HAVE_GETTIMEOFDAY - -/* Define if you have the `timeval' struct. */ -#define HAVE_STRUCT_TIMEVAL - -/* Define if you have the header file. */ -#define HAVE_NETDB_H - -/* Define if you have the header file. */ -#define HAVE_NETINET_IN_H - -/* Define if you have the header file. */ -#define HAVE_NET_IF_H - -/* Define if you have the `signal' function. */ -#define HAVE_SIGNAL - -/* Define if you have the `socket' function. */ -#define HAVE_SOCKET - -/* Define if you have the `stricmp' function. */ -#define HAVE_STRICMP - -/* Define if you have the header file. */ -#define HAVE_SYS_TYPES_H - -/* Define if you have the header file. */ -#define HAVE_TERMIOS_H - -/* Define if you have the header file. */ -#define HAVE_UNISTD_H - -/* The size of `int', as computed by sizeof. */ -#define SIZEOF_INT 4 - -/* The size of `size_t', as computed by sizeof. */ -#define SIZEOF_SIZE_T 4 - -/* Define if you have a working ioctl FIONBIO function. */ -#define HAVE_IOCTL_FIONBIO - -/* to disable LDAP */ -#define CURL_DISABLE_LDAP - -/* Define if you have the recv function. */ -#define HAVE_RECV 1 - -/* Define to the type of arg 1 for recv. */ -#define RECV_TYPE_ARG1 int - -/* Define to the type of arg 2 for recv. */ -#define RECV_TYPE_ARG2 void * - -/* Define to the type of arg 3 for recv. */ -#define RECV_TYPE_ARG3 size_t - -/* Define to the type of arg 4 for recv. */ -#define RECV_TYPE_ARG4 int - -/* Define to the function return type for recv. */ -#define RECV_TYPE_RETV ssize_t - -/* Define if you have the send function. */ -#define HAVE_SEND 1 - -/* Define to the type of arg 1 for send. */ -#define SEND_TYPE_ARG1 int - -/* Define to the type of arg 2 for send. */ -#define SEND_TYPE_ARG2 void * - -/* Define to the type of arg 3 for send. */ -#define SEND_TYPE_ARG3 size_t - -/* Define to the type of arg 4 for send. */ -#define SEND_TYPE_ARG4 int - -/* Define to the function return type for send. */ -#define SEND_TYPE_RETV ssize_t - -#endif /* HEADER_CURL_CONFIG_RISCOS_H */ diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 38b4706cb97c..d43baf3b3870 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -149,10 +149,6 @@ # include "config-mac.h" #endif -#ifdef __riscos__ -# include "config-riscos.h" -#endif - #ifdef __OS400__ # include "config-os400.h" #endif From 722362d87bad5df80ba76dbf1ccd578ec7b19e75 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 17 Aug 2026 13:29:34 +0200 Subject: [PATCH 0910/1062] scorecard: fix argument handling when count and parallel are both 1 Closes #22605 --- tests/http/scorecard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 27f062f71c06..3d0b232068f4 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -445,7 +445,7 @@ def downloads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict nsamples = meta['samples'] max_parallel = self._download_parallel if self._download_parallel > 0 else count cols = ['size'] - if not self._download_parallel: + if not self._download_parallel or count == 1: cols.append('single') if count > 1: cols.append(f'serial({count})') From c437d28c76485563b2570ee48ee2992e68ae7323 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 17 Aug 2026 13:08:42 +0200 Subject: [PATCH 0911/1062] urldata: cleanups - sockindex: int -> int8_t - move connectdata members around for fun and profit - remove data->state.current_speed as unused - remove data->state.trailers_bytes_sent as unsed - remove data->state.trailers_buf as unsed The sockindex type change spread a little. Closes #22604 --- lib/cf-https-connect.c | 4 +- lib/cf-https-connect.h | 2 +- lib/cf-recvbuf.c | 2 +- lib/cf-recvbuf.h | 2 +- lib/cf-setup.c | 4 +- lib/cf-setup.h | 2 +- lib/cf-socket.c | 4 +- lib/cf-socket.h | 4 +- lib/cfilters.c | 55 ++++++++++----------- lib/cfilters.h | 55 ++++++++++----------- lib/conncache.c | 9 ++-- lib/connect.c | 22 ++++----- lib/connect.h | 18 +++---- lib/easy.c | 3 +- lib/ftp.c | 2 +- lib/http2.c | 4 +- lib/http2.h | 2 +- lib/multi.c | 14 +++--- lib/openldap.c | 2 +- lib/pingpong.c | 4 +- lib/pingpong.h | 2 +- lib/transfer.c | 10 ++-- lib/transfer.h | 6 +-- lib/url.c | 15 ++---- lib/urldata.h | 108 +++++++++++++++++++---------------------- lib/vdns/cf-dns.c | 16 +++--- lib/vdns/cf-dns.h | 12 ++--- lib/vssh/libssh.c | 10 ++-- lib/vssh/libssh2.c | 12 ++--- lib/vtls/openssl.c | 2 +- lib/vtls/vtls.c | 8 +-- lib/vtls/vtls.h | 6 +-- lib/vtls/vtls_int.h | 4 +- 33 files changed, 208 insertions(+), 217 deletions(-) diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index e1a97097ac26..ed574ea9b66a 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -786,7 +786,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, static CURLcode cf_hc_add(struct Curl_easy *data, struct Curl_peer *destination, struct connectdata *conn, - int sockindex, + int8_t sockindex, uint8_t def_transport) { struct Curl_cfilter *cf; @@ -809,7 +809,7 @@ static CURLcode cf_hc_add(struct Curl_easy *data, CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct Curl_peer *destination, struct connectdata *conn, - int sockindex) + int8_t sockindex) { CURLcode result = CURLE_OK; diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index caa2b17e7f34..efac194856b4 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -38,7 +38,7 @@ extern struct Curl_cftype Curl_cft_http_connect; CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct Curl_peer *destination, struct connectdata *conn, - int sockindex); + int8_t sockindex); #endif /* !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/lib/cf-recvbuf.c b/lib/cf-recvbuf.c index 8ccc148bd3d9..55b01a26f5c8 100644 --- a/lib/cf-recvbuf.c +++ b/lib/cf-recvbuf.c @@ -137,7 +137,7 @@ static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, const uint8_t *buf, size_t blen) { struct Curl_cfilter *cf; diff --git a/lib/cf-recvbuf.h b/lib/cf-recvbuf.h index 1b08fcc3be66..e126c3c6b679 100644 --- a/lib/cf-recvbuf.h +++ b/lib/cf-recvbuf.h @@ -30,7 +30,7 @@ CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, const uint8_t *buf, size_t blen); extern struct Curl_cftype Curl_cft_recvbuf; diff --git a/lib/cf-setup.c b/lib/cf-setup.c index 7699ef5dfea3..2d77b1cb6d94 100644 --- a/lib/cf-setup.c +++ b/lib/cf-setup.c @@ -159,7 +159,7 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, /* Get the origin curl connects its socket to. * Can be origin or the first proxy. */ static struct Curl_peer *conn_get_first_origin(struct connectdata *conn, - int sockindex) + int8_t sockindex) { #ifndef CURL_DISABLE_PROXY if(conn->socks_proxy.peer) @@ -445,7 +445,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_setup_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, uint8_t transport, int ssl_mode) { diff --git a/lib/cf-setup.h b/lib/cf-setup.h index 80e20064b80a..71c6093fe52b 100644 --- a/lib/cf-setup.h +++ b/lib/cf-setup.h @@ -32,7 +32,7 @@ struct Curl_str; CURLcode Curl_cf_setup_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, uint8_t transport, int ssl_mode); diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 54df7cc2a3d3..b059a3cfe2e3 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -2320,7 +2320,7 @@ struct Curl_cftype Curl_cft_tcp_accept = { CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, struct connectdata *conn, - int sockindex, curl_socket_t *s) + int8_t sockindex, curl_socket_t *s) { CURLcode result; struct Curl_cfilter *cf = NULL; @@ -2360,7 +2360,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, } bool Curl_conn_is_tcp_listen(struct Curl_easy *data, - int sockindex) + int8_t sockindex) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; while(cf) { diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 37ddc02576b3..f4a6755fe3b8 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -122,7 +122,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, */ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, curl_socket_t *s); /** @@ -130,7 +130,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, * Curl_conn_tcp_listen_set(). */ bool Curl_conn_is_tcp_listen(struct Curl_easy *data, - int sockindex); + int8_t sockindex); /** * Peek at the socket and remote ip/port the socket filter is using. diff --git a/lib/cfilters.c b/lib/cfilters.c index 0af8c4bede88..ecfab014a0bf 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -128,7 +128,7 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, #ifdef CURLVERBOSE void Curl_conn_trc_filters(struct Curl_easy *data, - int sockindex, const char *info) + int8_t sockindex, const char *info) { if(CURL_TRC_M_is_verbose(data) && data->conn) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; @@ -177,14 +177,15 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, } void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int sockindex) + struct connectdata *conn, int8_t sockindex) { struct curltime *pt = &conn->shutdown.start[sockindex]; memset(pt, 0, sizeof(*pt)); Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) +CURLcode Curl_conn_shutdown(struct Curl_easy *data, + int8_t sockindex, bool *done) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; @@ -239,7 +240,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) return result; } -CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, +CURLcode Curl_cf_recv(struct Curl_easy *data, int8_t sockindex, char *buf, size_t len, size_t *pnread) { struct Curl_cfilter *cf; @@ -257,7 +258,7 @@ CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, return CURLE_FAILED_INIT; } -CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, +CURLcode Curl_cf_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { @@ -358,7 +359,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, void Curl_conn_cf_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_cfilter *cf) { DEBUGASSERT(conn); @@ -468,7 +469,7 @@ void Curl_conn_cntrl_update_info(struct Curl_easy *data, } void Curl_conn_remove_setup_filters(struct Curl_easy *data, - int sockindex) + int8_t sockindex) { struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; while(*anchor) { @@ -485,14 +486,14 @@ void Curl_conn_remove_setup_filters(struct Curl_easy *data, } } -bool Curl_conn_is_setup(struct connectdata *conn, int sockindex) +bool Curl_conn_is_setup(struct connectdata *conn, int8_t sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; return !!conn->cfilter[sockindex]; } -bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) +bool Curl_conn_is_connected(struct connectdata *conn, int8_t sockindex) { struct Curl_cfilter *cf; @@ -506,7 +507,7 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) return FALSE; } -bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) +bool Curl_conn_is_ip_connected(struct Curl_easy *data, int8_t sockindex) { struct Curl_cfilter *cf; @@ -533,14 +534,14 @@ static bool cf_is_tunneling(struct Curl_cfilter *cf) return FALSE; } -bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +bool Curl_conn_is_tunneling(struct connectdata *conn, int8_t sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE; } #else -bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +bool Curl_conn_is_tunneling(struct connectdata *conn, int8_t sockindex) { (void)conn; (void)sockindex; @@ -561,7 +562,7 @@ static bool cf_is_ssl(struct Curl_cfilter *cf) return FALSE; } -bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) +bool Curl_conn_is_ssl(struct connectdata *conn, int8_t sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; @@ -569,7 +570,7 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) } bool Curl_conn_get_ssl_info(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, int query, struct curl_tlssessioninfo *info) { @@ -586,7 +587,7 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data, } CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, bool *is_ipv6, struct ip_quadruple *ipquad) { struct Curl_cfilter *cf; @@ -596,7 +597,7 @@ CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, return Curl_conn_cf_get_ip_info(cf, data, is_ipv6, ipquad); } -bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) +bool Curl_conn_is_multiplex(struct connectdata *conn, int8_t sockindex) { struct Curl_cfilter *cf; @@ -673,7 +674,7 @@ unsigned char Curl_conn_http_version(struct Curl_easy *data, return (unsigned char)(result ? 0 : v); } -bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) +bool Curl_conn_data_pending(struct Curl_easy *data, int8_t sockindex) { struct Curl_cfilter *cf; @@ -703,7 +704,7 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, return (result || !pending) ? FALSE : TRUE; } -bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex) +bool Curl_conn_needs_flush(struct Curl_easy *data, int8_t sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; @@ -736,7 +737,7 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, { CURLcode result = CURLE_OK; bool want_io = !!ps->n; - int i; + int8_t i; DEBUGASSERT(data); DEBUGASSERT(conn); @@ -790,7 +791,7 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, return rc; } -void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, +void Curl_conn_get_current_host(struct Curl_easy *data, int8_t sockindex, const char **phost, int *pport) { struct Curl_cfilter *cf, *cf_proxy = NULL; @@ -924,7 +925,7 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data) } const struct Curl_sockaddr_ex *Curl_conn_get_remote_addr( - struct Curl_easy *data, int sockindex) + struct Curl_easy *data, int8_t sockindex) { struct Curl_cfilter *cf = (data->conn && CONN_SOCK_IDX_VALID(sockindex)) ? @@ -937,7 +938,7 @@ CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL); } -CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex) +CURLcode Curl_conn_flush(struct Curl_easy *data, int8_t sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -993,7 +994,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, struct connectdata *conn, - int sockindex) + int8_t sockindex) { struct Curl_cfilter *cf; CURLcode result; @@ -1013,7 +1014,7 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, int Curl_conn_get_stream_error(struct Curl_easy *data, struct connectdata *conn, - int sockindex) + int8_t sockindex) { struct Curl_cfilter *cf; CURLcode result; @@ -1028,7 +1029,7 @@ int Curl_conn_get_stream_error(struct Curl_easy *data, return (result || n < 0) ? 0 : n; } -int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) +int8_t Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) { if(data && data->conn && sockfd != CURL_SOCKET_BAD && sockfd == data->conn->sock[SECONDARYSOCKET]) @@ -1036,7 +1037,7 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) return FIRSTSOCKET; } -CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, +CURLcode Curl_conn_recv(struct Curl_easy *data, int8_t sockindex, char *buf, size_t len, size_t *pnread) { DEBUGASSERT(data); @@ -1049,7 +1050,7 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, return CURLE_FAILED_INIT; } -CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, +CURLcode Curl_conn_send(struct Curl_easy *data, int8_t sockindex, const void *buf, size_t len, bool eos, size_t *pnwritten) { diff --git a/lib/cfilters.h b/lib/cfilters.h index cfdb5782ee7a..b977039c8521 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -231,7 +231,7 @@ struct Curl_cfilter { struct Curl_cfilter *next; /* next filter in chain */ void *ctx; /* filter type specific settings */ struct connectdata *conn; /* the connection this filter belongs to */ - int sockindex; /* the index the filter is installed at */ + int8_t sockindex; /* the index the filter is installed at */ BIT(connected); /* != 0 iff this filter is connected */ BIT(shutdown); /* != 0 iff this filter has shut down */ }; @@ -286,7 +286,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, */ void Curl_conn_cf_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_cfilter *cf); /** @@ -314,7 +314,7 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, * Remove and destroy all filters at chain `sockindex` on connection `conn`. */ void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int sockindex); + struct connectdata *conn, int8_t sockindex); CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -359,31 +359,31 @@ const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, /** * Check if a filter chain at `sockindex` for connection `conn` exists. */ -bool Curl_conn_is_setup(struct connectdata *conn, int sockindex); +bool Curl_conn_is_setup(struct connectdata *conn, int8_t sockindex); /** * Check if the filter chain at `sockindex` for connection `conn` is * completely connected. */ -bool Curl_conn_is_connected(struct connectdata *conn, int sockindex); +bool Curl_conn_is_connected(struct connectdata *conn, int8_t sockindex); /** * Determine if we have reached the remote host on IP level, e.g. * have a TCP connection. This turns TRUE before a possible SSL * handshake has been started/done. */ -bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); +bool Curl_conn_is_ip_connected(struct Curl_easy *data, int8_t sockindex); /** * Determine if the connection is using SSL to the remote host * (or will be once connected). This will return FALSE, if SSL * is only used in proxying and not for the tunnel itself. */ -bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); +bool Curl_conn_is_ssl(struct connectdata *conn, int8_t sockindex); /* Determine if the connection has one or more proxy filters. * e.g. is tunneling. */ -bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex); +bool Curl_conn_is_tunneling(struct connectdata *conn, int8_t sockindex); /* * Fill `info` with information about the TLS instance securing the connection @@ -391,18 +391,18 @@ bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex); * FALSE. 'query' should be CF_QUERY_SSL_INFO or CF_QUERY_SSL_CTX_INFO. */ bool Curl_conn_get_ssl_info(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, int query, struct curl_tlssessioninfo *info); CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, bool *is_ipv6, struct ip_quadruple *ipquad); /** * Connection provides multiplexing of easy handles at `socketindex`. */ -bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); +bool Curl_conn_is_multiplex(struct connectdata *conn, int8_t sockindex); /** * Return the HTTP version used on the FIRSTSOCKET connection filters @@ -423,32 +423,33 @@ void Curl_conn_cntrl_update_info(struct Curl_easy *data, struct connectdata *conn); void Curl_conn_remove_setup_filters(struct Curl_easy *data, - int sockindex); + int8_t sockindex); /** * Shutdown the connection at `sockindex` non-blocking, using timeout * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. * Return CURLE_OK and *done == FALSE if not finished. */ -CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); +CURLcode Curl_conn_shutdown(struct Curl_easy *data, + int8_t sockindex, bool *done); /** * Return if data is pending in some connection filter at chain * `sockindex` for connection `data->conn`. */ bool Curl_conn_data_pending(struct Curl_easy *data, - int sockindex); + int8_t sockindex); /** * Return TRUE if any of the connection filters at chain `sockindex` * have data still to send. */ -bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex); +bool Curl_conn_needs_flush(struct Curl_easy *data, int8_t sockindex); /** * Flush any pending data on the connection filters at chain `sockindex`. */ -CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex); +CURLcode Curl_conn_flush(struct Curl_easy *data, int8_t sockindex); /** * Return the socket used on data's connection for FIRSTSOCKET, @@ -459,12 +460,12 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data); /* Return a pointer to the connected socket address or NULL. */ const struct Curl_sockaddr_ex * -Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex); +Curl_conn_get_remote_addr(struct Curl_easy *data, int8_t sockindex); /** * Tell filters to forget about the socket at sockindex. */ -void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); +void Curl_conn_forget_socket(struct Curl_easy *data, int8_t sockindex); /** * Adjust the pollset for the filter chain starting at `cf`. @@ -494,7 +495,7 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, * `data->conn`. Copy at most `len` bytes into `buf`. Return the * actual number of bytes copied in `*pnread`or an error. */ -CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, +CURLcode Curl_cf_recv(struct Curl_easy *data, int8_t sockindex, char *buf, size_t len, size_t *pnread); /** @@ -502,7 +503,7 @@ CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, * at connection `data->conn`. Return the actual number of bytes written * in `*pnwritten` or on error. */ -CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, +CURLcode Curl_cf_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); @@ -571,7 +572,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, * During connect, when tunneling proxies are involved (http or socks), * it will be the name and port the proxy currently negotiates with. */ -void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, +void Curl_conn_get_current_host(struct Curl_easy *data, int8_t sockindex, const char **phost, int *pport); /** @@ -580,18 +581,18 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, */ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, struct connectdata *conn, - int sockindex); + int8_t sockindex); /** * Get the underlying error code for a transfer stream or 0 if not known. */ int Curl_conn_get_stream_error(struct Curl_easy *data, struct connectdata *conn, - int sockindex); + int8_t sockindex); #ifdef CURLVERBOSE void Curl_conn_trc_filters(struct Curl_easy *data, - int sockindex, const char *info); + int8_t sockindex, const char *info); #endif /** @@ -599,20 +600,20 @@ void Curl_conn_trc_filters(struct Curl_easy *data, * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the * correct socket index. */ -int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); +int8_t Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); /* * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. * Return CURLE_AGAIN iff blocked on receiving. */ -CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, +CURLcode Curl_conn_recv(struct Curl_easy *data, int8_t sockindex, char *buf, size_t len, size_t *pnread); /* * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. * Return CURLE_AGAIN iff blocked on sending. */ -CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, +CURLcode Curl_conn_send(struct Curl_easy *data, int8_t sockindex, const void *buf, size_t len, bool eos, size_t *pnwritten); diff --git a/lib/conncache.c b/lib/conncache.c index 21b2e61e4143..cef53351b42b 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -658,7 +658,8 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, maxconnects = data->multi->maxconnects; } - conn->lastused = *Curl_pgrs_now(data); /* it was used up until now */ + /* remember times, connection had been used just before */ + conn->lastchecked = conn->lastupkeep = conn->lastused = *Curl_pgrs_now(data); if(cpool && maxconnects) { /* may be called form a callback already under lock */ bool do_lock = !CPOOL_IS_LOCKED(cpool); @@ -783,15 +784,17 @@ static int conn_upkeep(struct cpool *cpool, struct connectdata *conn, void *param) { + const struct curltime *pnow = Curl_pgrs_now(admin); + (void)param; - if(curlx_ptimediff_ms(Curl_pgrs_now(admin), &conn->keepalive) >= + if(curlx_ptimediff_ms(pnow, &conn->lastupkeep) >= admin->set.upkeep_interval_ms) { CURLcode result; + conn->lastupkeep = *pnow; /* briefly attach for action */ Curl_attach_connection(admin, conn, FALSE); result = Curl_conn_keep_alive(admin, conn); - conn->keepalive = *Curl_pgrs_now(admin); Curl_detach_connection(admin); if(result && !CONN_INUSE(conn)) { diff --git a/lib/connect.c b/lib/connect.c index 9d904b4e04b9..df7a02463d93 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -108,7 +108,7 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data) return timeleft_now_ms(data, Curl_pgrs_now(data)); } -void Curl_shutdown_start(struct Curl_easy *data, int sockindex, +void Curl_shutdown_start(struct Curl_easy *data, int8_t sockindex, int timeout_ms) { struct connectdata *conn = data->conn; @@ -128,7 +128,7 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex, timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, struct connectdata *conn, - int sockindex) + int8_t sockindex) { timediff_t left_ms; @@ -146,7 +146,7 @@ timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, struct connectdata *conn) { timediff_t left_ms = 0, ms; - int i; + int8_t i; for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) { if(!conn->shutdown.start[i].tv_sec) @@ -158,13 +158,13 @@ timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, return left_ms; } -void Curl_shutdown_clear(struct Curl_easy *data, int sockindex) +void Curl_shutdown_clear(struct Curl_easy *data, int8_t sockindex) { struct curltime *pt = &data->conn->shutdown.start[sockindex]; memset(pt, 0, sizeof(*pt)); } -bool Curl_shutdown_started(struct connectdata *conn, int sockindex) +bool Curl_shutdown_started(struct connectdata *conn, int8_t sockindex) { const struct curltime *pt = &conn->shutdown.start[sockindex]; return (pt->tv_sec > 0) || (pt->tv_usec > 0); @@ -232,7 +232,7 @@ void Curl_conncontrol(struct connectdata *conn, int ctrl) CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, int ssl_mode) { struct Curl_peer *first_peer = Curl_conn_get_first_peer(conn, sockindex); @@ -326,7 +326,7 @@ static void conn_report_connect_stats(struct Curl_cfilter *cf, } CURLcode Curl_conn_connect(struct Curl_easy *data, - int sockindex, + int8_t sockindex, bool blocking, bool *done) { @@ -385,7 +385,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, * socket and ip related information. */ Curl_conn_cntrl_update_info(data, data->conn); conn_report_connect_stats(cf, data); - data->conn->keepalive = *Curl_pgrs_now(data); + data->conn->lastupkeep = *Curl_pgrs_now(data); VERBOSE(result = conn_connect_trace(data, cf)); VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); Curl_conn_remove_setup_filters(data, sockindex); @@ -458,14 +458,14 @@ void Curl_conn_set_multiplex(struct connectdata *conn) } struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, - int sockindex) + int8_t sockindex) { return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; } struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, - int sockindex) + int8_t sockindex) { return (sockindex == SECONDARYSOCKET) ? (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : @@ -473,7 +473,7 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, } struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, - int sockindex) + int8_t sockindex) { #ifndef CURL_DISABLE_PROXY if(conn->socks_proxy.peer) diff --git a/lib/connect.h b/lib/connect.h index 33bc1cad49a6..a46e3c15811d 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -41,24 +41,24 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data); #define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000) -void Curl_shutdown_start(struct Curl_easy *data, int sockindex, +void Curl_shutdown_start(struct Curl_easy *data, int8_t sockindex, int timeout_ms); /* return how much time there is left to shutdown the connection at * sockindex. Returns 0 if there is no limit or shutdown has not started. */ timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, struct connectdata *conn, - int sockindex); + int8_t sockindex); /* return how much time there is left to shutdown the connection. * Returns 0 if there is no limit or shutdown has not started. */ timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, struct connectdata *conn); -void Curl_shutdown_clear(struct Curl_easy *data, int sockindex); +void Curl_shutdown_clear(struct Curl_easy *data, int8_t sockindex); /* TRUE iff shutdown has been started */ -bool Curl_shutdown_started(struct connectdata *conn, int sockindex); +bool Curl_shutdown_started(struct connectdata *conn, int8_t sockindex); /* * Used to extract socket and connectdata struct for the most recent @@ -95,7 +95,7 @@ void Curl_conncontrol(struct connectdata *conn, int ctrl); */ CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, int ssl_mode); /** @@ -105,7 +105,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, * When not `blocking`, calls may return without error and `*done != TRUE`, * while the individual filters negotiated the connection. */ -CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, +CURLcode Curl_conn_connect(struct Curl_easy *data, int8_t sockindex, bool blocking, bool *done); /* Set conn to allow multiplexing. */ @@ -113,17 +113,17 @@ void Curl_conn_set_multiplex(struct connectdata *conn); /* Get the origin peer at sockindex. */ struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, - int sockindex); + int8_t sockindex); /* Get the peer the connection actually connects to at sockindex. * Often the same as "origin", but can be redirected via "connect-to" * or "alt-svc". May tunnel through proxies. */ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, - int sockindex); + int8_t sockindex); /* Get the peer curl connects its socket to. * Can be origin, "connect-to" or the first proxy. */ struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, - int sockindex); + int8_t sockindex); #endif /* HEADER_CURL_CONNECT_H */ diff --git a/lib/easy.c b/lib/easy.c index 10cb72f1137c..86c672d56785 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1111,6 +1111,7 @@ void curl_easy_reset(CURL *curl) if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) { struct Curl_easy *data = curl; + data->state.lastconnect_id = -1; /* clear remembered connection id */ Curl_req_hard_reset(&data->req, data); Curl_hash_clean(&data->meta_hash); @@ -1128,8 +1129,6 @@ void curl_easy_reset(CURL *curl) Curl_initinfo(data); data->progress.hide = TRUE; - data->state.current_speed = -1; /* init to negative == impossible */ - data->state.lastconnect_id = -1; /* clear remembered connection id */ /* zero out authentication data: */ memset(&data->state.authhost, 0, sizeof(struct auth)); diff --git a/lib/ftp.c b/lib/ftp.c index 0b4b0eda7360..1e17cb346731 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -590,7 +590,7 @@ static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, static CURLcode ftp_readresp(struct Curl_easy *data, struct ftp_conn *ftpc, - int sockindex, + int8_t sockindex, struct pingpong *pp, int *ftpcodep, /* return the ftp-code if done */ size_t *size) /* size of the response */ diff --git a/lib/http2.c b/lib/http2.c index b4787f156fe9..47e3e075bc72 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2784,7 +2784,7 @@ struct Curl_cftype Curl_cft_nghttp2 = { static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, bool via_h1_upgrade) { struct Curl_cfilter *cf = NULL; @@ -2894,7 +2894,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) } CURLcode Curl_http2_upgrade(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, const char *mem, size_t nread) { struct Curl_cfilter *cf; diff --git a/lib/http2.h b/lib/http2.h index e38dc5745cca..c3270da1ccc5 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -54,7 +54,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data); CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_http2_upgrade(struct Curl_easy *data, - struct connectdata *conn, int sockindex, + struct connectdata *conn, int8_t sockindex, const char *mem, size_t nread); void *Curl_nghttp2_malloc(size_t size, void *user_data); diff --git a/lib/multi.c b/lib/multi.c index ceffe9911d84..6413ccc7b603 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -650,18 +650,19 @@ static void multi_done_locked(struct connectdata *conn, Curl_dnscache_prune(data); if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { - CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s, " + CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%u, " "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, conn->destination, + conn->connection_id, conn->origin->user_hostname, + conn->origin->port, data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); connclose(conn); Curl_conn_close(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { - CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" + CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%u was shutdown" " by server, not reusing", conn->connection_id, - conn->destination); + conn->origin->user_hostname, conn->origin->port); connclose(conn); Curl_conn_close(data, conn, (bool)mdctx->premature); } @@ -669,8 +670,9 @@ static void multi_done_locked(struct connectdata *conn, /* the connection is no longer in use by any transfer */ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ - infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", - conn->connection_id, conn->destination); + infof(data, "Connection #%" FMT_OFF_T " to host %s:%u left intact", + conn->connection_id, conn->origin->user_hostname, + conn->origin->port); } else { /* connection was removed from the cpool and destroyed. */ diff --git a/lib/openldap.c b/lib/openldap.c index 6c15734d29ac..8ce4758d620f 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -1100,7 +1100,7 @@ static CURLcode client_write(struct Curl_easy *data, return result; } -static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, +static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf, size_t len, size_t *pnread) { struct connectdata *conn = data->conn; diff --git a/lib/pingpong.c b/lib/pingpong.c index b40d968b3f48..b365772fb71a 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -222,7 +222,7 @@ CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp, } static CURLcode pingpong_read(struct Curl_easy *data, - int sockindex, + int8_t sockindex, char *buffer, size_t buflen, size_t *nread) @@ -236,7 +236,7 @@ static CURLcode pingpong_read(struct Curl_easy *data, * Reads a piece of a server response. */ CURLcode Curl_pp_readresp(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct pingpong *pp, int *code, /* return the server code if done */ size_t *size) /* size of the response */ diff --git a/lib/pingpong.h b/lib/pingpong.h index 02f496123036..09dca414ebea 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -132,7 +132,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, * Reads a piece of a server response. */ CURLcode Curl_pp_readresp(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct pingpong *pp, int *code, /* return the server code if done */ size_t *size); /* size of the response */ diff --git a/lib/transfer.c b/lib/transfer.c index 17312e41ef12..08120268f1d3 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -669,8 +669,8 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) static void xfer_setup( struct Curl_easy *data, /* transfer */ - int send_idx, /* sockindex to send on or -1 */ - int recv_idx, /* sockindex to receive on or -1 */ + int8_t send_idx, /* sockindex to send on or -1 */ + int8_t recv_idx, /* sockindex to receive on or -1 */ curl_off_t recv_size /* how much to receive, -1 if unknown */ ) { @@ -718,20 +718,20 @@ void Curl_xfer_setup_nop(struct Curl_easy *data) } void Curl_xfer_setup_sendrecv(struct Curl_easy *data, - int sockindex, + int8_t sockindex, curl_off_t recv_size) { xfer_setup(data, sockindex, sockindex, recv_size); } void Curl_xfer_setup_send(struct Curl_easy *data, - int sockindex) + int8_t sockindex) { xfer_setup(data, sockindex, -1, -1); } void Curl_xfer_setup_recv(struct Curl_easy *data, - int sockindex, + int8_t sockindex, curl_off_t recv_size) { xfer_setup(data, -1, sockindex, recv_size); diff --git a/lib/transfer.h b/lib/transfer.h index 9bdc93f3ed42..0ad0d549edd8 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -69,12 +69,12 @@ void Curl_xfer_setup_nop(struct Curl_easy *data); /* The transfer sends data on the given socket index */ void Curl_xfer_setup_send(struct Curl_easy *data, - int sockindex); + int8_t sockindex); /* The transfer receives data on the given socket index, the * amount to receive (or -1 if unknown). */ void Curl_xfer_setup_recv(struct Curl_easy *data, - int sockindex, + int8_t sockindex, curl_off_t recv_size); /* *After* Curl_xfer_setup_xxx(), tell the transfer to shutdown the @@ -89,7 +89,7 @@ void Curl_xfer_set_shutdown(struct Curl_easy *data, * the amount to receive or -1 if unknown. */ void Curl_xfer_setup_sendrecv(struct Curl_easy *data, - int sockindex, + int8_t sockindex, curl_off_t recv_size); /** diff --git a/lib/url.c b/lib/url.c index 946ebdd97656..1f7cf47d11da 100644 --- a/lib/url.c +++ b/lib/url.c @@ -476,7 +476,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->mid = UINT32_MAX; data->master_mid = UINT32_MAX; data->progress.hide = TRUE; - data->state.current_speed = -1; /* init to negative == impossible */ Curl_hash_init(&data->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry); @@ -497,7 +496,7 @@ CURLcode Curl_open(struct Curl_easy **curl) void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) { - size_t i; + int8_t i; DEBUGASSERT(conn); @@ -505,8 +504,8 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) !conn->bits.shutdown_handler) conn->scheme->run->disconnect(data, conn, TRUE); - for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { - Curl_conn_cf_discard_all(data, conn, (int)i); + for(i = 0; i < (int8_t)CURL_ARRAYSIZE(conn->cfilter); ++i) { + Curl_conn_cf_discard_all(data, conn, i); } #ifndef CURL_DISABLE_PROXY @@ -1227,11 +1226,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - /* Store creation time to help future close decision making */ - conn->created = *Curl_pgrs_now(data); - - /* Store current time to give a baseline to keepalive connection times. */ - conn->keepalive = conn->created; + /* Remember time this connection started */ + conn->lastused = conn->lastupkeep = conn->created = *Curl_pgrs_now(data); #ifndef CURL_DISABLE_FTP conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; @@ -1256,7 +1252,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) it may live on without (this specific) Curl_easy */ conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; - conn->lastused = conn->created; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) conn->gssapi_delegation = data->set.gssapi_delegation; #endif diff --git a/lib/urldata.h b/lib/urldata.h index bc7d9e6d4eb5..129e82e2ecdf 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -75,7 +75,7 @@ /* On error return, the value of `pnwritten` has no meaning */ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ + int8_t sockindex, /* socketindex */ const uint8_t *buf, /* data to write */ size_t len, /* amount to send */ bool eos, /* last chunk */ @@ -83,7 +83,7 @@ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ /* On error return, the value of `pnread` has no meaning */ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ + int8_t sockindex, /* socketindex */ char *buf, /* store data here */ size_t len, /* max amount to read */ size_t *pnread); /* how much received */ @@ -259,53 +259,60 @@ struct ip_quadruple { * unique for an entire connection. */ struct connectdata { - struct Curl_llist_node cpool_node; /* conncache lists */ - struct Curl_llist_node cshutdn_node; /* cshutdn list */ - - curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ - void *closesocket_client; + curl_off_t connection_id; /* Contains a unique number to make it easier to + track the connections in the log output */ /* This is used by the connection pool logic. If this returns TRUE, this handle is still used by one or more easy handles and can only used by any other easy handle without careful consideration (== only for multiplexing) and it cannot be used by another multi handle! */ #define CONN_INUSE(c) (!!(c)->attached_xfers) + uint32_t attached_xfers; /* # of attached easy handles */ - /**** Fields set when inited and not modified again */ - curl_off_t connection_id; /* Contains a unique number to make it easier to - track the connections in the log output */ - char *destination; /* string carrying normalized hostname+port+scope */ - - /* `meta_hash` is a general key-value store for implementations - * with the lifetime of the connection. - * Elements need to be added with their own destructor to be invoked when - * the connection is cleaned up (see Curl_hash_add2()).*/ - struct Curl_hash meta_hash; + /* A connection cache from a SHARE might be used in several multi handles. + * We MUST not reuse connections that are running in another multi, + * for concurrency reasons. That multi might run in another thread. + * `attached_multi` is set by the first transfer attached and cleared + * when the last one is detached. + * NEVER call anything on this multi, check for equality. */ + struct Curl_multi *attached_multi; /* Who the connection is talking to, ultimately */ struct Curl_peer *origin; /* connection ultimately talks to this */ struct Curl_peer *via_peer; /* if set, connection really talks to this */ struct Curl_peer *origin2; /* origin of SECONDARYSOCKET */ struct Curl_peer *via_peer2; /* peer of SECONDARYSOCKET */ -#ifndef CURL_DISABLE_PROXY - struct proxy_info socks_proxy; - struct proxy_info http_proxy; -#endif struct Curl_creds *creds; /* When connection itself is tied to credentials */ struct Curl_peer *creds_origin; /* origin tied credentials are for */ - char *options; /* options string, allocated */ + const struct Curl_scheme *scheme; /* Connection's real protocol handler */ + const struct Curl_scheme *given; /* The protocol first given */ + + /* `meta_hash` is a general key-value store for implementations + * with the lifetime of the connection. + * Elements need to be added with their own destructor to be invoked when + * the connection is cleaned up (see Curl_hash_add2()).*/ + struct Curl_hash meta_hash; + + struct Curl_llist_node cpool_node; /* conncache lists */ + struct Curl_llist_node cshutdn_node; /* cshutdn list */ + char *destination; /* hostname+port, used in conncache */ + struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ struct curltime lastchecked; /* when last checked alive status */ + struct curltime lastupkeep; /* when last done conn_upkeep */ + +#ifndef CURL_DISABLE_PROXY + struct proxy_info socks_proxy; + struct proxy_info http_proxy; +#endif - /* A connection can have one or two sockets and connection filters. - * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */ - curl_socket_t sock[2]; struct Curl_cfilter *cfilter[2]; /* connection filters */ Curl_recv *recv[2]; Curl_send *send[2]; - int recv_idx; /* on which socket index to receive, default 0 */ - int send_idx; /* on which socket index to send, default 0 */ + /* A connection can have one or two sockets and connection filters. + * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */ + curl_socket_t sock[2]; #define CONN_SOCK_IDX_VALID(i) (((i) >= 0) && ((i) < 2)) @@ -314,27 +321,14 @@ struct connectdata { timediff_t timeout_ms; /* 0 means no timeout */ } shutdown; + curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ + void *closesocket_client; + struct ssl_primary_config ssl_config; #ifndef CURL_DISABLE_PROXY struct ssl_primary_config proxy_ssl_config; #endif - struct ConnectBits bits; /* various state-flags for this connection */ - - const struct Curl_scheme *scheme; /* Connection's protocol handler */ - const struct Curl_scheme *given; /* The protocol first given */ - - /* Protocols can use a custom keepalive mechanism to keep connections alive. - This allows those protocols to track the last time the keepalive mechanism - was used on this connection. */ - struct curltime keepalive; - - /* A connection cache from a SHARE might be used in several multi handles. - * We MUST not reuse connections that are running in another multi, - * for concurrency reasons. That multi might run in another thread. - * `attached_multi` is set by the first transfer attached and cleared - * when the last one is detached. - * NEVER call anything on this multi, check for equality. */ - struct Curl_multi *attached_multi; + char *options; /* options string, allocated */ /*************** Request - specific items ************/ #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) @@ -360,13 +354,14 @@ struct connectdata { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - uint32_t attached_xfers; /* # of attached easy handles */ #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif uint16_t localportrange; uint16_t localport; + int8_t recv_idx; /* on which socket index to receive, default 0 */ + int8_t send_idx; /* on which socket index to send, default 0 */ uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily the transport the connection ends using due to Alt-Svc and happy eyeballing. Use Curl_conn_get_transport() for actual value once the @@ -376,6 +371,8 @@ struct connectdata { * 0 at start, then one of 09, 10, 11, etc. */ uint8_t httpversion_seen; uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */ + + struct ConnectBits bits; /* various state-flags for this connection */ }; #ifndef CURL_DISABLE_PROXY @@ -528,18 +525,7 @@ struct urlpieces { }; struct UrlState { - /* buffers to store authentication data in, as parsed from input options */ - struct curltime keeps_speed; /* for the progress meter really */ - curl_off_t lastconnect_id; /* The last assigned connection or -1 */ - struct dynbuf headerb; /* buffer to store headers in */ -#ifndef CURL_DISABLE_HSTS - struct curl_slist *hstslist; /* list of HSTS files set by - curl_easy_setopt(HSTS) calls */ -#endif - curl_off_t current_speed; /* the ProgressShow() function sets this, - bytes / second */ - /* Origin of the initial (e.g. not followed) request of a transfer. Credentials from CURLOPT_* are only valid for this origin. Always set once a transfer starts searching for connections. */ @@ -548,6 +534,13 @@ struct UrlState { * requests. */ struct Curl_peer *origin; + struct curltime keeps_speed; /* for the progress meter really */ + struct dynbuf headerb; /* buffer to store headers in */ +#ifndef CURL_DISABLE_HSTS + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ +#endif + int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL @@ -598,9 +591,6 @@ struct UrlState { curl_mimepart *formp; /* storage for old API form-posting, allocated on demand */ #endif - size_t trailers_bytes_sent; - struct dynbuf trailers_buf; /* a buffer containing the compiled trailing - headers */ struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout[2]; /* for external purposes */ #endif diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index a533ba7c2769..a7ab017bc482 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -453,7 +453,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, * The filter will resolve the peer on the first connect attempt. */ static CURLcode cf_dns_add(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport) @@ -502,7 +502,7 @@ static CURLcode cf_dns_insert_after(struct Curl_cfilter *cf_at, static CURLcode cf_dns_add_resolve(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport) @@ -552,7 +552,7 @@ static CURLcode cf_dns_add_resolve(struct Curl_easy *data, CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport) @@ -571,7 +571,7 @@ CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, * - error returned by the DNS resolv */ CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer) { struct Curl_cfilter *cf = conn->cfilter[sockindex]; @@ -595,7 +595,7 @@ CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, */ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, struct Curl_peer *peer, - int sockindex, + int8_t sockindex, int ai_family, unsigned int index) { @@ -633,7 +633,7 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, #ifdef USE_HTTPSRR CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer) { return cf_dns_add_resolve(data, conn, sockindex, peer, @@ -646,7 +646,7 @@ CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, */ const struct Curl_https_rrinfo * Curl_conn_dns_get_https(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct Curl_peer *peer) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; @@ -666,7 +666,7 @@ Curl_conn_dns_get_https(struct Curl_easy *data, } bool Curl_conn_dns_resolved_https(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct Curl_peer *peer) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; diff --git a/lib/vdns/cf-dns.h b/lib/vdns/cf-dns.h index ef90d711e2f7..6226d2161afb 100644 --- a/lib/vdns/cf-dns.h +++ b/lib/vdns/cf-dns.h @@ -33,33 +33,33 @@ struct Curl_peer; CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport); CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, struct Curl_peer *peer, - int sockindex, + int8_t sockindex, int ai_family, unsigned int index); #ifdef USE_HTTPSRR CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, struct connectdata *conn, - int sockindex, + int8_t sockindex, struct Curl_peer *peer); const struct Curl_https_rrinfo * Curl_conn_dns_get_https(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct Curl_peer *peer); bool Curl_conn_dns_resolved_https(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct Curl_peer *peer); #else #define Curl_conn_dns_get_https(a, b, c) NULL diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 3a72b31bc882..e9fe81022d28 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -1760,7 +1760,7 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, return SSH_NO_ERROR; } -static void conn_forget_socket(struct Curl_easy *data, int sockindex) +static void conn_forget_socket(struct Curl_easy *data, int8_t sockindex) { struct connectdata *conn = data->conn; if(conn && CONN_SOCK_IDX_VALID(sockindex)) { @@ -2778,7 +2778,7 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, return myssh_done(data, sshc, status); } -static CURLcode scp_send(struct Curl_easy *data, int sockindex, +static CURLcode scp_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { @@ -2812,7 +2812,7 @@ static CURLcode scp_send(struct Curl_easy *data, int sockindex, return CURLE_OK; } -static CURLcode scp_recv(struct Curl_easy *data, int sockindex, +static CURLcode scp_recv(struct Curl_easy *data, int8_t sockindex, char *mem, size_t len, size_t *pnread) { struct connectdata *conn = data->conn; @@ -2938,7 +2938,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, } /* return number of sent bytes */ -static CURLcode sftp_send(struct Curl_easy *data, int sockindex, +static CURLcode sftp_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { @@ -3019,7 +3019,7 @@ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, * Return number of received (decrypted) bytes * or <0 on error */ -static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, +static CURLcode sftp_recv(struct Curl_easy *data, int8_t sockindex, char *mem, size_t len, size_t *pnread) { struct connectdata *conn = data->conn; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 65a03ef3116e..9d2e8b8e94de 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3278,7 +3278,7 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; - int sockindex = Curl_conn_sockindex(data, sock); + int8_t sockindex = Curl_conn_sockindex(data, sock); size_t nread; CURLcode result; struct connectdata *conn = data->conn; @@ -3306,7 +3306,7 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; - int sockindex = Curl_conn_sockindex(data, sock); + int8_t sockindex = Curl_conn_sockindex(data, sock); size_t nwrite; CURLcode result; struct connectdata *conn = data->conn; @@ -3614,7 +3614,7 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, return ssh_done(data, status); } -static CURLcode scp_send(struct Curl_easy *data, int sockindex, +static CURLcode scp_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { @@ -3646,7 +3646,7 @@ static CURLcode scp_send(struct Curl_easy *data, int sockindex, return result; } -static CURLcode scp_recv(struct Curl_easy *data, int sockindex, +static CURLcode scp_recv(struct Curl_easy *data, int8_t sockindex, char *mem, size_t len, size_t *pnread) { struct connectdata *conn = data->conn; @@ -3772,7 +3772,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, } /* return number of sent bytes */ -static CURLcode sftp_send(struct Curl_easy *data, int sockindex, +static CURLcode sftp_send(struct Curl_easy *data, int8_t sockindex, const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { @@ -3803,7 +3803,7 @@ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, * Return number of received (decrypted) bytes * or <0 on error */ -static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, +static CURLcode sftp_recv(struct Curl_easy *data, int8_t sockindex, char *mem, size_t len, size_t *pnread) { struct connectdata *conn = data->conn; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index c25938c71601..0740c32fbb55 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5273,7 +5273,7 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, } static CURLcode ossl_get_channel_binding(struct Curl_easy *data, - int sockindex, + int8_t sockindex, struct dynbuf *binding) { X509 *cert; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 4a9d9df1ce78..3782238636d2 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -215,7 +215,7 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) } } -CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, +CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int8_t sockindex, struct dynbuf *binding) { if(Curl_ssl->get_channel_binding) @@ -1390,7 +1390,7 @@ static CURLcode cf_ssl_peer_init(struct Curl_cfilter *cf, CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, struct Curl_peer *origin, struct connectdata *conn, - int sockindex) + int8_t sockindex) { struct Curl_cfilter *cf; struct Curl_peer *peer = (sockindex == SECONDARYSOCKET) ? @@ -1552,7 +1552,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, } CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex, bool send_shutdown) + int8_t sockindex, bool send_shutdown) { struct Curl_cfilter *cf, *head; CURLcode result = CURLE_OK; @@ -1779,7 +1779,7 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data, const struct Curl_cftype *cft, - int sockindex) + int8_t sockindex) { if(data->conn && #ifndef CURL_DISABLE_PROXY diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 96cb7d15ed53..8ee22c8d5aaa 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -171,7 +171,7 @@ bool Curl_ssl_cert_status_request(void); * If channel binding is not supported, binding stays empty and CURLE_OK is * returned. */ -CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, +CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int8_t sockindex, struct dynbuf *binding); #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ @@ -179,7 +179,7 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, struct Curl_peer *origin, struct connectdata *conn, - int sockindex); + int8_t sockindex); CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, @@ -187,7 +187,7 @@ CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *peer); CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex, bool send_shutdown); + int8_t sockindex, bool send_shutdown); #ifndef CURL_DISABLE_PROXY CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index b3ef76af1e2d..330d8427ca70 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -184,7 +184,7 @@ struct Curl_ssl { CURLcode (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, const void *mem, size_t len, size_t *pnwritten); - CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex, + CURLcode (*get_channel_binding)(struct Curl_easy *data, int8_t sockindex, struct dynbuf *binding); }; @@ -209,7 +209,7 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, * data's connection at `sockindex` or NULL if not found/available. */ struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data, const struct Curl_cftype *cft, - int sockindex); + int8_t sockindex); #endif /* USE_SSL */ From 5406d2627c07dc2ed3d77650c00c97afdd2d9502 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 Aug 2026 08:25:54 +0200 Subject: [PATCH 0912/1062] urldata: drop four strings from the aptr struct - 'rtsp_transport' There is no need to keep this data around once the RTSP request has been issued. - 'accept_encoding' Only needed when creating the RTSP request. - 'uagent' The header is generated on demand from set.str[STRING_USERAGENT] - 'ref' Not necessary to keep around Closes #22603 --- lib/http.c | 25 +++++------------- lib/rtsp.c | 69 ++++++++++++++++++++++++-------------------------- lib/transfer.c | 12 --------- lib/url.c | 6 ----- lib/urldata.h | 6 ----- 5 files changed, 39 insertions(+), 79 deletions(-) diff --git a/lib/http.c b/lib/http.c index 869458fd3afa..6a4c1f39411b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2014,17 +2014,6 @@ void Curl_http_method(struct Curl_easy *data, *reqp = httpreq; } -static CURLcode http_useragent(struct Curl_easy *data) -{ - /* The User-Agent string might have been allocated already, because - it might have been used in the proxy connect, but if we have got a header - with the user-agent string specified, we erase the previously made string - here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent"))) - curlx_safefree(data->state.aptr.uagent); - return CURLE_OK; -} - static CURLcode http_set_aptr_host(struct Curl_easy *data) { struct connectdata *conn = data->conn; @@ -2961,10 +2950,12 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_USER_AGENT: - if(data->set.str[STRING_USERAGENT] && /* User-Agent: */ - *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent) - result = curlx_dyn_add(req, data->state.aptr.uagent); + if(!Curl_checkheaders(data, STRCONST("User-Agent"))) { + if(data->set.str[STRING_USERAGENT] && + *data->set.str[STRING_USERAGENT]) + result = curlx_dyn_addf(req, "User-Agent: %s\r\n", + data->set.str[STRING_USERAGENT]); + } break; case H1_HD_ACCEPT: @@ -2983,7 +2974,6 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_ACCEPT_ENCODING: - curlx_safefree(data->state.aptr.accept_encoding); if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", @@ -2991,7 +2981,6 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_REFERER: - curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && !Curl_checkheaders(data, STRCONST("Referer"))) result = curlx_dyn_addf(req, "Referer: %s\r\n", @@ -3114,8 +3103,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = Curl_http_output_auth(data, data->conn, method, httpreq, data->state.up.path, data->state.up.query, FALSE); - if(!result) - result = http_useragent(data); /* Setup input reader, resume information and ranges */ if(!result) result = set_reader(data, httpreq); diff --git a/lib/rtsp.c b/lib/rtsp.c index 1831a8479b23..a8db21c41fc5 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -319,14 +319,14 @@ struct rtsp_blocks { const char *request; const char *session_id; const char *accept; - const char *accept_encoding; const char *range; - const char *referrer; const char *stream_uri; - const char *transport; - const char *uagent; const char *hd_proxy_auth; const char *hd_auth; + char *referrer; + char *accept_encoding; + char *transport; + BIT(transport_alloc); /* if 'transport' is allocated */ }; static CURLcode rtsp_setup_request(struct Curl_easy *data, @@ -351,17 +351,16 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, if(data->set.str[STRING_RTSP_TRANSPORT]) { result = rtsp_header_alloc("Transport", data->set.str[STRING_RTSP_TRANSPORT], - &data->state.aptr.rtsp_transport); + &b->transport); if(result) return result; + b->transport_alloc = TRUE; } else { failf(data, "Refusing to issue an RTSP SETUP without a Transport: header."); return CURLE_BAD_FUNCTION_ARGUMENT; } - - b->transport = data->state.aptr.rtsp_transport; } /* Accept Headers for DESCRIBE requests */ @@ -375,26 +374,12 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, data->set.str[STRING_ENCODING]) { result = rtsp_header_alloc("Accept-Encoding", data->set.str[STRING_ENCODING], - &data->state.aptr.accept_encoding); + &b->accept_encoding); if(result) return result; - b->accept_encoding = data->state.aptr.accept_encoding; } } - /* The User-Agent string might have been allocated already, because - it might have been used in the proxy connect, but if we have got a header - with the user-agent string specified, we erase the previously made string - here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent")) && - data->state.aptr.uagent) { - curlx_safefree(data->state.aptr.uagent); - } - else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT]) { - b->uagent = data->state.aptr.uagent; - } - /* setup the authentication headers */ result = Curl_http_output_auth(data, conn, b->request, HTTPREQ_GET, b->stream_uri, NULL, FALSE); @@ -407,13 +392,13 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, b->hd_auth = data->req.hd_auth; /* Referrer */ - curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && - !Curl_checkheaders(data, STRCONST("Referer"))) - data->state.aptr.ref = + !Curl_checkheaders(data, STRCONST("Referer"))) { + b->referrer = curl_maprintf("Referer: %s\r\n", Curl_bufref_ptr(&data->state.referer)); - - b->referrer = data->state.aptr.ref; + if(!b->referrer) + result = CURLE_OUT_OF_MEMORY; + } /* * Range Header @@ -421,7 +406,8 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, * * Go ahead and use the Range stuff supplied for HTTP */ - if(data->state.use_range && + if(!result && + data->state.use_range && ((rtspreq == RTSPREQ_PLAY) || (rtspreq == RTSPREQ_PAUSE) || (rtspreq == RTSPREQ_RECORD))) { @@ -513,19 +499,26 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) "%s" /* accept-encoding */ "%s" /* range */ "%s" /* referrer */ - "%s" /* user-agent */ - "%s" /* hd_proxy_auth */ - "%s" /* hd_auth */ , block.transport ? block.transport : "", block.accept ? block.accept : "", block.accept_encoding ? block.accept_encoding : "", block.range ? block.range : "", - block.referrer ? block.referrer : "", - block.uagent ? block.uagent : "", - block.hd_proxy_auth ? block.hd_proxy_auth : "", - block.hd_auth ? block.hd_auth : ""); - + block.referrer ? block.referrer : ""); + + if(!result && + !Curl_checkheaders(data, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) + result = curlx_dyn_addf(&req_buffer, + "User-Agent: %s\r\n", + data->set.str[STRING_USERAGENT]); + + if(!result) + result = curlx_dyn_addf(&req_buffer, + "%s" /* hd_proxy_auth */ + "%s", /* hd_auth */ + block.hd_proxy_auth ? block.hd_proxy_auth : "", + block.hd_auth ? block.hd_auth : ""); if(result) goto out; @@ -567,6 +560,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) result = Curl_pgrsUpdate(data); } out: + if(block.transport_alloc) + curlx_free(block.transport); + curlx_free(block.accept_encoding); + curlx_free(block.referrer); curlx_dyn_free(&req_buffer); return result; } diff --git a/lib/transfer.c b/lib/transfer.c index 08120268f1d3..acab3e1edbfb 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -583,18 +583,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) result = Curl_hsts_loadcb(data, data->hsts); } - /* - * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * anything through an HTTP proxy we cannot limit this based on protocol. - */ - if(!result && data->set.str[STRING_USERAGENT]) { - curlx_free(data->state.aptr.uagent); - data->state.aptr.uagent = - curl_maprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); - if(!data->state.aptr.uagent) - return CURLE_OUT_OF_MEMORY; - } - data->req.headerbytecount = 0; Curl_headers_cleanup(data); return result; diff --git a/lib/url.c b/lib/url.c index 1f7cf47d11da..15b7bb68e3ed 100644 --- a/lib/url.c +++ b/lib/url.c @@ -279,17 +279,11 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_hash_destroy(&data->meta_hash); Curl_creds_unlink(&data->state.creds); - curlx_safefree(data->state.aptr.uagent); - curlx_safefree(data->state.aptr.accept_encoding); curlx_safefree(data->state.aptr.rangeline); - curlx_safefree(data->state.aptr.ref); curlx_safefree(data->state.aptr.host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); #endif -#ifndef CURL_DISABLE_RTSP - curlx_safefree(data->state.aptr.rtsp_transport); -#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); diff --git a/lib/urldata.h b/lib/urldata.h index 129e82e2ecdf..e1ea84b8aaf7 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -612,14 +612,8 @@ struct UrlState { /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { - char *uagent; - char *accept_encoding; char *rangeline; - char *ref; char *host; -#ifndef CURL_DISABLE_RTSP - char *rtsp_transport; -#endif } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; From 8e2eced23436c832a858802423b4ea8b45cf403a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 17 Aug 2026 14:18:50 +0200 Subject: [PATCH 0913/1062] multi: initialisation order When creating a multi handle, global_init() may not have been invoked that. There is a lazy check for this in easy_init() that trigger global_init(). Create the admin handle for a multi first, before any further operations, so easy_init() can trigger a global_init() when necessary. Closes #22606 --- lib/multi.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 6413ccc7b603..429651430aa8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -235,8 +235,23 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, multi->magic = CURLMULTI_MAGIC_NUMBER; + /* Initialisation order is important here! + * easy_init() does a lazy check on curl_global_init() which sets + * up platform specific things we need. For example calling curlx_pnow() + * before this is not safe. */ + multi->admin = curl_easy_init(); + if(!multi->admin) { + curlx_free(multi); + return NULL; + } + multi->admin->multi = multi; + multi->admin->state.internal = TRUE; + + /* Now we can use curlx_* things safely */ curlx_pnow(&multi->now); Curl_timeouts_init(&multi->timeouts, &multi->now); + multi_timeouts_init(multi->admin); + Curl_dnscache_init(&multi->dnscache, dnssize); Curl_mntfy_init(multi); Curl_multi_ev_init(multi, ev_hashsize); @@ -269,14 +284,6 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, Curl_uint32_tbl_resize(&multi->xfers, xfer_table_size)) goto error; - multi->admin = curl_easy_init(); - if(!multi->admin) - goto error; - /* Initialize admin handle to operate inside this multi */ - multi->admin->multi = multi; - multi->admin->state.internal = TRUE; - multi_timeouts_init(multi->admin); - #ifdef DEBUGBUILD if(getenv("CURL_DEBUG")) multi->admin->set.verbose = TRUE; From c2da5c7e66ce5f4edb6315dc4da6a6fa7d44f69f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 Aug 2026 16:28:31 +0200 Subject: [PATCH 0914/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 56 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index dc872260e1ea..7d176129be91 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -5,7 +5,7 @@ curl and libcurl 8.22.0 curl_easy_setopt() options: 312 Public functions in libcurl: 100 Authors: 1506 - Contributors: 3765 + Contributors: 3767 This release includes the following changes: @@ -23,6 +23,8 @@ This release includes the following bugfixes: o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] o build: assume POSIX `select()` is available [166] o build: clear `Require.private` for static-only builds in `libcurl.pc` [188] + o build: drop `dirent.h` and `opendir()` detections on Windows [186] + o build: drop detecting `gettimeofday()` on Windows [184] o build: drop superfluous `STDC_HEADERS` macro [51] o build: enable thread-safe `getaddrinfo()` for OpenBSD [35] o build: minor debug option message fixes/improvements [200] @@ -43,6 +45,8 @@ This release includes the following bugfixes: o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] o cmake: use built-in variable and target property dump functions with CMake 4.5+ [155] + o config-riscos.h: delete handcrafted RISC OS config header, in favor of autotools [178] + o config-win32.h: limit use to MSVC IDE Project builds [193] o configure: clarify --enable-debug option [133] o configure: fix misleading error messages [42] o configure: link `-lcrypt32` instead of `-lm` for wolfSSL on Windows [79] @@ -54,6 +58,8 @@ This release includes the following bugfixes: o conncache: connection alive checks intervals [20] o conncache: don't assume curl_off_t increment wrap-around [138] o connect: connection close tweaks [112] + o connect: only set connect timer on first socket [206] + o connections: use admin handles only for maintenance [213] o content_encoding: exact-match the identity transfer-coding token [189] o content_encoding: give a clear error on multi-member gzip [46] o cookie: refuse to load cookies set against a PSL domain [139] @@ -61,6 +67,7 @@ This release includes the following bugfixes: o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] o curl: help category cleanups [169] o curl_gssapi: document/update feature availability [145] + o curl_threads: always use native threads/mutex on Windows [185] o curl_trc: remove unused expire timers [147] o curl_url_set.md: expand the CURLU_NO_AUTHORITY description [134] o curl_ws_meta.md: polish and better vocabulary [19] @@ -73,6 +80,7 @@ This release includes the following bugfixes: o dnsd: fix bounds check in `read_https_alpn_part()` [143] o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] o DoH: improvements [203] + o file: support directory listing on Windows [205] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gitignore: maintenance updates [170] @@ -100,6 +108,7 @@ This release includes the following bugfixes: o lib: add "Curl_" prefix to two global functions [84] o lib: add multi_wakeup_internal [86] o lib: fix 'ns' -> 'us' in trace messages [57] + o lib: optimize struct layouts for reduced memory usage [212] o lib: ratelimit timestamps [14] o lib: update mentions of the legacy "sessionhandle" [157] o libcurl.pc: add `License` tag [190] @@ -112,9 +121,12 @@ This release includes the following bugfixes: o mime: reject CR and LF in mail part name and filename [30] o mod_curltest: fix compiler warnings [49] o mqtt: reject control bytes in the topic [43] + o multi: cap expire times to INT_MAX internally [216] o multi: forbid curl_easy_pause from within multi socket callback [22] o multi: hold timeout values in 'int' instead of 'long' [165] o multi: remove #if 0'ed code that uses old struct [150] + o multi: shrink expire timer indices [199] + o multi: timeout improvements [209] o multi: use index list for expire timeouts [197] o multihandle: move two struct fields [163] o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] @@ -191,7 +203,6 @@ This release includes the following bugfixes: o tool_operate: remove call to abort() [23] o tool_xattr: add support for Windows alternate data stream [129] o typecheck-gcc: allow passing `char[]` as callback data [153] - o uint-bset: add slot0 member [173] o uint-spbset: reused empty chunks [67] o unit3214: fix to pass on systems with >=128-bit pointers [107] o url: fix negotiate/ntlm connection reuse [176] @@ -202,6 +213,8 @@ This release includes the following bugfixes: o urlapi: do not keep an internal port string [31] o urlapi: improved return codes [148] o urlapi: preserve empty markers in relative URLs [61] + o urldata: cleanups [175] + o urldata: drop four strings from the aptr struct [136] o vms: fix symbol typo and missing closing quotes in `config_h.com` [124] o vquic: add Curl_ prefix to some global functions [76] o vquic: initialize new callback slot for nghttp3 v1.18.0+ [87] @@ -236,19 +249,19 @@ advice from friends like these: 11soda11, 1rhino2 on hackerone, AlanKingPL, Alb3e3, Alhuda Khan, anupamme, Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, - Christoph Reiter, claudex on github, Collin Funk, Dan Fandrich, - Daniel Gustafsson, Daniel Stenberg, dependabot[bot], + Christoph Reiter, claudex on github, Collin Funk, cybertron10 on github, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, - Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, Joel Depooter, - Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, Laurent Sabourin, - Marcel Jamin, Martin Dukek, Matthew John Cheetham, Max Dymond, Memduh Çelik, - Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, - Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, RMMoreton on github, - Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, Samuel Dainard, - Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, Stephan Zeisberg, - stze on hackerone, Thomas Chauchefoin, Viktor Szakats, xmoezzz on github, - Yoshiro Yoneya - (61 contributors) + GLaDOS-418 on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, + Joel Depooter, Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, + Laurent Sabourin, Marcel Jamin, Martin Dukek, Matthew John Cheetham, + Max Dymond, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, + Ralf Mueller, Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, + RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, + Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, + Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, Viktor Szakats, + xmoezzz on github, Yoshiro Yoneya + (63 contributors) References to bug reports and discussions on issues: @@ -386,6 +399,7 @@ References to bug reports and discussions on issues: [133] = https://curl.se/bug/?i=22512 [134] = https://curl.se/bug/?i=22515 [135] = https://curl.se/bug/?i=22413 + [136] = https://curl.se/bug/?i=22603 [137] = https://curl.se/bug/?i=22341 [138] = https://curl.se/bug/?i=22569 [139] = https://curl.se/bug/?i=22500 @@ -422,24 +436,36 @@ References to bug reports and discussions on issues: [170] = https://curl.se/bug/?i=22445 [171] = https://curl.se/bug/?i=22444 [172] = https://curl.se/bug/?i=22443 - [173] = https://curl.se/bug/?i=22561 [174] = https://curl.se/bug/?i=22490 + [175] = https://curl.se/bug/?i=22604 [176] = https://curl.se/bug/?i=22528 + [178] = https://curl.se/bug/?i=22598 [179] = https://curl.se/bug/?i=22547 [180] = https://curl.se/bug/?i=22560 [181] = https://curl.se/bug/?i=22555 [182] = https://curl.se/bug/?i=22557 + [184] = https://curl.se/bug/?i=22594 + [185] = https://curl.se/bug/?i=22593 + [186] = https://curl.se/bug/?i=22588 [187] = https://curl.se/bug/?i=22552 [188] = https://curl.se/bug/?i=22548 [189] = https://curl.se/bug/?i=22541 [190] = https://curl.se/bug/?i=22545 [191] = https://curl.se/bug/?i=22544 + [193] = https://curl.se/bug/?i=22595 [194] = https://curl.se/bug/?i=22536 [195] = https://curl.se/bug/?i=22540 [196] = https://curl.se/bug/?i=22524 [197] = https://curl.se/bug/?i=22473 + [199] = https://curl.se/bug/?i=22591 [200] = https://curl.se/bug/?i=22532 [202] = https://curl.se/bug/?i=22484 [203] = https://curl.se/bug/?i=22514 + [205] = https://curl.se/bug/?i=22577 + [206] = https://curl.se/bug/?i=22587 [208] = https://curl.se/bug/?i=22475 + [209] = https://curl.se/bug/?i=22584 [210] = https://curl.se/bug/?i=22519 + [212] = https://curl.se/bug/?i=22585 + [213] = https://curl.se/bug/?i=22567 + [216] = https://curl.se/bug/?i=22579 From bb406386d9e088873f5f640c55e14a69390abd01 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 30 Jun 2026 18:10:23 +0200 Subject: [PATCH 0915/1062] curlx_inet_ntop: return `CURLcode`, drop setting `errno` To simplify and to remove an exception where `errno` was reused to return a socket error codes on Windows. The error was used by one call site (`sockaddr2string()` in `cf-socket.c`), but it was in practice always propagated as `SOCKEAFNOSUPPORT` to callers. Also: - cf-socket: update 3 error messages to show `CURLcode` accordingly. - if2ip: handle `curlx_inet_ntop()` error in `Curl_if2ip()`. - dnsd: display `CURLcode` on two errors. Follow-up to 39dec13ec08f8511f22ddc78bb36b40241dee915 #22170 Closes #22229 --- lib/cf-socket.c | 69 ++++++++++++++++++++++--------------------- lib/curlx/inet_ntop.c | 50 ++++++++++--------------------- lib/curlx/inet_ntop.h | 2 +- lib/ftp.c | 8 ++--- lib/if2ip.c | 12 ++++---- lib/urlapi.c | 2 +- lib/vdns/httpsrr.c | 2 +- tests/server/dnsd.c | 15 +++++----- tests/unit/unit1607.c | 4 +-- tests/unit/unit1609.c | 4 +-- tests/unit/unit1961.c | 23 ++++----------- 11 files changed, 81 insertions(+), 110 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index b059a3cfe2e3..1511d6f03615 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -86,14 +86,15 @@ #endif /* retrieves ip address and port from a sockaddr structure. note it calls - * curlx_inet_ntop which sets errno on fail, not SOCKERRNO. + * curlx_inet_ntop() and returns CURLcode. * @unittest 1607 */ -UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port); -UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port) +UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port); +UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port) { + CURLcode result; struct sockaddr_in *si = NULL; #ifdef USE_IPV6 struct sockaddr_in6 *si6 = NULL; @@ -107,17 +108,21 @@ UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, switch(sa->sa_family) { case AF_INET: si = (struct sockaddr_in *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { + result = curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, + MAX_IPADR_LEN); + if(!result) { *port = ntohs(si->sin_port); - return TRUE; + return result; } break; #ifdef USE_IPV6 case AF_INET6: si6 = (struct sockaddr_in6 *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { + result = curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, + MAX_IPADR_LEN); + if(!result) { *port = ntohs(si6->sin6_port); - return TRUE; + return result; } break; #endif @@ -130,16 +135,16 @@ UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, else addr[0] = 0; /* socket with no name */ *port = 0; - return TRUE; + return CURLE_OK; #endif default: + result = CURLE_UNSUPPORTED_PROTOCOL; break; } addr[0] = '\0'; *port = 0; - errno = SOCKEAFNOSUPPORT; - return FALSE; + return result; } static void tcpnodelay(struct Curl_cfilter *cf, @@ -1087,18 +1092,19 @@ static void set_local_ip(struct Curl_cfilter *cf, /* TFTP does not connect, so it cannot get the IP like this */ struct Curl_sockaddr_storage ssloc; curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); - VERBOSE(char buffer[STRERROR_LEN]); memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { + VERBOSE(char buffer[STRERROR_LEN]); VERBOSE(int sockerr = SOCKERRNO); infof(data, "getsockname() failed with errno %d: %s", sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); } - else if(!sockaddr2string((struct sockaddr *)&ssloc, slen, - ctx->ip.local_ip, &ctx->ip.local_port)) { - infof(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, curlx_strerror(errno, buffer, sizeof(buffer))); + else { + CURLcode result = sockaddr2string((struct sockaddr *)&ssloc, slen, + ctx->ip.local_ip, &ctx->ip.local_port); + if(result) + infof(data, "ssloc inet_ntop() failed with %d", (int)result); } } #else @@ -1110,21 +1116,17 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result; /* store remote address and port used in this connection attempt */ ctx->ip.transport = ctx->transport; - if(!sockaddr2string(&ctx->addr.curl_sa_addr, - (curl_socklen_t)ctx->addr.addrlen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { - char buffer[STRERROR_LEN]; - - /* using bare errno instead of SOCKERRNO is safe here, because - sockaddr2string() calls curlx_inet_ntop(), and they both report failures - via errno (even on Windows builds). */ - ctx->sockerr = errno; + result = sockaddr2string(&ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, + ctx->ip.remote_ip, &ctx->ip.remote_port); + if(result) { + ctx->sockerr = SOCKEAFNOSUPPORT; /* malformed address or bug in inet_ntop, try next address */ - failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s", - errno, curlx_strerror(errno, buffer, sizeof(buffer))); + failf(data, "curl_sa_addr inet_ntop() failed with %d", (int)result); return CURLE_FAILED_INIT; } return CURLE_OK; @@ -2156,7 +2158,7 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, { struct cf_socket_ctx *ctx = cf->ctx; #ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; + CURLcode result; struct Curl_sockaddr_storage ssrem; curl_socklen_t plen; @@ -2165,15 +2167,16 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, plen = sizeof(ssrem); memset(&ssrem, 0, plen); if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) { + char buffer[STRERROR_LEN]; int sockerr = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return; } - if(!sockaddr2string((struct sockaddr *)&ssrem, plen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, curlx_strerror(errno, buffer, sizeof(buffer))); + result = sockaddr2string((struct sockaddr *)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port); + if(result) { + failf(data, "ssrem inet_ntop() failed with %d", (int)result); return; } #else diff --git a/lib/curlx/inet_ntop.c b/lib/curlx/inet_ntop.c index 7f6028a79101..ed06314520e5 100644 --- a/lib/curlx/inet_ntop.c +++ b/lib/curlx/inet_ntop.c @@ -48,12 +48,12 @@ /* * Format an IPv4 address, more or less like inet_ntop(). * - * Returns `dst' (as a const) + * Returns CURLcode. * Note: * - uses no static variables * - takes an unsigned char* not an in_addr as input */ -static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) +static CURLcode inet_ntop4(const unsigned char *src, char *dst, size_t size) { char tmp[sizeof("255.255.255.255")]; size_t len; @@ -68,22 +68,16 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) ((int)((unsigned char)src[3])) & 0xff); len = strlen(tmp); - if(len == 0 || len >= size) { -#ifdef USE_WINSOCK - errno = WSAEINVAL; -#else - errno = ENOSPC; -#endif - return NULL; - } + if(len == 0 || len >= size) + return CURLE_TOO_LARGE; curlx_strcopy(dst, size, tmp, len); - return dst; + return CURLE_OK; } /* * Convert IPv6 binary address into presentation (printable) format. */ -static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) +static CURLcode inet_ntop6(const unsigned char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough @@ -152,9 +146,9 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) */ if(i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { - if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { - return NULL; - } + CURLcode result = inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp)); + if(result) + return result; tp += strlen(tp); break; } @@ -181,31 +175,18 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) *tp++ = ':'; /* Check for overflow, copy, and we are done. */ - if((size_t)(tp - tmp) >= size) { -#ifdef USE_WINSOCK - errno = WSAEINVAL; -#else - errno = ENOSPC; -#endif - return NULL; - } - + if((size_t)(tp - tmp) >= size) + return CURLE_TOO_LARGE; curlx_strcopy(dst, size, tmp, tp - tmp); - return dst; + return CURLE_OK; } /* * Convert a network format address to presentation format. * - * Returns pointer to presentation format address (`buf'). - * Returns NULL on error and errno set with the specific - * error, EAFNOSUPPORT or ENOSPC. - * - * On Windows we store the error in the thread errno, not in the Winsock error - * code. This is to avoid losing the actual last Winsock error. When this - * function returns NULL, check errno not SOCKERRNO. + * Copies result to 'buf' and returns CURLcode. */ -char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) +CURLcode curlx_inet_ntop(int af, const void *src, char *buf, size_t size) { switch(af) { case AF_INET: @@ -213,7 +194,6 @@ char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) case AF_INET6: return inet_ntop6((const unsigned char *)src, buf, size); default: - errno = SOCKEAFNOSUPPORT; - return NULL; + return CURLE_UNSUPPORTED_PROTOCOL; } } diff --git a/lib/curlx/inet_ntop.h b/lib/curlx/inet_ntop.h index 8f4c636d6944..f0abeb80b723 100644 --- a/lib/curlx/inet_ntop.h +++ b/lib/curlx/inet_ntop.h @@ -25,6 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size); +CURLcode curlx_inet_ntop(int af, const void *src, char *buf, size_t size); #endif /* HEADER_CURL_INET_NTOP_H */ diff --git a/lib/ftp.c b/lib/ftp.c index 1e17cb346731..3a4859d18846 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1022,7 +1022,7 @@ static CURLcode ftp_port_default_host(struct Curl_easy *data, struct sockaddr_in6 * const sa6 = (void *)sa; #endif char buffer[STRERROR_LEN]; - const char *r; + CURLcode result; *sslenp = sizeof(*ss); if(getsockname(conn->sock[FIRSTSOCKET], sa, sslenp)) { @@ -1033,14 +1033,14 @@ static CURLcode ftp_port_default_host(struct Curl_easy *data, switch(sa->sa_family) { #ifdef USE_IPV6 case AF_INET6: - r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen); + result = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen); break; #endif default: - r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen); + result = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen); break; } - if(!r) + if(result) return CURLE_FTP_PORT_FAILED; *hostp = hbuf; diff --git a/lib/if2ip.c b/lib/if2ip.c index 5ae256e13c39..9a9fbe7691d3 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -112,9 +112,9 @@ if2ip_result_t Curl_if2ip(int af, if(iface->ifa_addr->sa_family == af) { if(curl_strequal(iface->ifa_name, interf)) { void *addr; - const char *ip; char scope[12] = ""; char ipstr[64]; + CURLcode result; #ifdef USE_IPV6 if(af == AF_INET6) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID @@ -155,8 +155,8 @@ if2ip_result_t Curl_if2ip(int af, addr = &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; res = IF2IP_FOUND; - ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr)); - curl_msnprintf(buf, buf_size, "%s%s", ip, scope); + result = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr)); + curl_msnprintf(buf, buf_size, "%s%s", result ? "" : ipstr, scope); break; } } @@ -188,7 +188,7 @@ if2ip_result_t Curl_if2ip(int af, struct sockaddr_in *s; curl_socket_t dummy; size_t len; - const char *r; + CURLcode result; #ifdef USE_IPV6 (void)remote_scope; @@ -228,10 +228,10 @@ if2ip_result_t Curl_if2ip(int af, s = (struct sockaddr_in *)(void *)&req.ifr_addr; memcpy(&in, &s->sin_addr, sizeof(in)); - r = curlx_inet_ntop(s->sin_family, &in, buf, buf_size); + result = curlx_inet_ntop(s->sin_family, &in, buf, buf_size); sclose(dummy); - if(!r) + if(result) return IF2IP_NOT_FOUND; return IF2IP_FOUND; } diff --git a/lib/urlapi.c b/lib/urlapi.c index 1b498e95d0b1..e787773ced1a 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -467,7 +467,7 @@ UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, hostname[hlen] = 0; /* end the address there */ if(curlx_inet_pton(AF_INET6, hostname, dest) != 1) return CURLUE_BAD_IPV6; - if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen + 1)) { + if(!curlx_inet_ntop(AF_INET6, dest, hostname, hlen + 1)) { hlen = strlen(hostname); /* might be shorter now */ hostname[hlen + 1] = 0; } diff --git a/lib/vdns/httpsrr.c b/lib/vdns/httpsrr.c index 29c78a333b9f..98a3c894964c 100644 --- a/lib/vdns/httpsrr.c +++ b/lib/vdns/httpsrr.c @@ -82,7 +82,7 @@ static CURLcode httpsrr_print_addr(struct dynbuf *dyn, CURLcode result = CURLE_OK; for(i = 0; (i < (total_len / alen)) && !result; ++i) { - if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) + if(curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) result = curlx_dyn_add(dyn, ""); else result = curlx_dyn_addf(dyn, "%s%s", sep, buf); diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 62cbb8d9cfa7..c2d01c0aaaa4 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -606,6 +606,7 @@ static int dnsd_make_answer(struct blob *blob, int query_id, }; uint16_t ancount = 0; unsigned char rcode = 0; + CURLcode result; /* read once per incoming query, which is probably more than one per test case */ @@ -656,12 +657,11 @@ static int dnsd_make_answer(struct blob *blob, int query_id, case QTYPE_A: for(a = 0; !rcode && (a < ancount_a); a++) { const unsigned char *store = ipv4_pref; - const char *ip; if(add_answer(blob, store, sizeof(ipv4_pref), QTYPE_A)) return 1; - ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)); - logmsg("[%d] response A (%x) '%s'", query_id, (unsigned int)QTYPE_A, - ip ? ip : "(null)"); + result = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)); + logmsg("[%d] response A (%x) '%s' (%d)", query_id, + (unsigned int)QTYPE_A, result ? "?" : addrbuf, (int)result); } if(!ancount_a) logmsg("[%d] response A empty", query_id); @@ -669,12 +669,11 @@ static int dnsd_make_answer(struct blob *blob, int query_id, case QTYPE_AAAA: for(a = 0; !rcode && (a < ancount_aaaa); a++) { const unsigned char *store = ipv6_pref; - const char *ip; if(add_answer(blob, store, sizeof(ipv6_pref), QTYPE_AAAA)) return 1; - ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)); - logmsg("[%d] response AAAA (%x) '%s'", query_id, - (unsigned int)QTYPE_AAAA, ip ? ip : "(null)"); + result = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)); + logmsg("[%d] response AAAA (%x) '%s' (%d)", query_id, + (unsigned int)QTYPE_AAAA, result ? "?" : addrbuf, (int)result); } if(!ancount_aaaa) logmsg("[%d] response AAAA empty", query_id); diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index 2b556213405d..72df3808ca67 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -147,8 +147,8 @@ static CURLcode test_unit1607(const char *arg) if(tests[i].address[j] == &skip) continue; - if(addr && !sockaddr2string(addr->ai_addr, addr->ai_addrlen, - ipaddress, &port)) { + if(addr && sockaddr2string(addr->ai_addr, addr->ai_addrlen, + ipaddress, &port)) { curl_mfprintf(stderr, "%s:%d tests[%zu] failed. " "getaddressinfo failed.\n", __FILE__, __LINE__, i); diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c index d7970a5cc6fe..076bf21499c5 100644 --- a/tests/unit/unit1609.c +++ b/tests/unit/unit1609.c @@ -146,8 +146,8 @@ static CURLcode test_unit1609(const char *arg) if(!addr && !tests[i].address[j]) break; - if(addr && !sockaddr2string(addr->ai_addr, addr->ai_addrlen, - ipaddress, &port)) { + if(addr && sockaddr2string(addr->ai_addr, addr->ai_addrlen, + ipaddress, &port)) { curl_mfprintf(stderr, "%s:%d tests[%zu] failed. Curl_addr2string failed.\n", __FILE__, __LINE__, i); diff --git a/tests/unit/unit1961.c b/tests/unit/unit1961.c index 338eeb176b43..1aa28a25bf9f 100644 --- a/tests/unit/unit1961.c +++ b/tests/unit/unit1961.c @@ -48,8 +48,6 @@ static int test_ntop(void) char ipv4res[sizeof("255.255.255.255")]; unsigned char ipv6a[26]; unsigned char ipv4a[5]; - const char *ipv6ptr = 0; - const char *ipv4ptr = 0; ipv4res[0] = '\0'; ipv4a[0] = 0xc0; @@ -57,12 +55,9 @@ static int test_ntop(void) ipv4a[2] = 0x64; ipv4a[3] = 0x01; ipv4a[4] = 0x01; - ipv4ptr = curlx_inet_ntop(AF_INET, ipv4a, ipv4res, sizeof(ipv4res)); - if(!ipv4ptr) + if(curlx_inet_ntop(AF_INET, ipv4a, ipv4res, sizeof(ipv4res))) return 1; /* fail */ - if(ipv4ptr != ipv4res) - return 1; /* fail */ - if(!ipv4ptr[0]) + if(!ipv4res[0]) return 1; /* fail */ if(memcmp(ipv4res, "192.168.100.1", 13)) return 1; /* fail */ @@ -80,12 +75,9 @@ static int test_ntop(void) ipv6a[14] = 0x76; ipv6a[15] = 0xc8; ipv6a[25] = 0x01; - ipv6ptr = curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); - if(!ipv6ptr) - return 1; /* fail */ - if(ipv6ptr != ipv6res) + if(curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res))) return 1; /* fail */ - if(!ipv6ptr[0]) + if(!ipv6res[0]) return 1; /* fail */ if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) return 1; /* fail */ @@ -96,12 +88,9 @@ static int test_ntop(void) ipv6a[13] = 0x0; ipv6a[14] = 0x0; ipv6a[15] = 0x01; - ipv6ptr = curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res)); - if(!ipv6ptr) - return 1; /* fail */ - if(ipv6ptr != ipv6res) + if(curlx_inet_ntop(AF_INET6, ipv6a, ipv6res, sizeof(ipv6res))) return 1; /* fail */ - if(!ipv6ptr[0]) + if(!ipv6res[0]) return 1; /* fail */ if(memcmp(ipv6res, "::127.0.0.1", 11)) return 1; /* fail */ From a01a24deaf7dee72e2a22f0d0f0a1e9db08f92aa Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 17 Aug 2026 18:43:28 +0200 Subject: [PATCH 0916/1062] curlx_inet_pton: drop setting `errno` on error Follow-up to bb406386d9e088873f5f640c55e14a69390abd01 #22229 Closes #22607 --- lib/curlx/inet_pton.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/curlx/inet_pton.c b/lib/curlx/inet_pton.c index b3e806602a01..23e76725fe68 100644 --- a/lib/curlx/inet_pton.c +++ b/lib/curlx/inet_pton.c @@ -196,10 +196,6 @@ static int inet_pton6(const char *src, unsigned char *dst) * 0 if the address was not valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * - * On Windows we store the error in the thread errno, not in the Winsock error - * code. This is to avoid losing the actual last Winsock error. When this - * function returns NULL, check errno not SOCKERRNO. - * * author: * Paul Vixie, 1996. */ @@ -211,7 +207,6 @@ int curlx_inet_pton(int af, const char *src, void *dst) case AF_INET6: return inet_pton6(src, (unsigned char *)dst); default: - errno = SOCKEAFNOSUPPORT; return -1; } /* NOTREACHED */ From 406edd036ae560dd1c37da327f213b2c1a82b654 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 15 Aug 2026 17:14:41 +0200 Subject: [PATCH 0917/1062] filter: change time reporting Replace the QUERY filter methods for connect and appconnect time with a new control CF_CTRL_REPORT_STATS that is triggered when a connect ends (successful or not). Filters in the connection can then report their statistics. Socket and TLS filters do this only once. Subsequent CF_CTRL_REPORT_STATS will do nothing. This prevents timers to be reported twice in STARTTLS scenarios. Fixes #22587 (again) Closes #22596 --- lib/cf-https-connect.c | 54 +++++++++++------------------- lib/cf-ip-happy.c | 30 ----------------- lib/cf-socket.c | 45 +++++++++++++++---------- lib/cfilters.c | 20 +++++++++++ lib/cfilters.h | 17 ++++++++-- lib/connect.c | 23 ++++--------- lib/vquic/cf-ngtcp2-cmn.c | 66 ++++++++++++++++++++++++++++++++++++- lib/vquic/cf-ngtcp2-cmn.h | 9 +++++ lib/vquic/cf-ngtcp2-proxy.c | 2 +- lib/vquic/cf-ngtcp2.c | 28 ++-------------- lib/vquic/cf-quiche.c | 21 +++++------- lib/vquic/vquic.c | 1 + lib/vtls/vtls.c | 19 +++++++---- lib/vtls/vtls_int.h | 1 + 14 files changed, 187 insertions(+), 149 deletions(-) diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index ed574ea9b66a..e77badec1de8 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -652,26 +652,6 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf, return FALSE; } -static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query) -{ - struct cf_hc_ctx *ctx = cf->ctx; - struct curltime t, tmax; - size_t i; - - memset(&tmax, 0, sizeof(tmax)); - for(i = 0; i < ctx->baller_count; i++) { - struct Curl_cfilter *cfb = ctx->ballers[i].cf; - memset(&t, 0, sizeof(t)); - if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) - tmax = t; - } - } - return tmax; -} - static CURLcode cf_hc_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) @@ -681,16 +661,6 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, if(!cf->connected) { switch(query) { - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); - return CURLE_OK; - } case CF_QUERY_NEED_FLUSH: { for(i = 0; i < ctx->baller_count; i++) if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) { @@ -717,12 +687,26 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, size_t i; if(!cf->connected) { - for(i = 0; i < ctx->baller_count; i++) { - result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2); - if(result && (result != CURLE_AGAIN)) - goto out; + switch(event) { + case CF_CTRL_REPORT_STATS: + for(i = 0; i < ctx->baller_count; i++) { + /* Make the first baller that connected at network level report */ + if(Curl_conn_cf_is_ip_connected(ctx->ballers[i].cf, data)) { + Curl_conn_cf_cntrl(ctx->ballers[i].cf, data, TRUE, + event, arg1, arg2); + break; + } + } + break; + default: + for(i = 0; i < ctx->baller_count; i++) { + result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2); + if(result && (result != CURLE_AGAIN)) + goto out; + } + result = CURLE_OK; + break; } - result = CURLE_OK; } out: return result; diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 3f52ee78acc6..fa7104ffca22 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -645,24 +645,6 @@ static bool cf_ip_ballers_pending(struct cf_ip_ballers *bs, return FALSE; } -static struct curltime cf_ip_ballers_max_time(struct cf_ip_ballers *bs, - struct Curl_easy *data, - int query) -{ - struct curltime t, tmax; - struct cf_ip_attempt *a; - - memset(&tmax, 0, sizeof(tmax)); - for(a = bs->running; a; a = a->next) { - memset(&t, 0, sizeof(t)); - if(a->cf && !a->cf->cft->query(a->cf, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) - tmax = t; - } - } - return tmax; -} - static int cf_ip_ballers_min_reply_ms(struct cf_ip_ballers *bs, struct Curl_easy *data) { @@ -940,18 +922,6 @@ static CURLcode cf_ip_happy_query(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1); return CURLE_OK; } - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - *when = cf_ip_ballers_max_time(&ctx->ballers, data, - CF_QUERY_TIMER_CONNECT); - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - *when = cf_ip_ballers_max_time(&ctx->ballers, data, - CF_QUERY_TIMER_APPCONNECT); - return CURLE_OK; - } default: break; } diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 1511d6f03615..c72c30f7b72c 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -988,6 +988,7 @@ struct cf_socket_ctx { BIT(accepted); /* socket was accepted, not connected */ BIT(sock_connected); /* socket is "connected", e.g. in UDP */ BIT(active); + BIT(stats_reported); }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, @@ -1729,6 +1730,26 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, case CF_CTRL_FORGET_SOCKET: ctx->sock = CURL_SOCKET_BAD; break; + case CF_CTRL_REPORT_STATS: + if(cf->connected && !ctx->stats_reported) { + struct curltime *ts = NULL; + switch(ctx->transport) { + case TRNSPRT_UDP: + case TRNSPRT_QUIC: + /* Since UDP connected sockets work different from TCP, we use the + * time of the first byte from the peer as the "connect" time. */ + if(ctx->got_first_byte) + ts = &ctx->first_byte_at; + break; + default: + ts = &ctx->connected_at; + break; + } + if(ts) { + Curl_pgrsTimeWas(data, TIMER_CONNECT, *ts); + ctx->stats_reported = TRUE; + } + } } return CURLE_OK; } @@ -1799,24 +1820,6 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, else *pres1 = -1; return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - switch(ctx->transport) { - case TRNSPRT_UDP: - case TRNSPRT_QUIC: - /* Since UDP connected sockets work different from TCP, we use the - * time of the first byte from the peer as the "connect" time. */ - if(ctx->got_first_byte) { - *when = ctx->first_byte_at; - break; - } - FALLTHROUGH(); - default: - *when = ctx->connected_at; - break; - } - return CURLE_OK; - } case CF_QUERY_IP_INFO: #ifdef USE_IPV6 *pres1 = (ctx->addr.family == AF_INET6); @@ -1825,6 +1828,12 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, #endif *(struct ip_quadruple *)pres2 = ctx->ip; return CURLE_OK; + case CF_QUERY_REALLY_CONNECTED: + if(cf->cft != &Curl_cft_udp) + *pres1 = cf->connected; + else + *pres1 = ctx->got_first_byte; + return CURLE_OK; default: break; } diff --git a/lib/cfilters.c b/lib/cfilters.c index ecfab014a0bf..8875473fb34f 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -462,12 +462,32 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, return result; } +bool Curl_conn_cf_is_ip_connected(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf) { + int value = 0; + if(!cf->cft->query(cf, data, CF_QUERY_REALLY_CONNECTED, &value, NULL)) + return !!value; + } + return FALSE; +} + void Curl_conn_cntrl_update_info(struct Curl_easy *data, struct connectdata *conn) { cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); } +void Curl_conn_cntrl_report_stats(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + if((unsigned)sockindex < CURL_ARRAYSIZE(conn->cfilter)) + (void)Curl_conn_cf_cntrl(conn->cfilter[sockindex], data, TRUE, + CF_CTRL_REPORT_STATS, 0, NULL); +} + void Curl_conn_remove_setup_filters(struct Curl_easy *data, int8_t sockindex) { diff --git a/lib/cfilters.h b/lib/cfilters.h index b977039c8521..979275b72979 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -121,6 +121,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, #define CF_CTRL_CONN_INFO_UPDATE (256 + 0) /* 0 NULL ignored */ #define CF_CTRL_FORGET_SOCKET (256 + 1) /* 0 NULL ignored */ #define CF_CTRL_FLUSH (256 + 2) /* 0 NULL first fail */ +#define CF_CTRL_REPORT_STATS (256 + 3) /* 0 NULL ignored */ /** * Handle event/control for the filter. @@ -156,13 +157,17 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, null-terminated string or NULL if none selected/handshake not done. Implemented by filter types CF_TYPE_SSL or CF_TYPE_IP_CONNECT. + * - CF_QUERY_REALLY_CONNECTED: implemented in socket filters to return + * if a reply from a server has really arrived. For + * non-UDP sockets this is TRUE when the socket became + * writable. For UDP sockets, this is TRUE when the + * first byte from the peer was received. */ /* query res1 res2 */ #define CF_QUERY_MAX_CONCURRENT 1 /* number - */ #define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */ #define CF_QUERY_SOCKET 3 /* - curl_socket_t */ -#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ -#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ +/* unused 4 + 5 */ #define CF_QUERY_STREAM_ERROR 6 /* error code - */ #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ @@ -175,6 +180,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_SSL_CTX_INFO 13 /* - struct curl_tlssessioninfo * */ #define CF_QUERY_TRANSPORT 14 /* TRNSPRT_* - * */ #define CF_QUERY_ALPN_NEGOTIATED 15 /* - const char * */ +#define CF_QUERY_REALLY_CONNECTED 16 /* TRUE/FALSE - */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -343,6 +349,9 @@ CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, struct Curl_easy *data); +bool Curl_conn_cf_is_ip_connected(struct Curl_cfilter *cf, + struct Curl_easy *data); + unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -422,6 +431,10 @@ const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, void Curl_conn_cntrl_update_info(struct Curl_easy *data, struct connectdata *conn); +void Curl_conn_cntrl_report_stats(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + void Curl_conn_remove_setup_filters(struct Curl_easy *data, int8_t sockindex); diff --git a/lib/connect.c b/lib/connect.c index df7a02463d93..e669f7630d38 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -306,22 +306,11 @@ static CURLcode conn_connect_trace(struct Curl_easy *data, /** * Update connection statistics */ -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void conn_report_stats(struct Curl_easy *data, int sockindex) { - if(cf && (cf->sockindex == FIRSTSOCKET)) { - struct curltime connected; - struct curltime appconnected; - - memset(&connected, 0, sizeof(connected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); - if(connected.tv_sec || connected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); - - memset(&appconnected, 0, sizeof(appconnected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); - if(appconnected.tv_sec || appconnected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + /* We do gather stats for the second socket...yet */ + if(sockindex == FIRSTSOCKET) { + Curl_conn_cntrl_report_stats(data, data->conn, sockindex); } } @@ -384,7 +373,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, * persist information at the connection. E.g. cf-socket sets the * socket and ip related information. */ Curl_conn_cntrl_update_info(data, data->conn); - conn_report_connect_stats(cf, data); + conn_report_stats(data, sockindex); data->conn->lastupkeep = *Curl_pgrs_now(data); VERBOSE(result = conn_connect_trace(data, cf)); VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); @@ -396,7 +385,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", (int)result); VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); - conn_report_connect_stats(cf, data); + conn_report_stats(data, sockindex); goto out; } diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 771b77b43431..6446581ebf1a 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -267,8 +267,11 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) /* In case of earlydata, where we simulate being connected, update * the handshake time when we really did connect */ - if(ctx->use_earlydata) + if(ctx->use_earlydata && !ctx->stats_reported && + !(cf->cft->flags & CF_TYPE_PROXY)) { Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + ctx->stats_reported = TRUE; + } if(ctx->use_earlydata) { #if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) ctx->earlydata_accepted = @@ -2008,4 +2011,65 @@ CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, return CURLE_OK; } + +CURLcode Curl_cf_ngtcp2_cmn_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_CONNECT_REPLY_MS: + if((ctx->q.sockfd != CURL_SOCKET_BAD) && ctx->q.got_first_byte) { + timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, + &ctx->started_at); + *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; + return CURLE_OK; + } + break; + case CF_QUERY_REALLY_CONNECTED: + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + *pres1 = ctx->q.got_first_byte; + return CURLE_OK; + } + break; + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +CURLcode Curl_cf_ngtcp2_cmn_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_REPORT_STATS: + if(cf->connected && !ctx->stats_reported) { + if((cf->cft->flags & CF_TYPE_PROXY) && + (ctx->q.sockfd != CURL_SOCKET_BAD) && ctx->q.got_first_byte) { + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->q.first_byte_at); + ctx->stats_reported = TRUE; + } + else if(ctx->handshake_at.tv_sec || ctx->handshake_at.tv_usec) { + if(ctx->q.sockfd != CURL_SOCKET_BAD) + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->q.first_byte_at); + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + ctx->stats_reported = TRUE; + } + } + break; + default: + break; + } + return result; +} + #endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ diff --git a/lib/vquic/cf-ngtcp2-cmn.h b/lib/vquic/cf-ngtcp2-cmn.h index 88554edfb6d3..f4dd9158ec90 100644 --- a/lib/vquic/cf-ngtcp2-cmn.h +++ b/lib/vquic/cf-ngtcp2-cmn.h @@ -142,6 +142,7 @@ struct cf_ngtcp2_ctx { BIT(use_earlydata); /* Using 0RTT data */ BIT(earlydata_accepted); /* 0RTT was accepted by server */ BIT(shutdown_started); /* queued shutdown packets */ + BIT(stats_reported); /* connect statistics reported */ }; /* How to access `call_data` from a cf_ngtcp2 filter */ @@ -234,6 +235,14 @@ bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending); +CURLcode Curl_cf_ngtcp2_cmn_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); + +CURLcode Curl_cf_ngtcp2_cmn_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2); + #endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ #endif /* HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H */ diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index f5c967c5b1e4..5e85c805948b 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -1229,7 +1229,7 @@ struct Curl_cftype Curl_cft_h3_proxy = { Curl_cf_def_cntrl, Curl_cf_ngtcp2_cmn_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + Curl_cf_ngtcp2_cmn_query, }; CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index e97a3d827140..f6fd6c55fc11 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -911,8 +911,6 @@ static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - (void)arg1; - (void)arg2; switch(event) { case CF_CTRL_DATA_SETUP: break; @@ -939,6 +937,7 @@ static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, } break; default: + result = Curl_cf_ngtcp2_cmn_cntrl(cf, data, event, arg1, arg2); break; } CF_DATA_RESTORE(cf, save); @@ -1035,27 +1034,6 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, CF_DATA_RESTORE(cf, save); return CURLE_OK; } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->q.got_first_byte) { - timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, - &ctx->started_at); - *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->q.got_first_byte) - *when = ctx->q.first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } case CF_QUERY_HTTP_VERSION: *pres1 = 30; return CURLE_OK; @@ -1076,9 +1054,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, default: break; } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; + return Curl_cf_ngtcp2_cmn_query(cf, data, query, pres1, pres2); } struct Curl_cftype Curl_cft_http3 = { diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index fa3367549731..52dad536b6ac 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -94,6 +94,7 @@ struct cf_quiche_ctx { BIT(goaway); /* got GOAWAY from server */ BIT(x509_store_setup); /* if x509 store has been set up */ BIT(shutdown_started); /* queued shutdown packets */ + BIT(stats_reported); /* connect statistics reported */ }; #ifdef DEBUG_QUICHE @@ -1254,6 +1255,14 @@ static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf, Curl_conn_set_multiplex(cf->conn); } break; + case CF_CTRL_REPORT_STATS: + if(cf->connected && !ctx->stats_reported && + (ctx->handshake_at.tv_sec || ctx->handshake_at.tv_usec)) { + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->q.first_byte_at); + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + ctx->stats_reported = TRUE; + } + break; default: break; } @@ -1550,18 +1559,6 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, else *pres1 = -1; return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->q.got_first_byte) - *when = ctx->q.first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } case CF_QUERY_HTTP_VERSION: *pres1 = 30; return CURLE_OK; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 2d2484957b89..eb13abad73e0 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "urldata.h" #include "vquic/vquic.h" +#include "vtls/vtls.h" #include "curl_trc.h" diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 3782238636d2..c1504fad3b84 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1064,7 +1064,10 @@ static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf, result = ssl_cf_connect(cf, data, done); if(!result && *done) { - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); + if(!connssl->stats_reported && (cf->cft == &Curl_cft_ssl)) { + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); + connssl->stats_reported = TRUE; + } switch(connssl->earlydata_state) { case ssl_earlydata_none: break; @@ -1232,12 +1235,6 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; switch(query) { - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected && !Curl_ssl_cf_is_proxy(cf)) - *when = connssl->handshake_done; - return CURLE_OK; - } case CF_QUERY_SSL_INFO: case CF_QUERY_SSL_CTX_INFO: if(!Curl_ssl_cf_is_proxy(cf)) { @@ -1287,6 +1284,14 @@ static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, cf->conn->httpversion_seen = 30; } break; + case CF_CTRL_REPORT_STATS: + if(cf->connected && !connssl->stats_reported && + (cf->cft == &Curl_cft_ssl) && + (connssl->handshake_done.tv_sec || connssl->handshake_done.tv_usec)) { + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); + connssl->stats_reported = TRUE; + } + break; } return CURLE_OK; } diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 330d8427ca70..7c1881b8d847 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -132,6 +132,7 @@ struct ssl_connect_data { BIT(peer_closed); /* peer has closed connection */ BIT(prefs_checked); /* SSL preferences have been checked */ BIT(input_pending); /* data for SSL_read() may be available */ + BIT(stats_reported); /* connect times have been reported */ }; /* Definitions for SSL Implementations */ From 05ddf5511ac765c8fc4eb44507ab7c585116f665 Mon Sep 17 00:00:00 2001 From: Anton Karpov Date: Mon, 10 Aug 2026 00:48:09 +0300 Subject: [PATCH 0918/1062] headers: name the arguments the way the definitions name them Eleven arguments are named one way in the header and another in the definition. the compiler only checks types so nothing is broken, but the header is what people read first, and then the name changes under them in the source: ``` curl_dbg_malloc size -> wantedsize curl_dbg_calloc n, size -> wanted_elements, wanted_size curl_dbg_realloc size -> wantedsize curl_dbg_strdup src -> source r_freeaddrinfo res -> cahead my_get_line fp, db -> input, buf curl_slist_append_ccsid l -> list curl_version_info_ccsid cid -> ccsid curl_easy_setopt_ccsid curl -> easy ``` Follow-up to df6014894b789768b139fff27e8aed9dbc46ed79 #20794 Closes #22550 --- lib/curl_setup.h | 10 ++++++---- lib/fake_addrinfo.h | 2 +- projects/OS400/ccsidcurl.c | 14 +++++++------- projects/OS400/ccsidcurl.h | 7 ++++--- src/tool_parsecfg.h | 2 +- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/curl_setup.h b/lib/curl_setup.h index d43baf3b3870..5924d54c05b3 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1373,13 +1373,15 @@ extern FILE *curl_dbg_logfile; /* memory functions */ CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source); CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) - void *curl_dbg_malloc(size_t size, int line, const char *source); + void *curl_dbg_malloc(size_t wantedsize, int line, const char *source); CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) - void *curl_dbg_calloc(size_t n, size_t size, int line, const char *source); + void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, + int line, const char *source); CURL_EXTERN ALLOC_SIZE(2) - void *curl_dbg_realloc(void *ptr, size_t size, int line, const char *source); + void *curl_dbg_realloc(void *ptr, size_t wantedsize, int line, + const char *source); CURL_EXTERN ALLOC_FUNC - char *curl_dbg_strdup(const char *str, int line, const char *src); + char *curl_dbg_strdup(const char *str, int line, const char *source); #if defined(_WIN32) && defined(UNICODE) CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source); diff --git a/lib/fake_addrinfo.h b/lib/fake_addrinfo.h index 07d5b6da8cdd..69579da2e51f 100644 --- a/lib/fake_addrinfo.h +++ b/lib/fake_addrinfo.h @@ -43,7 +43,7 @@ # include #endif -void r_freeaddrinfo(struct addrinfo *res); +void r_freeaddrinfo(struct addrinfo *cahead); int r_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index 1dcf9543f9c5..d6ad035b2996 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -1018,7 +1018,7 @@ int curl_formget_ccsid(struct curl_httppost *form, void *arg, return curl_formget(form, (void *)&lcfc, Curl_formget_callback_ccsid); } -CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) +CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) { CURLcode result; va_list arg; @@ -1026,7 +1026,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) char *cp = NULL; unsigned int ccsid; curl_off_t pfsize; - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; va_start(arg, tag); @@ -1143,7 +1143,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) } } - result = curl_easy_setopt(easy, tag, s); + result = curl_easy_setopt(curl, tag, s); free(s); break; @@ -1157,7 +1157,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) pfsize = data->set.postfieldsize; if(!s || !pfsize || ccsid == NOCONV_CCSID || ccsid == ASCII_CCSID) { - result = curl_easy_setopt(easy, CURLOPT_COPYPOSTFIELDS, s); + result = curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, s); break; } @@ -1204,7 +1204,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) cp = NULL; } - result = curl_easy_setopt(easy, CURLOPT_POSTFIELDS, s); + result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s); data->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ break; @@ -1241,12 +1241,12 @@ CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) blob.flags = bp->flags | CURL_BLOB_COPY; bp = &blob; } - result = curl_easy_setopt(easy, tag, bp); + result = curl_easy_setopt(curl, tag, bp); break; } FALLTHROUGH(); case CURLOPT_ERRORBUFFER: /* This is an output buffer. */ - result = Curl_vsetopt(easy, tag, arg); + result = Curl_vsetopt(curl, tag, arg); break; } diff --git a/projects/OS400/ccsidcurl.h b/projects/OS400/ccsidcurl.h index f9e667a9e41c..f38e57967c2c 100644 --- a/projects/OS400/ccsidcurl.h +++ b/projects/OS400/ccsidcurl.h @@ -37,13 +37,14 @@ CURL_EXTERN char *curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, int *outlength, unsigned int sccsid, unsigned int dccsid); -CURL_EXTERN struct curl_slist *curl_slist_append_ccsid(struct curl_slist *l, +CURL_EXTERN struct curl_slist *curl_slist_append_ccsid(struct curl_slist *list, const char *data, unsigned int ccsid); CURL_EXTERN time_t curl_getdate_ccsid(const char *p, const time_t *unused, unsigned int ccsid); -CURL_EXTERN curl_version_info_data *curl_version_info_ccsid(CURLversion stamp, - unsigned int cid); +CURL_EXTERN curl_version_info_data *curl_version_info_ccsid( + CURLversion stamp, + unsigned int ccsid); CURL_EXTERN const char *curl_easy_strerror_ccsid(CURLcode error, unsigned int ccsid); CURL_EXTERN const char *curl_share_strerror_ccsid(CURLSHcode error, diff --git a/src/tool_parsecfg.h b/src/tool_parsecfg.h index 860b9df38eda..54e826fd1061 100644 --- a/src/tool_parsecfg.h +++ b/src/tool_parsecfg.h @@ -29,6 +29,6 @@ #define CONFIG_MAX_LEVELS 5 ParameterError parseconfig(const char *filename, int max_recursive, char **resolved); -bool my_get_line(FILE *fp, struct dynbuf *db, bool *error); +bool my_get_line(FILE *input, struct dynbuf *buf, bool *error); #endif /* HEADER_CURL_TOOL_PARSECFG_H */ From 38c5ff6bfa67962b4f1724fd38c1526b2de67297 Mon Sep 17 00:00:00 2001 From: Samuel Henrique Date: Mon, 17 Aug 2026 22:21:27 -0700 Subject: [PATCH 0919/1062] tests: fix Content-Length mismatch in test 2064 Closes #22611 --- tests/data/test2064 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test2064 b/tests/data/test2064 index d3897535c600..0ab4048dcc74 100644 --- a/tests/data/test2064 +++ b/tests/data/test2064 @@ -9,7 +9,7 @@ HTTP Digest auth # Server-side - + HTTP/1.1 401 Authorization Required Server: Apache/1.3.27 (Darwin) PHP/4.1.2 WWW-Authenticate: Digest realm="testrealm", nonce="2053604145", algorithm="SHA-256" From 1d8f54823e2d0b1e4f071059ed0f14b03ad626a3 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 17 Aug 2026 15:45:01 +0200 Subject: [PATCH 0920/1062] Happy Eyeballing v3: resolution delay of 25ms After telemetry data from Mozilla reported the P99 DNS resolution differences between A and AAAA responses to by 21ms, reduce curl's resolution delay to 25ms. What could possibly go wrong? Closes #22612 --- lib/vdns/cf-dns.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index a7ab017bc482..5fb2ceb224ba 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -36,7 +36,9 @@ #include "vdns/httpsrr.h" #include "curlx/strparse.h" -#define CURL_HE_AAAA_AWAIT_MS 50 +/* Max time to wait for AAAA before connecting sub-filters, e.g. + * letting cf-ip-happy.c do its work. */ +#define CURL_HE_AAAA_AWAIT_MS 25 struct cf_dns_ctx { struct Curl_dns_entry *dns; @@ -320,6 +322,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, cf_dns_report(cf, data, ctx->dns); } + /* Delay connection sub-filters when we are still waiting for AAAA */ if(!cf_dns_ready_to_connect(cf, data)) { return CURLE_OK; } From b729003d837a3f0ab4a7606494724a490ba9b7ea Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Aug 2026 11:17:20 +0200 Subject: [PATCH 0921/1062] TODO: remove "Support MQTTS" implemented Follow-up to 6c31df453b02597e90c27c2e0bc69bb114105005 --- docs/TODO.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 6829910101e8..aecf43c4a42d 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -963,8 +963,6 @@ See [curl issue 4477](https://github.com/curl/curl/issues/4477) The rate-limiting logic is done in the PERFORMING state in multi.c but MQTT is not (yet) implemented to use that. -## Support MQTTS - ## Handle network blocks Running test suite with `CURL_DBG_SOCK_WBLOCK=90 ./runtests.pl -a mqtt` makes From b8b1dc0a0d44e565054382d3eced64713992333e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 18 Aug 2026 11:06:35 +0200 Subject: [PATCH 0922/1062] config-win32.h: drop UWP, c-ares, simplify more UWP requires VS2015+. Project files have no support for c-ares. Drop redundant guard, merge `#if` blocks. Follow-up to 1db93bdb7c1943132b13543539aaaf4866f31419 #22595 Closes #22613 --- lib/config-win32.h | 93 +++++++++------------------------------------- 1 file changed, 17 insertions(+), 76 deletions(-) diff --git a/lib/config-win32.h b/lib/config-win32.h index 30908bd0670a..42387b2c093e 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -24,40 +24,26 @@ * ***************************************************************************/ -/* ======================================================================== */ /* Handcrafted config file for building via Visual Studio IDE Project Files */ -/* ======================================================================== */ #if !defined(_MSC_VER) || _MSC_VER > 1800 #error This manual configuration requires MSVC 2010-2013 (IDE Project builds) #endif -/* ---------------------------------------------------------------- */ -/* HEADER FILES */ -/* ---------------------------------------------------------------- */ +/* + * Headers and functions + */ #define HAVE_FCNTL_H 1 #define HAVE_IO_H 1 #define HAVE_LOCALE_H 1 #if _MSC_VER >= 1800 #define HAVE_STDBOOL_H 1 +#define HAVE_BOOL_T 1 #endif #define HAVE_SYS_TYPES_H 1 #define HAVE_SYS_UTIME_H 1 -/* ---------------------------------------------------------------- */ -/* OTHER HEADER INFO */ -/* ---------------------------------------------------------------- */ - -/* Define to 1 if bool is an available type. */ -#if _MSC_VER >= 1800 -#define HAVE_BOOL_T 1 -#endif - -/* ---------------------------------------------------------------- */ -/* FUNCTIONS */ -/* ---------------------------------------------------------------- */ - #define HAVE_CLOSESOCKET 1 #define HAVE_FREEADDRINFO 1 #define HAVE_GETADDRINFO 1 @@ -84,30 +70,18 @@ #define SEND_TYPE_RETV int #define HAVE_SIGNAL 1 -/* ---------------------------------------------------------------- */ -/* TYPEDEF REPLACEMENTS */ -/* ---------------------------------------------------------------- */ - -/* Define if ssize_t is not an available 'typedefed' type. */ -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -#ifdef _WIN64 -# define ssize_t __int64 -#else -# define ssize_t int -#endif -#endif - -/* ---------------------------------------------------------------- */ -/* TYPE SIZES */ -/* ---------------------------------------------------------------- */ +/* + * Types and sizes + */ #define SIZEOF_INT 4 #define SIZEOF_LONG 4 #ifdef _WIN64 # define SIZEOF_SIZE_T 8 +# define ssize_t __int64 #else # define SIZEOF_SIZE_T 4 +# define ssize_t int #endif #define SIZEOF_CURL_OFF_T 8 /* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */ @@ -118,65 +92,32 @@ #endif #define SIZEOF_OFF_T 4 -/* ---------------------------------------------------------------- */ -/* STRUCT RELATED */ -/* ---------------------------------------------------------------- */ - -/* Define if you have struct sockaddr_storage. */ #define HAVE_STRUCT_SOCKADDR_STORAGE 1 - -/* Define if you have struct timeval. */ #define HAVE_STRUCT_TIMEVAL 1 - -/* Define if struct sockaddr_in6 has the sin6_scope_id member. */ #define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 -/* ---------------------------------------------------------------- */ -/* DNS RESOLVER SPECIALTY */ -/* ---------------------------------------------------------------- */ - /* - * Undefine both USE_ARES and USE_RESOLV_THREADED for synchronous DNS. + * Additional definitions */ /* Default define to enable threaded asynchronous DNS lookups. */ -#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \ - !defined(USE_RESOLV_THREADED) +#if !defined(USE_RESOLV_THREADED) && !defined(USE_SYNC_DNS) # define USE_RESOLV_THREADED 1 #endif -#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) -# error "Only one DNS lookup specialty may be defined at most" -#endif - -/* ---------------------------------------------------------------- */ -/* LDAP SUPPORT */ -/* ---------------------------------------------------------------- */ - -#ifndef CURL_WINDOWS_UWP -#define HAVE_LDAP_SSL 1 +#define HAVE_LDAP_SSL 1 #define USE_WIN32_LDAP 1 +#define USE_WIN32_CRYPTO 1 +#define USE_UNIX_SOCKETS 1 -/* Define to use the Windows crypto library. */ -#define USE_WIN32_CRYPTO -#endif /* CURL_WINDOWS_UWP */ - -/* Define to use Unix sockets. */ -#define USE_UNIX_SOCKETS - -/* ---------------------------------------------------------------- */ -/* ADDITIONAL DEFINITIONS */ -/* ---------------------------------------------------------------- */ - -/* Define cpu-machine-OS */ #ifndef CURL_OS -# ifdef _M_IX86 /* x86 */ +# ifdef _M_IX86 # define CURL_OS "i386-pc-win32" -# elif defined(_M_X64) /* x86_64 */ +# elif defined(_M_X64) # define CURL_OS "x86_64-pc-win32" # else # define CURL_OS "unknown-pc-win32" # endif -#endif /* !CURL_OS */ +#endif #endif /* HEADER_CURL_CONFIG_WIN32_H */ From 64ea25cfe6e65c450ffc085adcaba8bb2db2fcc1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Aug 2026 11:22:30 +0200 Subject: [PATCH 0923/1062] KNOWN_BUGS: remove old entries and NTLM related - The ancient Negotiate ones are just outdated - NTLM is going away, no need to list the bugs anymore Closes #22615 --- docs/KNOWN_BUGS.md | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index 59f11de0b5cb..6d98f26957d4 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -216,19 +216,6 @@ libcurl fails to build with MIT Kerberos for Windows (`KfW`) due to its library header files exporting symbols/macros that should be kept private to the library. -## NTLM in system context uses wrong name - -NTLM authentication using SSPI (on Windows) when (lib)curl is running in -"system context" makes it use wrong(?) username - at least when compared to -what `winhttp` does. See https://curl.se/bug/view.cgi?id=535 - -## NTLM does not support password with Unicode 'SECTION SIGN' character - -Code point: U+00A7 - -https://en.wikipedia.org/wiki/Section_sign -[curl issue 2120](https://github.com/curl/curl/issues/2120) - ## libcurl can fail to try alternatives with `--proxy-any` When connecting via a proxy using `--proxy-any`, a failure to establish an @@ -256,24 +243,6 @@ code is not a documented error for `InitializeSecurityContext` (digest). [curl issue 6302](https://github.com/curl/curl/issues/6302) -## curl never completes Negotiate over HTTP - -Apparently it is not working correctly...? - -See [curl issue 5235](https://github.com/curl/curl/issues/5235) - -## Negotiate on Windows fails - -When using `--negotiate` (or NTLM) with curl on Windows, SSL/TLS handshake -fails despite having a valid kerberos ticket cached. Works without any issue -in Unix/Linux. - -[curl issue 5881](https://github.com/curl/curl/issues/5881) - -## Negotiate authentication against Hadoop - -[curl issue 8264](https://github.com/curl/curl/issues/8264) - # FTP ## FTP with ACCT From 58cb1e2f1fa8d8f1c71596918707e566c6353cb2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 18 Aug 2026 12:28:05 +0200 Subject: [PATCH 0924/1062] runtests: allow resetting `CURL_TEST_MIN` via `--min=0` Set the default to `CURL_TEST_MIN` if set, first. Then process the command-line options to override it. Previously, the env took over if the command-line explicitly tried to set it to zero. Ref: #22617 Follow-up to 3f1cd809eeae05f39fec72fe780f3a69d21972fb #19942 Closes #22618 --- tests/runtests.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/runtests.pl b/tests/runtests.pl index 4b5b5b0826fb..e4b7c1005394 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2346,6 +2346,10 @@ sub pickrunner { push(@ARGV, split(' ', $ENV{'TFLAGS'})) if defined($ENV{'TFLAGS'}); } +if($ENV{"CURL_TEST_MIN"}) { + $mintotal = $ENV{"CURL_TEST_MIN"}; +} + $args = join(' ', @ARGV); $valgrind = checktestcmd("valgrind"); @@ -2747,10 +2751,6 @@ sub pickrunner { setlogfunc(\&logmsg); } -if(!$mintotal && $ENV{"CURL_TEST_MIN"}) { - $mintotal = $ENV{"CURL_TEST_MIN"}; -} - ####################################################################### # Output curl version and host info being tested # From e669179681044514099ca726dbaa55653528f4fb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Aug 2026 12:01:51 +0200 Subject: [PATCH 0925/1062] runtests: introduce a subset option To make it easier to run different subsets of the tests in separate invokes. Closes #22616 --- docs/runtests.md | 18 ++++++++++++++++++ tests/runtests.pl | 29 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/docs/runtests.md b/docs/runtests.md index a1190004afc7..0b4b1432e5ad 100644 --- a/docs/runtests.md +++ b/docs/runtests.md @@ -260,6 +260,24 @@ script randomly discards entries to fail until the amount is **num**. The random seed initially set for this is fixed per month and can be set with *--seed*. +## `--subset=
/` + +Tell runtests to run a subset of the available tests. The test selection is +done first based on existing options. The list of selected tests is then +divided into a number of *parts*. The selected *section* specifier is the +set of tests this invoke runs. Note that both numbers must be provided and +*section* is zero indexed so it must be smaller than *parts*. + +Using this option you can for example split up an identical test run into +three separate invokes that combined run all the tests: + + ./runtests.pl --subset=0/3 + ./runtests.pl --subset=1/3 + ./runtests.pl --subset=2/3 + +This option works fine in combination with `-R` but consider also using +`--seed` for that. + ## `-t[num]` Selects a **torture** test for the given tests. This makes runtests.pl first diff --git a/tests/runtests.pl b/tests/runtests.pl index e4b7c1005394..864a344f9eba 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2355,6 +2355,8 @@ sub pickrunner { $valgrind = checktestcmd("valgrind"); my $number = 0; my $fromnum = -1; +my $useshares; +my $usepart; my @testthis; while(@ARGV) { if($ARGV[0] eq "-v") { @@ -2470,6 +2472,14 @@ sub pickrunner { $tortalloc = $1; } } + elsif($ARGV[0] =~ /^--subset=(\d+)\/(\d+)$/) { + # split all tests into $2 parts. + # this invoke then runs the part number $1 (0-indexed) + ($usepart, $useshares) = ($1, $2); + if($useshares < 1 || $usepart >= $useshares) { + die "illegal subset specified"; + } + } elsif($ARGV[0] =~ /--shallow=(\d+)/) { # Fail no more than this amount per tests when running # torture. @@ -2897,6 +2907,25 @@ sub disabledtests { $TESTCASES = join(" ", @rand); } +if($useshares) { + my @a = grep { length($_) } split(/ +/, $TESTCASES); + my $n = scalar(@a); + + if($useshares < 1 || $usepart >= $useshares) { + die "illegal subset specified"; + } + + my $start = int(($n * $usepart) / $useshares); + my $end = int(($n * ($usepart + 1)) / $useshares); # one past last index + my $run = $end - $start; + + printf STDERR "Subset: 1/%u of the tests (run %u tests out of %u). Part %u\n", + $useshares, $run, $n, $usepart; + + my @s = $run ? @a[$start .. $end - 1] : (); + $TESTCASES = join(" ", @s); +} + # Display the contents of the given file. Line endings are canonicalized # and excessively long files are elided sub displaylogcontent { From 66486691ee1c5c768c74e402b8b95902b88d90fb Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 18 Aug 2026 13:37:48 +0200 Subject: [PATCH 0926/1062] runtests: divide minimum required tests by subsets When the minimum comes from `CURL_TEST_MIN`, to improve the default behavior. It may still need manual adjustment in cases. Follow-up to e669179681044514099ca726dbaa55653528f4fb #22616 Follow-up to 58cb1e2f1fa8d8f1c71596918707e566c6353cb2 #22618 Cherry-picked from #22617 Closes #22619 --- tests/runtests.pl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/runtests.pl b/tests/runtests.pl index 864a344f9eba..b5a2b4df9cd2 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -2346,12 +2346,10 @@ sub pickrunner { push(@ARGV, split(' ', $ENV{'TFLAGS'})) if defined($ENV{'TFLAGS'}); } -if($ENV{"CURL_TEST_MIN"}) { - $mintotal = $ENV{"CURL_TEST_MIN"}; -} - $args = join(' ', @ARGV); +my $mintotalany = 0; + $valgrind = checktestcmd("valgrind"); my $number = 0; my $fromnum = -1; @@ -2450,6 +2448,7 @@ sub pickrunner { elsif($ARGV[0] =~ /--min=(\d+)/) { my ($num) = ($1); $mintotal = $num; + $mintotalany = 1; } elsif($ARGV[0] eq "-n") { # no valgrind @@ -2761,6 +2760,13 @@ sub pickrunner { setlogfunc(\&logmsg); } +if(!$mintotalany && $ENV{"CURL_TEST_MIN"}) { + $mintotal = $ENV{"CURL_TEST_MIN"}; + if($useshares) { + $mintotal /= $useshares; + } +} + ####################################################################### # Output curl version and host info being tested # From 79132a1daf8f3b2b6db101be386fa9e8cb3f73f5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 18 Aug 2026 12:16:32 +0200 Subject: [PATCH 0927/1062] GHA: use `--subset` runtests option, run random subset in riscv64 job Also: - drop most hand-tuned `--min` options, in favor of defaults. - enable tests on emulated riscv64. Suggested-by: Dan Fandrich Ref: https://github.com/curl/curl/pull/22590#issuecomment-5303238102 - drop building examples on emulated riscv64. Adding around 3.5 minutes to the riscv64 job: 0.5 Perl install, 2 minutes to build tests, 1 minute to run 100 tests. Follow-up to 66486691ee1c5c768c74e402b8b95902b88d90fb #22619 Follow-up to e669179681044514099ca726dbaa55653528f4fb #22616 Follow-up to 58cb1e2f1fa8d8f1c71596918707e566c6353cb2 #22618 Follow-up to d00000673d05fdf6ceee8941ccd63ad2906257e4 #22602 Closes #22617 --- .github/workflows/linux.yml | 28 ++++++++++++++-------------- .github/workflows/macos.yml | 6 +++--- .github/workflows/non-native.yml | 4 ++-- .github/workflows/windows.yml | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ae0c6924c5b3..c7d0d0506a20 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -98,14 +98,14 @@ jobs: image: ubuntu-24.04-arm install_packages: libnghttp2-dev libldap-dev libkrb5-dev valgrind install_steps: libressl-c-arm - tflags: '--min=870 1 to 950' + tflags: '--subset=0/2' generate: -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON -DCURL_ENABLE_NTLM=ON - name: 'libressl krb5 valgrind 2' image: ubuntu-24.04-arm install_packages: libnghttp2-dev libldap-dev libkrb5-dev valgrind install_steps: libressl-c-arm - tflags: '--min=900 951 to 9999' + tflags: '--subset=1/2' generate: -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON -DCURL_ENABLE_NTLM=ON - name: 'libressl clang' @@ -132,7 +132,7 @@ jobs: image: ubuntu-24.04-arm install_packages: libnghttp2-dev libidn2-dev libldap-dev libgss-dev valgrind install_steps: mbedtls-latest-arm - tflags: '--min=850 1 to 1000' + tflags: '--subset=0/2' LDFLAGS: -Wl,-rpath,/home/runner/mbedtls/lib PKG_CONFIG_PATH: /home/runner/mbedtls/lib/pkgconfig generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_GSSAPI=ON -DCURL_DROP_UNUSED=ON @@ -141,7 +141,7 @@ jobs: image: ubuntu-24.04-arm install_packages: libnghttp2-dev libidn2-dev libldap-dev libgss-dev valgrind install_steps: mbedtls-latest-arm - tflags: '--min=900 1001 to 9999' + tflags: '--subset=1/2' LDFLAGS: -Wl,-rpath,/home/runner/mbedtls/lib PKG_CONFIG_PATH: /home/runner/mbedtls/lib/pkgconfig generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_GSSAPI=ON @@ -175,13 +175,13 @@ jobs: - name: 'rustls valgrind 1' install_packages: libnghttp2-dev libldap-dev valgrind install_steps: rust rustls - tflags: '--min=820 1 to 1000' + tflags: '--subset=0/2' generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'rustls valgrind 2' install_packages: libnghttp2-dev libldap-dev valgrind install_steps: rust rustls - tflags: '--min=830 1001 to 9999' + tflags: '--subset=1/2' generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'rustls' @@ -199,7 +199,7 @@ jobs: image: ubuntu-26.04-arm install_packages: valgrind install_steps: wolfssl-opensslextra-arm - tflags: '--min=815 1 to 1000' + tflags: '--subset=0/2' LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug --enable-httpsig @@ -207,7 +207,7 @@ jobs: image: ubuntu-26.04-arm install_packages: valgrind install_steps: wolfssl-opensslextra-arm - tflags: '--min=835 1001 to 9999' + tflags: '--subset=1/2' LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug --enable-httpsig @@ -219,14 +219,14 @@ jobs: image: ubuntu-26.04-arm install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind CC: gcc-16 - tflags: '--min=965 1 to 1000' + tflags: '--subset=0/2' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_GCC_ANALYZER=ON -DCURL_ENABLE_NTLM=ON - name: 'openssl libssh2 sync-resolver valgrind 2' image: ubuntu-26.04-arm install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind CC: gcc-16 - tflags: '--min=920 1001 to 9999' + tflags: '--subset=1/2' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON - name: 'openssl intel C89' @@ -243,13 +243,13 @@ jobs: - name: 'openssl -O3 libssh valgrind 1' install_packages: libssh-dev valgrind CFLAGS: -O3 - tflags: '--min=950 1 to 1000' + tflags: '--subset=0/2' generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 -DCURL_ENABLE_NTLM=ON - name: 'openssl -O3 libssh valgrind 2' install_packages: libssh-dev valgrind CFLAGS: -O3 - tflags: '--min=900 1001 to 9999' + tflags: '--subset=1/2' generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 -DCURL_ENABLE_NTLM=ON - name: 'openssl clang krb5 openldap static' @@ -294,13 +294,13 @@ jobs: - name: 'openssl torture 1' install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev - tflags: '-t --shallow=25 --min=960 1 to 1000' + tflags: '-t --shallow=25 --subset=0/2' torture: true generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON -DCURL_DISABLE_HTTPSIG=OFF - name: 'openssl torture 2' install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev - tflags: '-t --shallow=25 --min=915 1001 to 9999' + tflags: '-t --shallow=25 --subset=1/2' torture: true generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON -DCURL_DISABLE_HTTPSIG=OFF diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0033dc02908f..41d21a5305db 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -391,21 +391,21 @@ jobs: install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON - tflags: '-t --shallow=25 --min=480 1 to 500' + tflags: '-t --shallow=25 --subset=0/3' - name: 'OpenSSL torture 2' compiler: clang install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON - tflags: '-t --shallow=25 --min=730 501 to 1250' + tflags: '-t --shallow=25 --subset=1/3' - name: 'OpenSSL torture 3' compiler: clang install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON - tflags: '-t --shallow=25 --min=628 1251 to 9999' + tflags: '-t --shallow=25 --subset=2/3' steps: - name: 'brew unlink openssl' diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index ca42ba7507fd..b08949062d12 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -73,8 +73,8 @@ jobs: install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl', tflags: 'skipall', - install: 'cmake-core ninja', + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl !examples', tflags: 'HTTP --min=0 --subset=0/10 -R --seed', + install: 'cmake-core ninja perl5', options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', tflags: 'skipall', diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 00e088d00bc3..f47de59b0a32 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -287,11 +287,11 @@ jobs: install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-libssh2' } # MinGW torture - { name: 'schannel U torture 1', type: 'Debug', - build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --min=820 1 to 950', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --subset=0/2', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-libssh2' } - { name: 'schannel U torture 2', type: 'Debug', - build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --min=820 951 to 9999', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --subset=1/2 --min=885', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-libssh2' } # WARNING: libssh uses hard-coded world-writable paths (C:ProgramData/, /etc/..., ~/.ssh/) From 695aa15743685a9d46c4c41bb9c95221d4659541 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Aug 2026 23:58:16 +0200 Subject: [PATCH 0928/1062] urldata: remove the aptr struct Move the two remaining fields into the state struct instead, within the HTTP ifdef. Also: make the AWS sigv4 code always rely on the http_host value. It was previously conditionally trying to also work if it was not set, but that logic was not creating an identical copy so it would fail. Closes #22620 --- lib/http.c | 35 +++++++++++++++++------------------ lib/http_aws_sigv4.c | 17 ++++++----------- lib/http_httpsig.c | 4 ++-- lib/http_proxy.c | 2 +- lib/rtsp.c | 4 ++-- lib/url.c | 6 ++++-- lib/urldata.h | 8 ++------ 7 files changed, 34 insertions(+), 42 deletions(-) diff --git a/lib/http.c b/lib/http.c index 6a4c1f39411b..511229cd7a6b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1840,7 +1840,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, /* only send this if the contents was non-blank or done special */ - if(data->state.aptr.host && + if(data->state.http_host && /* a Host: header was sent already, do not pass on any custom Host: header as that will produce *two* in the same request! */ @@ -2017,10 +2017,9 @@ void Curl_http_method(struct Curl_easy *data, static CURLcode http_set_aptr_host(struct Curl_easy *data) { struct connectdata *conn = data->conn; - struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr = NULL; - curlx_safefree(aptr->host); + curlx_safefree(data->state.http_host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); #endif @@ -2065,8 +2064,8 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) #endif if(!curl_strequal("Host:", ptr)) { - aptr->host = curl_maprintf("Host:%s", &ptr[5]); - if(!aptr->host) + data->state.http_host = curl_maprintf("Host:%s", &ptr[5]); + if(!data->state.http_host) return CURLE_OUT_OF_MEMORY; } } @@ -2096,7 +2095,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) result = curlx_dyn_addf(&tmp, ":%u", data->state.origin->port); } - aptr->host = result ? NULL : curlx_dyn_take(&tmp, &hlen); + data->state.http_host = result ? NULL : curlx_dyn_take(&tmp, &hlen); curlx_dyn_free(&tmp); return result; } @@ -2650,23 +2649,23 @@ static CURLcode http_range(struct Curl_easy *data, if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && !Curl_checkheaders(data, STRCONST("Range"))) { /* if a line like this was already allocated, free the previous one */ - curlx_free(data->state.aptr.rangeline); - data->state.aptr.rangeline = curl_maprintf("Range: bytes=%s\r\n", + curlx_free(data->state.rangeline); + data->state.rangeline = curl_maprintf("Range: bytes=%s\r\n", data->state.range); - if(!data->state.aptr.rangeline) + if(!data->state.rangeline) return CURLE_OUT_OF_MEMORY; } else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && !Curl_checkheaders(data, STRCONST("Content-Range"))) { curl_off_t req_clen = Curl_creader_total_length(data); /* if a line like this was already allocated, free the previous one */ - curlx_free(data->state.aptr.rangeline); + curlx_free(data->state.rangeline); if(data->set.set_resume_from < 0) { /* Upload resume was asked for, but we do not know the size of the remote part so we tell the server (and act accordingly) that we upload the whole file (again) */ - data->state.aptr.rangeline = + data->state.rangeline = curl_maprintf("Content-Range: bytes 0-%" FMT_OFF_T "/" "%" FMT_OFF_T "\r\n", req_clen - 1, req_clen); } @@ -2678,7 +2677,7 @@ static CURLcode http_range(struct Curl_easy *data, curl_off_t total_len = data->req.authneg ? data->state.infilesize : (data->state.resume_from + req_clen); - data->state.aptr.rangeline = + data->state.rangeline = curl_maprintf("Content-Range: bytes %s%" FMT_OFF_T "/" "%" FMT_OFF_T "\r\n", data->state.range, total_len - 1, total_len); @@ -2686,11 +2685,11 @@ static CURLcode http_range(struct Curl_easy *data, else { /* Range was selected and then we pass the incoming range and append total size */ - data->state.aptr.rangeline = + data->state.rangeline = curl_maprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", data->state.range, req_clen); } - if(!data->state.aptr.rangeline) + if(!data->state.rangeline) return CURLE_OUT_OF_MEMORY; } } @@ -2925,8 +2924,8 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_HOST: - if(data->state.aptr.host) { - result = curlx_dyn_add(req, data->state.aptr.host); + if(data->state.http_host) { + result = curlx_dyn_add(req, data->state.http_host); if(!result) result = curlx_dyn_addn(req, STRCONST("\r\n")); } @@ -2945,8 +2944,8 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_RANGE: - if(data->state.use_range && data->state.aptr.rangeline) - result = curlx_dyn_add(req, data->state.aptr.rangeline); + if(data->state.use_range && data->state.rangeline) + result = curlx_dyn_add(req, data->state.rangeline); break; case H1_HD_USER_AGENT: diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index bff66a958227..b142749f8897 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -369,7 +369,6 @@ static CURLcode merge_duplicate_headers(struct curl_slist *head) /* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */ static CURLcode make_headers(struct Curl_easy *data, - const char *hostname, char *timestamp, const char *provider1, size_t plen, /* length of provider1 */ @@ -397,13 +396,10 @@ static CURLcode make_headers(struct Curl_easy *data, /* provider1 lowercase */ Curl_strntolower(&date_full_hdr[2], provider1, plen); - if(!Curl_checkheaders(data, STRCONST("Host"))) { - char *fullhost; - - if(data->state.aptr.host) - fullhost = curlx_strdup(data->state.aptr.host); - else - fullhost = curl_maprintf("host:%s", hostname); + if(!Curl_checkheaders(data, STRCONST("Host")) && + data->state.http_host) { + /* Host: [host]:[port] */ + char *fullhost = curlx_strdup(data->state.http_host); if(fullhost) head = Curl_slist_append_nodup(NULL, fullhost); @@ -933,7 +929,6 @@ static CURLcode get_timestamp(char *timestamp, size_t stampsize) } static CURLcode make_canonical_request(struct Curl_easy *data, - const char *hostname, char *timestamp, struct Curl_str *provider1, struct Curl_str *service, @@ -953,7 +948,7 @@ static CURLcode make_canonical_request(struct Curl_easy *data, curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); - result = make_headers(data, hostname, timestamp, + result = make_headers(data, timestamp, curlx_str(provider1), curlx_strlen(provider1), date_header_out, content_sha256_hdr, canonical_headers, signed_headers); @@ -1208,7 +1203,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) result = get_timestamp(timestamp, sizeof(timestamp)); if(!result) - result = make_canonical_request(data, hostname, timestamp, + result = make_canonical_request(data, timestamp, &provider1, &service, method, payload_hash, payload_hash_len, &date_header, content_sha256_hdr, diff --git a/lib/http_httpsig.c b/lib/http_httpsig.c index bcf796900f6c..c0329261dc5b 100644 --- a/lib/http_httpsig.c +++ b/lib/http_httpsig.c @@ -125,12 +125,12 @@ static CURLcode decode_hex_key(struct Curl_easy *data, } /* @authority matches the Host header field-value when available (RFC 9421). - data->state.aptr.host is produced by http_set_aptr_host() before auth. */ + data->state.http_host is produced by http_set_aptr_host() before auth. */ static CURLcode httpsig_authority(struct Curl_easy *data, struct connectdata *conn, struct dynbuf *authority_buf) { - const char *h = data->state.aptr.host; + const char *h = data->state.http_host; if(h && curl_strnequal(h, "host:", 5)) { const char *value = h + 5; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index c658b824781e..96cd101ca78c 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -132,7 +132,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, /* trim surrounding whitespace so a padded field name (e.g. `Authorization :`) cannot slip past the Authorization/Cookie check */ curlx_str_trimblanks(&name); - if(data->state.aptr.host && + if(data->state.http_host && /* a Host: header was sent already, do not pass on any custom Host: header as that will produce *two* in the same request! */ curlx_str_casecompare(&name, "Host")) diff --git a/lib/rtsp.c b/lib/rtsp.c index a8db21c41fc5..c31263a8c857 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -416,9 +416,9 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { result = rtsp_header_alloc("Range", data->state.range, - &data->state.aptr.rangeline); + &data->state.rangeline); if(!result) - b->range = data->state.aptr.rangeline; + b->range = data->state.rangeline; } } return result; diff --git a/lib/url.c b/lib/url.c index 15b7bb68e3ed..3b81bf00679d 100644 --- a/lib/url.c +++ b/lib/url.c @@ -279,8 +279,10 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_hash_destroy(&data->meta_hash); Curl_creds_unlink(&data->state.creds); - curlx_safefree(data->state.aptr.rangeline); - curlx_safefree(data->state.aptr.host); +#ifndef CURL_DISABLE_HTTP + curlx_safefree(data->state.rangeline); + curlx_safefree(data->state.http_host); +#endif #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); #endif diff --git a/lib/urldata.h b/lib/urldata.h index e1ea84b8aaf7..f0c6d7ae1b8c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -609,13 +609,9 @@ struct UrlState { struct Curl_creds *creds; /* Credentials for the origin only */ - /* Dynamically allocated strings, MUST be freed before this struct is - killed. */ - struct dynamically_allocated_data { - char *rangeline; - char *host; - } aptr; #ifndef CURL_DISABLE_HTTP + char *rangeline; /* allocated */ + char *http_host; /* allocated */ struct http_negotiation http_neg; #endif #ifndef CURL_DISABLE_RTSP From fc6c67ce8613ee89956e8397a2c0c3e5169b3913 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 19 Aug 2026 09:03:41 +0200 Subject: [PATCH 0929/1062] docs: clarify that cookies need domain set to match Especially important if reading cookies from HTTP headers, which is a style we discourage. Reported-by: accl on hackerone Closes #22621 --- docs/cmdline-opts/cookie.md | 11 ++++++----- docs/libcurl/opts/CURLOPT_COOKIEFILE.md | 15 +++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/cmdline-opts/cookie.md b/docs/cmdline-opts/cookie.md index 30288fbcbad4..95b0a8c3c81f 100644 --- a/docs/cmdline-opts/cookie.md +++ b/docs/cmdline-opts/cookie.md @@ -42,15 +42,16 @@ If the filename is an empty string ("") and is the only cookie input, curl activates the cookie engine without any cookies. The file format of the file to read cookies from should be plain HTTP headers -(Set-Cookie style) or the Netscape/Mozilla cookie file format. +(Set-Cookie style) or the Netscape/Mozilla cookie file format. We discourage +the use of the HTTP header style. The file specified with --cookie is only used as input. No cookies are written to that file. To store cookies, use the --cookie-jar option. -If you use the Set-Cookie file format and do not specify a domain then the -cookie is not sent since the domain never matches. To address this, set a -domain in Set-Cookie line (doing that includes subdomains) or preferably: use -the Netscape format. +If you read cookies from a plain HTTP headers file, make sure each +`Set-Cookie` line specifies a `Domain` attribute. Without an explicit domain, +the cookie cannot be reliably matched to a target host and may be applied in +unexpected ways. We suggest using the Netscape file format instead. Users often want to both read cookies from a file and write updated cookies back to a file, so using both --cookie and --cookie-jar in the same command diff --git a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md index bf2d374464ca..ccccba5fe325 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md +++ b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md @@ -39,14 +39,13 @@ By passing the empty string ("") to this option, you enable the cookie engine without reading any initial cookies. If you tell libcurl the filename is "-" (a single minus sign), libcurl instead reads from stdin. -This option only **reads** cookies. To make libcurl write cookies to file, -see CURLOPT_COOKIEJAR(3). - -If you read cookies from a plain HTTP headers file and it does not specify a -domain in the Set-Cookie line, then the cookie is not sent since the cookie -domain cannot match the target URL's. To address this, set a domain in -Set-Cookie line (doing that includes subdomains) or preferably: use the -Netscape format. +This option only **reads** cookies. To make libcurl write cookies to file, see +CURLOPT_COOKIEJAR(3). + +If you read cookies from a plain HTTP headers file, make sure each +`Set-Cookie` line specifies a `Domain` attribute. Without an explicit domain, +libcurl cannot reliably associate the cookie with a host and it may be applied +in unexpected ways. We suggest using the Netscape file format instead. The application does not have to keep the string around after setting this option. From 9b29495863c967e2768ea1c517f3ad28a50a9a1b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 19 Aug 2026 09:32:16 +0200 Subject: [PATCH 0930/1062] http: custom Authorization: header overrides Negotiate As documented and as it does for other methods. Difficult to test since it needs a successful kerberos ticket. Reported-by: sdgh179 on github Fixes #22610 Closes #22622 --- lib/http.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/http.c b/lib/http.c index 511229cd7a6b..5701bdc09b4e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -687,10 +687,19 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #endif #ifdef USE_SPNEGO if(authstatus->picked == CURLAUTH_NEGOTIATE) { - auth = "Negotiate"; - result = Curl_output_negotiate(data, conn, proxy); - if(result) - return result; + if( +#ifndef CURL_DISABLE_PROXY + (proxy && !Curl_checkProxyheaders(data, conn, + STRCONST("Proxy-authorization"))) || +#endif + (!proxy && !Curl_checkheaders(data, STRCONST("Authorization")))) { + auth = "Negotiate"; + result = Curl_output_negotiate(data, conn, proxy); + if(result) + return result; + } + else + authstatus->done = TRUE; } else #endif From f31251b6451c6f5da78457273ff9307c2be0ecb7 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Wed, 19 Aug 2026 09:27:38 -0700 Subject: [PATCH 0931/1062] test_07_upload: skip tests if nghttpx lacks QUIC If nghttpx is available but lacks QUIC support, disable the tests that depend on that support. Co-authored-by: Stefan Eissing Fixes #22609 Closes #22624 --- tests/http/test_07_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 7ad396d73c38..4c2ecfe60105 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -654,7 +654,7 @@ def test_07_63_upload_exp100_paused(self, env: Env, httpd, nghttpx, proto): r.check_exit_code(0) # nghttpx is the only server we have that supports TLS early data - @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx") + @pytest.mark.skipif(condition=not Env.have_h3_server(), reason="no QUIC in nghttpx") @pytest.mark.parametrize("proto,upload_size", [ pytest.param('http/1.1', 100, id='h1-small-body'), pytest.param('http/1.1', 10 * 1024, id='h1-medium-body'), From aab0518d04599627368ab78b1f8713683098b435 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 20 Aug 2026 12:39:59 +0200 Subject: [PATCH 0932/1062] url: fix handling of empty user in ntml matching Line should already have been removed in 7103a93, but neither humans nor clankers caught it. tststs... Prior to this change it appears all reuse of empty cred connection was blocked for ntlm instead of the intended behavior of allowing reuse in the limited circumstance when it is the same connection used by the transfer before. Closes https://github.com/curl/curl/pull/22629 --- lib/url.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/url.c b/lib/url.c index 3b81bf00679d..0b2d414526ad 100644 --- a/lib/url.c +++ b/lib/url.c @@ -925,18 +925,12 @@ static bool url_match_auth_ntlm(struct connectdata *conn, { if(conn->http_ntlm_state != NTLMSTATE_NONE) { /* Connection is using NTLM. We cannot reuse if transfer - * has different Auth input parameters. - * Empty user: Negotiate on Windows can make use of an "ambient" - * user from a "SecurityToken" associated with the current thread or - * process. This token can be switched at any time. We are therefore - * not able to find out reliably what token the connection really - * used, nor what token in the next connect attempt will use. - * To avoid TOCTOU attacks, do not reuse on empty credentials. */ + * has different Auth input parameters. */ if(!m->want_ntlm_http || - !Curl_creds_has_user(conn->creds) || !Curl_creds_same(conn->creds, m->data->state.creds) || !Curl_peer_equal(conn->creds_origin, m->data->state.origin)) return FALSE; + /* Empty credentials need more careful matching for WINDOWS_SSPI */ if(!url_allow_sspi_empty_creds(conn->creds, m->data, conn)) return FALSE; } From 74b732f63792317a9ca8ad7877729cad445bc985 Mon Sep 17 00:00:00 2001 From: Alb3e3 <74142887+Alb3e3@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:53:44 +0400 Subject: [PATCH 0933/1062] tool_msgs: make notef() respect --silent Until 8.17.0 the --silent check lived in voutf(), so it covered every message type that went through it. 56450ce26f moved that check out to the callers so that errorf() could honour --show-error, and warnf() and errorf() got the check back, but notef() did not. Since then "Note:" messages have been printed even with --silent, as long as tracing was on. Reported-by: Jace Laquerre Fixes #22623 Closes #22625 --- docs/cmdline-opts/silent.md | 4 +-- src/tool_msgs.c | 4 +-- tests/data/Makefile.am | 2 +- tests/data/test1740 | 58 +++++++++++++++++++++++++++++++++++++ tests/data/test1741 | 58 +++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 tests/data/test1740 create mode 100644 tests/data/test1741 diff --git a/docs/cmdline-opts/silent.md b/docs/cmdline-opts/silent.md index 2498ca56f4fb..ac7dc5abed11 100644 --- a/docs/cmdline-opts/silent.md +++ b/docs/cmdline-opts/silent.md @@ -17,8 +17,8 @@ Example: # `--silent` -Silent or quiet mode. Do not show progress meter, warning messages or error -messages. Makes curl mute. It still outputs the data you ask for, potentially +Silent or quiet mode. Do not show progress meter, note messages, warning +messages or error messages. Makes curl mute. It still outputs the data you ask for, potentially even to the terminal/stdout unless you redirect it. Use --show-error in addition to this option to disable progress meter but diff --git a/src/tool_msgs.c b/src/tool_msgs.c index 2e1245503f6b..50a2fa086075 100644 --- a/src/tool_msgs.c +++ b/src/tool_msgs.c @@ -74,11 +74,11 @@ static void voutf(const char *prefix, const char *fmt, va_list ap) /* * Emit 'note' formatted message on configured 'errors' stream, if verbose was - * selected. + * selected and mute (--silent) was not. */ void notef(const char *fmt, ...) { - if(global && global->tracetype) { + if(global && global->tracetype && !global->silent) { va_list ap; va_start(ap, fmt); voutf(NOTE_PREFIX, fmt, ap); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 5291ccc23219..7238eed74900 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -225,7 +225,7 @@ test1685 test1686 \ \ test1700 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ -test1720 test1721 test1722 test1723 test1724 test1725 \ +test1720 test1721 test1722 test1723 test1724 test1725 test1740 test1741 \ \ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ diff --git a/tests/data/test1740 b/tests/data/test1740 new file mode 100644 index 000000000000..dda4b397755e --- /dev/null +++ b/tests/data/test1740 @@ -0,0 +1,58 @@ + + + + +HTTP +HTTP POST +--silent + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +# set the terminal wide to avoid word wrap in the message +COLUMNS=10000 + + +--silent suppresses Note: messages when verbose + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --silent -X POST -d foo -o %LOGDIR/out%TESTNUMBER -w '%{stderr}done\n' + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +foo + + +done + + + diff --git a/tests/data/test1741 b/tests/data/test1741 new file mode 100644 index 000000000000..e2e5cd838930 --- /dev/null +++ b/tests/data/test1741 @@ -0,0 +1,58 @@ + + + + +HTTP +HTTP POST + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +# set the terminal wide to avoid word wrap in the message +COLUMNS=10000 + + +Note: messages are shown when verbose without --silent + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -X POST -d foo -o %LOGDIR/out%TESTNUMBER --no-progress-meter -w '%{stderr}done\n' + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +foo + + +Note: Unnecessary use of -X or --request, POST is already inferred. +done + + + From 3bc93643cbc0e805d5de23af959d9159671a89de Mon Sep 17 00:00:00 2001 From: Jayanth Vennamreddy Date: Wed, 4 Feb 2026 15:59:16 -0500 Subject: [PATCH 0934/1062] docs: connection reuse behavior for socket callbacks Document observed behavior around connections reuse and socket callbacks. When CURLOPT_CLOSESOCKETFUNCTION is copied and when it is invoked. Closes #20521 --- docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md | 3 ++- docs/libcurl/opts/CURLOPT_CLOSESOCKETFUNCTION.md | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md index 2774ccbfc7f4..fb67c01a60a9 100644 --- a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md @@ -7,6 +7,7 @@ Source: libcurl See-also: - CURLMOPT_SOCKETDATA (3) - CURLMOPT_TIMERFUNCTION (3) + - CURLOPT_CLOSESOCKETFUNCTION (3) - curl_multi_socket_action (3) Protocol: - All @@ -97,7 +98,7 @@ writable. ## CURL_POLL_REMOVE -The specified socket/file descriptor is no longer used by libcurl for any +The specified socket or file descriptor is no longer used by libcurl for any active transfer. It might soon be added again. When a socket is given a CURL_POLL_REMOVE value, it might be because libcurl diff --git a/docs/libcurl/opts/CURLOPT_CLOSESOCKETFUNCTION.md b/docs/libcurl/opts/CURLOPT_CLOSESOCKETFUNCTION.md index 0dc9bb4c6115..42a58e25a505 100644 --- a/docs/libcurl/opts/CURLOPT_CLOSESOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CLOSESOCKETFUNCTION.md @@ -7,6 +7,7 @@ Source: libcurl See-also: - CURLOPT_CLOSESOCKETDATA (3) - CURLOPT_OPENSOCKETFUNCTION (3) + - CURLMOPT_SOCKETFUNCTION (3) Protocol: - All Added-in: 7.21.7 @@ -47,6 +48,18 @@ after the easy handle has been cleaned up. The callback and data is inherited by a new connection and that connection may live longer than the transfer itself in the multi/share handle's connection cache. +# NOTES ON CONNECTION REUSE + +The close socket callback is invoked when libcurl closes a socket it owns. +When using the multi interface, the callback and +CURLOPT_CLOSESOCKETDATA(3) are copied from the *first* easy handle that +creates the socket used for a connection; changing this option on a subsequent +easy handle that reuses the same connection has no effect for that connection. +The callback is stored with the connection because the connection and its +associated socket may outlive the easy handle that created it, so that libcurl +can still invoke it when the socket is closed even after that handle has been +cleaned up. + # DEFAULT Use the standard socket close function. From 2ba2fe354026aa4d60ab06fbc9b6ac1e56d1b92f Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Thu, 16 Jul 2026 22:03:35 +0530 Subject: [PATCH 0935/1062] ldap: base64-encode LDIF values beginning with colon or less-than A value whose first byte is ':' or '<' is not a SAFE-INIT-CHAR per RFC 2849 and must be base64-encoded, but ldap_value_needs_base64() and the inline check in oldap_recv() only encoded on a control byte or leading/trailing blank. Such a value was emitted verbatim, producing LDIF that strict parsers reject. Closes #22339 --- lib/ldap.c | 6 ++++-- lib/openldap.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ldap.c b/lib/ldap.c index 350bd1737746..2676f732bb1a 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -238,8 +238,10 @@ static bool ldap_value_needs_base64(const char *attr, size_t attr_len, if((attr_len > 7) && curl_strequal(";binary", attr + attr_len - 7)) return TRUE; - /* check for leading or trailing whitespace */ - if(val->bv_len && (ISBLANK(val->bv_val[0]) || + /* check for a leading ':' or '<' (not a SAFE-INIT-CHAR per RFC 2849) or + leading or trailing whitespace */ + if(val->bv_len && ((val->bv_val[0] == ':') || (val->bv_val[0] == '<') || + ISBLANK(val->bv_val[0]) || ISBLANK(val->bv_val[val->bv_len - 1]))) return TRUE; diff --git a/lib/openldap.c b/lib/openldap.c index 8ce4758d620f..887336e5ca7a 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -1206,9 +1206,11 @@ static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf, break; if(!binary) { - /* check for leading or trailing whitespace */ + /* check for a leading ':' or '<' (not a SAFE-INIT-CHAR per RFC + 2849) or leading or trailing whitespace */ if(bvals[i].bv_len && - (ISBLANK(bvals[i].bv_val[0]) || + ((bvals[i].bv_val[0] == ':') || (bvals[i].bv_val[0] == '<') || + ISBLANK(bvals[i].bv_val[0]) || ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))) binval = TRUE; else { From 961c95fea6e097ef4dbc19ee996e5127e58acbac Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Thu, 20 Aug 2026 09:03:11 -0700 Subject: [PATCH 0936/1062] openssl: fix DER buffer leak in Apple SecTrust verification `ossl_chain_get_der()` allocates a DER encoding of each peer certificate via `i2d_X509()`, but `Curl_vtls_apple_verify()` only copies it into a CFData and never frees the original. This leaks per certificate, per handshake, whenever USE_APPLE_SECTRUST is used with the OpenSSL/LibreSSL/BoringSSL backend. Fix frees the buffer inside openssl.c itself, so the GnuTLS backend (which borrows rather than allocates) is unaffected. Closes #22631 --- lib/vtls/openssl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 0740c32fbb55..802f3da86220 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4669,6 +4669,7 @@ static CURLcode ossl_infof_cert(struct Curl_cfilter *cf, struct ossl_certs_ctx { STACK_OF(X509) *sk; size_t num_certs; + unsigned char *last_der; }; static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf, @@ -4682,6 +4683,9 @@ static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf, X509 *cert; int der_len; + OPENSSL_free(chain->last_der); + chain->last_der = NULL; + (void)cf; (void)data; *pder_len = 0; @@ -4695,6 +4699,7 @@ static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf, der_len = i2d_X509(cert, pder); if(der_len < 0) return CURLE_FAILED_INIT; + chain->last_der = *pder; *pder_len = (size_t)der_len; return CURLE_OK; } @@ -4748,6 +4753,8 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, result = Curl_vtls_apple_verify(cf, data, peer, chain.num_certs, ossl_chain_get_der, &chain, ocsp_data, ocsp_len); + OPENSSL_free(chain.last_der); + chain.last_der = NULL; if(!result && ocsp_missing && conn_config->verifystatus && !octx->reused_session) { /* verified, but OCSP stapling is required and server sent none */ From c8df3defd9664cd276adc33754210ee07b5bbf71 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 20 Aug 2026 16:06:17 +0200 Subject: [PATCH 0937/1062] lib: new easy option string storage Change the storage of easy handle option strings from an array sized for all possible options to a hash set to reduce memory footprint. Give the hash set initially room for 4 strings, with first allocation happening when it goes beyond that. Measurements without test suite and a forced fail on growing the set gives: Size Result 2 1261 tests out of 1951 reported OK: 64% 4 1792 tests out of 1951 reported OK: 91% 8 1944 tests out of 1951 reported OK: 99% 16 1949 tests out of 1951 reported OK: 99% 32 single fail of 3211, unit test for u8_strset Add u8_strset that keeps the tuples (uint8_t id, char *str) and allows set/unset by `id`. Add that as data->set.strings. Define MACROS * CURL_EASY_STR(data, id) for access * CURL_EASY_STR_SET(data, id, s) for setting, making a copy * CURL_EASY_STR_SETN(data, id, s) for setting, no copy * CURL_EASY_STR_CLEAR(data, id) for unsetting * CURL_EASY_STR_CLEAR0(data, id) for unsetting and zero-ing value Add `data->set.str_copypostfields` to handle former `STRING_COPYPOSTFIELDS` string that was not always a string and could carry NUL bytes. Add unit tests to test3211. Closes #22628 --- lib/Makefile.inc | 2 + lib/cf-haproxy.c | 4 +- lib/cf-socket.c | 6 +- lib/cookie.c | 8 +- lib/easy.c | 44 ++--- lib/ftp.c | 22 +-- lib/getinfo.c | 4 +- lib/http.c | 50 +++--- lib/http2.c | 9 +- lib/http_aws_sigv4.c | 2 +- lib/http_httpsig.c | 9 +- lib/http_proxy.c | 15 +- lib/imap.c | 2 +- lib/pop3.c | 2 +- lib/proxy.c | 31 ++-- lib/rtsp.c | 48 +++--- lib/setopt.c | 329 +++++++++++++++++++------------------ lib/setopt.h | 3 +- lib/smtp.c | 17 +- lib/transfer.c | 19 ++- lib/uint-hashset.c | 305 ++++++++++++++++++++++++++++++++++ lib/uint-hashset.h | 80 +++++++++ lib/url.c | 77 +++++---- lib/urldata.h | 23 ++- lib/vdns/asyn-ares.c | 8 +- lib/vdns/doh.c | 27 +-- lib/vquic/cf-ngtcp2.c | 6 +- lib/vquic/cf-quiche.c | 6 +- lib/vquic/vquic-tls.c | 6 +- lib/vquic/vquic.c | 2 +- lib/vssh/libssh.c | 28 ++-- lib/vssh/libssh2.c | 39 +++-- lib/vssh/vssh.c | 12 +- lib/vtls/gtls.c | 6 +- lib/vtls/mbedtls.c | 7 +- lib/vtls/openssl.c | 27 +-- lib/vtls/rustls.c | 10 +- lib/vtls/schannel.c | 6 +- lib/vtls/vtls.c | 2 +- lib/vtls/vtls_config.c | 84 +++++----- lib/vtls/wolfssl.c | 15 +- projects/OS400/ccsidcurl.c | 2 +- tests/data/test3211 | 2 +- tests/unit/unit1620.c | 2 +- tests/unit/unit3211.c | 108 +++++++++++- 45 files changed, 1028 insertions(+), 488 deletions(-) create mode 100644 lib/uint-hashset.c create mode 100644 lib/uint-hashset.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 754275b78c95..c4e26849aa20 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -292,6 +292,7 @@ LIB_CFILES = \ transfer.c \ uint-bset.c \ uint-hash.c \ + uint-hashset.c \ uint-spbset.c \ uint-table.c \ url.c \ @@ -425,6 +426,7 @@ LIB_HFILES = \ transfer.h \ uint-bset.h \ uint-hash.h \ + uint-hashset.h \ uint-spbset.h \ uint-table.h \ url.h \ diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 16ee25066c0e..e3286107a328 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -81,8 +81,8 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, if(result) return result; - if(data->set.str[STRING_HAPROXY_CLIENT_IP]) { - client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + client_source_ip = CURL_EASY_STR(data, STRING_HAPROXY_CLIENT_IP); + if(client_source_ip) { client_dest_ip = client_source_ip; is_ipv6 = !Curl_is_ipv4addr(client_source_ip); } diff --git a/lib/cf-socket.c b/lib/cf-socket.c index c72c30f7b72c..77d850b299cf 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -656,9 +656,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; - const char *dev = data->set.str[STRING_DEVICE]; - const char *iface_input = data->set.str[STRING_INTERFACE]; - const char *host_input = data->set.str[STRING_BINDHOST]; + const char *dev = CURL_EASY_STR(data, STRING_DEVICE); + const char *iface_input = CURL_EASY_STR(data, STRING_INTERFACE); + const char *host_input = CURL_EASY_STR(data, STRING_BINDHOST); const char *iface = iface_input ? iface_input : dev; const char *host = host_input ? host_input : dev; int sockerr; diff --git a/lib/cookie.c b/lib/cookie.c index c1a28bf8b145..51ce3a9c8b29 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -1666,13 +1666,13 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) might be cookie files that were not loaded so saving the file is the wrong thing. */ if(data->cookies) { - if(data->set.str[STRING_COOKIEJAR] && data->cookies->running) { + const char *cookiejar = CURL_EASY_STR(data, STRING_COOKIEJAR); + if(cookiejar && data->cookies->running) { /* if we have a destination file for all the cookies to get dumped to */ - CURLcode result = cookie_output(data, data->cookies, - data->set.str[STRING_COOKIEJAR]); + CURLcode result = cookie_output(data, data->cookies, cookiejar); if(result) infof(data, "WARNING: failed to save cookies in %s: %s", - data->set.str[STRING_COOKIEJAR], curl_easy_strerror(result)); + cookiejar, curl_easy_strerror(result)); } if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { diff --git a/lib/easy.c b/lib/easy.c index 86c672d56785..117af41f08d3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -890,7 +890,6 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) { CURLcode result = CURLE_OK; - enum dupstring i; enum dupblob j; /* Copy src->set into dst->set first, then deal with the strings @@ -899,17 +898,17 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) dst->set.mimepostp = NULL; #endif + dst->set.str_copypostfields = NULL; + + Curl_u8_strset_init(&dst->set.strings); /* clear all dest string and blob pointers first, in case we error out mid-function */ - memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all strings */ - for(i = (enum dupstring)0; i < STRING_LASTZEROTERMINATED; i++) { - result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); - if(result) - return result; - } + result = Curl_u8_strset_copy(&dst->set.strings, &src->set.strings); + if(result) + return result; /* duplicate all blobs */ for(j = (enum dupblob)0; j < BLOB_LAST; j++) { @@ -919,18 +918,18 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) } /* duplicate memory areas pointed to */ - i = STRING_COPYPOSTFIELDS; - if(src->set.str[i]) { + if(src->set.str_copypostfields) { if(src->set.postfieldsize == -1) - dst->set.str[i] = curlx_strdup(src->set.str[i]); + dst->set.str_copypostfields = curlx_strdup(src->set.str_copypostfields); else /* postfieldsize is curl_off_t, curlx_memdup() takes a size_t ... */ - dst->set.str[i] = curlx_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); - if(!dst->set.str[i]) + dst->set.str_copypostfields = + curlx_memdup(src->set.str_copypostfields, + curlx_sotouz(src->set.postfieldsize)); + if(!dst->set.str_copypostfields) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ - dst->set.postfields = dst->set.str[i]; + dst->set.postfields = dst->set.str_copypostfields; } #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) @@ -974,6 +973,7 @@ CURL *curl_easy_duphandle(CURL *curl) if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) { struct Curl_easy *data = curl; + const char *str; outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); if(!outcurl) @@ -1047,8 +1047,9 @@ CURL *curl_easy_duphandle(CURL *curl) /* Reinitialize an SSL engine for the new handle * note: the engine name has already been copied by dupset */ - if(outcurl->set.str[STRING_SSL_ENGINE]) { - if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) + str = CURL_EASY_STR(outcurl, STRING_SSL_ENGINE); + if(str) { + if(Curl_ssl_set_engine(outcurl, str)) goto fail; } @@ -1057,8 +1058,9 @@ CURL *curl_easy_duphandle(CURL *curl) outcurl->asi = Curl_altsvc_init(); if(!outcurl->asi) goto fail; - if(outcurl->set.str[STRING_ALTSVC]) - (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); + str = CURL_EASY_STR(outcurl, STRING_ALTSVC); + if(str) + (void)Curl_altsvc_load(outcurl->asi, str); } #endif #ifndef CURL_DISABLE_HSTS @@ -1066,9 +1068,9 @@ CURL *curl_easy_duphandle(CURL *curl) outcurl->hsts = Curl_hsts_init(); if(!outcurl->hsts) goto fail; - if(outcurl->set.str[STRING_HSTS]) - (void)Curl_hsts_loadfile(outcurl, - outcurl->hsts, outcurl->set.str[STRING_HSTS]); + str = CURL_EASY_STR(outcurl, STRING_HSTS); + if(str) + (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, str); (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); /* Copy entries learned at runtime. (E.g. Strict-Transport-Security diff --git a/lib/ftp.c b/lib/ftp.c index 3a4859d18846..5a880de78d0c 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1333,7 +1333,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, curl_socklen_t sslen; char hbuf[NI_MAXHOST]; const char *host = NULL; - const char *string_ftpport = data->set.str[STRING_FTPPORT]; + const char *string_ftpport = CURL_EASY_STR(data, STRING_FTPPORT); struct Curl_dns_entry *dns_entry = NULL; const struct Curl_addrinfo *res = NULL; const struct Curl_addrinfo *ai = NULL; @@ -1477,8 +1477,8 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data, to prepare the server for the upcoming PASV */ if(!ftpc->file) result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s", - data->set.str[STRING_CUSTOMREQUEST] ? - data->set.str[STRING_CUSTOMREQUEST] : + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) ? + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) : (data->state.list_only ? "NLST" : "LIST")); else if(data->state.upload) result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file); @@ -1572,8 +1572,8 @@ static CURLcode ftp_state_list(struct Curl_easy *data, } cmd = curl_maprintf("%s%s%.*s", - data->set.str[STRING_CUSTOMREQUEST] ? - data->set.str[STRING_CUSTOMREQUEST] : + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) ? + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) : (data->state.list_only ? "NLST" : "LIST"), lstArg ? " " : "", lstArglen, lstArg ? lstArg : ""); @@ -2956,7 +2956,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = ftp_state_loggedin(data, ftpc); } else if(ftpcode == 332) { - const char *account = data->set.str[STRING_FTP_ACCOUNT]; + const char *account = CURL_EASY_STR(data, STRING_FTP_ACCOUNT); if(!account) { failf(data, "ACCT requested but none available"); result = CURLE_LOGIN_DENIED; @@ -2977,7 +2977,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, 530 User ... access denied (the server denies to log the specified user) */ - const char *alt = data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]; + const char *alt = CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER); if(alt && !ftpc->ftp_trying_alternative) { /* Ok, USER failed. Let's try the supplied command. */ if(ftp_has_ctrl(alt)) { @@ -4436,16 +4436,16 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* clone connection related data that is FTP specific */ - if(data->set.str[STRING_FTP_ACCOUNT]) { - ftpc->account = curlx_strdup(data->set.str[STRING_FTP_ACCOUNT]); + if(CURL_EASY_STR(data, STRING_FTP_ACCOUNT)) { + ftpc->account = curlx_strdup(CURL_EASY_STR(data, STRING_FTP_ACCOUNT)); if(!ftpc->account) { Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); return CURLE_OUT_OF_MEMORY; } } - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { + if(CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER)) { ftpc->alternative_to_user = - curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + curlx_strdup(CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER)); if(!ftpc->alternative_to_user) { curlx_safefree(ftpc->account); Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); diff --git a/lib/getinfo.c b/lib/getinfo.c index 30ee405e9947..23f1de270c49 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -86,7 +86,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, } break; case CURLINFO_EFFECTIVE_METHOD: { - const char *m = data->set.str[STRING_CUSTOMREQUEST]; + const char *m = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); if(!m) { if(data->set.opt_no_body) m = "HEAD"; @@ -149,7 +149,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, break; case CURLINFO_RTSP_SESSION_ID: #ifndef CURL_DISABLE_RTSP - *param_charp = data->set.str[STRING_RTSP_SESSION_ID]; + *param_charp = CURL_EASY_STR(data, STRING_RTSP_SESSION_ID); #else *param_charp = NULL; #endif diff --git a/lib/http.c b/lib/http.c index 5701bdc09b4e..98608793c79a 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1153,7 +1153,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, static void http_switch_to_get(struct Curl_easy *data, int code) { - const char *req = data->set.str[STRING_CUSTOMREQUEST]; + const char *req = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); if((req || data->state.httpreq != HTTPREQ_GET) && (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) { @@ -1306,8 +1306,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, rewind_result = Curl_req_soft_reset(&data->req, data); infof(data, "Issue another request to this URL: '%s'", follow_url); if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) && - data->set.str[STRING_CUSTOMREQUEST] && - !data->state.http_ignorecustom) { + !data->state.http_ignorecustom && + CURL_EASY_STR(data, STRING_CUSTOMREQUEST)) { data->state.http_ignorecustom = TRUE; infof(data, "Drop custom request method for next request"); } @@ -1991,9 +1991,9 @@ void Curl_http_method(struct Curl_easy *data, httpreq = HTTPREQ_PUT; /* Now set the 'request' pointer to the proper request string */ - if(data->set.str[STRING_CUSTOMREQUEST] && - !data->state.http_ignorecustom) { - request = data->set.str[STRING_CUSTOMREQUEST]; + if(!data->state.http_ignorecustom && + CURL_EASY_STR(data, STRING_CUSTOMREQUEST)) { + request = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); } else { if(data->req.no_body) @@ -2124,8 +2124,8 @@ static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn = data->conn; #endif - if(data->set.str[STRING_TARGET]) { - path = data->set.str[STRING_TARGET]; + if(CURL_EASY_STR(data, STRING_TARGET)) { + path = CURL_EASY_STR(data, STRING_TARGET); query = NULL; } @@ -2194,8 +2194,8 @@ static CURLcode http_target(struct Curl_easy *data, curl_url_cleanup(h); /* target or URL */ - result = curlx_dyn_add(r, data->set.str[STRING_TARGET] ? - data->set.str[STRING_TARGET] : url); + result = curlx_dyn_add(r, CURL_EASY_STR(data, STRING_TARGET) ? + CURL_EASY_STR(data, STRING_TARGET) : url); curlx_free(url); if(result) return result; @@ -2576,12 +2576,12 @@ static CURLcode http_cookies(struct Curl_easy *data, struct dynbuf *r) { CURLcode result = CURLE_OK; - char *addcookies = NULL; + const char *addcookies = NULL; bool linecap = FALSE; - if(data->set.str[STRING_COOKIE] && + if(CURL_EASY_STR(data, STRING_COOKIE) && !Curl_checkheaders(data, STRCONST("Cookie")) && Curl_auth_allowed_to_host(data)) - addcookies = data->set.str[STRING_COOKIE]; + addcookies = CURL_EASY_STR(data, STRING_COOKIE); if(data->cookies || addcookies) { struct Curl_llist list; @@ -2957,14 +2957,12 @@ static CURLcode http_add_hd(struct Curl_easy *data, result = curlx_dyn_add(req, data->state.rangeline); break; - case H1_HD_USER_AGENT: - if(!Curl_checkheaders(data, STRCONST("User-Agent"))) { - if(data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT]) - result = curlx_dyn_addf(req, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - } + case H1_HD_USER_AGENT: { + const char *ua = CURL_EASY_STR(data, STRING_USERAGENT); + if(ua && *ua && !Curl_checkheaders(data, STRCONST("User-Agent"))) + result = curlx_dyn_addf(req, "User-Agent: %s\r\n", ua); break; + } case H1_HD_ACCEPT: if(!Curl_checkheaders(data, STRCONST("Accept"))) @@ -2981,12 +2979,12 @@ static CURLcode http_add_hd(struct Curl_easy *data, #endif break; - case H1_HD_ACCEPT_ENCODING: - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) - result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", - data->set.str[STRING_ENCODING]); + case H1_HD_ACCEPT_ENCODING: { + const char *enc = CURL_EASY_STR(data, STRING_ENCODING); + if(enc && !Curl_checkheaders(data, STRCONST("Accept-Encoding"))) + result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", enc); break; + } case H1_HD_REFERER: if(Curl_bufref_ptr(&data->state.referer) && @@ -3317,7 +3315,7 @@ static CURLcode http_header_c(struct Curl_easy *data, } } while(1); } - v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ? + v = (!k->http_bodyless && CURL_EASY_STR(data, STRING_ENCODING)) ? HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; if(v) { /* diff --git a/lib/http2.c b/lib/http2.c index 47e3e075bc72..9ac0d20fb3fe 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2088,10 +2088,11 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, if(result) goto out; - result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, &nwritten); + result = Curl_h1_req_parse_read( + &stream->h1, buf, len, NULL, + !data->state.http_ignorecustom ? + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) : NULL, + 0, &nwritten); if(result) goto out; *pnwritten = nwritten; diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index b142749f8897..b16c2cd2aaa9 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -816,7 +816,7 @@ static CURLcode parse_sigv4_params(struct Curl_easy *data, struct Curl_str *region, struct Curl_str *service) { - const char *line = data->set.str[STRING_AWS_SIGV4]; + const char *line = CURL_EASY_STR(data, STRING_AWS_SIGV4); if(!line || !*line) line = "aws:amz"; diff --git a/lib/http_httpsig.c b/lib/http_httpsig.c index c0329261dc5b..37f23a09cbe5 100644 --- a/lib/http_httpsig.c +++ b/lib/http_httpsig.c @@ -376,7 +376,7 @@ static CURLcode parse_components(struct Curl_easy *data, size_t *ncomp_out, char **hdrs_copy_out) { - const char *hdrs = data->set.str[STRING_HTTPSIG_HEADERS]; + const char *hdrs = CURL_EASY_STR(data, STRING_HTTPSIG_HEADERS); size_t ncomp = 0; *hdrs_copy_out = NULL; @@ -534,8 +534,8 @@ CURLcode Curl_output_httpsig(struct Curl_easy *data) const char *query; Curl_HttpReq httpreq; const char *method = NULL; - const char *hexkey; - const char *keyid; + const char *hexkey = CURL_EASY_STR(data, STRING_HTTPSIG_KEY); + const char *keyid = CURL_EASY_STR(data, STRING_HTTPSIG_KEYID); enum httpsig_alg alg; time_t created; struct dynbuf sig_params; @@ -560,9 +560,6 @@ CURLcode Curl_output_httpsig(struct Curl_easy *data) return CURLE_BAD_FUNCTION_ARGUMENT; } - hexkey = data->set.str[STRING_HTTPSIG_KEY]; - keyid = data->set.str[STRING_HTTPSIG_KEYID]; - if(!hexkey || !*hexkey) { failf(data, "httpsig: CURLOPT_HTTPSIG_KEY is required"); return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 96cd101ca78c..7774589ecd64 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -201,6 +201,7 @@ static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, proxy_http_ver ver) { char *authority = NULL; + const char *ua; int httpversion = proxy_http_ver_major(ver); CURLcode result; struct httpreq *req = NULL; @@ -242,10 +243,10 @@ static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, goto out; } + ua = CURL_EASY_STR(data, STRING_USERAGENT); if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { - result = Curl_dynhds_cadd(&req->headers, "User-Agent", - data->set.str[STRING_USERAGENT]); + ua && *ua) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", ua); if(result) goto out; } @@ -276,7 +277,7 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, struct Curl_peer *dest, proxy_http_ver ver) { - const char *proxy_scheme = "http"; + const char *proxy_scheme = "http", *ua; const char *proxy_host = cf->conn->http_proxy.peer->hostname; int httpversion = proxy_http_ver_major(ver); char *authority = NULL; @@ -381,11 +382,11 @@ static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, goto out; } + ua = CURL_EASY_STR(data, STRING_USERAGENT); if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { - result = Curl_dynhds_cadd(&req->headers, "User-Agent", - data->set.str[STRING_USERAGENT]); + ua && *ua) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", ua); if(result) goto out; } diff --git a/lib/imap.c b/lib/imap.c index a1a4ac4363b1..d854a2e8ea75 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1923,7 +1923,7 @@ static CURLcode imap_parse_custom_request(struct Curl_easy *data, struct IMAP *imap) { CURLcode result = CURLE_OK; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); if(custom) { /* URL decode the custom request */ diff --git a/lib/pop3.c b/lib/pop3.c index 7634dfb6294e..b32663ce1473 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -250,7 +250,7 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data) { CURLcode result = CURLE_OK; struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); if(!pop3) return CURLE_FAILED_INIT; diff --git a/lib/proxy.c b/lib/proxy.c index b69d89a2ce0a..5d7c02579ddd 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -451,21 +451,21 @@ static CURLcode parse_proxy(struct Curl_easy *data, if(proxyuser || proxypasswd) { result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, - data->set.str[STRING_PROXY_SERVICE_NAME], + CURL_EASY_STR(data, STRING_PROXY_SERVICE_NAME), CREDS_URL, &proxyinfo->creds); if(result) goto error; } else if(!for_pre_proxy && - (data->set.str[STRING_PROXYUSERNAME] || - data->set.str[STRING_PROXYPASSWORD] || - data->set.str[STRING_PROXY_SERVICE_NAME])) { + (CURL_EASY_STR(data, STRING_PROXYUSERNAME) || + CURL_EASY_STR(data, STRING_PROXYPASSWORD) || + CURL_EASY_STR(data, STRING_PROXY_SERVICE_NAME))) { /* No user/passwd in URL, if this is not a pre-proxy, the * CURLOPT_PROXY* settings apply. */ - result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], - data->set.str[STRING_PROXYPASSWORD], + result = Curl_creds_create(CURL_EASY_STR(data, STRING_PROXYUSERNAME), + CURL_EASY_STR(data, STRING_PROXYPASSWORD), NULL, NULL, - data->set.str[STRING_PROXY_SERVICE_NAME], + CURL_EASY_STR(data, STRING_PROXY_SERVICE_NAME), CREDS_OPTION, &proxyinfo->creds); } else @@ -498,7 +498,7 @@ static bool proxy_do_not_proxy(struct Curl_easy *data) if(data->state.origin->scheme->flags & PROTOPT_NONETWORK) return TRUE; - no_proxy = data->set.str[STRING_NOPROXY]; + no_proxy = CURL_EASY_STR(data, STRING_NOPROXY); if(!no_proxy) { const char *p = "no_proxy"; env_no_proxy = curl_getenv(p); @@ -521,6 +521,7 @@ CURLcode Curl_proxy_init_conn(struct Curl_easy *data, { char *proxy = NULL; char *pre_proxy = NULL; + const char *str = NULL; bool do_env_detect = TRUE; CURLcode result = CURLE_OK; @@ -536,9 +537,10 @@ CURLcode Curl_proxy_init_conn(struct Curl_easy *data, * Detect what (if any) proxy to use *************************************************************/ /* the empty config strings disable proxy use and env detects */ - if(data->set.str[STRING_PROXY]) { - if(*data->set.str[STRING_PROXY]) { - proxy = curlx_strdup(data->set.str[STRING_PROXY]); + str = CURL_EASY_STR(data, STRING_PROXY); + if(str) { + if(*str) { + proxy = curlx_strdup(str); /* if global proxy is set, this is it */ if(!proxy) { failf(data, "memory shortage"); @@ -550,9 +552,10 @@ CURLcode Curl_proxy_init_conn(struct Curl_easy *data, do_env_detect = FALSE; } - if(data->set.str[STRING_PRE_PROXY]) { - if(*data->set.str[STRING_PRE_PROXY]) { - pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); + str = CURL_EASY_STR(data, STRING_PRE_PROXY); + if(str) { + if(*str) { + pre_proxy = curlx_strdup(str); /* if global socks proxy is set, this is it */ if(!pre_proxy) { failf(data, "memory shortage"); diff --git a/lib/rtsp.c b/lib/rtsp.c index c31263a8c857..fabdda3d3eed 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -336,11 +336,11 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - b->session_id = data->set.str[STRING_RTSP_SESSION_ID]; + b->session_id = CURL_EASY_STR(data, STRING_RTSP_SESSION_ID); /* Stream URI. Default to server '*' if not specified */ - if(data->set.str[STRING_RTSP_STREAM_URI]) - b->stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; + if(CURL_EASY_STR(data, STRING_RTSP_STREAM_URI)) + b->stream_uri = CURL_EASY_STR(data, STRING_RTSP_STREAM_URI); else b->stream_uri = "*"; @@ -348,10 +348,10 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, b->transport = Curl_checkheaders(data, STRCONST("Transport")); if(rtspreq == RTSPREQ_SETUP && !b->transport) { /* New Transport: setting? */ - if(data->set.str[STRING_RTSP_TRANSPORT]) { - result = rtsp_header_alloc("Transport", - data->set.str[STRING_RTSP_TRANSPORT], - &b->transport); + if(CURL_EASY_STR(data, STRING_RTSP_TRANSPORT)) { + result = rtsp_header_alloc( + "Transport", CURL_EASY_STR(data, STRING_RTSP_TRANSPORT), + &b->transport); if(result) return result; b->transport_alloc = TRUE; @@ -371,9 +371,9 @@ static CURLcode rtsp_setup_request(struct Curl_easy *data, /* Accept-Encoding header */ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { + CURL_EASY_STR(data, STRING_ENCODING)) { result = rtsp_header_alloc("Accept-Encoding", - data->set.str[STRING_ENCODING], + CURL_EASY_STR(data, STRING_ENCODING), &b->accept_encoding); if(result) return result; @@ -430,6 +430,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; const unsigned char rtspreq = data->set.rtspreq; + const char *str; struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); struct dynbuf req_buffer; struct rtsp_blocks block; @@ -506,12 +507,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) block.range ? block.range : "", block.referrer ? block.referrer : ""); - if(!result && - !Curl_checkheaders(data, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) + str = CURL_EASY_STR(data, STRING_USERAGENT); + if(!result && str && *str && + !Curl_checkheaders(data, STRCONST("User-Agent"))) result = curlx_dyn_addf(&req_buffer, - "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); + "User-Agent: %s\r\n", str); if(!result) result = curlx_dyn_addf(&req_buffer, @@ -976,7 +976,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) data->state.rtsp_CSeq_recv = rtsp->CSeq_recv = (uint32_t)CSeq; } else if(checkprefix("Session:", header)) { - const char *start, *end; + const char *start, *end, *str; size_t idlen; /* Find the first non-space letter */ @@ -999,24 +999,24 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) end++; idlen = end - start; - if(data->set.str[STRING_RTSP_SESSION_ID]) { + str = CURL_EASY_STR(data, STRING_RTSP_SESSION_ID); + if(str) { /* If the Session ID is set, then compare */ - if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || - strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { + if(strlen(str) != idlen || + strncmp(start, str, idlen)) { failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", - start, data->set.str[STRING_RTSP_SESSION_ID]); + start, str); return CURLE_RTSP_SESSION_ERROR; } } else { /* If the Session ID is not set, and we find it in a response, then set * it. - */ - - /* Copy the id substring into a new buffer */ - data->set.str[STRING_RTSP_SESSION_ID] = curlx_memdup0(start, idlen); - if(!data->set.str[STRING_RTSP_SESSION_ID]) + * Copy the id substring into a new buffer */ + void *mem = curlx_memdup0(start, idlen); + if(!mem || + CURL_EASY_STR_SETN(data, STRING_RTSP_SESSION_ID, mem)) return CURLE_OUT_OF_MEMORY; } } diff --git a/lib/setopt.c b/lib/setopt.c index d6c641f780a6..fe991a145f61 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -78,23 +78,14 @@ static CURLcode setopt_set_timeout_ms(timediff_t *ptimeout_ms, long ms) return CURLE_OK; } -CURLcode Curl_setstropt(char **charp, const char *s) +CURLcode Curl_setstropt(struct Curl_easy *data, + enum dupstring id, const char *s) { - /* Release the previous storage at `charp' and replace by a dynamic storage - copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - - curlx_safefree(*charp); - - if(s) { - if(strlen(s) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - - *charp = curlx_strdup(s); - if(!*charp) - return CURLE_OUT_OF_MEMORY; - } + DEBUGASSERT((unsigned)id <= UINT8_MAX); + if(s && (strlen(s) > CURL_MAX_INPUT_LENGTH)) + return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_OK; + return CURL_EASY_STR_SET(data, (uint8_t)id, s); } CURLcode Curl_setblobopt(struct curl_blob **blobp, @@ -160,34 +151,34 @@ static CURLcode setstropt_userpwd(const char *option, char **userp, return CURLE_OK; } -static CURLcode setstropt_interface(char *option, char **devp, - char **ifacep, char **hostp) +static CURLcode setstropt_interface(struct Curl_easy *data, char *option) { char *dev = NULL; char *iface = NULL; char *host = NULL; CURLcode result; - DEBUGASSERT(devp); - DEBUGASSERT(ifacep); - DEBUGASSERT(hostp); - if(option) { /* Parse the interface details if set, otherwise clear them all */ result = Curl_parse_interface(option, &dev, &iface, &host); if(result) return result; } - curlx_free(*devp); - *devp = dev; - - curlx_free(*ifacep); - *ifacep = iface; - curlx_free(*hostp); - *hostp = host; - - return CURLE_OK; + result = CURL_EASY_STR_SETN(data, STRING_DEVICE, dev); + dev = NULL; + if(!result) { + result = CURL_EASY_STR_SETN(data, STRING_INTERFACE, iface); + iface = NULL; + } + if(!result) { + result = CURL_EASY_STR_SETN(data, STRING_BINDHOST, host); + host = NULL; + } + curlx_free(dev); + curlx_free(iface); + curlx_free(host); + return result; } #ifdef USE_SSL @@ -952,7 +943,7 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, case CURLOPT_SSL_ENABLE_NPN: break; case CURLOPT_SSLENGINE_DEFAULT: - curlx_safefree(s->str[STRING_SSL_ENGINE]); + CURL_EASY_STR_CLEAR(data, STRING_SSL_ENGINE); result = Curl_ssl_set_engine_default(data); break; default: @@ -1211,9 +1202,8 @@ static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, case CURLOPT_POSTFIELDSIZE: if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - if(s->postfieldsize < arg && - s->postfields == s->str[STRING_COPYPOSTFIELDS]) { - curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); + if(s->postfieldsize < arg && s->str_copypostfields) { + curlx_safefree(s->str_copypostfields); s->postfields = NULL; } s->postfieldsize = arg; @@ -1458,7 +1448,7 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, * pass CURLU to set URL */ Curl_bufref_free(&data->state.url); - curlx_safefree(s->str[STRING_SET_URL]); + CURL_EASY_STR_CLEAR(data, STRING_SET_URL); s->uh = va_arg(param, CURLU *); break; #ifndef CURL_DISABLE_HTTP @@ -1604,13 +1594,14 @@ static CURLcode cookiefile(struct Curl_easy *data, const char *ptr) #ifndef CURL_DISABLE_PROXY static CURLcode setproxy(struct Curl_easy *data, const char *proxy) { - if((data->set.str[STRING_PROXY] && proxy) && + const char *str = CURL_EASY_STR(data, STRING_PROXY); + if(str && proxy && /* there was one set, is this a new one? */ - !strcmp(data->set.str[STRING_PROXY], proxy)) + !strcmp(str, proxy)) return CURLE_OK; /* same one as before */ changeproxy(data); - return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); + return Curl_setstropt(data, STRING_PROXY, proxy); } static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, @@ -1629,15 +1620,21 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, /* URL decode the components */ if(!result) { - curlx_safefree(s->str[STRING_PROXYUSERNAME]); - curlx_safefree(s->str[STRING_PROXYPASSWORD]); - if(u) - result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); + char *str = NULL; + CURL_EASY_STR_CLEAR(data, STRING_PROXYUSERNAME); + CURL_EASY_STR_CLEAR(data, STRING_PROXYPASSWORD); + if(u) { + result = Curl_urldecode(u, 0, &str, NULL, REJECT_ZERO); + if(!result) + result = Curl_u8_strset_setn(&s->strings, STRING_PROXYUSERNAME, str); + } + if(!result && p) { + str = NULL; + result = Curl_urldecode(p, 0, &str, NULL, REJECT_ZERO); + if(!result) + result = Curl_u8_strset_setn(&s->strings, STRING_PROXYPASSWORD, str); + } } - if(!result && p) - result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, - REJECT_ZERO); curlx_free(u); curlx_strzero(p); curlx_free(p); @@ -1647,55 +1644,55 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, /* * authentication username to use in the operation */ - return Curl_setstropt(&s->str[STRING_PROXYUSERNAME], ptr); + return Curl_setstropt(data, STRING_PROXYUSERNAME, ptr); case CURLOPT_PROXYPASSWORD: /* * authentication password to use in the operation */ - return Curl_setstropt(&s->str[STRING_PROXYPASSWORD], ptr); + return Curl_setstropt(data, STRING_PROXYPASSWORD, ptr); case CURLOPT_NOPROXY: /* * proxy exception list */ - return Curl_setstropt(&s->str[STRING_NOPROXY], ptr); + return Curl_setstropt(data, STRING_NOPROXY, ptr); case CURLOPT_PROXY_SSLCERT: /* * String that holds filename of the SSL certificate to use for proxy */ - return Curl_setstropt(&s->str[STRING_CERT_PROXY], ptr); + return Curl_setstropt(data, STRING_CERT_PROXY, ptr); case CURLOPT_PROXY_SSLCERTTYPE: /* * String that holds file type of the SSL certificate to use for proxy */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE_PROXY], ptr); + return Curl_setstropt(data, STRING_CERT_TYPE_PROXY, ptr); case CURLOPT_PROXY_SSLKEY: /* * String that holds filename of the SSL key to use for proxy */ - return Curl_setstropt(&s->str[STRING_KEY_PROXY], ptr); + return Curl_setstropt(data, STRING_KEY_PROXY, ptr); case CURLOPT_PROXY_KEYPASSWD: /* * String that holds the SSL private key password for proxy. */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD_PROXY], ptr); + return Curl_setstropt(data, STRING_KEY_PASSWD_PROXY, ptr); case CURLOPT_PROXY_SSLKEYTYPE: /* * String that holds file type of the SSL key to use for proxy */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE_PROXY], ptr); + return Curl_setstropt(data, STRING_KEY_TYPE_PROXY, ptr); case CURLOPT_PROXY_SSL_CIPHER_LIST: if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { /* set a list of cipher we want to use in the SSL connection for proxy */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST_PROXY], ptr); + return Curl_setstropt(data, STRING_SSL_CIPHER_LIST_PROXY, ptr); } else return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_TLS13_CIPHERS: if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) /* set preferred list of TLS 1.3 cipher suites for proxy */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST_PROXY], ptr); + return Curl_setstropt(data, STRING_SSL_CIPHER13_LIST_PROXY, ptr); else return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY: @@ -1717,13 +1714,13 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * If the proxy is set to "" or NULL we explicitly say that we do not want * to use the socks proxy. */ - return Curl_setstropt(&s->str[STRING_PRE_PROXY], ptr); + return Curl_setstropt(data, STRING_PRE_PROXY, ptr); case CURLOPT_SOCKS5_GSSAPI_SERVICE: case CURLOPT_PROXY_SERVICE_NAME: /* * Set proxy authentication service name for Kerberos 5 and SPNEGO */ - return Curl_setstropt(&s->str[STRING_PROXY_SERVICE_NAME], ptr); + return Curl_setstropt(data, STRING_PROXY_SERVICE_NAME, ptr); case CURLOPT_PROXY_PINNEDPUBLICKEY: /* * Set pinned public key for SSL connection. @@ -1731,7 +1728,7 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY_PROXY], ptr); + return Curl_setstropt(data, STRING_SSL_PINNEDPUBLICKEY_PROXY, ptr); #endif return CURLE_NOT_BUILT_IN; @@ -1739,18 +1736,19 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, /* * Set the client IP to send through HAProxy PROXY protocol */ - result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); + result = Curl_setstropt(data, STRING_HAPROXY_CLIENT_IP, ptr); /* enable the HAProxy protocol if an IP is provided */ - s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; + s->haproxyprotocol = !!CURL_EASY_STR(data, STRING_HAPROXY_CLIENT_IP); break; case CURLOPT_PROXY_CAINFO: /* * Set CA info SSL connection for proxy. Specify filename of the * CA certificate */ - result = Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); - s->proxy_ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE_PROXY]; + result = Curl_setstropt(data, STRING_SSL_CAFILE_PROXY, ptr); + s->proxy_ssl.custom_cafile = + !!CURL_EASY_STR(data, STRING_SSL_CAFILE_PROXY); return result; case CURLOPT_PROXY_CRLFILE: /* @@ -1758,14 +1756,14 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * CRL to check certificates revocation */ if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + return Curl_setstropt(data, STRING_SSL_CRLFILE_PROXY, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT: /* * Set Issuer certificate file to check certificates issuer */ if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + return Curl_setstropt(data, STRING_SSL_ISSUERCERT_PROXY, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_CAPATH: /* @@ -1775,8 +1773,9 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - result = Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); - s->proxy_ssl.custom_capath = !!s->str[STRING_SSL_CAPATH_PROXY]; + result = Curl_setstropt(data, STRING_SSL_CAPATH_PROXY, ptr); + s->proxy_ssl.custom_capath = + !!CURL_EASY_STR(data, STRING_SSL_CAPATH_PROXY); return result; } #endif @@ -1799,8 +1798,16 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) CURLcode result = CURLE_OK; if(s->postfieldsize < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - if(!ptr || s->postfieldsize == -1) - result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); + if(!ptr || s->postfieldsize == -1) { + if(ptr && (strlen(ptr) > CURL_MAX_INPUT_LENGTH)) + return CURLE_BAD_FUNCTION_ARGUMENT; + curlx_safefree(s->str_copypostfields); + if(ptr) { + s->str_copypostfields = curlx_strdup(ptr); + if(!s->str_copypostfields) + return CURLE_OUT_OF_MEMORY; + } + } else { size_t pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); if(pflen == SIZE_MAX) @@ -1813,13 +1820,13 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) if(!p) return CURLE_OUT_OF_MEMORY; else { - curlx_free(s->str[STRING_COPYPOSTFIELDS]); - s->str[STRING_COPYPOSTFIELDS] = p; + curlx_free(s->str_copypostfields); + s->str_copypostfields = p; } } } - s->postfields = s->str[STRING_COPYPOSTFIELDS]; + s->postfields = s->str_copypostfields; s->method = HTTPREQ_POST; return result; } @@ -1847,12 +1854,12 @@ static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) else if(plen > 4 && !strncmp(ptr, "ecl:", 4)) { if(!s->tls_ech) s->tls_ech = CURLECH_HARD; - result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); + result = Curl_setstropt(data, STRING_ECH_CONFIG, ptr + 4); } else if(plen > 3 && !strncmp(ptr, "pn:", 3)) { if(!s->tls_ech) s->tls_ech = CURLECH_HARD; - result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); + result = Curl_setstropt(data, STRING_ECH_PUBLIC, ptr + 3); } else result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -1870,22 +1877,21 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, char *ptr) { CURLcode result = CURLE_OK; - struct UserDefined *s = &data->set; switch(option) { case CURLOPT_KEYPASSWD: /* * String that holds the SSL or SSH private key password. */ - result = Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); + result = Curl_setstropt(data, STRING_KEY_PASSWD, ptr); break; #ifdef USE_SSL case CURLOPT_CAINFO: /* * Set CA info for SSL connection. Specify filename of the CA certificate */ - result = Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); - s->ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE]; + result = Curl_setstropt(data, STRING_SSL_CAFILE, ptr); + data->set.ssl.custom_cafile = !!CURL_EASY_STR(data, STRING_SSL_CAFILE); return result; case CURLOPT_CAPATH: /* @@ -1894,8 +1900,8 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, */ if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - result = Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); - s->ssl.custom_capath = !!s->str[STRING_SSL_CAPATH]; + result = Curl_setstropt(data, STRING_SSL_CAPATH, ptr); + data->set.ssl.custom_capath = !!CURL_EASY_STR(data, STRING_SSL_CAPATH); return result; } return CURLE_NOT_BUILT_IN; @@ -1905,18 +1911,18 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * to check certificates revocation */ if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE], ptr); + return Curl_setstropt(data, STRING_SSL_CRLFILE, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_CIPHER_LIST: if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) /* set a list of cipher we want to use in the SSL connection */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST], ptr); + return Curl_setstropt(data, STRING_SSL_CIPHER_LIST, ptr); else return CURLE_NOT_BUILT_IN; case CURLOPT_TLS13_CIPHERS: if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) /* set preferred list of TLS 1.3 cipher suites */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST], ptr); + return Curl_setstropt(data, STRING_SSL_CIPHER13_LIST, ptr); else return CURLE_NOT_BUILT_IN; case CURLOPT_RANDOM_FILE: @@ -1928,7 +1934,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * Set an SSL_CTX callback parameter pointer */ if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { - s->ssl.fsslctxp = ptr; + data->set.ssl.fsslctxp = ptr; break; } else @@ -1937,28 +1943,28 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, /* * String that holds filename of the SSL certificate to use */ - return Curl_setstropt(&s->str[STRING_CERT], ptr); + return Curl_setstropt(data, STRING_CERT, ptr); case CURLOPT_SSLCERTTYPE: /* * String that holds file type of the SSL certificate to use */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); + return Curl_setstropt(data, STRING_CERT_TYPE, ptr); case CURLOPT_SSLKEY: /* * String that holds filename of the SSL key to use */ - return Curl_setstropt(&s->str[STRING_KEY], ptr); + return Curl_setstropt(data, STRING_KEY, ptr); case CURLOPT_SSLKEYTYPE: /* * String that holds file type of the SSL key to use */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); + return Curl_setstropt(data, STRING_KEY_TYPE, ptr); case CURLOPT_SSLENGINE: /* * String that holds the SSL crypto engine. */ if(ptr && ptr[0]) { - result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); + result = Curl_setstropt(data, STRING_SSL_ENGINE, ptr); if(!result) { result = Curl_ssl_set_engine(data, ptr); } @@ -1970,7 +1976,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * to check certificates issuer */ if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return Curl_setstropt(data, STRING_SSL_ISSUERCERT, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_EC_CURVES: /* @@ -1978,7 +1984,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * Specify colon-delimited list of curve algorithm names. */ if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) - return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); + return Curl_setstropt(data, STRING_SSL_EC_CURVES, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_SIGNATURE_ALGORITHMS: /* @@ -1986,7 +1992,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * Specify colon-delimited list of signature scheme names. */ if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) - return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); + return Curl_setstropt(data, STRING_SSL_SIGNATURE_ALGORITHMS, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_PINNEDPUBLICKEY: /* @@ -1994,7 +2000,7 @@ static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, * Specify filename of the public key in DER format. */ if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); + return Curl_setstropt(data, STRING_SSL_PINNEDPUBLICKEY, ptr); return CURLE_NOT_BUILT_IN; case CURLOPT_ECH: return setopt_ech(data, ptr); @@ -2023,7 +2029,7 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, */ s->postfields = ptr; /* Release old copied data. */ - curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str_copypostfields); s->method = HTTPREQ_POST; break; @@ -2043,15 +2049,13 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, */ if(ptr && !*ptr) { ptr = Curl_get_content_encodings(); - if(ptr) { - curlx_free(s->str[STRING_ENCODING]); - s->str[STRING_ENCODING] = ptr; - } + if(ptr) + result = CURL_EASY_STR_SETN(data, STRING_ENCODING, ptr); else result = CURLE_OUT_OF_MEMORY; return result; } - return Curl_setstropt(&s->str[STRING_ENCODING], ptr); + return Curl_setstropt(data, STRING_ENCODING, ptr); #ifndef CURL_DISABLE_AWS case CURLOPT_AWS_SIGV4: @@ -2059,23 +2063,23 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, * String that is merged to some authentication * parameters are used by the algorithm. */ - result = Curl_setstropt(&s->str[STRING_AWS_SIGV4], ptr); + result = Curl_setstropt(data, STRING_AWS_SIGV4, ptr); /* * Basic has been set by default; it needs to be unset here. */ - if(s->str[STRING_AWS_SIGV4]) + if(CURL_EASY_STR(data, STRING_AWS_SIGV4)) s->httpauth = CURLAUTH_AWS_SIGV4; break; #endif #ifndef CURL_DISABLE_HTTPSIG case CURLOPT_HTTPSIG_KEY: - result = Curl_setstropt(&s->str[STRING_HTTPSIG_KEY], ptr); + result = Curl_setstropt(data, STRING_HTTPSIG_KEY, ptr); break; case CURLOPT_HTTPSIG_KEYID: - result = Curl_setstropt(&s->str[STRING_HTTPSIG_KEYID], ptr); + result = Curl_setstropt(data, STRING_HTTPSIG_KEYID, ptr); break; case CURLOPT_HTTPSIG_HEADERS: - result = Curl_setstropt(&s->str[STRING_HTTPSIG_HEADERS], ptr); + result = Curl_setstropt(data, STRING_HTTPSIG_HEADERS, ptr); break; #endif case CURLOPT_REFERER: @@ -2083,21 +2087,21 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, * String to set in the HTTP Referer: field. */ Curl_bufref_free(&data->state.referer); - result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); + result = Curl_setstropt(data, STRING_SET_REFERER, ptr); break; case CURLOPT_USERAGENT: /* * String to use in the HTTP User-Agent field */ - return Curl_setstropt(&s->str[STRING_USERAGENT], ptr); + return Curl_setstropt(data, STRING_USERAGENT, ptr); #ifndef CURL_DISABLE_COOKIES case CURLOPT_COOKIE: /* * Cookie string to send to the remote server in the request. */ - return Curl_setstropt(&s->str[STRING_COOKIE], ptr); + return Curl_setstropt(data, STRING_COOKIE, ptr); case CURLOPT_COOKIEFILE: return cookiefile(data, ptr); @@ -2106,7 +2110,7 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, /* * Set cookie filename to dump all cookies to when we are done. */ - result = Curl_setstropt(&s->str[STRING_COOKIEJAR], ptr); + result = Curl_setstropt(data, STRING_COOKIEJAR, ptr); if(!result) { /* * Activate the cookie parser. This may or may not already @@ -2143,12 +2147,12 @@ static CURLcode setopt_cptr_ssh(struct Curl_easy *data, CURLoption option, /* * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); + return Curl_setstropt(data, STRING_SSH_PUBLIC_KEY, ptr); case CURLOPT_SSH_PRIVATE_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa file */ - return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); + return Curl_setstropt(data, STRING_SSH_PRIVATE_KEY, ptr); case CURLOPT_SSH_KEYDATA: /* * Custom client data to pass to the SSH keyfunc callback @@ -2160,18 +2164,18 @@ static CURLcode setopt_cptr_ssh(struct Curl_easy *data, CURLoption option, * Option to allow for the MD5 of the host public key to be checked * for validation purposes. */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); + return Curl_setstropt(data, STRING_SSH_HOST_PUBLIC_KEY_MD5, ptr); case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* * Option to allow for the SHA256 of the host public key to be checked * for validation purposes. */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); + return Curl_setstropt(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256, ptr); case CURLOPT_SSH_KNOWNHOSTS: /* * Store the filename to read known hosts from. */ - return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); + return Curl_setstropt(data, STRING_SSH_KNOWNHOSTS, ptr); #ifdef USE_LIBSSH2 case CURLOPT_SSH_HOSTKEYDATA: /* @@ -2198,15 +2202,15 @@ static CURLcode setopt_cptr_ftp(struct Curl_easy *data, CURLoption option, /* * Use FTP PORT, this also specifies which IP address to use */ - result = Curl_setstropt(&s->str[STRING_FTPPORT], ptr); - s->ftp_use_port = !!(s->str[STRING_FTPPORT]); + result = Curl_setstropt(data, STRING_FTPPORT, ptr); + s->ftp_use_port = !!CURL_EASY_STR(data, STRING_FTPPORT); break; case CURLOPT_FTP_ACCOUNT: - return Curl_setstropt(&s->str[STRING_FTP_ACCOUNT], ptr); + return Curl_setstropt(data, STRING_FTP_ACCOUNT, ptr); case CURLOPT_FTP_ALTERNATIVE_TO_USER: - return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); + return Curl_setstropt(data, STRING_FTP_ALTERNATIVE_TO_USER, ptr); case CURLOPT_KRBLEVEL: return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ @@ -2226,44 +2230,41 @@ static CURLcode setopt_cptr_ftp(struct Curl_easy *data, CURLoption option, static CURLcode setopt_cptr_net(struct Curl_easy *data, CURLoption option, char *ptr) { - struct UserDefined *s = &data->set; switch(option) { case CURLOPT_INTERFACE: /* * Set what interface or address/hostname to bind the socket to when * performing an operation and thus what from-IP your connection will use. */ - return setstropt_interface(ptr, - &s->str[STRING_DEVICE], - &s->str[STRING_INTERFACE], - &s->str[STRING_BINDHOST]); + return setstropt_interface(data, ptr); + #ifdef USE_RESOLV_ARES case CURLOPT_DNS_SERVERS: - return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); + return Curl_setstropt(data, STRING_DNS_SERVERS, ptr); case CURLOPT_DNS_INTERFACE: - return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); + return Curl_setstropt(data, STRING_DNS_INTERFACE, ptr); case CURLOPT_DNS_LOCAL_IP4: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); + return Curl_setstropt(data, STRING_DNS_LOCAL_IP4, ptr); case CURLOPT_DNS_LOCAL_IP6: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); + return Curl_setstropt(data, STRING_DNS_LOCAL_IP6, ptr); #endif #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: - s->abstract_unix_socket = FALSE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); + data->set.abstract_unix_socket = FALSE; + return Curl_setstropt(data, STRING_UNIX_SOCKET_PATH, ptr); case CURLOPT_ABSTRACT_UNIX_SOCKET: - s->abstract_unix_socket = TRUE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); + data->set.abstract_unix_socket = TRUE; + return Curl_setstropt(data, STRING_UNIX_SOCKET_PATH, ptr); #endif #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_URL: { - CURLcode result = Curl_setstropt(&s->str[STRING_DOH], ptr); - s->doh = !!(s->str[STRING_DOH]); + CURLcode result = Curl_setstropt(data, STRING_DOH, ptr); + data->set.doh = !!CURL_EASY_STR(data, STRING_DOH); return result; } #endif @@ -2280,19 +2281,19 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, switch(option) { case CURLOPT_REQUEST_TARGET: - return Curl_setstropt(&s->str[STRING_TARGET], ptr); + return Curl_setstropt(data, STRING_TARGET, ptr); #ifndef CURL_DISABLE_NETRC case CURLOPT_NETRC_FILE: - return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); + return Curl_setstropt(data, STRING_NETRC_FILE, ptr); #endif case CURLOPT_CUSTOMREQUEST: - return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); + return Curl_setstropt(data, STRING_CUSTOMREQUEST, ptr); /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we were using the already set type and this changes the actual request keyword */ case CURLOPT_SERVICE_NAME: - return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); + return Curl_setstropt(data, STRING_SERVICE_NAME, ptr); case CURLOPT_HEADERDATA: s->writeheader = ptr; @@ -2334,27 +2335,40 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, s->errorbuffer = ptr; break; case CURLOPT_URL: - result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); - Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); + result = Curl_setstropt(data, STRING_SET_URL, ptr); + Curl_bufref_set(&data->state.url, + CURL_EASY_STR(data, STRING_SET_URL), 0, NULL); break; - case CURLOPT_USERPWD: - return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], - &s->str[STRING_PASSWORD]); + case CURLOPT_USERPWD: { + char *u = NULL, *p = NULL; + result = setstropt_userpwd(ptr, &u, &p); + if(!result) { + result = CURL_EASY_STR_SETN(data, STRING_USERNAME, u); + u = NULL; + } + if(!result) { + result = CURL_EASY_STR_SETN(data, STRING_PASSWORD, p); + p = NULL; + } + curlx_free(u); + curlx_free(p); + return result; + } case CURLOPT_USERNAME: - return Curl_setstropt(&s->str[STRING_USERNAME], ptr); + return Curl_setstropt(data, STRING_USERNAME, ptr); case CURLOPT_PASSWORD: - return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); + return Curl_setstropt(data, STRING_PASSWORD, ptr); case CURLOPT_LOGIN_OPTIONS: - return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); + return Curl_setstropt(data, STRING_OPTIONS, ptr); case CURLOPT_XOAUTH2_BEARER: - return Curl_setstropt(&s->str[STRING_BEARER], ptr); + return Curl_setstropt(data, STRING_BEARER, ptr); case CURLOPT_RANGE: - return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); + return Curl_setstropt(data, STRING_SET_RANGE, ptr); case CURLOPT_PRIVATE: s->private_data = ptr; break; @@ -2382,25 +2396,25 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, break; case CURLOPT_DEFAULT_PROTOCOL: /* Set the protocol to use when the URL does not include any protocol */ - return Curl_setstropt(&s->str[STRING_DEFAULT_PROTOCOL], ptr); + return Curl_setstropt(data, STRING_DEFAULT_PROTOCOL, ptr); #ifndef CURL_DISABLE_SMTP case CURLOPT_MAIL_FROM: /* Set the SMTP mail originator */ - return Curl_setstropt(&s->str[STRING_MAIL_FROM], ptr); + return Curl_setstropt(data, STRING_MAIL_FROM, ptr); case CURLOPT_MAIL_AUTH: /* Set the SMTP auth originator */ - return Curl_setstropt(&s->str[STRING_MAIL_AUTH], ptr); + return Curl_setstropt(data, STRING_MAIL_AUTH, ptr); #endif case CURLOPT_SASL_AUTHZID: /* Authorization identity (identity to act as) */ - return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); + return Curl_setstropt(data, STRING_SASL_AUTHZID, ptr); #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: - return Curl_setstropt(&s->str[STRING_RTSP_SESSION_ID], ptr); + return Curl_setstropt(data, STRING_RTSP_SESSION_ID, ptr); case CURLOPT_RTSP_STREAM_URI: - return Curl_setstropt(&s->str[STRING_RTSP_STREAM_URI], ptr); + return Curl_setstropt(data, STRING_RTSP_STREAM_URI, ptr); case CURLOPT_RTSP_TRANSPORT: - return Curl_setstropt(&s->str[STRING_RTSP_TRANSPORT], ptr); + return Curl_setstropt(data, STRING_RTSP_TRANSPORT, ptr); case CURLOPT_INTERLEAVEDATA: s->rtp_out = ptr; break; @@ -2427,7 +2441,7 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, return CURLE_OUT_OF_MEMORY; } if(ptr) { - result = Curl_setstropt(&s->str[STRING_HSTS], ptr); + result = Curl_setstropt(data, STRING_HSTS, ptr); if(result) return result; /* this needs to build a list of filenames to read from, so that it can @@ -2459,7 +2473,7 @@ static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, if(!data->asi) return CURLE_OUT_OF_MEMORY; } - result = Curl_setstropt(&s->str[STRING_ALTSVC], ptr); + result = Curl_setstropt(data, STRING_ALTSVC, ptr); if(result) break; if(ptr) @@ -2719,10 +2733,9 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, if(offt < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - if(s->postfieldsize < offt && - s->postfields == s->str[STRING_COPYPOSTFIELDS]) { + if(s->postfieldsize < offt && s->str_copypostfields) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str_copypostfields); s->postfields = NULL; } s->postfieldsize = offt; diff --git a/lib/setopt.h b/lib/setopt.h index c421f5c5e5bc..e386262f88cc 100644 --- a/lib/setopt.h +++ b/lib/setopt.h @@ -31,7 +31,8 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, #define Curl_setopt_SSLVERSION(a, b, c) CURLE_NOT_BUILT_IN #endif -CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT; +CURLcode Curl_setstropt(struct Curl_easy *data, + enum dupstring id, const char *s) WARN_UNUSED_RESULT; CURLcode Curl_setblobopt(struct curl_blob **blobp, const struct curl_blob *blob) WARN_UNUSED_RESULT; CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) diff --git a/lib/smtp.c b/lib/smtp.c index c01151e45573..fbbac28327de 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -207,7 +207,7 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data, struct SMTP *smtp) { CURLcode result = CURLE_OK; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); /* URL decode the custom request */ if(custom) @@ -912,6 +912,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, char *from = NULL; char *auth = NULL; char *size = NULL; + const char *str; CURLcode result = CURLE_OK; /* We notify the server we are sending UTF-8 data if a) it supports the @@ -921,15 +922,15 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, bool utf8 = FALSE; /* Calculate the FROM parameter */ - if(data->set.str[STRING_MAIL_FROM]) { + str = CURL_EASY_STR(data, STRING_MAIL_FROM); + if(str) { char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; const char *suffix = ""; /* Parse the FROM mailbox into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM], - &address, &host, &suffix); + result = smtp_parse_address(data, str, &address, &host, &suffix); if(result) goto out; @@ -962,16 +963,16 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, } /* Calculate the optional AUTH parameter */ - if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) { - if(data->set.str[STRING_MAIL_AUTH][0] != '\0') { + str = CURL_EASY_STR(data, STRING_MAIL_AUTH); + if(str && smtpc->sasl.authused) { + if(str[0] != '\0') { char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; const char *suffix = ""; /* Parse the AUTH mailbox into the local address and hostname parts, converting the hostname to an IDN A-label if necessary */ - result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH], - &address, &host, &suffix); + result = smtp_parse_address(data, str, &address, &host, &suffix); if(result) goto out; diff --git a/lib/transfer.c b/lib/transfer.c index acab3e1edbfb..1e7f3cd1e519 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -451,7 +451,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) * By resetting it here, we ensure each new request starts fresh. */ data->state.retrycount = 0; - if(!data->set.str[STRING_SET_URL] && !data->set.uh) { + if(!CURL_EASY_STR(data, STRING_SET_URL) && !data->set.uh) { /* we cannot do anything without URL */ failf(data, "No URL set"); return CURLE_URL_MALFORMAT; @@ -460,19 +460,22 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) /* CURLOPT_CURLU overrides CURLOPT_URL and the contents of the CURLU handle is allowed to be changed by the user between transfers */ if(data->set.uh) { + char *url = NULL; CURLUcode uc; - curlx_free(data->set.str[STRING_SET_URL]); - uc = curl_url_get(data->set.uh, - CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); + uc = curl_url_get(data->set.uh, CURLUPART_URL, &url, 0); if(uc) { /* clear the pointer to not point to freed memory anymore */ Curl_bufref_set(&data->state.url, NULL, 0, NULL); failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } + result = CURL_EASY_STR_SETN(data, STRING_SET_URL, url); + if(result) + return result; } - Curl_bufref_set(&data->state.url, data->set.str[STRING_SET_URL], 0, NULL); + Curl_bufref_set(&data->state.url, CURL_EASY_STR(data, STRING_SET_URL), + 0, NULL); if(data->set.postfields && data->set.set_resume_from) { /* we cannot */ @@ -505,9 +508,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) Curl_data_priority_clear_state(data); if(data->set.http_auto_referer) Curl_bufref_free(&data->state.referer); - if(data->set.str[STRING_SET_REFERER]) - Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], - 0, NULL); + if(CURL_EASY_STR(data, STRING_SET_REFERER)) + Curl_bufref_set(&data->state.referer, + CURL_EASY_STR(data, STRING_SET_REFERER), 0, NULL); else Curl_bufref_free(&data->state.referer); diff --git a/lib/uint-hashset.c b/lib/uint-hashset.c new file mode 100644 index 000000000000..fabc0aee9ca7 --- /dev/null +++ b/lib/uint-hashset.c @@ -0,0 +1,305 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "uint-hashset.h" + +/* random patterns for API verification */ +#ifdef DEBUGBUILD +#define CURL_U8_STRSET_MAGIC 0x7117e783 +#endif + +#define CURL_U8_STRSET_DEBUG 0 + +#define CURL_SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) + +static const uint8_t u8_smask[] = { + 0x00U, + 0x01U, + 0x03U, + 0x07U, + 0x0FU, + 0x1FU, + 0x3FU, + 0x7FU, + 0xFFU, +}; + +#define CURL_U8_SET_SLOT_IDX(s, i) (uint8_t)((i) & u8_smask[(s)->slotbits]) +#define CURL_U8_SLOT_CNT(i) ((uint16_t)u8_smask[(i)] + 1) +#define CURL_U8_SET_SLOT_CNT(s) CURL_U8_SLOT_CNT((s)->slotbits) + +/* A hashset for tuples (id, string) using Robin Hood Hashing. + * + * The basic idea here to handle collisions by robbing "rich" entries and + * giving to the "poor": + * - We have an array: (id, string) are ideally placed at index "id % size". + * - If slot at index is already occupied, we have a collision. + * - A simple collision strategy would look at the next index, and the + * next until until finding an empty slot. + * - The drawback is that this may lead to many checks on lookups, as it + * will need to also look at subsequent slots until it finds the match. + * The amount of lookups is the "probe sequence length" (psl) and this + * may vary greatly between entries. + * - Robin Hood Hashing balances the 'psl's of all entries more evenly: + * - psl == 0 means an entry is in exactly the right slot + * - pasl == 1 means it is in the slot right after. psl == 2 is the slot + * after that, etc. + * - when inserting a new entry, track its psl. Finding a slot where + * the existing entry has a lower psl makes a swap. Put the new entry + * and its psl there, take the previous entry and its psl and find + * the next best slot for the previous entry. */ +void Curl_u8_strset_init(struct u8_strset *set) +{ +#if defined(__GNUC__) && __GNUC__ >= 13 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + memset(set, 0, sizeof(*set)); +#if defined(__GNUC__) && __GNUC__ >= 13 +#pragma GCC diagnostic pop +#endif + set->data = set->sdata; + set->ids = set->sids; + set->psl = set->spsl; + set->slotbits = CURL_U8_STRSET_START_BITS; + set->count = 0; +#ifdef DEBUGBUILD + set->init = CURL_U8_STRSET_MAGIC; +#endif +} + +void Curl_u8_strset_clear(struct u8_strset *set) +{ + uint16_t i; + DEBUGASSERT(set->init == CURL_U8_STRSET_MAGIC); + for(i = 0; i < CURL_U8_SET_SLOT_CNT(set); ++i) + curlx_safefree(set->data[i]); + + if(set->data != set->sdata) + curlx_safefree(set->data); + Curl_u8_strset_init(set); +} + +static void u8_strset_addn(struct u8_strset *set, uint8_t id, char *val) +{ + uint8_t i = CURL_U8_SET_SLOT_IDX(set, id); + uint8_t psl = 0; + while(set->data[i]) { + if(psl > set->psl[i]) { /* SWAP */ + char *tmpdata; + tmpdata = set->data[i]; + set->data[i] = val; + val = tmpdata; + CURL_SWAP(set->psl[i], psl); + CURL_SWAP(set->ids[i], id); + } + i = CURL_U8_SET_SLOT_IDX(set, i + 1); + ++psl; + } + set->ids[i] = id; + set->data[i] = val; + set->psl[i] = psl; + ++set->count; +} + +static bool u8_strset_grow(struct u8_strset *set) +{ + uint8_t i, *prev_ids, nslotbits; + uint16_t prev_slots; + char **prev_data; + size_t nslots; + void *d; + + if(set->slotbits >= 8) + return FALSE; + nslotbits = (uint8_t)(set->slotbits + 1); +#if CURL_U8_STRSET_DEBUG + curl_mfprintf(stderr, "u8_strset_grow from %d to %d\n", + set->slotbits, nslotbits); +#endif + nslots = CURL_U8_SLOT_CNT(nslotbits); + d = curlx_calloc(1, (nslots * sizeof(char *)) + (2 * nslots)); + if(!d) + return FALSE; + + prev_data = set->data; + prev_ids = set->ids; + prev_slots = set->slotbits; +#if defined(__GNUC__) && __GNUC__ >= 13 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-allocation-size" +#endif + set->data = (char **)d; +#if defined(__GNUC__) && __GNUC__ >= 13 +#pragma GCC diagnostic pop +#endif + set->ids = (uint8_t *)d + (nslots * sizeof(char *)); + set->psl = set->ids + nslots; + set->slotbits = nslotbits; + set->count = 0; + /* re-add previous entries */ + for(i = 0; i < CURL_U8_SLOT_CNT(prev_slots); ++i) { + if(prev_data[i]) + u8_strset_addn(set, prev_ids[i], prev_data[i]); + } + if(prev_data != set->sdata) + curlx_free(prev_data); + return TRUE; +} + +static bool u8_strset_get_index(struct u8_strset *set, + uint8_t id, uint8_t *pindex) +{ + uint8_t i = CURL_U8_SET_SLOT_IDX(set, id); + uint8_t psl = 0; + while(set->data[i] && (psl <= set->psl[i])) { + if(set->ids[i] == id) { + *pindex = i; +#if CURL_U8_STRSET_DEBUG + curl_mfprintf(stderr, "u8_strset_index %d=%s\n", id, set->data[i]); +#endif + return TRUE; + } + i = CURL_U8_SET_SLOT_IDX(set, i + 1); + ++psl; + } +#if CURL_U8_STRSET_DEBUG + curl_mfprintf(stderr, "u8_strset_index %d not found\n", id); +#endif + *pindex = 0; + return FALSE; +} + +uint16_t Curl_u8_strset_count(struct u8_strset *set) +{ + return set->count; +} + +const char *Curl_u8_strset_get(struct u8_strset *set, uint8_t id) +{ + uint8_t i; + DEBUGASSERT(set->init == CURL_U8_STRSET_MAGIC); + if(u8_strset_get_index(set, id, &i)) + return set->data[i]; + return NULL; +} + +CURLcode Curl_u8_strset_setn(struct u8_strset *set, + uint8_t id, char *str) +{ + uint8_t i; + + DEBUGASSERT(set->init == CURL_U8_STRSET_MAGIC); +#if CURL_U8_STRSET_DEBUG + curl_mfprintf(stderr, "u8_strset_setn %d=%s\n", id, str); +#endif + if(!str) { + Curl_u8_strset_unset(set, id); + return CURLE_OK; + } + + if(u8_strset_get_index(set, id, &i)) { + /* `id` is in set, replace value */ + curlx_free(set->data[i]); + set->data[i] = str; + return CURLE_OK; + } + /* `id` not in set yet, grow if full */ + if((set->count >= CURL_U8_SET_SLOT_CNT(set)) && !u8_strset_grow(set)) { + curlx_free(str); + return CURLE_OUT_OF_MEMORY; + } + + u8_strset_addn(set, id, str); + return CURLE_OK; +} + + +CURLcode Curl_u8_strset_set(struct u8_strset *set, + uint8_t id, const char *str) +{ + char *val; + + DEBUGASSERT(set->init == CURL_U8_STRSET_MAGIC); + if(!str) { + Curl_u8_strset_unset(set, id); + return CURLE_OK; + } + + val = curlx_strdup(str); + if(!val) + return CURLE_OUT_OF_MEMORY; + return Curl_u8_strset_setn(set, id, val); +} + +static void u8_strset_unset(struct u8_strset *set, uint8_t id, bool zero) +{ + uint8_t i, j; + + DEBUGASSERT(set->init == CURL_U8_STRSET_MAGIC); + if(u8_strset_get_index(set, id, &i)) { + /* `id` is in set */ + if(zero) + curlx_strzero(set->data[i]); + curlx_safefree(set->data[i]); + set->ids[i] = set->psl[i] = 0; + --set->count; + j = CURL_U8_SET_SLOT_IDX(set, i + 1); + /* shift all entries with positive psl "down" */ + while(set->data[j] && set->psl[j]) { + set->data[i] = set->data[j]; + set->ids[i] = set->ids[j]; + set->psl[i] = (uint8_t)(set->psl[j] - 1); + set->data[j] = NULL; + set->ids[j] = set->psl[j] = 0; + i = j; + j = CURL_U8_SET_SLOT_IDX(set, i + 1); + } + } +} + +void Curl_u8_strset_unset(struct u8_strset *set, uint8_t id) +{ + u8_strset_unset(set, id, FALSE); +} + +void Curl_u8_strset_unset0(struct u8_strset *set, uint8_t id) +{ + u8_strset_unset(set, id, TRUE); +} + +CURLcode Curl_u8_strset_copy(struct u8_strset *dest, struct u8_strset *src) +{ + CURLcode result = CURLE_OK; + uint16_t i; + + DEBUGASSERT(src->init == CURL_U8_STRSET_MAGIC); + Curl_u8_strset_clear(dest); + for(i = 0; !result && (i < CURL_U8_SET_SLOT_CNT(src)); ++i) { + if(src->data[i]) + result = Curl_u8_strset_set(dest, src->ids[i], src->data[i]); + } + return result; +} diff --git a/lib/uint-hashset.h b/lib/uint-hashset.h new file mode 100644 index 000000000000..48622ae4271f --- /dev/null +++ b/lib/uint-hashset.h @@ -0,0 +1,80 @@ +#ifndef HEADER_CURL_UINT_HASHSET_H +#define HEADER_CURL_UINT_HASHSET_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +/* How large should the initial set be? + * Measuring our test suite with set growth force fail, gives + * BITS RESULT + * 1 1261 tests out of 1951 reported OK: 64% + * 2 1792 tests out of 1951 reported OK: 91% + * 3 1944 tests out of 1951 reported OK: 99% + * 4 1949 tests out of 1951 reported OK: 99% + * 5 single fail of 3211, unit test for u8_strset + * meaning 91% of our tests to not set more than 4 strings and + * 99% do not set more than 8. + */ +#define CURL_U8_STRSET_START_BITS 2 +#define CURL_U8_STRSET_START_DIM (1U << CURL_U8_STRSET_START_BITS) + +/* A set that can hold up to 256 strings identified by an `id'. + * Setting a string for an existing id replaces the previous one. + * Getting the string for an id not in the set returns NULL. + * Setting an id to NULL unsets the id. + */ +struct u8_strset { + char **data; /* #slots array of null-terminated strings */ + uint8_t *ids; /* #slots array of `id` values */ + uint8_t *psl; /* #slots array of "probe sequence length" values */ + char *sdata[CURL_U8_STRSET_START_DIM]; + uint8_t sids[CURL_U8_STRSET_START_DIM]; + uint8_t spsl[CURL_U8_STRSET_START_DIM]; + uint16_t count; + uint8_t slotbits; +#ifdef DEBUGBUILD + int32_t init; +#endif +}; + +void Curl_u8_strset_init(struct u8_strset *set); +void Curl_u8_strset_clear(struct u8_strset *set); + +uint16_t Curl_u8_strset_count(struct u8_strset *set); +const char *Curl_u8_strset_get(struct u8_strset *set, uint8_t id); + +/* Set string for id, makes a copy. */ +CURLcode Curl_u8_strset_set(struct u8_strset *set, + uint8_t id, const char *str); +/* Set string for id, takes ownership of `str` even on failure. */ +CURLcode Curl_u8_strset_setn(struct u8_strset *set, + uint8_t id, char *str); +void Curl_u8_strset_unset(struct u8_strset *set, uint8_t id); + +/* Remove the string if in the set and zero its memory */ +void Curl_u8_strset_unset0(struct u8_strset *set, uint8_t id); + +CURLcode Curl_u8_strset_copy(struct u8_strset *dest, struct u8_strset *src); + +#endif /* HEADER_CURL_UINT_HASHSET_H */ diff --git a/lib/url.c b/lib/url.c index 0b2d414526ad..16e14aaac0af 100644 --- a/lib/url.c +++ b/lib/url.c @@ -148,21 +148,17 @@ static curl_prot_t get_protocol_family(const struct Curl_scheme *s) void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ - enum dupstring i; enum dupblob j; - for(i = (enum dupstring)0; i < STRING_LAST; i++) { - if(i == STRING_PASSWORD || - i == STRING_KEY_PASSWD || + CURL_EASY_STR_CLEAR0(data, STRING_PASSWORD); + CURL_EASY_STR_CLEAR0(data, STRING_KEY_PASSWD); + CURL_EASY_STR_CLEAR0(data, STRING_BEARER); #ifndef CURL_DISABLE_PROXY - i == STRING_PROXYPASSWORD || - i == STRING_KEY_PASSWD_PROXY || + CURL_EASY_STR_CLEAR0(data, STRING_PROXYPASSWORD); + CURL_EASY_STR_CLEAR0(data, STRING_KEY_PASSWD_PROXY); #endif - i == STRING_BEARER) { - curlx_strzero(data->set.str[i]); - } - curlx_safefree(data->set.str[i]); - } + Curl_u8_strset_clear(&data->set.strings); + curlx_safefree(data->set.str_copypostfields); for(j = (enum dupblob)0; j < BLOB_LAST; j++) { curlx_safefree(data->set.blobs[j]); @@ -257,11 +253,11 @@ CURLcode Curl_close(struct Curl_easy **datap) curlx_dyn_free(&data->state.headerb); Curl_flush_cookies(data, TRUE); #ifndef CURL_DISABLE_ALTSVC - Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); + Curl_altsvc_save(data, data->asi, CURL_EASY_STR(data, STRING_ALTSVC)); Curl_altsvc_cleanup(&data->asi); #endif #ifndef CURL_DISABLE_HSTS - Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); + Curl_hsts_save(data, data->hsts, CURL_EASY_STR(data, STRING_HSTS)); if(!data->share || !data->share->hsts) Curl_hsts_cleanup(&data->hsts); curl_slist_free_all(data->state.hstslist); /* clean up list */ @@ -321,6 +317,8 @@ void Curl_init_userdefined(struct Curl_easy *data) set->in_set = stdin; /* default input from stdin */ set->err = stderr; /* default stderr to stderr */ + Curl_u8_strset_init(&data->set.strings); + #if defined(__clang__) && __clang_major__ >= 16 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-function-type-strict" @@ -475,6 +473,8 @@ CURLcode Curl_open(struct Curl_easy **curl) Curl_hash_init(&data->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry); + DEBUGASSERT(STRING_LAST <= UINT8_MAX); + Curl_u8_strset_init(&data->set.strings); curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); Curl_bufref_init(&data->state.url); Curl_bufref_init(&data->state.referer); @@ -1228,8 +1228,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ - if(data->set.str[STRING_DEVICE]) { - conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]); + if(CURL_EASY_STR(data, STRING_DEVICE)) { + conn->localdev = curlx_strdup(CURL_EASY_STR(data, STRING_DEVICE)); if(!conn->localdev) goto error; } @@ -1384,7 +1384,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, ret = Curl_netrc_scan(data, &data->state.netrc, data->state.origin->hostname, Curl_creds_user(ncreds_in), - data->set.str[STRING_NETRC_FILE], + CURL_EASY_STR(data, STRING_NETRC_FILE), &ncreds_out); DEBUGASSERT(!ret || !ncreds_out); if(ret == NETRC_OUT_OF_MEMORY) { @@ -1395,8 +1395,8 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { infof(data, "Could not find host %s in the %s file; using defaults", data->state.origin->hostname, - (data->set.str[STRING_NETRC_FILE] ? - data->set.str[STRING_NETRC_FILE] : ".netrc")); + (CURL_EASY_STR(data, STRING_NETRC_FILE) ? + CURL_EASY_STR(data, STRING_NETRC_FILE) : ".netrc")); } else if(ret) { const char *m = Curl_netrc_strerror(ret); @@ -1450,17 +1450,17 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) struct Curl_creds *newcreds = NULL; CURLcode result = CURLE_OK; - if((data->set.str[STRING_USERNAME] || - data->set.str[STRING_PASSWORD] || - data->set.str[STRING_BEARER] || - data->set.str[STRING_SASL_AUTHZID] || - data->set.str[STRING_SERVICE_NAME]) && + if((CURL_EASY_STR(data, STRING_USERNAME) || + CURL_EASY_STR(data, STRING_PASSWORD) || + CURL_EASY_STR(data, STRING_BEARER) || + CURL_EASY_STR(data, STRING_SASL_AUTHZID) || + CURL_EASY_STR(data, STRING_SERVICE_NAME)) && Curl_auth_allowed_to_origin(data, data->state.origin)) { - result = Curl_creds_create(data->set.str[STRING_USERNAME], - data->set.str[STRING_PASSWORD], - data->set.str[STRING_BEARER], - data->set.str[STRING_SASL_AUTHZID], - data->set.str[STRING_SERVICE_NAME], + result = Curl_creds_create(CURL_EASY_STR(data, STRING_USERNAME), + CURL_EASY_STR(data, STRING_PASSWORD), + CURL_EASY_STR(data, STRING_BEARER), + CURL_EASY_STR(data, STRING_SASL_AUTHZID), + CURL_EASY_STR(data, STRING_SERVICE_NAME), CREDS_OPTION, &newcreds); if(result) goto out; @@ -1547,8 +1547,8 @@ static CURLcode url_set_conn_origin_etc(struct Curl_easy *data, goto out; /* set the connection options */ - if(data->set.str[STRING_OPTIONS]) { - conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]); + if(CURL_EASY_STR(data, STRING_OPTIONS)) { + conn->options = curlx_strdup(CURL_EASY_STR(data, STRING_OPTIONS)); if(!conn->options) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1579,14 +1579,14 @@ static CURLcode setup_range(struct Curl_easy *data) { struct UrlState *s = &data->state; s->resume_from = data->set.set_resume_from; - if(s->resume_from || data->set.str[STRING_SET_RANGE]) { + if(s->resume_from || CURL_EASY_STR(data, STRING_SET_RANGE)) { if(s->rangestringalloc) curlx_free(s->range); if(s->resume_from) s->range = curl_maprintf("%" FMT_OFF_T "-", s->resume_from); else - s->range = curlx_strdup(data->set.str[STRING_SET_RANGE]); + s->range = curlx_strdup(CURL_EASY_STR(data, STRING_SET_RANGE)); if(!s->range) return CURLE_OUT_OF_MEMORY; @@ -2052,11 +2052,10 @@ static CURLcode url_create_needle(struct Curl_easy *data, /************************************************************* * Set UDS first. It overrides "via_peer" and proxy settings. *************************************************************/ - if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) { - result = Curl_peer_uds_create(needle->origin->scheme, - data->set.str[STRING_UNIX_SOCKET_PATH], - (bool)data->set.abstract_unix_socket, - &needle->via_peer); + if(network_scheme && CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH)) { + result = Curl_peer_uds_create( + needle->origin->scheme, CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH), + (bool)data->set.abstract_unix_socket, &needle->via_peer); if(result) goto out; } @@ -2169,10 +2168,10 @@ static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data) /* Calculate the *real* URL this transfer uses, applying defaults * where information is missing. */ - if(data->set.str[STRING_DEFAULT_PROTOCOL] && + if(CURL_EASY_STR(data, STRING_DEFAULT_PROTOCOL) && !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { char *url = curl_maprintf("%s://%s", - data->set.str[STRING_DEFAULT_PROTOCOL], + CURL_EASY_STR(data, STRING_DEFAULT_PROTOCOL), Curl_bufref_ptr(&data->state.url)); if(!url) { result = CURLE_OUT_OF_MEMORY; diff --git a/lib/urldata.h b/lib/urldata.h index f0c6d7ae1b8c..8bb82871797b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -69,6 +69,7 @@ #include "request.h" #include "ratelimit.h" #include "netrc.h" +#include "uint-hashset.h" #include "vdns/asyn.h" #include "vdns/hostip.h" #include "vtls/vtls_config.h" @@ -804,14 +805,6 @@ enum dupstring { STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */ STRING_SSL_SIGNATURE_ALGORITHMS, /* CURLOPT_SSL_SIGNATURE_ALGORITHMS */ - /* -- end of null-terminated strings -- */ - - STRING_LASTZEROTERMINATED, - - /* -- below this are pointers to binary data that cannot be strdup'ed. --- */ - - STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ - STRING_LAST /* not used, an end-of-list marker */ }; @@ -839,6 +832,7 @@ struct UserDefined { uint32_t httpauth; /* kind of HTTP authentication to use (bitmask) */ uint32_t proxyauth; /* kind of proxy authentication to use (bitmask) */ void *postfields; /* if POST, set the fields' values here */ + char *str_copypostfields; /* CURLOPT_COPYPOSTFIELDS value */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead of strlen(), and then the data *may* be binary @@ -933,7 +927,7 @@ struct UserDefined { uint32_t ssh_auth_types; /* allowed SSH auth types */ uint32_t new_directory_perms; /* when creating remote dirs */ #endif - char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ + struct u8_strset strings; struct curl_blob *blobs[BLOB_LAST]; uint32_t new_file_perms; /* when creating remote files */ #ifdef USE_IPV6 @@ -1230,6 +1224,17 @@ struct Curl_easy { valid after a client has asked for it */ }; +#define CURL_EASY_STR(d, id) \ + Curl_u8_strset_get(&(d)->set.strings, (uint8_t)(id)) +#define CURL_EASY_STR_SET(d, id, s) \ + Curl_u8_strset_set(&(d)->set.strings, (uint8_t)(id), (s)) +#define CURL_EASY_STR_SETN(d, id, s) \ + Curl_u8_strset_setn(&(d)->set.strings, (uint8_t)(id), (s)) +#define CURL_EASY_STR_CLEAR(d, id) \ + Curl_u8_strset_unset(&(d)->set.strings, (uint8_t)(id)) +#define CURL_EASY_STR_CLEAR0(d, id) \ + Curl_u8_strset_unset0(&(d)->set.strings, (uint8_t)(id)) + #define LIBCURL_NAME "libcurl" #endif /* HEADER_CURL_URLDATA_H */ diff --git a/lib/vdns/asyn-ares.c b/lib/vdns/asyn-ares.c index 7d27c7fc8d73..a98c4de756c5 100644 --- a/lib/vdns/asyn-ares.c +++ b/lib/vdns/asyn-ares.c @@ -660,7 +660,7 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, { struct async_ares_ctx *ares = async ? &async->ares : NULL; CURLcode result = CURLE_NOT_BUILT_IN; - const char *servers = data->set.str[STRING_DNS_SERVERS]; + const char *servers = CURL_EASY_STR(data, STRING_DNS_SERVERS); int ares_result = ARES_SUCCESS; #ifdef DEBUGBUILD @@ -696,7 +696,7 @@ static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, struct Curl_resolv_async *async) { struct async_ares_ctx *ares = async ? &async->ares : NULL; - const char *interf = data->set.str[STRING_DNS_INTERFACE]; + const char *interf = CURL_EASY_STR(data, STRING_DNS_INTERFACE); if(!interf) interf = ""; @@ -713,7 +713,7 @@ static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, { struct async_ares_ctx *ares = async ? &async->ares : NULL; struct in_addr a4; - const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; + const char *local_ip4 = CURL_EASY_STR(data, STRING_DNS_LOCAL_IP4); if(!local_ip4 || (local_ip4[0] == 0)) { a4.s_addr = 0; /* disabled: do not bind to a specific address */ @@ -738,7 +738,7 @@ static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, #ifdef USE_IPV6 struct async_ares_ctx *ares = async ? &async->ares : NULL; unsigned char a6[INET6_ADDRSTRLEN]; - const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; + const char *local_ip6 = CURL_EASY_STR(data, STRING_DNS_LOCAL_IP6); if(!local_ip6 || (local_ip6[0] == 0)) { /* disabled: do not bind to a specific address */ diff --git a/lib/vdns/doh.c b/lib/vdns/doh.c index fe6f9a890735..3b89cb9620e3 100644 --- a/lib/vdns/doh.c +++ b/lib/vdns/doh.c @@ -336,17 +336,20 @@ static CURLcode doh_probe_run(struct Curl_easy *data, doh->set.ssl.custom_cafile = data->set.ssl.custom_cafile; doh->set.ssl.custom_capath = data->set.ssl.custom_capath; doh->set.ssl.custom_cablob = data->set.ssl.custom_cablob; - if(data->set.str[STRING_SSL_CAFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); + if(CURL_EASY_STR(data, STRING_SSL_CAFILE)) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO, + CURL_EASY_STR(data, STRING_SSL_CAFILE)); } if(data->set.blobs[BLOB_CAINFO]) { ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, data->set.blobs[BLOB_CAINFO]); } - if(data->set.str[STRING_SSL_CAPATH]) { - ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH]); + if(CURL_EASY_STR(data, STRING_SSL_CAPATH)) { + ERROR_CHECK_SETOPT(CURLOPT_CAPATH, + CURL_EASY_STR(data, STRING_SSL_CAPATH)); } - if(data->set.str[STRING_SSL_CRLFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, data->set.str[STRING_SSL_CRLFILE]); + if(CURL_EASY_STR(data, STRING_SSL_CRLFILE)) { + ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, + CURL_EASY_STR(data, STRING_SSL_CRLFILE)); } if(data->set.ssl.certinfo) ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); @@ -354,9 +357,9 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); if(data->set.ssl.fsslctxp) ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); - if(data->set.str[STRING_SSL_EC_CURVES]) { + if(CURL_EASY_STR(data, STRING_SSL_EC_CURVES)) { ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, - data->set.str[STRING_SSL_EC_CURVES]); + CURL_EASY_STR(data, STRING_SSL_EC_CURVES)); } (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, @@ -442,7 +445,8 @@ CURLcode Curl_doh(struct Curl_easy *data, if(async->dns_queries & CURL_DNSQ_AAAA) { /* create IPv6 DoH request */ result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, - async->peer->hostname, data->set.str[STRING_DOH], + async->peer->hostname, + CURL_EASY_STR(data, STRING_DOH), data->multi, async->id, &dohp->probe_mid[DOH_SLOT_IPV6]); if(result) @@ -454,7 +458,8 @@ CURLcode Curl_doh(struct Curl_easy *data, /* create IPv4 DoH request */ if(async->dns_queries & CURL_DNSQ_A) { result = doh_probe_run(data, CURL_DNS_TYPE_A, - async->peer->hostname, data->set.str[STRING_DOH], + async->peer->hostname, + CURL_EASY_STR(data, STRING_DOH), data->multi, async->id, &dohp->probe_mid[DOH_SLOT_IPV4]); if(result) @@ -473,7 +478,7 @@ CURLcode Curl_doh(struct Curl_easy *data, } result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS, qname ? qname : async->peer->hostname, - data->set.str[STRING_DOH], data->multi, + CURL_EASY_STR(data, STRING_DOH), data->multi, async->id, &dohp->probe_mid[DOH_SLOT_HTTPS_RR]); curlx_free(qname); diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index f6fd6c55fc11..8c7c84928b98 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -689,9 +689,9 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, } result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); + !data->state.http_ignorecustom ? + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) : NULL, + 0, pnwritten); if(result) goto out; if(!stream->h1.done) { diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index 52dad536b6ac..c269926dca53 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -1012,9 +1012,9 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); result = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); + !data->state.http_ignorecustom ? + CURL_EASY_STR(data, STRING_CUSTOMREQUEST) : NULL, + 0, pnwritten); if(result) goto out; if(!stream->h1.done) { diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 58f139306a3d..75e2f4e1e2c4 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -166,9 +166,9 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)conn_config; result = Curl_ossl_check_peer_cert(cf, data, &ctx->ossl, peer); #elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(cf, data, ctx->gtls.session, - conn_config, &data->set.ssl, peer, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + result = Curl_gtls_verifyserver( + cf, data, ctx->gtls.session, conn_config, &data->set.ssl, peer, + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY)); if(result) return result; #elif defined(USE_WOLFSSL) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index eb13abad73e0..2603f6b7b1a2 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -1022,7 +1022,7 @@ CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, CURLECH_ENABLED(data) && Curl_ssl_supports(data, SSLSUPP_ECH) && (data->set.tls_ech != CURLECH_GREASE) && - !data->set.str[STRING_ECH_CONFIG]) { + !CURL_EASY_STR(data, STRING_ECH_CONFIG)) { result = Curl_conn_dns_add_https_resolve(data, cf_at->conn, cf_at->sockindex, origin); } diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index e9fe81022d28..03a84d218b7a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -109,10 +109,10 @@ static CURLcode sftp_error_to_CURLE(int err) } /* Multiple options: - * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256] is set with a SHA256 - * hash. - * 2. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 - * hash (90s style auth, not sure we should have it here) + * 1. CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256) is set + * with a SHA256 hash. + * 2. CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_MD5) is set + * with an MD5 hash (90s style auth, not sure we should have it here) * 3. data->set.ssh_keyfunc callback is set. Then we do trust on first * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE * is returned by it. @@ -143,9 +143,9 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) if(rc != SSH_OK) return rc; - if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + if(CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256)) { const char *pubkey_sha256 = - data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256); char *fingerprint_b64 = NULL; size_t fingerprint_b64_len; size_t pub_pos = 0; @@ -197,8 +197,9 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) goto cleanup; } - if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + if(CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_MD5)) { + const char *pubkey_md5 = + CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_MD5); char md5buffer[33]; int i; @@ -227,7 +228,7 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) goto cleanup; } - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + if(CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)) { /* Get the known_key from the known hosts file */ vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, @@ -2618,15 +2619,16 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) } } - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]); + if(CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)) { + infof(data, "Known hosts: %s", + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_KNOWNHOSTS, - data->set.str[STRING_SSH_KNOWNHOSTS]); + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); if(rc == SSH_OK) /* libssh has two separate options for this. Set both to the same file to avoid surprises */ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, - data->set.str[STRING_SSH_KNOWNHOSTS]); + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); if(rc != SSH_OK) { failf(data, "Could not set known hosts file path"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 9d2e8b8e94de..098b680f7fe9 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -316,7 +316,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, int rc = 0; CURLcode result = CURLE_OK; - if(!data->set.str[STRING_SSH_KNOWNHOSTS]) { + if(!CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)) { infof(data, "SSH: no knownhosts file configured"); return CURLE_OK; } @@ -460,12 +460,12 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, /* now we write the entire in-memory list of known hosts to the known_hosts file */ int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); + libssh2_knownhost_writefile( + sshc->kh, CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS), + LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(wrc) { infof(data, "WARNING: writing %s failed", - data->set.str[STRING_SSH_KNOWNHOSTS]); + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); } } } @@ -481,8 +481,10 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, static CURLcode ssh_check_fingerprint(struct Curl_easy *data, struct ssh_conn *sshc) { - const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; - const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + const char *pubkey_md5 = + CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_MD5); + const char *pubkey_sha256 = + CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256); if(pubkey_sha256) { const char *fingerprint = NULL; @@ -646,8 +648,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, bool found = FALSE; if(sshc->kh && - !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] && - !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + !CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_MD5) && + !CURL_EASY_STR(data, STRING_SSH_HOST_PUBLIC_KEY_SHA256)) { struct libssh2_knownhost *store = NULL; struct connectdata *conn = data->conn; /* lets try to find our host in the known hosts file */ @@ -663,7 +665,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, const char *kh_name_end = strstr(store->name, "]:"); if(!kh_name_end) { infof(data, "SSH: invalid host pattern %s in %s", - store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); + store->name, + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); continue; } p = kh_name_end + 2; /* start of port number */ @@ -693,7 +696,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, int rc; const char *hostkey_method = NULL; infof(data, "SSH: found host '%s' in '%s'", - conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); + conn->origin->hostname, + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { case LIBSSH2_KNOWNHOST_KEY_ED25519: @@ -738,7 +742,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } else { infof(data, "SSH: did not find host '%s' in '%s'", - conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); + conn->origin->hostname, + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); } } @@ -3475,7 +3480,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) infof(data, "SSH: failed to enable compression for session"); } - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + if(CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)) { int rc; sshc->kh = libssh2_knownhost_init(sshc->ssh_session); if(!sshc->kh) { @@ -3485,12 +3490,12 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) } /* read all known hosts from there */ - rc = libssh2_knownhost_readfile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); + rc = libssh2_knownhost_readfile( + sshc->kh, CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS), + LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(rc < 0) infof(data, "SSH: failed to read known hosts from %s", - data->set.str[STRING_SSH_KNOWNHOSTS]); + CURL_EASY_STR(data, STRING_SSH_KNOWNHOSTS)); } #ifdef CURL_LIBSSH2_DEBUG diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c index ac0217182907..f8e687382c39 100644 --- a/lib/vssh/vssh.c +++ b/lib/vssh/vssh.c @@ -368,8 +368,9 @@ CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) if(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) { sshc->pub_key = sshc->priv_key = NULL; - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - sshc->priv_key = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + if(CURL_EASY_STR(data, STRING_SSH_PRIVATE_KEY)) { + sshc->priv_key = curlx_strdup( + CURL_EASY_STR(data, STRING_SSH_PRIVATE_KEY)); if(!sshc->priv_key) goto fail; } @@ -417,10 +418,11 @@ CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) * library extract the public key from the private key file. This is done * by passing sshc->pub_key = NULL. */ - if(data->set.str[STRING_SSH_PUBLIC_KEY] && + if(CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY) && /* treat empty string the same way as NULL */ - data->set.str[STRING_SSH_PUBLIC_KEY][0]) { - sshc->pub_key = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY)[0]) { + sshc->pub_key = curlx_strdup( + CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY)); if(!sshc->pub_key) goto fail; } diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index b49148982bb2..ba9da769df3e 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1868,10 +1868,10 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); #ifndef CURL_DISABLE_PROXY const char *pinned_key = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #else - const char *pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + const char *pinned_key = CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #endif CURLcode result; diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 664d1f97a2a1..d10c4b27f901 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -1075,10 +1075,11 @@ static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, #ifdef HAVE_PINNED_PUBKEY #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #else - const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + const char * const pinnedpubkey = + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #endif #endif diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 802f3da86220..4505cc31eb8c 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3477,7 +3477,7 @@ bool Curl_ossl_need_httpsrr(struct Curl_easy *data) if(!CURLECH_ENABLED(data)) return FALSE; if((data->set.tls_ech == CURLECH_GREASE) || - data->set.str[STRING_ECH_CONFIG]) + CURL_EASY_STR(data, STRING_ECH_CONFIG)) return FALSE; return TRUE; } @@ -3487,9 +3487,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, struct Curl_easy *data, struct ssl_peer *peer) { - unsigned char *ech_config = NULL; - size_t ech_config_len = 0; - char *outername = data->set.str[STRING_ECH_PUBLIC]; + const char *outername = CURL_EASY_STR(data, STRING_ECH_PUBLIC); int trying_ech_now = 0; if(!CURLECH_ENABLED(data)) @@ -3503,10 +3501,12 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); #endif } - else if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { + else if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) { #ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ - const char *b64 = data->set.str[STRING_ECH_CONFIG]; + const char *b64 = CURL_EASY_STR(data, STRING_ECH_CONFIG); + uint8_t *ech_config; + size_t ech_config_len = 0; CURLcode result; if(!b64) { @@ -3530,13 +3530,16 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, curlx_free(ech_config); trying_ech_now = 1; #else - ech_config = (unsigned char *)data->set.str[STRING_ECH_CONFIG]; + const char *ech_config = CURL_EASY_STR(data, STRING_ECH_CONFIG); + size_t ech_config_len = 0; if(!ech_config) { infof(data, "ECH: ECHConfig from command line empty"); return CURLE_SSL_CONNECT_ERROR; } - ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); - if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { + ech_config_len = strlen(ech_config); + if(SSL_set1_ech_config_list(octx->ssl, + (const uint8_t *)ech_config, + ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; @@ -4599,10 +4602,10 @@ static CURLcode ossl_check_pinned_key(struct Curl_cfilter *cf, (void)cf; #ifndef CURL_DISABLE_PROXY ptr = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #else - ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + ptr = CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #endif if(ptr) { result = ossl_pkp_pin_peer_pubkey(data, server_cert, ptr); diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index ef2ace26460a..31e7a5f35130 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -923,7 +923,7 @@ static bool cr_ech_need_httpsrr(struct Curl_easy *data) if(!CURLECH_ENABLED(data)) return FALSE; if((data->set.tls_ech == CURLECH_GREASE) || - data->set.str[STRING_ECH_CONFIG]) + CURL_EASY_STR(data, STRING_ECH_CONFIG)) return FALSE; return TRUE; } @@ -948,7 +948,7 @@ init_config_builder_ech(struct Curl_easy *data, goto cleanup; } - if(data->set.str[STRING_ECH_PUBLIC]) { + if(CURL_EASY_STR(data, STRING_ECH_PUBLIC)) { failf(data, "rustls: ECH outername not supported"); result = CURLE_SSL_CONNECT_ERROR; goto cleanup; @@ -964,8 +964,8 @@ init_config_builder_ech(struct Curl_easy *data, return CURLE_OK; } - if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { - const char *b64 = data->set.str[STRING_ECH_CONFIG]; + if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) { + const char *b64 = CURL_EASY_STR(data, STRING_ECH_CONFIG); size_t decode_result; if(!b64) { infof(data, "rustls: ECHConfig from command line empty"); @@ -1005,7 +1005,7 @@ init_config_builder_ech(struct Curl_easy *data, } cleanup: /* if we base64 decoded, we can free now */ - if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) { curlx_free(ech_config); } if(dns) { diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index eea5f3af55e9..cee933b3aadb 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1458,10 +1458,10 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, #ifndef CURL_DISABLE_PROXY pubkey_ptr = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #else - pubkey_ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + pubkey_ptr = CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #endif if(pubkey_ptr) { result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr); diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index c1504fad3b84..f6cc370890d9 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1431,7 +1431,7 @@ CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, CURLECH_ENABLED(data) && Curl_ssl_supports(data, SSLSUPP_ECH) && (data->set.tls_ech != CURLECH_GREASE) && - !data->set.str[STRING_ECH_CONFIG]) { + !CURL_EASY_STR(data, STRING_ECH_CONFIG)) { result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex, origin); } diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index 252c5ad885ce..afd66d475f2f 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -236,12 +236,18 @@ static void ssl_easy_config_compl_options(struct Curl_peer *origin, !!(options & CURLSSLOPT_AUTO_CLIENT_CERT); } +static char *ssl_easy_steal(struct Curl_easy *data, enum dupstring id) +{ + /* For connection matching, we borrow string references from data + * THIS IS NOT REALLY NICE. */ + return CURL_UNCONST(CURL_EASY_STR(data, id)); +} + CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, struct Curl_peer *origin) { struct ssl_config_data *sslc = &data->set.ssl; #if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) - struct UserDefined *set = &data->set; CURLcode result; #endif @@ -253,42 +259,43 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, sslc->native_ca_store = TRUE; #endif #ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); + if(!sslc->custom_capath && !CURL_EASY_STR(data, STRING_SSL_CAPATH)) { + result = Curl_setstropt(data, STRING_SSL_CAPATH, CURL_CA_PATH); if(result) return result; } #endif #ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); + if(!sslc->custom_cafile && !CURL_EASY_STR(data, STRING_SSL_CAFILE)) { + result = Curl_setstropt(data, STRING_SSL_CAFILE, CURL_CA_BUNDLE); if(result) return result; } #endif } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; + sslc->primary.CAfile = ssl_easy_steal(data, STRING_SSL_CAFILE); + sslc->primary.CRLfile = ssl_easy_steal(data, STRING_SSL_CRLFILE); + sslc->primary.CApath = ssl_easy_steal(data, STRING_SSL_CAPATH); + sslc->primary.cipher_list = ssl_easy_steal(data, STRING_SSL_CIPHER_LIST); + sslc->primary.cipher_list13 = ssl_easy_steal(data, STRING_SSL_CIPHER13_LIST); sslc->primary.signature_algorithms = - data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; + ssl_easy_steal(data, STRING_SSL_SIGNATURE_ALGORITHMS); sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; + sslc->primary.curves = ssl_easy_steal(data, STRING_SSL_EC_CURVES); /* Maybe these should not be used for another origin. But for * backwards compatibility, keep them in. */ - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + sslc->primary.issuercert = ssl_easy_steal(data, STRING_SSL_ISSUERCERT); sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; if(Curl_peer_equal(data->state.initial_origin, origin)) { - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + sslc->primary.pinned_key = + ssl_easy_steal(data, STRING_SSL_PINNEDPUBLICKEY); sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; - sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; - sslc->primary.key = data->set.str[STRING_KEY]; - sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; - sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; - sslc->primary.clientcert = data->set.str[STRING_CERT]; + sslc->primary.cert_type = ssl_easy_steal(data, STRING_CERT_TYPE); + sslc->primary.key = ssl_easy_steal(data, STRING_KEY); + sslc->primary.key_type = ssl_easy_steal(data, STRING_KEY_TYPE); + sslc->primary.key_passwd = ssl_easy_steal(data, STRING_KEY_PASSWD); + sslc->primary.clientcert = ssl_easy_steal(data, STRING_CERT); sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; } else { @@ -313,37 +320,40 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, sslc->native_ca_store = TRUE; #endif #ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], - CURL_CA_PATH); + if(!sslc->custom_capath && + !CURL_EASY_STR(data, STRING_SSL_CAPATH_PROXY)) { + result = Curl_setstropt(data, STRING_SSL_CAPATH_PROXY, CURL_CA_PATH); if(result) return result; } #endif #ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], - CURL_CA_BUNDLE); + if(!sslc->custom_cafile && + !CURL_EASY_STR(data, STRING_SSL_CAFILE_PROXY)) { + result = Curl_setstropt(data, STRING_SSL_CAFILE_PROXY, CURL_CA_BUNDLE); if(result) return result; } #endif } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + sslc->primary.CAfile = ssl_easy_steal(data, STRING_SSL_CAFILE_PROXY); + sslc->primary.CApath = ssl_easy_steal(data, STRING_SSL_CAPATH_PROXY); + sslc->primary.cipher_list = + ssl_easy_steal(data, STRING_SSL_CIPHER_LIST_PROXY); + sslc->primary.cipher_list13 = + ssl_easy_steal(data, STRING_SSL_CIPHER13_LIST_PROXY); + sslc->primary.pinned_key = + ssl_easy_steal(data, STRING_SSL_PINNEDPUBLICKEY_PROXY); sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + sslc->primary.issuercert = ssl_easy_steal(data, STRING_SSL_ISSUERCERT_PROXY); sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; - sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - sslc->primary.key = data->set.str[STRING_KEY_PROXY]; - sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; + sslc->primary.CRLfile = ssl_easy_steal(data, STRING_SSL_CRLFILE_PROXY); + sslc->primary.cert_type = ssl_easy_steal(data, STRING_CERT_TYPE_PROXY); + sslc->primary.key = ssl_easy_steal(data, STRING_KEY_PROXY); + sslc->primary.key_type = ssl_easy_steal(data, STRING_KEY_TYPE_PROXY); + sslc->primary.key_passwd = ssl_easy_steal(data, STRING_KEY_PASSWD_PROXY); + sslc->primary.clientcert = ssl_easy_steal(data, STRING_CERT_PROXY); sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; #endif /* CURL_DISABLE_PROXY */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 05d89ea1eac6..b8a07fb6fd65 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1260,7 +1260,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, { int trying_ech_now = 0; - if(data->set.str[STRING_ECH_PUBLIC]) { + if(CURL_EASY_STR(data, STRING_ECH_PUBLIC)) { infof(data, "ECH: outername not (yet) supported" " with wolfSSL"); return CURLE_SSL_CONNECT_ERROR; @@ -1269,8 +1269,8 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, infof(data, "ECH: GREASE is done by default by" " wolfSSL: no need to ask"); } - if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { - char *b64val = data->set.str[STRING_ECH_CONFIG]; + if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) { + const char *b64val = CURL_EASY_STR(data, STRING_ECH_CONFIG); word32 b64len = 0; b64len = (word32)strlen(b64val); @@ -1477,7 +1477,7 @@ bool Curl_wssl_need_httpsrr(struct Curl_easy *data) if(!CURLECH_ENABLED(data)) return FALSE; if((data->set.tls_ech == CURLECH_GREASE) || - data->set.str[STRING_ECH_CONFIG]) + CURL_EASY_STR(data, STRING_ECH_CONFIG)) return FALSE; return TRUE; #else @@ -1580,10 +1580,11 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #else - const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + const char * const pinnedpubkey = + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); (void)cf; #endif diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index d6ad035b2996..1cce770a3f66 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -1205,7 +1205,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) } result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s); - data->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ + data->set.str_copypostfields = s; /* Give to library. */ break; default: diff --git a/tests/data/test3211 b/tests/data/test3211 index 826c961a4d63..1c2643abc143 100644 --- a/tests/data/test3211 +++ b/tests/data/test3211 @@ -13,7 +13,7 @@ uint_bset unittest -uint_bset unit tests +uint_bset and uint_hashset unit tests diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c index a58a586afe51..3fd4f4b0a46c 100644 --- a/tests/unit/unit1620.c +++ b/tests/unit/unit1620.c @@ -127,7 +127,7 @@ static CURLcode test_unit1620(const char *arg) Curl_freeset(empty); for(i = (enum dupstring)0; i < STRING_LAST; i++) { - fail_unless(!empty->set.str[i], "Curl_free() did not set to NULL"); + fail_unless(!CURL_EASY_STR(empty, i), "Curl_free() did not set to NULL"); } result = Curl_close(&dupe); diff --git a/tests/unit/unit3211.c b/tests/unit/unit3211.c index 0d1dde200d23..516f516af6a8 100644 --- a/tests/unit/unit3211.c +++ b/tests/unit/unit3211.c @@ -24,10 +24,11 @@ #include "unitcheck.h" #include "urldata.h" #include "uint-bset.h" +#include "uint-hashset.h" #include "curl_trc.h" -static void check_set(const char *name, uint32_t capacity, - const uint32_t *s, size_t slen) +static void t3211_check_bset(const char *name, uint32_t capacity, + const uint32_t *s, size_t slen) { struct uint32_bset bset; size_t i, j; @@ -122,6 +123,103 @@ static void check_set(const char *name, uint32_t capacity, Curl_uint32_bset_destroy(&bset); } +static bool t3211_strcmp(const char *s1, const char *s2) +{ + if(s1 && s2) + return strcmp(s1, s2); + return s1 == s2; +} + +static void t3211_check_strset1(void) +{ + struct u8_strset set; + char buf[128]; + CURLcode result; + uint8_t i, idx; + int j; + + Curl_u8_strset_init(&set); + fail_unless(!Curl_u8_strset_count(&set), "initial strset not empty"); + + result = Curl_u8_strset_set(&set, 0, "123"); + fail_unless(!result, "add1 failed"); + fail_unless(Curl_u8_strset_get(&set, 0), "get failed"); + fail_unless(!t3211_strcmp("123", Curl_u8_strset_get(&set, 0)), "wrong get1"); + result = Curl_u8_strset_set(&set, 0, "456"); + fail_unless(!result, "add2 failed"); + fail_unless(!t3211_strcmp("456", Curl_u8_strset_get(&set, 0)), "wrong get2"); + Curl_u8_strset_unset(&set, 0); + fail_unless(!Curl_u8_strset_get(&set, 0), "unset failed"); + + /* Initial size is 4, add 4 hash collisions */ + for(i = 0; i < 4; ++i) { + idx = (uint8_t)((4 * i) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + result = Curl_u8_strset_set(&set, idx, buf); + fail_unless(!result, "loop4-add failed"); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong get loop4"); + } + + /* Remove collided entry 2, check again */ + idx = (uint8_t)((4 * 2) + 3); + Curl_u8_strset_unset(&set, idx); + fail_unless(!Curl_u8_strset_get(&set, idx), "unset2 failed"); + for(i = 0; i < 4; ++i) { + if(i == 2) + continue; + idx = (uint8_t)((4 * i) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong get loop6"); + } + + /* Add entry 2 again, check */ + idx = (uint8_t)((4 * 2) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + result = Curl_u8_strset_set(&set, idx, buf); + fail_unless(!result, "re-add 2 failed"); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong re-add 2 get"); + for(i = 0; i < 4; ++i) { + idx = (uint8_t)((4 * i) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong get loop6"); + } + + /* Add a 5th, set grows */ + fail_unless(Curl_u8_strset_count(&set) == 4, "wrong count pre add 5"); + idx = (uint8_t)((4 * 4) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + result = Curl_u8_strset_set(&set, idx, buf); + fail_unless(!result, "add4 failed"); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong get4"); + for(i = 0; i < 5; ++i) { + idx = (uint8_t)((4 * i) + 3); + curl_msnprintf(buf, sizeof(buf), "str-%d", idx); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), + "wrong get loop5"); + } + fail_unless(Curl_u8_strset_count(&set) == 5, "wrong count aftger add 5"); + + Curl_u8_strset_clear(&set); + + /* Make a full set */ + for(j = 0; j <= UINT8_MAX; ++j) { + i = (uint8_t)j; + curl_msnprintf(buf, sizeof(buf), "str-%d", i); + result = Curl_u8_strset_set(&set, i, buf); + fail_unless(!result, "loop256-add failed"); + fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, i)), + "wrong get loop256"); + } + + Curl_u8_strset_clear(&set); + fail_unless(!Curl_u8_strset_count(&set), "cleared strset not empty"); +} + static CURLcode test_unit3211(const char *arg) { UNITTEST_BEGIN_SIMPLE @@ -142,8 +240,10 @@ static CURLcode test_unit3211(const char *arg) 120, 121, 122, 123, 124, 125, 126, 127, }; - check_set("s1", 100, s1, CURL_ARRAYSIZE(s1)); - check_set("s2", 1000, s2, CURL_ARRAYSIZE(s2)); + t3211_check_bset("s1", 100, s1, CURL_ARRAYSIZE(s1)); + t3211_check_bset("s2", 1000, s2, CURL_ARRAYSIZE(s2)); + + t3211_check_strset1(); UNITTEST_END_SIMPLE } From 4f8dabcec2087b731ad00c9f18f8a04e9f5802a4 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 21 Aug 2026 08:41:49 +0200 Subject: [PATCH 0938/1062] GHA: install MSYS2 Perl manually for latest GitHub runners As the MSYS2 update propagates into more GitHub runner images, we need to install Perl to avoid native Windows (aka Strawberry) Perl breaking jobs that are running tests. For others jobs, this patch leaves the new default Strawberry Perl. It's fine to use it a little bit more. Also: - libssh 32-bit was dropped upstream, replace with libssh2, and move libssh to the 64-bit job running tests. https://github.com/msys2/MINGW-packages/commit/f6ae36581d8dd1f6878442575cc8471830b863ed https://github.com/msys2/MINGW-packages/pull/30777 Refs: https://github.com/actions/runner-images/issues/14562 https://github.com/actions/runner-images/pull/14541#issuecomment-5294304716 https://github.com/msys2/MSYS2-packages/commit/636489312cbc55011cb7ec493550598125e7716f https://github.com/msys2/MSYS2-packages/pull/6491 https://github.com/msys2/MSYS2-packages/pull/6479 Bug: https://github.com/curl/curl/pull/22537#issuecomment-5359154818 Follow-up to d854ab4673c2f9d8048c7f0f6d164b7e4d5e0865 #22580 Closes #22632 --- .github/workflows/distcheck.yml | 2 +- .github/workflows/windows.yml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index bfa3d15f2534..6031afa6154e 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -278,7 +278,7 @@ jobs: cache: false path-type: inherit install: >- - mingw-w64-x86_64-zlib mingw-w64-x86_64-zstd mingw-w64-x86_64-libpsl mingw-w64-x86_64-libssh2 mingw-w64-x86_64-nghttp2 mingw-w64-x86_64-openssl + perl mingw-w64-x86_64-zlib mingw-w64-x86_64-zstd mingw-w64-x86_64-libpsl mingw-w64-x86_64-libssh2 mingw-w64-x86_64-nghttp2 mingw-w64-x86_64-openssl - name: 'install prereqs' timeout-minutes: 3 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f47de59b0a32..3c9853ec1990 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -604,7 +604,8 @@ jobs: ver: '16.1.0' url: 'https://github.com/skeeto/w64devkit/releases/download/v2.8.0/w64devkit-x64-2.8.0.7z.exe' SHA256: 6252bf34fe2231a55ac7f03d482b36d2c7c58697990551bba508102cfb3f342e - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON -DCMAKE_C_FLAGS=-DCURL_DEBUG_NO_WIN32_WND' + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBSSH=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON -DCMAKE_C_FLAGS=-DCURL_DEBUG_NO_WIN32_WND' + install: perl mingw-w64-x86_64-libssh type: 'Debug' - name: 'schannel' # mingw-w64 10.0 sys: 'mingw64' @@ -634,8 +635,8 @@ jobs: ver: '6.4.0' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/6.4.0/threads-win32/dwarf/i686-6.4.0-release-win32-dwarf-rt_v5-rev0.7z' SHA256: 12d2c62ad4527ec8a52275ea8485678dcbe20bec4716a3c7ba274f225d696085 - config: '-DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBSSH=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_TARGET_WINDOWS_VERSION=0x0600' - install: mingw-w64-i686-libssh + config: '-DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_TARGET_WINDOWS_VERSION=0x0600' + install: perl mingw-w64-i686-libssh2 type: 'Debug' tflags: 'skiprun' - name: 'schannel !examples' # mingw-w64 3.0 From 2f1dda96911c41c725747eb1775da1759ef9bc95 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 21 Aug 2026 11:08:23 +0200 Subject: [PATCH 0939/1062] u8_strset: start with 8 default entries The previous 4 entries were exhausted on simple command lines, depending on environment/build. Grow the initial size to 8 to avoid early allocs. Closes #22633 --- lib/uint-hashset.h | 2 +- tests/unit/unit3211.c | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/uint-hashset.h b/lib/uint-hashset.h index 48622ae4271f..973142ff89ae 100644 --- a/lib/uint-hashset.h +++ b/lib/uint-hashset.h @@ -36,7 +36,7 @@ * meaning 91% of our tests to not set more than 4 strings and * 99% do not set more than 8. */ -#define CURL_U8_STRSET_START_BITS 2 +#define CURL_U8_STRSET_START_BITS 3 #define CURL_U8_STRSET_START_DIM (1U << CURL_U8_STRSET_START_BITS) /* A set that can hold up to 256 strings identified by an `id'. diff --git a/tests/unit/unit3211.c b/tests/unit/unit3211.c index 516f516af6a8..9fcbe85779a5 100644 --- a/tests/unit/unit3211.c +++ b/tests/unit/unit3211.c @@ -151,9 +151,9 @@ static void t3211_check_strset1(void) Curl_u8_strset_unset(&set, 0); fail_unless(!Curl_u8_strset_get(&set, 0), "unset failed"); - /* Initial size is 4, add 4 hash collisions */ - for(i = 0; i < 4; ++i) { - idx = (uint8_t)((4 * i) + 3); + /* Initial size is 8, add 8 hash collisions */ + for(i = 0; i < 8; ++i) { + idx = (uint8_t)((8 * i) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); result = Curl_u8_strset_set(&set, idx, buf); fail_unless(!result, "loop4-add failed"); @@ -162,47 +162,47 @@ static void t3211_check_strset1(void) } /* Remove collided entry 2, check again */ - idx = (uint8_t)((4 * 2) + 3); + idx = (uint8_t)((8 * 2) + 3); Curl_u8_strset_unset(&set, idx); fail_unless(!Curl_u8_strset_get(&set, idx), "unset2 failed"); - for(i = 0; i < 4; ++i) { + for(i = 0; i < 8; ++i) { if(i == 2) continue; - idx = (uint8_t)((4 * i) + 3); + idx = (uint8_t)((8 * i) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), "wrong get loop6"); } /* Add entry 2 again, check */ - idx = (uint8_t)((4 * 2) + 3); + idx = (uint8_t)((8 * 2) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); result = Curl_u8_strset_set(&set, idx, buf); fail_unless(!result, "re-add 2 failed"); fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), "wrong re-add 2 get"); - for(i = 0; i < 4; ++i) { - idx = (uint8_t)((4 * i) + 3); + for(i = 0; i < 8; ++i) { + idx = (uint8_t)((8 * i) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), "wrong get loop6"); } - /* Add a 5th, set grows */ - fail_unless(Curl_u8_strset_count(&set) == 4, "wrong count pre add 5"); - idx = (uint8_t)((4 * 4) + 3); + /* Add a 9th, set grows */ + fail_unless(Curl_u8_strset_count(&set) == 8, "wrong count pre add 5"); + idx = (uint8_t)((9 * 4) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); result = Curl_u8_strset_set(&set, idx, buf); fail_unless(!result, "add4 failed"); fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), "wrong get4"); - for(i = 0; i < 5; ++i) { - idx = (uint8_t)((4 * i) + 3); + for(i = 0; i < 9; ++i) { + idx = (uint8_t)((8 * i) + 3); curl_msnprintf(buf, sizeof(buf), "str-%d", idx); fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)), "wrong get loop5"); } - fail_unless(Curl_u8_strset_count(&set) == 5, "wrong count aftger add 5"); + fail_unless(Curl_u8_strset_count(&set) == 9, "wrong count aftger add 5"); Curl_u8_strset_clear(&set); From 5d6dc816785358f505ba6922ceb507b4521c5421 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 20 Aug 2026 00:33:42 +0200 Subject: [PATCH 0940/1062] GHA/checksrc: preprocess workflows to lint more shell code `zizmor` keeps being confused by non-"well-known" shell designators `msys2 {0}`, `\bash.exe '{0}'`, `cpa.sh {0}`, while `actionlint` silently skips checking such shell code. Though it's all POSIX/bash. Replace the unrecognized shell designators with `bash` before running the linters, to remove these blind spots. zizmor pedantic persona: Before: `No findings to report. Good job! (1 ignored, 61 suppressed)` After: `No findings to report. Good job! (1 ignored)` zizmor auditor persona: Before: `No findings to report. Good job! (62 ignored)` After: `No findings to report. Good job! (1 ignored)` Closes #22626 --- .github/workflows/checksrc.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index f6a2ec84e52b..7efc740dc03a 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -178,6 +178,10 @@ jobs: with: persist-credentials: false + - name: 'zizmor/actionlint (prepare)' + # Replace custom bash-like shell designators with the standard one to make linters process them + run: sed -i.bak -E 's/shell\x3a .+ zizmor\x3a ignore.+$/shell\x3a bash/g' .github/workflows/*.yml + - name: 'zizmor GHA' env: GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' From 7e7ee16dd3a6e36029fd41c595724c6614176bcb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 21 Aug 2026 11:50:33 +0200 Subject: [PATCH 0941/1062] ws: fix write callback error handling When websocket writes out data thought the registered write callback and that returns an error, clear the write buffers, so a sub-sequent flush will not attempt another write. The transfer is failed at that point and should no longer invoke the callback. Fixes #22627 Reported-by: Scott Talbert Closes #22634 --- lib/ws.c | 6 +- tests/http/test_20_websockets.py | 15 +++- tests/libtest/Makefile.inc | 1 + tests/libtest/cli_ws_write_err.c | 136 +++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 tests/libtest/cli_ws_write_err.c diff --git a/lib/ws.c b/lib/ws.c index 46f4505a6a11..a7653df6781b 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -754,7 +754,8 @@ static CURLcode ws_cw_write(struct Curl_easy *data, } else if(result) { failf(data, "[WS] decode payload error %d", (int)result); - return result; + Curl_bufq_reset(&ctx->buf); + goto out; } } @@ -812,7 +813,8 @@ static CURLcode ws_cw_flush(struct Curl_easy *data, } else if(result) { failf(data, "[WS] decode payload error %d", (int)result); - return result; + Curl_bufq_reset(&ctx->buf); + goto out; } } } diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index 8f8b2efaf07c..c859279c0ee6 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -296,7 +296,10 @@ def srv(): except OSError as e: st["err"] = e - curl = CurlClient(env=env) + run_env = os.environ.copy() + if 'CURL_DEBUG' in run_env: + del run_env['CURL_DEBUG'] + curl = CurlClient(env=env, run_env=run_env) send_rounds = 2 threading.Thread(target=srv, daemon=True).start() while "p" not in st and "err" not in st: @@ -344,3 +347,13 @@ def test_20_13_pause_frames_large(self, env: Env, ws_4frames): url = f'ws://localhost:{ws_4frames.port}/large' r = client.run(args=[url, payload]) r.check_exit_code(0) + + # test handling of write callback errors + def test_20_14_write_err(self, env: Env, ws_4frames): + payload = 127 * "x" + client = LocalClient(env=env, name='cli_ws_write_err') + if not client.exists(): + pytest.skip(f'example client not built: {client.name}') + url = f'ws://localhost:{ws_4frames.port}/small' + r = client.run(args=[url, payload]) + r.check_exit_code(0) diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 6311fe0b5363..1554f1b4d22c 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -66,6 +66,7 @@ TESTS_C = \ cli_ws_data.c \ cli_ws_pause.c \ cli_ws_pingpong.c \ + cli_ws_write_err.c \ \ lib500.c lib501.c lib502.c lib503.c lib504.c lib505.c lib506.c lib507.c \ lib508.c lib509.c lib510.c lib511.c lib512.c lib513.c lib514.c lib515.c \ diff --git a/tests/libtest/cli_ws_write_err.c b/tests/libtest/cli_ws_write_err.c new file mode 100644 index 000000000000..7018224ff686 --- /dev/null +++ b/tests/libtest/cli_ws_write_err.c @@ -0,0 +1,136 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +#include "testtrace.h" + +#ifndef CURL_DISABLE_WEBSOCKETS + +struct test_ws_write_err_ctx { + CURL *easy; + int callback_count; + int errors; + int closed; +}; + +static size_t test_ws_write_err_write_cb(char *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct test_ws_write_err_ctx *ctx = userdata; + size_t nbytes = size * nmemb; + const struct curl_ws_frame *meta = curl_ws_meta(ctx->easy); + + (void)ptr; + ctx->callback_count++; + if(!meta) { + curl_mfprintf(stderr, "write_cb: ERROR call #%d with meta=NULL\n", + ctx->callback_count); + ++ctx->errors; + return CURL_WRITEFUNC_ERROR; + } + if(ctx->callback_count > 2) { + ctx->errors++; + curl_mfprintf(stderr, "write_cb: call #%d should not happen\n", + ctx->callback_count); + return 0; + } + if(ctx->callback_count > 1) { + curl_mfprintf(stderr, "write_cb: call #%d return error\n", + ctx->callback_count); + return 0; + } + return nbytes; +} +#endif /* CURL_DISABLE_WEBSOCKETS */ + +static CURLcode test_cli_ws_write_err(const char *URL) +{ +#ifndef CURL_DISABLE_WEBSOCKETS + struct test_ws_write_err_ctx ctx; + CURLM *multi; + int still_running = 0; + int msgs_left = 0; + int done = 0; + + memset(&ctx, 0, sizeof(ctx)); + setbuf(stdout, NULL); + + curl_global_init(CURL_GLOBAL_ALL); + + ctx.easy = curl_easy_init(); + multi = curl_multi_init(); + if(!ctx.easy || !multi) { + curl_mfprintf(stderr, "main: ERROR creating easy/multi\n"); + ctx.errors = 1; + goto out; + } + + curl_easy_setopt(ctx.easy, CURLOPT_URL, URL); + curl_easy_setopt(ctx.easy, CURLOPT_WRITEFUNCTION, + test_ws_write_err_write_cb); + curl_easy_setopt(ctx.easy, CURLOPT_WRITEDATA, &ctx); + curl_easy_setopt(ctx.easy, CURLOPT_VERBOSE, 1L); + + curl_multi_add_handle(multi, ctx.easy); + curl_multi_perform(multi, &still_running); + + while(still_running && !ctx.closed && !ctx.errors) { + + curl_mfprintf(stderr, "main: poll\n"); + curl_multi_poll(multi, NULL, 0, 100, NULL); + curl_mfprintf(stderr, "main: perform\n"); + curl_multi_perform(multi, &still_running); + + while(!done) { + CURLMsg *msg = curl_multi_info_read(multi, &msgs_left); + if(!msg) + break; + if(msg->msg == CURLMSG_DONE) { + curl_mfprintf(stderr, "main: done result=%d (%s)\n", + (int)msg->data.result, + curl_easy_strerror(msg->data.result)); + done = 1; + } + } + } + +out: + if(ctx.easy) { + if(multi) + curl_multi_remove_handle(multi, ctx.easy); + curl_easy_cleanup(ctx.easy); + } + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + + return (ctx.errors || (ctx.callback_count != 2)) ? + CURLE_WRITE_ERROR : CURLE_OK; + +#else /* !CURL_DISABLE_WEBSOCKETS */ + (void)URL; + curl_mfprintf(stderr, "WebSockets not enabled in libcurl\n"); + return (CURLcode)1; +#endif /* CURL_DISABLE_WEBSOCKETS */ +} From 6c04b424bd0a32c9e4a0bd9c5ba3d290360190b7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 22 Aug 2026 00:06:42 +0200 Subject: [PATCH 0942/1062] VERSIONS.md: document Rock-solid curl releases While they are not curl releases done by the Open Source project or provided openly, these version numbers might appear in the wild so let's document them in this separate list. Closes #22636 --- docs/VERSIONS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 5db6a51a7673..68842b941693 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -344,3 +344,14 @@ same version numbers. - 0.3: February 1 1997 - 0.2: December 17 1996 - 0.1: November 11 1996 + +## Rock-solid curl + +The long-term support releases of curl, called [Rock-solid +curl](https://rock-solid.curl.dev/), are managed separately and are for paying +customers only. They are made as releases branches off previous standard curl +releases, and use version numbers that were never used by the freely available +version. + +- Rock-solid curl 8.18.1: February 25, 2026 +- Rock-solid curl 8.18.2: September 2, 2026 From 1086f513b86dde051517066c29f4e3ac6e80d10d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:32:58 +0000 Subject: [PATCH 0943/1062] GHA: update dependency pizlonator/fil-c to v0.684 Closes #22637 --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c7d0d0506a20..3098853def86 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,7 +40,7 @@ env: # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com BORINGSSL_VERSION: 0.20260730.0 # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.683 + FIL_C_VERSION: 0.684 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com From 5c61e168698a72b87437d58ed728bcdea6d5db42 Mon Sep 17 00:00:00 2001 From: zaveshaa Date: Sat, 22 Aug 2026 15:01:09 +0300 Subject: [PATCH 0944/1062] docs: make 5 example snippets compile cleanly with clang verify-examples.pl invokes 'gcc', which on macOS is clang. With -Wall -Wextra... -Werror -pedantic, clang rejects two patterns that GNU gcc accepts: - passing a char * to %p without a cast (-Wformat-pedantic): the C standard says %p expects void *, so cast explicitly in the CURLOPT_PROGRESSDATA, CURLOPT_PROGRESSFUNCTION, CURLOPT_XFERINFODATA and CURLOPT_XFERINFOFUNCTION examples - returning CURLcode result from main() when curl_easy_init() returned NULL leaves it uninitialized (-Wsometimes-uninitialized) in the CURLINFO_TLS_SSL_PTR example: initialize it to CURLE_OK like other man pages already do Closes #22638 --- docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md | 2 +- docs/libcurl/opts/CURLOPT_PROGRESSDATA.md | 2 +- docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md | 2 +- docs/libcurl/opts/CURLOPT_XFERINFODATA.md | 2 +- docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md index aea8ec45e55a..5dd44f48e63f 100644 --- a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md +++ b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md @@ -142,7 +142,7 @@ static size_t wf(char *ptr, size_t size, size_t nmemb, void *stream) int main(int argc, char *argv[]) { - CURLcode result; + CURLcode result = CURLE_OK; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md index 6222995e618b..621a246d1a0c 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md @@ -50,7 +50,7 @@ static int progress_callback(void *clientp, double ulnow) { struct progress *memory = clientp; - printf("private: %p\n", memory->private); + printf("private: %p\n", (void *)memory->private); /* use the values */ diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md index d1d6ba3d3791..82cf3585f76d 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md @@ -96,7 +96,7 @@ static int progress_callback(void *clientp, double ulnow) { struct progress *memory = clientp; - printf("private: %p\n", memory->private); + printf("private: %p\n", (void *)memory->private); /* use the values */ diff --git a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md index 5099e0a46944..01e46918b848 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md @@ -53,7 +53,7 @@ static int progress_cb(void *clientp, curl_off_t ulnow) { struct progress *memory = clientp; - printf("private ptr: %p\n", memory->private); + printf("private ptr: %p\n", (void *)memory->private); /* use the values */ return 0; /* all is good */ diff --git a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md index 161be8fd4b3d..c9c6cd6e2aa3 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md @@ -92,7 +92,7 @@ static int xferinfo_callback(void *clientp, curl_off_t ulnow) { struct progress *memory = clientp; - printf("my ptr: %p\n", memory->private); + printf("my ptr: %p\n", (void *)memory->private); /* use the values */ From 8770c49de4d992aa901157bc0d78de88eb12880e Mon Sep 17 00:00:00 2001 From: zaveshaa Date: Sun, 23 Aug 2026 09:02:27 +0300 Subject: [PATCH 0945/1062] docs: remove doubled word in SECURITY-ADVISORY.md Closes #22642 --- docs/SECURITY-ADVISORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SECURITY-ADVISORY.md b/docs/SECURITY-ADVISORY.md index 34c342509d57..98f23e53c769 100644 --- a/docs/SECURITY-ADVISORY.md +++ b/docs/SECURITY-ADVISORY.md @@ -8,7 +8,7 @@ SPDX-License-Identifier: curl As described in the [vulnerability disclosure policy](https://curl.se/dev/vuln-disclosure.html), when a vulnerability has -been reported to the project and and it has been confirmed by the team, we +been reported to the project and it has been confirmed by the team, we author an advisory document for the issue.

This advisory document should ideally be written in cooperation with the From 85c8f6f3a5b107ee605122019cea4fb4edcfe61d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 23 Aug 2026 17:03:06 +0200 Subject: [PATCH 0946/1062] cookie: allow loading cookies for localhost The added code to avoid loading PSL cookies from files from PR #22500 (c041895, not shipped in a release), calls psl_is_public_suffix(), returns TRUE also for "localhost" (!). We now allow cookies again for localhost. Added test 320 to verify. Reported-by: Athos Ribeiro Fixes #22645 Closes #22646 --- lib/cookie.c | 12 ++++++--- tests/data/Makefile.am | 2 +- tests/data/test320 | 59 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tests/data/test320 diff --git a/lib/cookie.c b/lib/cookie.c index 51ce3a9c8b29..8c72f6596657 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -839,10 +839,14 @@ static bool is_public_suffix(struct Curl_easy *data, lcase[dlen] = 0; acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); } - else - /* note that this PSL function returns the opposite value than - psl_is_cookie_domain_acceptable() does */ - acceptable = !psl_is_public_suffix(psl, lcookie); + else { + /* libpsl says localhost is a PSL, we think not */ + acceptable = + curl_strequal(lcookie, "localhost") || + /* note that this PSL function returns the opposite value than + psl_is_cookie_domain_acceptable() does */ + !psl_is_public_suffix(psl, lcookie); + } Curl_psl_release(data); } else diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 7238eed74900..1b6fd78272a8 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -60,7 +60,7 @@ test280 test281 test282 test283 test284 test285 test286 test287 test288 \ test289 test290 test291 test292 test293 test294 test295 test296 test297 \ test298 test299 test300 test301 test302 test303 test304 test305 test306 \ test307 test308 test309 test310 test311 test312 test313 test314 test315 \ -test316 test317 test318 test319 \ +test316 test317 test318 test319 test320 \ test325 test326 test327 test328 test329 test330 test331 test332 test333 \ test334 test335 test336 test337 test338 test339 test340 test341 test342 \ test343 test344 test345 test346 test347 test348 test349 test350 test351 \ diff --git a/tests/data/test320 b/tests/data/test320 new file mode 100644 index 000000000000..645299d5dea0 --- /dev/null +++ b/tests/data/test320 @@ -0,0 +1,59 @@ + + + + +HTTP +cookies + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: something-close, close-something, close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +cookies +PSL + + +http + + +Load cookies from file for localhost + + +http://localhost:%HTTPPORT/%TESTNUMBER -b %LOGDIR/cookies.txt --resolve localhost:%HTTPPORT:%HOSTIP + + +localhost%TABTRUE%TAB/%TABFALSE%TAB0%TABgood%TABallowed + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: localhost:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: good=allowed + + + + From a334227e8ac74047c372c94bbb8950173f9a409c Mon Sep 17 00:00:00 2001 From: Kieran Gannon <143231290+Clevis22@users.noreply.github.com> Date: Sun, 23 Aug 2026 15:20:43 -0400 Subject: [PATCH 0947/1062] tidy-up: fix typos in docs and comments Correct grammar and spelling in documentation and source comments. Also fix the Aspell configuration key and one user-facing OpenSSL error message. Closes #22647 --- .github/scripts/pyspelling.yaml | 4 ++-- docs/EARLY-RELEASE.md | 2 +- docs/TODO.md | 6 +++--- docs/cmdline-opts/_URL.md | 4 ++-- docs/internals/MULTI-EV.md | 8 ++++---- docs/internals/THRDPOOL-AND-QUEUE.md | 6 +++--- docs/internals/TIME-KEEPING.md | 2 +- docs/libcurl/libcurl-security.md | 4 ++-- docs/libcurl/libcurl-url.md | 4 ++-- docs/libcurl/opts/CURLOPT_COOKIE.md | 4 ++-- docs/libcurl/opts/CURLOPT_URL.md | 2 +- lib/cf-socket.c | 2 +- lib/cw-out.c | 2 +- lib/http.c | 2 +- lib/multihandle.h | 2 +- lib/smtp.c | 4 ++-- lib/uint-hashset.c | 4 ++-- lib/urlapi.c | 2 +- lib/urldata.h | 4 ++-- lib/vauth/digest.c | 2 +- lib/vssh/libssh2.c | 2 +- lib/vtls/openssl.c | 6 +++--- lib/vtls/schannel.c | 2 +- lib/vtls/wolfssl.c | 2 +- 24 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/scripts/pyspelling.yaml b/.github/scripts/pyspelling.yaml index bb0585ab7ab8..0161bc376c6a 100644 --- a/.github/scripts/pyspelling.yaml +++ b/.github/scripts/pyspelling.yaml @@ -7,8 +7,8 @@ matrix: - name: Markdown expect_match: false - apsell: - mode: en + aspell: + lang: en dictionary: wordlists: - wordlist.txt diff --git a/docs/EARLY-RELEASE.md b/docs/EARLY-RELEASE.md index 8ec74c3e20c7..833a785ce064 100644 --- a/docs/EARLY-RELEASE.md +++ b/docs/EARLY-RELEASE.md @@ -57,7 +57,7 @@ the three ones above are all 'no'. - Can the bug be fixed "easily" by applying a patch? - Does the bug break the build? Most users do not build curl themselves. - How long is it until the already scheduled next release? -- Can affected users safely rather revert to a former release until the next +- Can affected users safely revert to a former release until the next scheduled release? - Is it a performance regression with no functionality side-effects? If so it has to be substantial. diff --git a/docs/TODO.md b/docs/TODO.md index aecf43c4a42d..28f804eb41d6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -702,10 +702,10 @@ RFC 6266 documents how UTF-8 names can be passed to a client in the [curl issue 1888](https://github.com/curl/curl/issues/1888) -## Option to make `-Z` merge lined based outputs on stdout +## Option to make `-Z` merge line-based outputs on stdout -When a user requests multiple lined based files using `-Z` and sends them to -stdout, curl does not *merge* and send complete lines fine but may send +When a user requests multiple line-based files using `-Z` and sends them to +stdout, curl does not *merge* and send complete lines but may send partial lines from several sources. [curl issue 5175](https://github.com/curl/curl/issues/5175) diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index c20cd762bdd4..a68b005893eb 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -20,5 +20,5 @@ setup handshakes. This improves speed. Connection reuse can only be done for URLs specified for a single command line invocation and cannot be performed between separate curl runs. -Everything provided on the command line that is not a command line option or -its argument, curl assumes is a URL and treats it as such. +curl assumes everything provided on the command line that is not a command +line option or its argument is a URL and treats it as such. diff --git a/docs/internals/MULTI-EV.md b/docs/internals/MULTI-EV.md index f5d2fa831c57..cc899faf3624 100644 --- a/docs/internals/MULTI-EV.md +++ b/docs/internals/MULTI-EV.md @@ -55,11 +55,11 @@ compared to the *previous* pollset. If relevant changes are detected, * a socket was also in the previous one, but IN/OUT flags changed * a socket in the previous one is no longer part of the current -`multi_ev.c` keeps a `struct mev_sh_entry` for each sockets in a hash +`multi_ev.c` keeps a `struct mev_sh_entry` for each socket in a hash with the socket as key. It tracks in each entry which transfers are -interested in this particular socket. How many transfer want to read -and/or write and what the summarized `POLLIN/POLLOUT` action, that -had been reported to `multi->socket_cb` was. +interested in this particular socket, how many transfers want to read +and/or write and the summarized `POLLIN/POLLOUT` action reported to +`multi->socket_cb`. This is necessary as a socket may be in use by several transfers at the same time (think HTTP/2 on the same connection). When a transfer diff --git a/docs/internals/THRDPOOL-AND-QUEUE.md b/docs/internals/THRDPOOL-AND-QUEUE.md index e55f8a2be313..65a2f70dc544 100644 --- a/docs/internals/THRDPOOL-AND-QUEUE.md +++ b/docs/internals/THRDPOOL-AND-QUEUE.md @@ -57,12 +57,12 @@ A thread pool can be destroyed via `Curl_thrdpool_destroy(pool, join)` where ### Safety -The thread pool operates use a mutex and condition variables to manage +The thread pool operates using a mutex and condition variables to manage concurrency. All interactions and callback invocation are done under the pool's mutex lock, *except* the "process" callback which is invoked unlocked. -To avoid deadlocks, no callback must invoked other pool functions. Also, +To avoid deadlocks, no callback must invoke other pool functions. Also, any call of pool functions may result in callback invocations. The "work items", once "taken" by the pool, should not be referenced @@ -102,7 +102,7 @@ the queue. Calling `Curl_thrdq_recv()` delivers processed items back. ### Safety -The thread queue operates use a mutex and condition variables to manage +The thread queue operates using a mutex and condition variables to manage concurrency. All interactions and callback invocation are done under the queue's mutex lock, *except* the "process" callback which is invoked unlocked. diff --git a/docs/internals/TIME-KEEPING.md b/docs/internals/TIME-KEEPING.md index b43daae0be61..4d99d63ce89d 100644 --- a/docs/internals/TIME-KEEPING.md +++ b/docs/internals/TIME-KEEPING.md @@ -25,7 +25,7 @@ this was mostly not noticeable. On slow machines or in CI, this led to rare and annoying test failures. (Especially when we added assertions that the reported "timeline" of a -transfer was in the correct order: *queue -> nameloopup -> connect -> +transfer was in the correct order: *queue -> namelookup -> connect -> appconnect ->...*.) ## Revised Approach diff --git a/docs/libcurl/libcurl-security.md b/docs/libcurl/libcurl-security.md index 81b29cfd16f0..cefb386380b2 100644 --- a/docs/libcurl/libcurl-security.md +++ b/docs/libcurl/libcurl-security.md @@ -54,8 +54,8 @@ stored in a home directory that is NFS mounted or used on another network based file system, so the clear text password flies through your network every time anyone reads that file. -For applications that enable .netrc use, a user who manage to set the right -URL might then be possible to pass on passwords. +For applications that enable .netrc use, a user who manages to set the right +URL might then make it possible to pass on passwords. To avoid these problems, do not use .netrc files and never store passwords in plain text anywhere. diff --git a/docs/libcurl/libcurl-url.md b/docs/libcurl/libcurl-url.md index b39d0304d5c6..bc936fcb4d0a 100644 --- a/docs/libcurl/libcurl-url.md +++ b/docs/libcurl/libcurl-url.md @@ -108,8 +108,8 @@ with it. # SET PARTS -A user set individual URL parts, either after having parsed a full URL or -instead of parsing such. +A user can set individual URL parts, either after having parsed a full URL or +instead of parsing one. ~~~c rc = curl_url_set(urlp, CURLUPART_FRAGMENT, "anchor", 0); diff --git a/docs/libcurl/opts/CURLOPT_COOKIE.md b/docs/libcurl/opts/CURLOPT_COOKIE.md index 4c47c1fdafa5..1e363cf7ea7b 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIE.md +++ b/docs/libcurl/opts/CURLOPT_COOKIE.md @@ -43,12 +43,12 @@ multiple requests are done due to authentication, followed redirections or similar, they all get this cookie passed on. The cookies set by this option are separate from the internal cookie storage -held by the cookie engine and they are not be modified by it. If you enable +held by the cookie engine and they are not modified by it. If you enable the cookie engine and either you have imported a cookie of the same name (e.g. 'foo') or the server has set one, it has no effect on the cookies you set here. A request to the server sends both the 'foo' held by the cookie engine and the 'foo' held by this option. To set a cookie that is instead held by the -cookie engine and can be modified by the server use CURLOPT_COOKIELIST(3). +cookie engine and can be modified by the server, use CURLOPT_COOKIELIST(3). Since this custom cookie is appended to the Cookie: header in addition to any cookies set by the cookie engine, there is a risk that the header ends up too diff --git a/docs/libcurl/opts/CURLOPT_URL.md b/docs/libcurl/opts/CURLOPT_URL.md index b9e5e52b4f36..1790ce3288e9 100644 --- a/docs/libcurl/opts/CURLOPT_URL.md +++ b/docs/libcurl/opts/CURLOPT_URL.md @@ -72,7 +72,7 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. Note however that -libcurl needs a URL set to be able to performed a transfer. +libcurl needs a URL set to be able to perform a transfer. The parser used for handling the URL set with CURLOPT_URL(3) is the same that curl_url_set(3) uses. diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 77d850b299cf..73b4934f4ee2 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1204,7 +1204,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, ctx->started_at = *Curl_pgrs_now(data); #ifdef SOCK_NONBLOCK /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set - * because we would not know how socketype is about to be used in the + * because we would not know how socktype is about to be used in the * callback, SOCK_NONBLOCK might get factored out before calling socket(). */ if(!data->set.fopensocket) diff --git a/lib/cw-out.c b/lib/cw-out.c index b47587781682..0137975b1eba 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -413,7 +413,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx, out: if(result) { - /* We do not want to invoked client callbacks a second time after + /* We do not want to invoke client callbacks a second time after * encountering an error. See issue #13337 */ ctx->errored = TRUE; cw_out_bufs_free(ctx); diff --git a/lib/http.c b/lib/http.c index 98608793c79a..47e7cd3df66e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -524,7 +524,7 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) /* * Examine the current authentication state to see if this is an error. The * idea is for this function to get called after processing all the headers - * in a response message. If we have been to asked to authenticate + * in a response message. If we have been asked to authenticate at * a particular stage, and we have done it, we are OK. If we are already * completely authenticated, it is not OK to get another 401 or 407. * diff --git a/lib/multihandle.h b/lib/multihandle.h index 3f5448f91c7e..2a8fba158300 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -86,7 +86,7 @@ typedef enum { /* This is the struct known as CURLM on the outside */ struct Curl_multi { - /* First a simple identifier to easier detect if a user mix up + /* First a simple identifier to more easily detect if a user mixes up this multi handle with an easy handle. Set this to CURLMULTI_MAGIC_NUMBER. */ uint32_t magic; diff --git a/lib/smtp.c b/lib/smtp.c index fbbac28327de..8ce4706f9e9b 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -1423,8 +1423,8 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, is_smtp_err = (smtpcode / 100 != 2); - /* If there is multiple RCPT TO to be issued, it is possible to ignore errors - and proceed with only the valid addresses. */ + /* If there are multiple RCPT TO commands to issue, it is possible to + ignore errors and proceed with only the valid addresses. */ is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails); if(is_smtp_err) { diff --git a/lib/uint-hashset.c b/lib/uint-hashset.c index fabc0aee9ca7..7e0281d91137 100644 --- a/lib/uint-hashset.c +++ b/lib/uint-hashset.c @@ -57,14 +57,14 @@ static const uint8_t u8_smask[] = { * - We have an array: (id, string) are ideally placed at index "id % size". * - If slot at index is already occupied, we have a collision. * - A simple collision strategy would look at the next index, and the - * next until until finding an empty slot. + * next until finding an empty slot. * - The drawback is that this may lead to many checks on lookups, as it * will need to also look at subsequent slots until it finds the match. * The amount of lookups is the "probe sequence length" (psl) and this * may vary greatly between entries. * - Robin Hood Hashing balances the 'psl's of all entries more evenly: * - psl == 0 means an entry is in exactly the right slot - * - pasl == 1 means it is in the slot right after. psl == 2 is the slot + * - psl == 1 means it is in the slot right after. psl == 2 is the slot * after that, etc. * - when inserting a new entry, track its psl. Finding a slot where * the existing entry has a lower psl makes a swap. Put the new entry diff --git a/lib/urlapi.c b/lib/urlapi.c index e787773ced1a..734efbc35e69 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -1699,7 +1699,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ifmissing = CURLUE_NO_QUERY; plusdecode = flags & CURLU_URLDECODE; if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY)) - /* there was a blank query and the user do not ask for it */ + /* there was a blank query and the user does not ask for it */ ptr = NULL; break; case CURLUPART_FRAGMENT: diff --git a/lib/urldata.h b/lib/urldata.h index 8bb82871797b..fe842b2c99fc 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1157,8 +1157,8 @@ typedef void multi_sub_xfer_done_cb(struct Curl_easy *data, */ struct Curl_easy { - /* First a simple identifier to easier detect if a user mix up this easy - handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ + /* First a simple identifier to more easily detect if a user mixes up this + easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ uint32_t magic; /* once an easy handle is added to a multi, either explicitly by the * libcurl application or implicitly during `curl_easy_perform()`, diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 683933a953b6..748c0d834726 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -179,7 +179,7 @@ static char *auth_digest_string_quoted(const char *s) } /* Retrieves the value for a corresponding key from the challenge string - * returns TRUE if the key could be found, FALSE if it does not exists + * returns TRUE if the key could be found, FALSE if it does not exist */ static bool auth_digest_get_key_value(const char *chlg, const char *key, char *buf, size_t buflen) diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 098b680f7fe9..24a651d30ebb 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -3386,7 +3386,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ - /* libcurl MUST to set custom memory functions so that the kbd_callback + /* libcurl MUST set custom memory functions so that the kbd_callback function's memory allocations can be properly freed */ sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, my_libssh2_free, diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4505cc31eb8c..7ad8f3289244 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1203,7 +1203,7 @@ static int engineload(struct Curl_easy *data, } if(!params.cert) { - failf(data, "SSL engine did not initialized the certificate properly."); + failf(data, "SSL engine did not initialize the certificate properly."); return 0; } @@ -1926,7 +1926,7 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, break; case SSL_ERROR_NONE: /* did not get anything */ case SSL_ERROR_WANT_READ: - /* SSL has send its notify and now wants to read the reply + /* SSL has sent its notify and now wants to read the reply * from the server. We are not really interested in that. */ CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); connssl->io_need = CURL_SSL_IO_NEED_RECV; @@ -3773,7 +3773,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } /* OpenSSL contains code to work around lots of bugs and flaws in various - SSL-implementations. SSL_CTX_set_options() is used to enabled those + SSL-implementations. SSL_CTX_set_options() is used to enable those workarounds. The man page for this option states that SSL_OP_ALL enables all the workarounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index cee933b3aadb..1162c9c2c231 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1418,7 +1418,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: 1. If we are renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data + complete (from the server perspective), it can encrypt app data (not handshake data) in an extra buffer at this point. 2. (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a connection and this extra data is part of the handshake. diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index b8a07fb6fd65..8b4d8a18f40d 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -1987,7 +1987,7 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, break; case WOLFSSL_ERROR_NONE: /* did not get anything */ case WOLFSSL_ERROR_WANT_READ: - /* wolfSSL has send its notify and now wants to read the reply + /* wolfSSL has sent its notify and now wants to read the reply * from the server. We are not really interested in that. */ CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); connssl->io_need = CURL_SSL_IO_NEED_RECV; From 740d9520a9691b99be195f41408024f83abb1654 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 23 Aug 2026 22:57:36 +0200 Subject: [PATCH 0948/1062] docs: mention possible auth option conflicts Do not mix HTTP auth options with custom Authorization headers. Closes #22648 --- docs/libcurl/opts/CURLOPT_HTTPAUTH.md | 4 ++++ docs/libcurl/opts/CURLOPT_HTTPHEADER.md | 10 ++++++++++ docs/libcurl/opts/CURLOPT_PROXYAUTH.md | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md index 925033281f12..6715ddc3e328 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md +++ b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md @@ -37,6 +37,10 @@ extra network round-trip. Set the actual name and password with the CURLOPT_USERPWD(3) option or with the CURLOPT_USERNAME(3) and the CURLOPT_PASSWORD(3) options. +Custom `Authorization:` headers set with CURLOPT_HTTPHEADER(3) may interfere +with and cause unintended side-effects if combined with authentication set +with CURLOPT_HTTPAUTH(3). + For authentication with a proxy, see CURLOPT_PROXYAUTH(3). ## CURLAUTH_BASIC diff --git a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md index 2c9ed039c77c..34a864adb11e 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md +++ b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md @@ -102,6 +102,16 @@ Host: is not sent at all over the wire. Tells libcurl the upload is to be done using this chunked encoding instead of providing the Content-Length: field in the request. +## Authorization: + +Custom `Authorization:` headers may interfere with and cause unintended +side-effects when combined with authentication set with CURLOPT_HTTPAUTH(3). + +## Proxy-Authorization: + +Custom `Proxy-Authorization:` headers may interfere with and cause unintended +side-effects when combined with authentication set with CURLOPT_PROXYAUTH(3). + # SPECIFIC MIME HEADERS When used to build a MIME email for IMAP or SMTP, the following document-level diff --git a/docs/libcurl/opts/CURLOPT_PROXYAUTH.md b/docs/libcurl/opts/CURLOPT_PROXYAUTH.md index 459fd429c371..a13c992dfcbd 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYAUTH.md +++ b/docs/libcurl/opts/CURLOPT_PROXYAUTH.md @@ -40,6 +40,10 @@ option. The bitmask can be constructed by the bits listed and described in the CURLOPT_HTTPAUTH(3) man page. +Custom `Proxy-Authorization:` headers set with CURLOPT_PROXYHEADER(3) may +interfere with and cause unintended side-effects if combined with +authentication set with CURLOPT_PROXYAUTH(3). + # DEFAULT CURLAUTH_BASIC From be459eb4e39bbe352f52f83f0cee560372958036 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 23 Aug 2026 23:34:48 +0200 Subject: [PATCH 0949/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 65 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 7d176129be91..235633c4ed60 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1506 - Contributors: 3767 + Authors: 1511 + Contributors: 3776 This release includes the following changes: @@ -46,6 +46,7 @@ This release includes the following bugfixes: o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] o cmake: use built-in variable and target property dump functions with CMake 4.5+ [155] o config-riscos.h: delete handcrafted RISC OS config header, in favor of autotools [178] + o config-win32.h: drop UWP, c-ares, simplify more [231] o config-win32.h: limit use to MSVC IDE Project builds [193] o configure: clarify --enable-debug option [133] o configure: fix misleading error messages [42] @@ -75,23 +76,34 @@ This release includes the following bugfixes: o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] + o curlx_inet_ntop: return `CURLcode`, drop setting `errno` [237] + o curlx_inet_pton: drop setting `errno` on error [236] o DEPRECATE.md: HTTP/2 Server Push gets removed in March 2027 [174] o dict: avoid busy-loop in sendf() when the socket is not writable [99] o dnsd: fix bounds check in `read_https_alpn_part()` [143] o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] + o docs: clarify that cookies need domain set to match [224] + o docs: connection reuse behavior for socket callbacks [219] + o docs: make 5 example snippets compile cleanly with clang [192] + o docs: mention possible auth option conflicts [114] + o docs: remove doubled word in SECURITY-ADVISORY.md [183] o DoH: improvements [203] o file: support directory listing on Windows [205] + o filter: change time reporting [235] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gitignore: maintenance updates [170] o gopher: reject CR and LF in the selector [1] o h2: bootstrap max streams from multi handle if in use [132] o h3-proxy: fix NULL deref when non-:status header arrives before :status [167] + o Happy Eyeballing v3: resolution delay of 25ms [232] o header api: add guards [168] + o headers: name the arguments the way the definitions name them [234] o HISTORY: add when c-ares support was introduced (2004) o hostip: only cache negative resolves for authoritative answers [16] o http2: make server push transfers inherit share from parent [81] o http: avoid length underflow in Curl_compareheader [78] + o http: custom Authorization: header overrides Negotiate [223] o http: fix non-tunneling proxy hostname use [116] o http: stop dropping large custom headers [69] o http: trim custom header name before the Authorization drop [17] @@ -100,6 +112,7 @@ This release includes the following bugfixes: o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] o keylog: add a random size argument to Curl_tls_keylog_write() [180] + o ldap: base64-encode LDIF values beginning with colon or less-than [218] o ldap: reject control characters in URL-decoded filter values [196] o ldap: support empty username and password [106] o ldap: support insecure mode for Windows native LDAP [3] @@ -108,6 +121,7 @@ This release includes the following bugfixes: o lib: add "Curl_" prefix to two global functions [84] o lib: add multi_wakeup_internal [86] o lib: fix 'ns' -> 'us' in trace messages [57] + o lib: new easy option string storage [215] o lib: optimize struct layouts for reduced memory usage [212] o lib: ratelimit timestamps [14] o lib: update mentions of the legacy "sessionhandle" [157] @@ -135,6 +149,7 @@ This release includes the following bugfixes: o openssl+sectrust: fix session reuse [4] o openssl+sectrust: move session verified set into result check [82] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] + o openssl: fix DER buffer leak in Apple SecTrust verification [217] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] o progress: cleanup, less memory [179] @@ -173,6 +188,7 @@ This release includes the following bugfixes: o tests: address mutable class vars and naive datetime in Python code o tests: change whitespace and comments in Python test code o tests: convert unit test 1396 and 1398 into libtests [146] + o tests: fix Content-Length mismatch in test 2064 [233] o tests: fix the FTP check for unexpected RST [117] o tests: fix type promotion on 32-bit arches in http test code [88] o tests: fix typo in assert message in http test @@ -188,6 +204,7 @@ This release includes the following bugfixes: o tidy-up: comments, messages, formatting [172] o tidy-up: drop redundant includes [110] o tidy-up: fix Perl syntax and formatting nits [123] + o tidy-up: fix typos in docs and comments [173] o tidy-up: minor code fixes and improvements [171] o tidy-up: typos, comment nits [60] o timeval: make `Curl_freq` variable static (Windows) [90] @@ -198,6 +215,7 @@ This release includes the following bugfixes: o tool_cb_hdr: de-duplicate filename setter [24] o tool_cb_prg: avoid integer overflows [93] o tool_doswin: add stdin relay auth [130] + o tool_msgs: make notef() respect --silent [220] o tool_operate: limit `is_using_schannel()` call to Windows [140] o tool_operate: only check for schannel if on windows [187] o tool_operate: remove call to abort() [23] @@ -205,6 +223,7 @@ This release includes the following bugfixes: o typecheck-gcc: allow passing `char[]` as callback data [153] o uint-spbset: reused empty chunks [67] o unit3214: fix to pass on systems with >=128-bit pointers [107] + o url: fix handling of empty user in NTLM matching [221] o url: fix negotiate/ntlm connection reuse [176] o url: reject control codes in credentials set via CURLOPT [70] o urlapi: allow URLs to not have userauth (hostname) [92] @@ -215,6 +234,7 @@ This release includes the following bugfixes: o urlapi: preserve empty markers in relative URLs [61] o urldata: cleanups [175] o urldata: drop four strings from the aptr struct [136] + o VERSIONS.md: document Rock-solid curl releases [201] o vms: fix symbol typo and missing closing quotes in `config_h.com` [124] o vquic: add Curl_ prefix to some global functions [76] o vquic: initialize new callback slot for nghttp3 v1.18.0+ [87] @@ -224,6 +244,7 @@ This release includes the following bugfixes: o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] o websocket: pause writing and meta data fix [135] o wolfssl: fix build for wolfssl without bio chain support [75] + o ws: fix write callback error handling [204] o ws: pause/unpause write handling [55] This release includes the following known bugs: @@ -246,22 +267,25 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - 11soda11, 1rhino2 on hackerone, AlanKingPL, Alb3e3, Alhuda Khan, anupamme, - Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, + 11soda11, 1rhino2 on hackerone, accl on hackerone, AlanKingPL, Alb3e3, + Alhuda Khan, Anton Karpov, anupamme, Athos Ribeiro, Bartel Sielski, + Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, Christoph Reiter, claudex on github, Collin Funk, cybertron10 on github, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, - GLaDOS-418 on github, Graham Campbell, Hendrik Hübner, HwangRock, itzTanos29, - Joel Depooter, Johannes Schindelin, Keng-Yu Lin, kit-ty-kate on github, + Fred Klassen, GLaDOS-418 on github, Graham Campbell, Hendrik Hübner, + HwangRock, itzTanos29, Jace Laquerre, Jayanth Vennamreddy, Joel Depooter, + Johannes Schindelin, Keng-Yu Lin, Kieran Gannon, kit-ty-kate on github, Laurent Sabourin, Marcel Jamin, Martin Dukek, Matthew John Cheetham, Max Dymond, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, - Samuel Dainard, Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, - Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, Viktor Szakats, - xmoezzz on github, Yoshiro Yoneya - (63 contributors) + Samuel Dainard, Samuel Henrique, Scott Talbert, sdgh179 on github, + Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, Stephan Zeisberg, + stze on hackerone, Thomas Chauchefoin, Viktor Szakats, xmoezzz on github, + Yoshiro Yoneya, zaveshaa + (74 contributors) References to bug reports and discussions on issues: @@ -378,6 +402,7 @@ References to bug reports and discussions on issues: [111] = https://curl.se/bug/?i=22387 [112] = https://curl.se/bug/?i=22379 [113] = https://curl.se/bug/?i=22372 + [114] = https://curl.se/bug/?i=22648 [115] = https://curl.se/bug/?i=22276 [116] = https://curl.se/bug/?i=22382 [117] = https://curl.se/bug/?i=22305 @@ -436,6 +461,7 @@ References to bug reports and discussions on issues: [170] = https://curl.se/bug/?i=22445 [171] = https://curl.se/bug/?i=22444 [172] = https://curl.se/bug/?i=22443 + [173] = https://curl.se/bug/?i=22647 [174] = https://curl.se/bug/?i=22490 [175] = https://curl.se/bug/?i=22604 [176] = https://curl.se/bug/?i=22528 @@ -444,6 +470,7 @@ References to bug reports and discussions on issues: [180] = https://curl.se/bug/?i=22560 [181] = https://curl.se/bug/?i=22555 [182] = https://curl.se/bug/?i=22557 + [183] = https://curl.se/bug/?i=22642 [184] = https://curl.se/bug/?i=22594 [185] = https://curl.se/bug/?i=22593 [186] = https://curl.se/bug/?i=22588 @@ -452,6 +479,7 @@ References to bug reports and discussions on issues: [189] = https://curl.se/bug/?i=22541 [190] = https://curl.se/bug/?i=22545 [191] = https://curl.se/bug/?i=22544 + [192] = https://curl.se/bug/?i=22638 [193] = https://curl.se/bug/?i=22595 [194] = https://curl.se/bug/?i=22536 [195] = https://curl.se/bug/?i=22540 @@ -459,8 +487,10 @@ References to bug reports and discussions on issues: [197] = https://curl.se/bug/?i=22473 [199] = https://curl.se/bug/?i=22591 [200] = https://curl.se/bug/?i=22532 + [201] = https://curl.se/bug/?i=22636 [202] = https://curl.se/bug/?i=22484 [203] = https://curl.se/bug/?i=22514 + [204] = https://curl.se/bug/?i=22627 [205] = https://curl.se/bug/?i=22577 [206] = https://curl.se/bug/?i=22587 [208] = https://curl.se/bug/?i=22475 @@ -468,4 +498,19 @@ References to bug reports and discussions on issues: [210] = https://curl.se/bug/?i=22519 [212] = https://curl.se/bug/?i=22585 [213] = https://curl.se/bug/?i=22567 + [215] = https://curl.se/bug/?i=22628 [216] = https://curl.se/bug/?i=22579 + [217] = https://curl.se/bug/?i=22631 + [218] = https://curl.se/bug/?i=22339 + [219] = https://curl.se/bug/?i=20521 + [220] = https://curl.se/bug/?i=22623 + [221] = https://curl.se/bug/?i=22629 + [223] = https://curl.se/bug/?i=22610 + [224] = https://curl.se/bug/?i=22621 + [231] = https://curl.se/bug/?i=22613 + [232] = https://curl.se/bug/?i=22612 + [233] = https://curl.se/bug/?i=22611 + [234] = https://curl.se/bug/?i=22550 + [235] = https://curl.se/bug/?i=22587 + [236] = https://curl.se/bug/?i=22607 + [237] = https://curl.se/bug/?i=22229 From a1bca29bdd7c8ae2d7e195e47bca1f471f333e54 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 24 Aug 2026 09:27:42 +0200 Subject: [PATCH 0950/1062] lib: silence gcc-16 compiler warnings `-Wmaybe-uninitialized` Seen in the 'curl-for-win / Windows gcc zlib-classic (x64)' CI job, after it got an upstream upgrade from gcc-15 to gcc-16: ``` lib/http.c:206:6: error: 'out.str' may be used uninitialized [-Werror=maybe-uninitialized] 206 | if(header_has_value(&header, &out)) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ lib/api.h:188:3: error: 'guard.data' may be used uninitialized [-Werror=maybe-uninitialized] 188 | Curl_mapi_enter((g), (m), CURL_MAPI_FN_##fn, (r)) && (m) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ lib/multi.c:1695:6: note: in expansion of macro 'CURL_MAPI_ENTER' 1695 | if(CURL_MAPI_ENTER(&guard, m, multi_poll, &mresult)) { | ^~~~~~~~~~~~~~~ ``` Ref: https://github.com/curl/curl/actions/runs/32626356576/job/97162205296#step:3:4906 Closes #22651 --- lib/easy.c | 2 +- lib/http.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/easy.c b/lib/easy.c index 117af41f08d3..8c225d0804a3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -815,7 +815,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) */ CURLcode curl_easy_perform(CURL *curl) { - struct Curl_eapi_guard guard; + struct Curl_eapi_guard guard = { 0 }; CURLcode result; if(CURL_EAPI_ENTER(&guard, curl, easy_perform, &result)) { diff --git a/lib/http.c b/lib/http.c index 47e7cd3df66e..ed2dff373ff0 100644 --- a/lib/http.c +++ b/lib/http.c @@ -200,7 +200,7 @@ static bool http_header_is_empty(const char *header) */ static CURLcode copy_custom_value(const char *header, char **valp) { - struct Curl_str out; + struct Curl_str out = { 0 }; /* find the end of the header name */ if(header_has_value(&header, &out)) { From 541968e36153f8433ae12d2b9dc8f0c7b9449457 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 24 Aug 2026 10:18:22 +0200 Subject: [PATCH 0951/1062] circleci: update URL in comment, include in future URL checks Also: - mdlinkcheck: check two CI files for broken URLs. Closes #22654 --- .circleci/config.yml | 2 +- scripts/mdlinkcheck | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 30eec16e5ae0..04730458adc7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ # View these jobs in the browser: https://app.circleci.com/pipelines/github/curl/curl -# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/configuration-reference/ +# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/reference/configuration-reference/ version: 2.1 commands: diff --git a/scripts/mdlinkcheck b/scripts/mdlinkcheck index 180203c41e41..4d30e90d392e 100755 --- a/scripts/mdlinkcheck +++ b/scripts/mdlinkcheck @@ -90,7 +90,7 @@ if(defined $ARGV[0] && $ARGV[0] eq "--dry-run") { } # list all files to scan for links -my @files = qx(git ls-files docs include lib scripts src); +my @files = qx(git ls-files .circleci appveyor.yml docs include lib scripts src); sub storelink { my ($f, $line, $link) = @_; From 50bd20abb215c1fa3cc5d05c470be44fa7b4b42e Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 24 Aug 2026 10:54:36 +0200 Subject: [PATCH 0952/1062] circleci: bump images to their latest revision Ref: https://circleci.com/developer/images?imageType=machine Closes #22655 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04730458adc7..2f16bebe3b45 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -114,7 +114,7 @@ commands: executors: ubuntu: machine: - image: ubuntu-2204:2025.09.1 + image: ubuntu-2204:2026.05.1 jobs: basic: @@ -157,7 +157,7 @@ jobs: arm: machine: - image: ubuntu-2204:2025.09.1 + image: ubuntu-2204:2026.05.1 resource_class: arm.medium steps: - checkout @@ -168,7 +168,7 @@ jobs: arm-cares: machine: - image: ubuntu-2204:2025.09.1 + image: ubuntu-2204:2026.05.1 resource_class: arm.medium steps: - checkout From c2676bf9e639e31e44b12a2cc82cf2046231b056 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 24 Aug 2026 11:55:29 +0200 Subject: [PATCH 0953/1062] vssh: silence gcc-11 `-Wnull-dereference`, dedupe `CURL_EASY_STR()` calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silencing: ``` In file included from libcurl_unity.c:179: vssh/vssh.c: In function ‘Curl_ssh_setup_pkey.part.0’: vssh/vssh.c:423:50: error: potential null pointer dereference [-Werror=null-dereference] 423 | CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY)[0]) { libtool: compile: gcc -DHAVE_CONFIG_H -I../include -I../lib -I../lib -I. -DBUILDING_LIBCURL -DCURL_STATICLIB -DUNITTESTS -D_GNU_SOURCE -Werror-implicit-functi ``` Ref: https://app.circleci.com/pipelines/gh/curl/curl/20825/workflows/c3eb591b-5a7d-40da-baa8-d2d9980b0cb2/jobs/181277 Follow-up to c8df3defd9664cd276adc33754210ee07b5bbf71 #22628 Closes #22656 --- lib/vssh/vssh.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c index f8e687382c39..9834546987d9 100644 --- a/lib/vssh/vssh.c +++ b/lib/vssh/vssh.c @@ -366,6 +366,8 @@ CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) { char *home = NULL; if(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) { + const char *str; + sshc->pub_key = sshc->priv_key = NULL; if(CURL_EASY_STR(data, STRING_SSH_PRIVATE_KEY)) { @@ -418,11 +420,9 @@ CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) * library extract the public key from the private key file. This is done * by passing sshc->pub_key = NULL. */ - if(CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY) && - /* treat empty string the same way as NULL */ - CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY)[0]) { - sshc->pub_key = curlx_strdup( - CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY)); + str = CURL_EASY_STR(data, STRING_SSH_PUBLIC_KEY); + if(str && *str) { /* treat empty string the same way as NULL */ + sshc->pub_key = curlx_strdup(str); if(!sshc->pub_key) goto fail; } From 2687751471c04e07a9c5da78338569d97aff7fd4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Aug 2026 11:07:17 +0200 Subject: [PATCH 0954/1062] protocol: simpler Curl_getn_scheme runs faster Iterating unit test 1627 50,000 times show the new version to be 31% faster on my machine. - unit1627: add more test strings, In particular three, five and six letter non-existing schemes. - remove scripts/schemetable.c, not used anymore Closes #22658 --- .github/scripts/codespell.sh | 1 + .github/scripts/typos.toml | 1 + lib/protocol.c | 243 ++++++++++++++++++++++++++--------- scripts/Makefile.am | 2 +- scripts/schemetable.c | 186 --------------------------- tests/data/test1627 | 2 +- tests/unit/unit1627.c | 111 ++++++++++++---- 7 files changed, 274 insertions(+), 272 deletions(-) delete mode 100644 scripts/schemetable.c diff --git a/.github/scripts/codespell.sh b/.github/scripts/codespell.sh index f93a788a1e49..c6a0cb9b4b74 100755 --- a/.github/scripts/codespell.sh +++ b/.github/scripts/codespell.sh @@ -17,6 +17,7 @@ codespell \ --skip 'RELEASE-NOTES' \ --skip 'scripts/wcurl' \ --skip 'tests/unit/unit1625.c' \ + --skip 'tests/unit/unit1627.c' \ --ignore-regex '.*spellchecker:disable-line' \ --ignore-words '.github/scripts/codespell-ignore.words' \ -- diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 880664361dc7..9f2a0e0a2acc 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -35,4 +35,5 @@ extend-exclude = [ "scripts/wcurl", "tests/data/test*", "tests/unit/unit1625.c", + "tests/unit/unit1627.c", ] diff --git a/lib/protocol.c b/lib/protocol.c index 8d57c058c6b7..20f9c8137b3e 100644 --- a/lib/protocol.c +++ b/lib/protocol.c @@ -462,72 +462,199 @@ const struct Curl_scheme Curl_scheme_wss = { PORT_HTTPS /* defport */ }; + +static const struct Curl_scheme *two_letter_scheme(const char *scheme) +{ + if((Curl_raw_tolower(scheme[0]) == 'w') && + (Curl_raw_tolower(scheme[1]) == 's')) + return &Curl_scheme_ws; + return NULL; +} + +static const struct Curl_scheme *three_letter_scheme(const char *scheme) +{ + char s0 = Curl_raw_tolower(scheme[0]); + char s1 = Curl_raw_tolower(scheme[1]); + char s2 = Curl_raw_tolower(scheme[2]); + if(s0 == 'f') { + if(s1 == 't' && s2 == 'p') + return &Curl_scheme_ftp; + } + else if(s0 == 'w') { + if(s1 == 's' && s2 == 's') + return &Curl_scheme_wss; + } + else if(s0 == 's') { + if(s1 == 'c' && s2 == 'p') + return &Curl_scheme_scp; + if(s1 == 'm' && s2 == 'b') + return &Curl_scheme_smb; + } + return NULL; +} + +static const struct Curl_scheme *four_letter_scheme(const char *scheme) +{ + char s0 = Curl_raw_tolower(scheme[0]); + char s1 = Curl_raw_tolower(scheme[1]); + char s2 = Curl_raw_tolower(scheme[2]); + char s3 = Curl_raw_tolower(scheme[3]); + if(s3 == 'p') { + if(s0 == 'h') { + if(s1 == 't' && s2 == 't') + return &Curl_scheme_http; + } + else if(s0 == 'i') { + if(s1 == 'm' && s2 == 'a') + return &Curl_scheme_imap; + } + else if(s0 == 'l') { + if(s1 == 'd' && s2 == 'a') + return &Curl_scheme_ldap; + } + else if(s0 == 'r') { + if(s1 == 't' && s2 == 's') + return &Curl_scheme_rtsp; + } + else if(s0 == 't') { + if(s1 == 'f' && s2 == 't') + return &Curl_scheme_tftp; + } + else if(s0 == 's') { + if(s1 == 'f' && s2 == 't') + return &Curl_scheme_sftp; + if(s1 == 'm' && s2 == 't') + return &Curl_scheme_smtp; + } + } + else if(s0 == 'f') { + if(s1 == 't' && s2 == 'p' && s3 == 's') + return &Curl_scheme_ftps; + if(s1 == 'i' && s2 == 'l' && s3 == 'e') + return &Curl_scheme_file; + } + else if(s0 == 'm') { + if(s1 == 'q' && s2 == 't' && s3 == 't') + return &Curl_scheme_mqtt; + } + else if(s0 == 'p') { + if(s1 == 'o' && s2 == 'p' && s3 == '3') + return &Curl_scheme_pop3; + } + else if(s0 == 'd') { + if(s1 == 'i' && s2 == 'c' && s3 == 't') + return &Curl_scheme_dict; + } + else if(s0 == 's') { + if(s1 == 'm' && s2 == 'b' && s3 == 's') + return &Curl_scheme_smbs; + } + return NULL; +} + +static const struct Curl_scheme *five_letter_scheme(const char *scheme) +{ + char s4 = Curl_raw_tolower(scheme[4]); + if(s4 == 's') { + char s0 = Curl_raw_tolower(scheme[0]); + char s1 = Curl_raw_tolower(scheme[1]); + char s2 = Curl_raw_tolower(scheme[2]); + char s3 = Curl_raw_tolower(scheme[3]); + if(s3 == 'p') { + switch(s0) { + case 'h': + if(s1 == 't' && s2 == 't') + return &Curl_scheme_https; + break; + case 'l': + if(s1 == 'd' && s2 == 'a') + return &Curl_scheme_ldaps; + break; + case 'i': + if(s1 == 'm' && s2 == 'a') + return &Curl_scheme_imaps; + break; + case 's': + if(s1 == 'm' && s2 == 't') + return &Curl_scheme_smtps; + break; + default: + break; + } + } + else if(s0 == 'p') { + if(s1 == 'o' && s2 == 'p' && s3 == '3') + return &Curl_scheme_pop3s; + } + else if(s0 == 'm') { + if(s1 == 'q' && s2 == 't' && s3 == 't') + return &Curl_scheme_mqtts; + } + else if(s0 == 's') { + if(s1 == 'o' && s2 == 'c' && s3 == 'k') + return &Curl_scheme_socks; + } + } + return NULL; +} + +static const struct Curl_scheme *six_letter_scheme(const char *scheme) +{ + char s0 = Curl_raw_tolower(scheme[0]); + switch(s0) { + case 's': + if(curl_strnequal("ocks4", &scheme[1], 5)) + return &Curl_scheme_socks4; + if(curl_strnequal("ocks5", &scheme[1], 5)) + return &Curl_scheme_socks5; + break; + case 'g': + if(curl_strnequal("opher", &scheme[1], 5)) + return &Curl_scheme_gopher; + break; + case 't': + if(curl_strnequal("elnet", &scheme[1], 5)) + return &Curl_scheme_telnet; + break; + } + return NULL; +} + +static const struct Curl_scheme *seven_letter_scheme(const char *scheme) +{ + char s0 = Curl_raw_tolower(scheme[0]); + if(s0 == 's') { + if(curl_strnequal("ocks4a", &scheme[1], 6)) + return &Curl_scheme_socks4a; + if(curl_strnequal("ocks5h", &scheme[1], 6)) + return &Curl_scheme_socks5h; + } + else if(s0 == 'g') { + if(curl_strnequal("ophers", &scheme[1], 6)) + return &Curl_scheme_gophers; + } + return NULL; +} + /* Returns a struct scheme pointer if the name is a known scheme. Check the ->run struct field for non-NULL to figure out if an implementation is present. */ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) { - /* table generated by schemetable.c: - 1. gcc schemetable.c && ./a.out - 2. check how small the table gets - 3. tweak the hash algorithm, then rerun from 1 - 4. when the table is good enough - 5. copy the table into this source code - 6. make sure this function uses the same hash function that worked for - schemetable.c - */ - static const struct Curl_scheme * const all_schemes[59] = { NULL, - &Curl_scheme_pop3, NULL, - &Curl_scheme_smtps, - &Curl_scheme_socks, - &Curl_scheme_socks4, - &Curl_scheme_socks5, NULL, NULL, - &Curl_scheme_gophers, - &Curl_scheme_ws, - &Curl_scheme_sftp, - &Curl_scheme_socks4a, - &Curl_scheme_scp, - &Curl_scheme_rtsp, - &Curl_scheme_dict, NULL, NULL, - &Curl_scheme_gopher, NULL, NULL, NULL, - &Curl_scheme_wss, NULL, - &Curl_scheme_smb, NULL, - &Curl_scheme_ldap, - &Curl_scheme_ldaps, - &Curl_scheme_imap, NULL, NULL, NULL, - &Curl_scheme_imaps, - &Curl_scheme_https, - &Curl_scheme_tftp, - &Curl_scheme_telnet, NULL, NULL, NULL, - &Curl_scheme_file, - &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &Curl_scheme_ftp, - &Curl_scheme_mqtt, NULL, - &Curl_scheme_socks5h, - &Curl_scheme_http, - &Curl_scheme_pop3s, NULL, - &Curl_scheme_mqtts, NULL, - &Curl_scheme_smbs, - &Curl_scheme_ftps, + typedef const struct Curl_scheme *(*letterfunc)(const char *ptr); + static const letterfunc parse[] = { + two_letter_scheme, + three_letter_scheme, + four_letter_scheme, + five_letter_scheme, + six_letter_scheme, + seven_letter_scheme }; - if(len && (len <= 7)) { - const char *s = scheme; - size_t l = len; - const struct Curl_scheme *h; - unsigned int c = 443; - while(l) { - c <<= 5; - c += (unsigned int)Curl_raw_tolower(*s); - s++; - l--; - } + if(len < 2 || len > 7) + return NULL; - h = all_schemes[c % 59]; - if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) - return h; - } - return NULL; + return parse[len - 2](scheme); } const struct Curl_scheme *Curl_get_scheme(const char *scheme) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6d0e92432d81..c490c04fe5df 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -23,7 +23,7 @@ ########################################################################### EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ - checksrc-all.pl mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd \ + checksrc-all.pl mk-ca-bundle.pl mk-unity.pl cd2nroff nroff2cd \ cdall cd2cd managen dmaketgz maketgz release-tools.sh verify-release \ cmakelint.sh cmakeopts.sh mdlinkcheck CMakeLists.txt perlcheck.sh \ pythonlint.sh spacecheck.pl randdisable wcurl top-complexity \ diff --git a/scripts/schemetable.c b/scripts/schemetable.c deleted file mode 100644 index 67f6b81b144e..000000000000 --- a/scripts/schemetable.c +++ /dev/null @@ -1,186 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include -#include - -/* - * Use this tool to generate an updated table for the Curl_getn_scheme_handler - * function in url.c. - */ - -static const char * const scheme[] = { - "dict", - "file", - "ftp", - "ftps", - "gopher", - "gophers", - "http", - "https", - "imap", - "imaps", - "ldap", - "ldaps", - "mqtt", - "mqtts", - "pop3", - "pop3s", - "rtsp", - "scp", - "sftp", - "smb", - "smbs", - "smtp", - "smtps", - "socks", - "socks4", - "socks4a", - "socks5", - "socks5h", - "telnet", - "tftp", - "ws", - "wss", - NULL, -}; - -unsigned int calc(const char *s, int add, int shift) -{ - const char *so = s; - unsigned int c = add; - while(*s) { - c <<= shift; - c += *s; - s++; - } - return c; -} - -unsigned int num[100]; -unsigned int ix[100]; - -static void showtable(int try, int init, int shift) -{ - int nulls = 0; - int i; - for(i = 0; scheme[i]; ++i) - num[i] = calc(scheme[i], init, shift); - for(i = 0; scheme[i]; ++i) - ix[i] = num[i] % try; - printf("/*\n" - " unsigned int c = %d\n" - " while(l) {\n" - " c <<= %d;\n" - " c += Curl_raw_tolower(*s);\n" - " s++;\n" - " l--;\n" - " }\n" - "*/\n", - init, shift); - - printf(" static const struct Curl_scheme * const all_schemes[%d] = {", try); - - /* generate table */ - for(i = 0; i < try; i++) { - int match = 0; - int j; - for(j = 0; scheme[j]; j++) { - if(ix[j] == i) { - printf("\n &Curl_scheme_%s,", scheme[j]); - match = 1; - nulls = 0; - break; - } - } - if(!match) - printf(" NULL,"); - } - printf("\n };\n"); -} - -int main(void) -{ - int i; - int try; - int besttry = 9999; - int bestadd = 0; - int bestshift = 0; - int add; - int shift; - for(shift = 0; shift < 8; shift++) { - for(add = 0; add < 999; add++) { - for(i = 0; scheme[i]; ++i) { - unsigned int v = calc(scheme[i], add, shift); - int j; - int badcombo = 0; - for(j = 0; j < i; j++) { - - if(num[j] == v) { -#if 0 - printf("NOPE: %u is a dupe (%s and %s)\n", - v, scheme[i], scheme[j]); -#endif - badcombo = 1; - break; - } - } - if(badcombo) - break; - num[i] = v; - } -#if 0 - for(i = 0; scheme[i].n; ++i) { - printf("%u - %s\n", num[i], scheme[i].n); - } -#endif - /* try different remainders to find smallest possible table */ - for(try = 28; try < 199; try++) { - int good = 1; - for(i = 0; scheme[i]; ++i) { - ix[i] = num[i] % try; - } - /* check for dupes */ - for(i = 0; scheme[i] && good; ++i) { - int j; - for(j = 0; j < i; j++) { - if(ix[j] == ix[i]) { - good = 0; - break; - } - } - } - if(good) { - if(try < besttry) { - besttry = try; - bestadd = add; - bestshift = shift; - } - break; - } - } - } - } - - showtable(besttry, bestadd, bestshift); -} diff --git a/tests/data/test1627 b/tests/data/test1627 index 807352612d04..95fd47d83fb2 100644 --- a/tests/data/test1627 +++ b/tests/data/test1627 @@ -18,7 +18,7 @@ Curl_get_scheme unit test -199 invokes +547 invokes diff --git a/tests/unit/unit1627.c b/tests/unit/unit1627.c index 06bd30cff5e8..cbdd3383086d 100644 --- a/tests/unit/unit1627.c +++ b/tests/unit/unit1627.c @@ -26,11 +26,13 @@ #include "url.h" #include "strcase.h" +#define ITERATIONS1627 1 /* edit for performance measurements */ + static CURLcode test_unit1627(const char *arg) { UNITTEST_BEGIN_SIMPLE - size_t i, j; + size_t i = 0, j = 0, a; /* existing schemes in different cases */ static const char * const okay[] = { /* all upper */ @@ -48,9 +50,19 @@ static CURLcode test_unit1627(const char *arg) "imAP", "imaPS", "LDap", "LDAps", "mQTT", "mqtTS", "pOP3", "pOP3s", "RtsP", "ScP", "SFtP", "Smb", "smBS", "sMTP", "SMTPs", "TELNEt", "tFTP", "Ws", "wSS", + + "SOCKS", "SOCKS4", "SOCKS5", "SOCKS4A", "SOCKS5H", + "socks", "socks4", "socks5", "socks4a", "socks5h", + "Socks", "sOcks4", "soCks5", "socKs4a", "sockS5h", }; /* non-existing schemes */ static const char * const notokay[] = { + "a", "A", "ht", "tt", "tt", "p+", "TPP", "PPS", "TSP", + "HER", "1CT", "AbG", "LQp", "rtW", "PkY", "xZq", "LmO", + "hyT", "wQA", "dfG", "BvC", "iuY", "ewQ", "dfG", "Jkl", + "NbV", "cXz", "OiU", "SrE", "QaS", "ghJ", "LmN", "VcX", "PoI", + "YtR", "WqA", "DfG", "JkL", "XcV", "NmM", "WeR", "YuI", "PaS", + "a", "A", "htt", "ttp", "httt", "http+", "HTTPP", "HTTPPS", "HTTSP", "GROPHER", "D1CT", "AbG", "zLQp", "mNrtW", "PkY", "bVcxZq", "LmO", "iUhyT", "rEwQA", "xSdfG", "nBvC", "pOiuY", "tRewQ", "aSdfG", "hJkl", @@ -64,37 +76,84 @@ static CURLcode test_unit1627(const char *arg) "AsDf", "GhJk", "LzXc", "VbNm", "qWeR", "tYuI", "oPaS", "dFgH", "jKlZ", "xCvB", "nMqW", "eRtY", "uIoP", "aSdF", "gHjK", "lZxC", "vBnM", "QwEr", "TyUi", "OpAs", "DfGh", "JkLz", "XcVb", "NmqW", "ErTy", "UiOp", "AsDf", - "GhJk", "LzXc", "VbNm" + "GhJk", "LzXc", "VbNm", + + "aa", "Aa", "htta", "ttpa", "httta", "http+a", "HTTPPa", "HTTPPSa", + "HTTSPa", "GROPHERa", "D1CTa", "AbGa", "zLQpa", "mNrtWa", "PkYa", + "bVcxZqa", "LmOa", "iUhyTa", "rEwQAa", "xSdfGa", "nBvCa", "pOiuYa", + "tRewQa", "aSdfGa", "hJkla", "mNbVa", "cXza", "pOiUa", "yTrEa", "wQaSa", + "dFghJa", "kLmNa", "bVcXa", "zPoIa", "uYtRa", "eWqAa", "sDfGa", "hJkLa", + "zXcVa", "bNmMa", "qWeRa", "tYuIa", "oPaSa", "dFgHa", "jKlZa", "xCvBa", + "nMqWa", "eRtYa", "uIoPa", "aSdFa", "gHjKa", "lZxCa", "vBnMa", "QwEra", + "TyUia", "OpAsa", "DfGha", "JkLza", "XcVba", "NmqWa", "ErTya", "UiOpa", + "AsDfa", "GhJka", "LzXca", "VbNma", "qweRa", "tyuIa", "opaDa", "fghJa", + "klzxa", "cvbna", "mQWa", "ErTya", "UiOpa", "AsDfa", "GhJka", "LzXca", + "VbNma", "QwEra", "TyUia", "OpAsa", "DfGha", "JkLza", "XcVba", "NmqWa", + "ErTya", "UiOpa", "AsDfa", "GhJka", "LzXca", "VbNma", "qWeRa", "tYuIa", + "oPaSa", "dFgHa", "jKlZa", "xCvBa", "nMqWa", "eRtYa", "uIoPa", "aSdFa", + "gHjKa", "lZxCa", "vBnMa", "QwEra", "TyUia", "OpAsa", "DfGha", "JkLza", + "XcVba", "NmqWa", "ErTya", "UiOpa", "AsDfa", "GhJka", "LzXca", "VbNma", + + "aab", "Aab", "httab", "ttpab", "htttab", "http+ab", "HTTPPab", "HTTPPSab", + "HTTSPab", "GROPHERab", "D1CTab", "AbGab", "zLQpab", "mNrtWab", "PkYab", + "bVcxZqab", "LmOab", "iUhyTab", "rEwQAab", "xSdfGab", "nBvCab", "pOiuYab", + "tRewQab", "aSdfGab", "hJklab", "mNbVab", "cXzab", "pOiUab", "yTrEab", + "wQaSab", "dFghJab", "kLmNab", "bVcXab", "zPoIab", "uYtRab", "eWqAab", + "sDfGab", "hJkLab", "zXcVab", "bNmMab", "qWeRab", "tYuIab", "oPaSab", + "dFgHab", "jKlZab", "xCvBab", "nMqWab", "eRtYab", "uIoPab", "aSdFab", + "gHjKab", "lZxCab", "vBnMab", "QwErab", "TyUiab", "OpAsab", "DfGhab", + "JkLzab", "XcVbab", "NmqWab", "ErTyab", "UiOpab", "AsDfab", "GhJkab", + "LzXcab", "VbNmab", "qweRab", "tyuIab", "opaDab", "fghJab", "klzxab", + "cvbnab", "mQWab", "ErTyab", "UiOpab", "AsDfab", "GhJkab", "LzXcab", + "VbNmab", "QwErab", "TyUiab", "OpAsab", "DfGhab", "JkLzab", "XcVbab", + "NmqWab", "ErTyab", "UiOpab", "AsDfab", "GhJkab", "LzXcab", "VbNmab", + "qWeRab", "tYuIab", "oPaSab", "dFgHab", "jKlZab", "xCvBab", "nMqWab", + "eRtYab", "uIoPab", "aSdFab", "gHjKab", "lZxCab", "vBnMab", "QwErab", + "TyUiab", "OpAsab", "DfGhab", "JkLzab", "XcVbab", "NmqWab", "ErTyab", + "UiOpab", "AsDfab", "GhJkab", "LzXcab", "VbNmab", + + "dictt", "filee", "ftpp", "ftpss", "gopherr", "gopherss", "httpp", + "httpss", "imapp", "imapss", "ldapp", "ldapss", "mqttt", "mqttss", + "pop33", "pop3ss", "rtspp", "scpp", "sftpp", "smbb", "smbss", "smtpp", + "smtpss", "telnett", "tftpp", "wsw", "wsss", + + "DIC", "FIL", "FT", "TPS", "GOPHE", "OPHER", "HTT", "TTPS", + "IMA", "APS", "LDA", "APS", "MQT", "TTS", "POP", "P3S", + "RTS", "SC", "SFT", "SM", "BS", "SMT", "TPS", + "TELNE", "TFT", "W", "S", }; - for(i = 0; i < CURL_ARRAYSIZE(okay); i++) { - char buffer[32]; - const struct Curl_scheme *get = Curl_get_scheme(okay[i]); - if(get) { - /* verify that we got the correct scheme */ - if(!curl_strequal(get->name, okay[i])) - get = NULL; - } - if(!get) { - curl_mprintf("Input: %s, expected okay\n", okay[i]); - break; - } - Curl_strntolower(buffer, okay[i], strlen(okay[i])); - buffer[strlen(okay[i])] = 0; - if(strcmp(buffer, get->name)) { - curl_mprintf("Input: %s is not lowercase: %s\n", buffer, get->name); - break; + for(a = 0 ; a < ITERATIONS1627; a++) { + + for(i = 0; i < CURL_ARRAYSIZE(okay); i++) { + char buffer[32]; + const struct Curl_scheme *get = Curl_get_scheme(okay[i]); + if(get) { + /* verify that we got the correct scheme */ + if(!curl_strequal(get->name, okay[i])) + get = NULL; + } + if(!get) { + curl_mprintf("Input: %s, expected okay\n", okay[i]); + break; + } + Curl_strntolower(buffer, okay[i], strlen(okay[i])); + buffer[strlen(okay[i])] = 0; + if(strcmp(buffer, get->name)) { + curl_mprintf("Input: %s is not lowercase: %s\n", buffer, get->name); + break; + } } - } - for(j = 0; j < CURL_ARRAYSIZE(notokay); j++) { - const struct Curl_scheme *get = Curl_get_scheme(notokay[j]); - if(get) { - curl_mprintf("Input: %s, expected not okay\n", notokay[j]); - break; + for(j = 0; j < CURL_ARRAYSIZE(notokay); j++) { + const struct Curl_scheme *get = Curl_get_scheme(notokay[j]); + if(get) { + curl_mprintf("Input: %s, expected not okay\n", notokay[j]); + break; + } } } - curl_mprintf("%zu invokes\n", i + j); + curl_mprintf("%zu invokes\n", (i + j) * ITERATIONS1627); if(i != CURL_ARRAYSIZE(okay) || j != CURL_ARRAYSIZE(notokay)) From c2ce945ac07ce51ee32779c7cf1650bb12179afe Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 25 Aug 2026 07:47:24 +0200 Subject: [PATCH 0955/1062] vtls_scache: use case sensitive path match Provided-by: 1rhino2 on hackerone --- lib/vtls/vtls_scache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 3a4c467699a9..102d7d87ced2 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -774,7 +774,7 @@ static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data, /* check for entries with known peer_key */ for(i = 0; scache && i < scache->peer_count; i++) { if(scache->peers[i].ssl_peer_key && - curl_strequal(ssl_peer_key, scache->peers[i].ssl_peer_key) && + !strcmp(ssl_peer_key, scache->peers[i].ssl_peer_key) && cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) { /* yes, we have a cached session for this! */ *ppeer = &scache->peers[i]; From 67221a788266a5066364cf14aefe300e6cb00e0c Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+11soda11@users.noreply.github.com> Date: Thu, 7 May 2026 01:01:56 +0200 Subject: [PATCH 0956/1062] tool_doswin: don't use `TerminateThread` in stdin relay It is a dangerous function. Remove usage by only starting the thread after setup completed successfully. Ref: https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminatethread Ref: https://github.com/curl/curl/pull/21467#discussion_r3173013432 Ref: https://github.com/curl/curl/pull/18996#issuecomment-3389155817 Ref: https://github.com/curl/curl/pull/18451 Follow-up to 9a26633 #17572 Closes https://github.com/curl/curl/pull/22383 --- .github/workflows/windows.yml | 3 - src/tool_doswin.c | 321 ++++++++++++++++++++-------------- tests/data/test1498 | 3 - 3 files changed, 188 insertions(+), 139 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3c9853ec1990..27d5f4c82c28 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -535,9 +535,6 @@ jobs: unset CURL_TEST_SSH_KEYALGO # libssh2 built with WinCNG does not support ssh-ed25519 hostkeys export CURL_TEST_SSH_ENABLE_KEX=diffie-hellman-group-exchange-sha256 fi - if [[ "${TFLAGS}" = *'-t'* ]]; then - TFLAGS+=' !2300' # Leaks memory and file handle via tool_doswin.c / win32_stdin_read_thread() - fi if [[ "${MATRIX_INSTALL} " = *'-libssh '* && \ "${MATRIX_ENV}" = *'x86_64'* ]]; then export CURL_TEST_SSH_DISABLE_KEX=mlkem768x25519-sha256 # broken with libssh 0.12.0 Windows x64 diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 36ef53243901..ffa442b1fa60 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -30,6 +30,7 @@ #ifdef _WIN32 # include "curlx/winapi.h" /* for curlx_win32_random() */ +# include "curlx/nonblock.h" /* for curlx_nonblock() */ # include #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ # define CURL_USE_LFN(f) 0 /* long filenames never available */ @@ -703,87 +704,123 @@ static void init_terminal(void) #ifdef USE_WINSOCK /* The following STDIN non - blocking read techniques are heavily inspired by nmap and ncat (https://nmap.org/ncat/) */ -struct win_thread_data { +static struct win_thread_data { /* This is a copy of the true stdin file handle before any redirection. It is read by the thread. */ HANDLE stdin_handle; - /* This is the listen socket for the thread. It is closed after the first - connection. */ - curl_socket_t socket_l; - /* This is the random number which the background thread uses to verify - the peer. */ - uint64_t expected_auth_val; -}; - -static DWORD WINAPI win_stdin_thread_func(void *thread_data) + /* This is the socket the thread will forward stdin to. It is connected to + the socket which replaces the stdin handle. */ + curl_socket_t socket_w; + /* This is a mutex-like object which we use to synchronize + * cleanup_tdata_sync() */ + CRITICAL_SECTION crit_sect; +} tdata = { NULL, CURL_SOCKET_BAD, {0}}; + +static void cleanup_tdata_sync(void) { - struct win_thread_data *tdata = (struct win_thread_data *)thread_data; - struct sockaddr_in clientAddr; - int clientAddrLen = sizeof(clientAddr); - size_t nread = 0; - uint64_t auth_val = 0; - - curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, - (struct sockaddr *)&clientAddr, - &clientAddrLen); - - if(socket_w == CURL_SOCKET_BAD) { - errorf("accept error: %d", SOCKERRNO); - goto ThreadCleanup; + EnterCriticalSection(&tdata.crit_sect); + if(tdata.stdin_handle) { + CloseHandle(tdata.stdin_handle); + tdata.stdin_handle = NULL; } - sclose(tdata->socket_l); - tdata->socket_l = CURL_SOCKET_BAD; - - do { - ssize_t ret = sread(socket_w, ((unsigned char *)&auth_val) + nread, - sizeof(auth_val) - nread); - if(ret <= 0) { - if(!ret) { - errorf("relay peer disconnected"); - } - else { - errorf("read error: %d", SOCKERRNO); - } - - goto ThreadCleanup; - } - nread += ret; - } while(nread < sizeof(auth_val)); - - if(auth_val != tdata->expected_auth_val) { - errorf("relay peer auth failed"); - goto ThreadCleanup; + if(tdata.socket_w != CURL_SOCKET_BAD) { + sclose(tdata.socket_w); + tdata.socket_w = CURL_SOCKET_BAD; } + LeaveCriticalSection(&tdata.crit_sect); +} + +static DWORD WINAPI win_stdin_thread_func(void *thread_data) +{ + (void)thread_data; - if(shutdown(socket_w, SHUT_RD)) { - errorf("shutdown error: %d", SOCKERRNO); - goto ThreadCleanup; - } for(;;) { DWORD n; ssize_t nwritten; char buffer[BUFSIZ]; - if(!ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL)) + if(!ReadFile(tdata.stdin_handle, buffer, sizeof(buffer), &n, NULL)) break; if(n == 0) break; - nwritten = swrite(socket_w, buffer, n); + nwritten = swrite(tdata.socket_w, buffer, n); if(nwritten == -1) break; if((DWORD)nwritten != n) break; } -ThreadCleanup: - if(tdata->socket_l != CURL_SOCKET_BAD) { - sclose(tdata->socket_l); - tdata->socket_l = CURL_SOCKET_BAD; - } - if(socket_w != CURL_SOCKET_BAD) - sclose(socket_w); - curlx_free(tdata); + cleanup_tdata_sync(); + + return 0; +} + +static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, + const unsigned char *data, + size_t nbytes) +{ + fd_set fdwrite; + fd_set fdexcep; + size_t nwritten = 0; + + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + FD_SET(nonblock_sock, &fdwrite); + + do { + ssize_t ret; + + FD_SET(nonblock_sock, &fdexcep); + + if(select(0, NULL, &fdwrite, &fdexcep, NULL) <= 0) { + errorf("select error: %d", SOCKERRNO); + return -1; + } + + if(FD_ISSET(nonblock_sock, &fdexcep)) { + int sock_err = 0; + int sock_err_size = sizeof(sock_err); + getsockopt(nonblock_sock, SOL_SOCKET, SO_ERROR, + (char *)&sock_err, &sock_err_size); + errorf("connect failure: %d", sock_err); + return -1; + } + + ret = swrite(nonblock_sock, data + nwritten, nbytes - nwritten); + if(ret <= 0) { + if(SOCK_EAGAIN(SOCKERRNO)) + continue; + + errorf("socket write error: %d", SOCKERRNO); + return -1; + } + + nwritten += ret; + } while(nwritten < nbytes); + + return 0; +} + +static int read_auth_val(curl_socket_t sock, uint64_t* auth_val_ptr) +{ + size_t nread = 0; + + do { + ssize_t ret = sread(sock, (unsigned char *)auth_val_ptr + nread, + sizeof(*auth_val_ptr) - nread); + if(ret <= 0) { + if(!ret) + errorf("stdin relay peer disconnected"); + else + errorf("read error: %d", SOCKERRNO); + + return -1; + } + nread += ret; + } while(nread < sizeof(*auth_val_ptr)); + return 0; } @@ -791,34 +828,26 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - struct win_thread_data *tdata = NULL; - HANDLE stdin_handle = NULL; - uint64_t auth_rnd; - size_t nwritten = 0; - static HANDLE stdin_thread = NULL; + HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; + curl_socket_t socket_l = CURL_SOCKET_BAD; + uint64_t auth_rnd = 0; + uint64_t recvd_val = 1; if(socket_r != CURL_SOCKET_BAD) { - DEBUGASSERT(stdin_thread); return socket_r; } - DEBUGASSERT(!stdin_thread); do { curl_socklen_t socksize = 0; struct sockaddr_in selfaddr; - /* Prepare handles for thread */ - tdata = (struct win_thread_data *) - curlx_calloc(1, sizeof(struct win_thread_data)); - if(!tdata) { - errorf("curlx_calloc() error"); - break; - } - /* Create the listening socket for the thread. When it starts, it accepts - * our connection and begin writing STDIN data to the connection. */ - tdata->socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(tdata->socket_l == CURL_SOCKET_BAD) { + InitializeCriticalSection(&tdata.crit_sect); + + /* Create the listening socket. It is used to create the writing socket by + * accepting a connection from the reading socket. */ + socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(socket_l == CURL_SOCKET_BAD) { errorf("socket() error: %d", SOCKERRNO); break; } @@ -828,122 +857,148 @@ curl_socket_t win32_stdin_read_thread(void) selfaddr.sin_family = AF_INET; selfaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Bind to any available loopback port */ - if(bind(tdata->socket_l, (const struct sockaddr *)&selfaddr, socksize)) { + if(bind(socket_l, (const struct sockaddr *)&selfaddr, socksize)) { errorf("bind error: %d", SOCKERRNO); break; } /* Retrieve the assigned loopback port/address */ - if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { + if(getsockname(socket_l, (struct sockaddr *)&selfaddr, &socksize)) { errorf("getsockname error: %d", SOCKERRNO); break; } - if(listen(tdata->socket_l, 1)) { + if(listen(socket_l, 1)) { errorf("listen error: %d", SOCKERRNO); break; } - if(curlx_win32_random((unsigned char *)&auth_rnd, sizeof(auth_rnd))) { - errorf("curlx_win32_random() error"); + /* Create the reading socket */ + socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(socket_r == CURL_SOCKET_BAD) { + errorf("socket error: %d", SOCKERRNO); break; } - tdata->expected_auth_val = auth_rnd; - /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ - if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), - GetCurrentProcess(), &stdin_handle, - 0, FALSE, DUPLICATE_SAME_ACCESS)) { - errorf("DuplicateHandle error: 0x%08lx", GetLastError()); + /* Hard close the socket on closesocket() */ + setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); + + /* Make the reading socket nonblocking */ + if(curlx_nonblock(socket_r, TRUE)) { + errorf("curlx_nonblock() error"); break; } - tdata->stdin_handle = stdin_handle; - /* Start up the thread. We do not bother keeping a reference to it - because it runs until program termination. From here on out all reads - from the stdin handle or file descriptor 0 is reading from the - socket that is fed by the thread. */ - stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, - tdata, 0, NULL); - if(!stdin_thread) { - errorf("CreateThread error: 0x%08lx", GetLastError()); - break; + /* Connect to the listening socket */ + if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { + int sockerr = SOCKERRNO; + if(!SOCK_EAGAIN(sockerr)) { + errorf("connect error: %d", sockerr); + break; + } } - tdata = NULL; /* win_stdin_thread_func owns it now */ - /* Connect to the thread and rearrange our own STDIN handles */ - socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(socket_r == CURL_SOCKET_BAD) { - errorf("socket error: %d", SOCKERRNO); + /* Accept the connection on the other end, creating the writing socket + * which will be given to the background thread */ + tdata.socket_w = CURL_ACCEPT(socket_l, NULL, NULL); + + if(tdata.socket_w == CURL_SOCKET_BAD) { + errorf("accept error: %d", SOCKERRNO); break; } - /* Hard close the socket on closesocket() */ - setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); + /* We don't need the listening socket anymore */ + sclose(socket_l); + socket_l = CURL_SOCKET_BAD; - if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { - errorf("connect error: %d", SOCKERRNO); + /* Authenticate the reading socket to the writing socket to make sure + * we don't leak information.*/ + if(curlx_win32_random((unsigned char *)&auth_rnd, sizeof(auth_rnd))) { + errorf("curlx_win32_random() error"); break; } - do { - ssize_t ret = swrite(socket_r, ((unsigned char *)&auth_rnd) + nwritten, - sizeof(auth_rnd) - nwritten); + if(swrite_blocking_on_nonblock(socket_r, (unsigned char *)&auth_rnd, + sizeof(auth_rnd))) + break; - if(ret <= 0) { - errorf("socket write error: %d", SOCKERRNO); - goto err; - } + if(read_auth_val(tdata.socket_w, &recvd_val)) + break; + + if(recvd_val != auth_rnd) { + errorf("relay peer auth failed"); + break; + } - nwritten += ret; - } while(nwritten < sizeof(auth_rnd)); + if(shutdown(tdata.socket_w, SHUT_RD)) { + errorf("shutdown error: %d", SOCKERRNO); + break; + } if(shutdown(socket_r, SHUT_WR)) { errorf("shutdown error: %d", SOCKERRNO); break; } + /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ + if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), + GetCurrentProcess(), &tdata.stdin_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + errorf("DuplicateHandle error: 0x%08lx", GetLastError()); + break; + } + /* Set the stdin handle to read from the socket. */ if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { errorf("SetStdHandle error: 0x%08lx", GetLastError()); break; } + /* Start up the thread. We do not bother keeping a reference to it + because it runs until program termination. From here on out all reads + from the stdin handle or file descriptor 0 is reading from the + socket that is fed by the thread. */ + stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, + NULL, 0, NULL); + if(!stdin_thread) { + errorf("CreateThread error: 0x%08lx", GetLastError()); + break; + } + CloseHandle(stdin_thread); + + /* Starting the thread is the last thing we do, since there aren't any + * reliable ways to close it in case of subsequent errors. */ + rc = 1; } while(0); -err: if(rc != 1) { - if(stdin_thread) { - TerminateThread(stdin_thread, 1); - CloseHandle(stdin_thread); - stdin_thread = NULL; - } - + /* we rely on the background thread not running at this point, as there + * could be TOCTOU bugs otherwise */ if(socket_r != CURL_SOCKET_BAD) { if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && - stdin_handle) { + tdata.stdin_handle) { /* restore STDIN */ - SetStdHandle(STD_INPUT_HANDLE, stdin_handle); - stdin_handle = NULL; + SetStdHandle(STD_INPUT_HANDLE, tdata.stdin_handle); + tdata.stdin_handle = NULL; } sclose(socket_r); socket_r = CURL_SOCKET_BAD; } - if(tdata) { - if(tdata->stdin_handle) - CloseHandle(tdata->stdin_handle); - if(tdata->socket_l != CURL_SOCKET_BAD) - sclose(tdata->socket_l); + if(socket_l != CURL_SOCKET_BAD) + sclose(socket_l); - curlx_free(tdata); - } + cleanup_tdata_sync(); + DeleteCriticalSection(&tdata.crit_sect); return CURL_SOCKET_BAD; } + /* prevent mem leak warnings */ + atexit(&cleanup_tdata_sync); + DEBUGASSERT(socket_r != CURL_SOCKET_BAD); return socket_r; } diff --git a/tests/data/test1498 b/tests/data/test1498 index 67c3f8409d28..362cf7fcf681 100644 --- a/tests/data/test1498 +++ b/tests/data/test1498 @@ -26,9 +26,6 @@ blablabla http - -!win32 - HTTP PUT from stdin using period From 3ca86602ef9a2ce8a2e22813736a96ba544471cf Mon Sep 17 00:00:00 2001 From: felix h Date: Mon, 10 Aug 2026 23:24:18 +0200 Subject: [PATCH 0957/1062] spnego_sspi: pass channel bindings on initial context HTTP Negotiate can create and send a Kerberos token before receiving a server challenge. The SSPI backend only supplied Schannel endpoint bindings when a challenge token was present, causing IIS with EPA enabled to reject preemptive Kerberos authentication with STATUS_BAD_BINDINGS. Build the SSPI input descriptor independently of the challenge token so Schannel endpoint bindings are included on both initial and subsequent InitializeSecurityContext calls. Assisted-by: martin-fzi@users.noreply.github.com Fixes https://github.com/curl/curl/issues/22466 Closes https://github.com/curl/curl/pull/22537 --- lib/vauth/ntlm_sspi.c | 12 ++++++--- lib/vauth/spnego_sspi.c | 58 +++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 4f977865c9d6..17fb1d2922b3 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -242,6 +242,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SecBufferDesc type_3_desc; SECURITY_STATUS status; unsigned long attrs; +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + SecPkgContext_Bindings pkgBindings = { 0, NULL }; +#endif (void)creds; @@ -262,9 +265,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 */ if(ntlm->sslContext) { - SEC_CHANNEL_BINDINGS channelBindings; - SecPkgContext_Bindings pkgBindings; - pkgBindings.Bindings = &channelBindings; status = Curl_pSecFn->QueryContextAttributes( ntlm->sslContext, SECPKG_ATTR_ENDPOINT_BINDINGS, @@ -296,6 +296,12 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, 0, ntlm->context, &type_3_desc, &attrs, NULL); + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + if(pkgBindings.Bindings) + Curl_pSecFn->FreeContextBuffer(pkgBindings.Bindings); +#endif + if(status != SEC_E_OK) { infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", (unsigned long)status); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 3a140ad84e25..7d3c9ce2d86d 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -93,6 +93,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SecBufferDesc chlg_desc; SecBufferDesc resp_desc; unsigned long attrs; +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + SecPkgContext_Bindings pkgBindings = { 0, NULL }; +#endif if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server @@ -194,6 +197,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 0; + chlg_desc.pBuffers = chlg_buf; + if(chlg64 && *chlg64) { /* Decode the base-64 encoded challenge message */ if(*chlg64 != '=') { @@ -209,38 +216,33 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf[0]; chlg_buf[0].BufferType = SECBUFFER_TOKEN; chlg_buf[0].pvBuffer = chlg; chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); + } #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* SSL context comes from Schannel. - * When extended protection is used in IIS server, - * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS does not pass the authentication (401 response). - * Minimum supported version is Windows 7. - * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 - */ - if(nego->sslContext) { - SEC_CHANNEL_BINDINGS channelBindings; - SecPkgContext_Bindings pkgBindings; - pkgBindings.Bindings = &channelBindings; - nego->status = Curl_pSecFn->QueryContextAttributes( - nego->sslContext, - SECPKG_ATTR_ENDPOINT_BINDINGS, - &pkgBindings); - if(nego->status == SEC_E_OK) { - chlg_desc.cBuffers++; - chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; - chlg_buf[1].cbBuffer = pkgBindings.BindingsLength; - chlg_buf[1].pvBuffer = pkgBindings.Bindings; - } + /* SSL context comes from Schannel. + * When extended protection is used in IIS server, pass its channel + * bindings on the initial call too. HTTP Negotiate can create and send a + * Kerberos token before receiving a challenge from the server. + * Minimum supported version is Windows 7. + * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 + */ + if(nego->sslContext) { + nego->status = Curl_pSecFn->QueryContextAttributes( + nego->sslContext, + SECPKG_ATTR_ENDPOINT_BINDINGS, + &pkgBindings); + if(nego->status == SEC_E_OK) { + SecBuffer *binding_buf = &chlg_buf[chlg_desc.cBuffers++]; + binding_buf->BufferType = SECBUFFER_CHANNEL_BINDINGS; + binding_buf->cbBuffer = pkgBindings.BindingsLength; + binding_buf->pvBuffer = pkgBindings.Bindings; } -#endif } +#endif /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; @@ -261,11 +263,17 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->spn, sspi_flags, 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, + chlg_desc.cBuffers ? + &chlg_desc : NULL, 0, nego->context, &resp_desc, &attrs, NULL); } +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + if(pkgBindings.Bindings) + Curl_pSecFn->FreeContextBuffer(pkgBindings.Bindings); +#endif + /* Free the decoded challenge as it is not required anymore */ curlx_free(chlg); From 81dcba3549b029e789d37c1cc45d44c947039ada Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 25 Aug 2026 10:51:10 +0200 Subject: [PATCH 0958/1062] cmake/FindLibgsasl: fix to set `LIBGSASL_VERSION` with pkg-config detection Follow-up to 16f073ef49f94412000218c9f6ad04e3fd7e4d01 #16973 Closes #22662 --- CMake/FindLibgsasl.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/CMake/FindLibgsasl.cmake b/CMake/FindLibgsasl.cmake index 7fbaa7128f9f..25bae0750333 100644 --- a/CMake/FindLibgsasl.cmake +++ b/CMake/FindLibgsasl.cmake @@ -46,6 +46,7 @@ endif() if(_libgsasl_FOUND) set(Libgsasl_FOUND TRUE) set(LIBGSASL_FOUND TRUE) + set(LIBGSASL_VERSION ${_libgsasl_VERSION}) message(STATUS "Found Libgsasl (via pkg-config): ${_libgsasl_INCLUDE_DIRS} (found version \"${LIBGSASL_VERSION}\")") else() find_path(LIBGSASL_INCLUDE_DIR NAMES "gsasl.h") From fb22f58cc3f33d9bea74c03530d284fa7423aa55 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 22 Jun 2026 13:54:52 +0200 Subject: [PATCH 0959/1062] cmake: optimize OpenSSL fork detection Stop detecting further forks if one has been positively identified. Closes #22664 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be551fd8e7f8..f92d6f050fbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -851,10 +851,10 @@ if(CURL_USE_OPENSSL) if(NOT DEFINED HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() - if(NOT DEFINED HAVE_BORINGSSL) + if(NOT DEFINED HAVE_BORINGSSL AND NOT HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) endif() - if(NOT DEFINED HAVE_LIBRESSL) + if(NOT DEFINED HAVE_LIBRESSL AND NOT HAVE_AWSLC AND NOT HAVE_BORINGSSL) check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" HAVE_LIBRESSL) endif() cmake_pop_check_state() From a8defa2a5e80fdc73ec08d42caf024b110e15666 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 08:57:03 +0000 Subject: [PATCH 0960/1062] Dockerfile: update debian:bookworm-slim Docker digest to 8820086 Closes #22663 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 00450978b44f..934d4b119caf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:abd67ffcfa541b485a3dff59865ab629aa048a6c613e639d36e7456b0b229241 +FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ From 5267ed859d545534d0c21675a2b70af5a3b6e3ef Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Aug 2026 23:37:49 +0200 Subject: [PATCH 0961/1062] openssl: no server cert is only okay if also not pinned Reported-by: Stanislav Fort Closes #22660 --- lib/vtls/openssl.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 7ad8f3289244..271643e46dd8 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4592,21 +4592,24 @@ static CURLcode ossl_check_issuer(struct Curl_cfilter *cf, return result; } -static CURLcode ossl_check_pinned_key(struct Curl_cfilter *cf, - struct Curl_easy *data, - X509 *server_cert) +static const char *pinned(struct Curl_cfilter *cf, + struct Curl_easy *data) { - const char *ptr; - CURLcode result = CURLE_OK; - (void)cf; + return #ifndef CURL_DISABLE_PROXY - ptr = Curl_ssl_cf_is_proxy(cf) ? + Curl_ssl_cf_is_proxy(cf) ? CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY_PROXY) : - CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); -#else - ptr = CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); #endif + CURL_EASY_STR(data, STRING_SSL_PINNEDPUBLICKEY); +} + +static CURLcode ossl_check_pinned_key(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509 *server_cert) +{ + CURLcode result = CURLE_OK; + const char *ptr = pinned(cf, data); if(ptr) { result = ossl_pkp_pin_peer_pubkey(data, server_cert, ptr); if(result) @@ -4795,7 +4798,8 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, server_cert = SSL_get1_peer_certificate(octx->ssl); if(!server_cert) { /* no verification at all, this maybe acceptable */ - if(!(conn_config->verifypeer || conn_config->verifyhost)) + if(!(conn_config->verifypeer || conn_config->verifyhost) && + !pinned(cf, data)) goto out; failf(data, "SSL: could not get peer certificate"); From d1702a24b3629e25d972685d4c45d3c51b6beccb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 24 Aug 2026 12:46:30 +0200 Subject: [PATCH 0962/1062] ngtcp2+openssL: fix early data Openssl needs an additional call into its API at just the right time to *really* enable early data use. Add checks after handshake if early data was really successful, more stringent than we used before. test_02_32: enable early data test wiht more backends and platforms to see how CI now copes with it. Fixes #22649 Closes #22657 Reported-by: Rarylson Freitas --- docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md | 1 - lib/vquic/cf-ngtcp2-cmn.c | 36 +++++++++++++++++++----- tests/http/test_02_download.py | 10 ++----- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 13dd45077dcc..ccbb4895ef9b 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -103,7 +103,6 @@ BoringSSL). It works on TCP and QUIC connections using ngtcp2. This option works on a best effort basis, in cases when it was not possible to send early data the request is resent normally post-handshake. -This option does not work when using QUIC. (Added in 8.11.0 for GnuTLS and 8.13.0 for OpenSSL, quictls and wolfSSL) # DEFAULT diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 6446581ebf1a..e0cd384b0923 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -274,15 +274,24 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) } if(ctx->use_earlydata) { #if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_accepted = - (SSL_get_early_data_status(ctx->tls.ossl.ssl) != - SSL_EARLY_DATA_REJECTED); + /* Check for bug that OpenSSL did not even send the early data. */ + if(SSL_get_early_data_status(ctx->tls.ossl.ssl) == SSL_EARLY_DATA_NOT_SENT) + CURL_TRC_CF(data, cf, "OpenSSL did not send early data"); #endif -#ifdef USE_GNUTLS + +#if NGTCP2_VERSION_NUM >= 0x011700 + ctx->earlydata_accepted = + !ngtcp2_conn_get_tls_early_data_rejected2(ctx->qconn); +#else /* older NGTCP2 */ +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + int ossl_early_status = SSL_get_early_data_status(ctx->tls.ossl.ssl); + if(ossl_early_status == SSL_EARLY_DATA_NOT_SENT) + CURL_TRC_CF(data, cf, "OpenSSL did not send early data"); + ctx->earlydata_accepted = (ossl_early_status == SSL_EARLY_DATA_ACCEPTED); +#elif defined(USE_GNUTLS) int flags = gnutls_session_get_flags(ctx->tls.gtls.session); ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); -#endif -#ifdef USE_WOLFSSL +#elif defined(USE_WOLFSSL) #ifdef WOLFSSL_EARLY_DATA ctx->earlydata_accepted = (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) != @@ -291,7 +300,8 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) DEBUGASSERT(0); /* should not come here if ED is disabled. */ ctx->earlydata_accepted = FALSE; #endif /* WOLFSSL_EARLY_DATA */ -#endif +#endif /* OPENSSL or GNUTLS or WOLFSSL */ +#endif /* older NGTCP2 */ CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? @@ -1068,6 +1078,18 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #error "ngtcp2 TLS backend not defined" #endif +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) && \ + defined(OPENSSL_QUIC_API2) + /* We need to tell OpenSSL to *really* use Early Data for QUIC and + * this only works *after* ngtcp2 has tweaked all SSL parameters, + * otherwise OpenSSL does not accept it. */ + if(ctx->use_earlydata && + !SSL_set_quic_tls_early_data_enabled(ctx->tls.ossl.ssl, 1)) { + CURL_TRC_CF(data, cf, "OpenSSL refused to use early data"); + ctx->use_earlydata = FALSE; + cf->connected = FALSE; + } +#endif ngtcp2_ccerr_default(&ctx->last_error); return CURLE_OK; diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index e556c014b821..473502809b46 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -526,7 +526,7 @@ def test_02_31_parallel_upgrade(self, env: Env, httpd, nghttpx): assert r.total_connects <= 3, r.dump_logs() # nghttpx is the only server we have that supports TLS early data - @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx") + @pytest.mark.skipif(condition=not Env.have_h3_server(), reason="no nghttpx with QUIC") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") @pytest.mark.parametrize("proto", Env.http_protos()) @@ -535,12 +535,6 @@ def test_02_32_earlydata(self, env: Env, httpd, nghttpx, proto): pytest.skip('TLS earlydata not implemented') if proto == 'h3' and not env.curl_can_h3_early_data(): pytest.skip("h3 early data not supported") - if proto != 'h3' and sys.platform.startswith('darwin') and env.ci_run: - pytest.skip('failing on macOS CI runners') - if proto == 'h3' and env.curl_uses_lib('wolfssl'): - pytest.skip('h3 wolfssl early data failing') - if proto == 'h3' and env.curl_uses_lib('gnutls'): - pytest.skip('h3 gnutls early data failing') count = 2 docname = 'data-10k' # we want this test to always connect to nghttpx, since it is @@ -571,7 +565,7 @@ def test_02_32_earlydata(self, env: Env, httpd, nghttpx, proto): if m: earlydata[int(m.group(1))] = int(m.group(2)) continue - if re.match(r'\[1-1] \* SSL reusing session.*', line): + if re.match(r'\[1-1] \* (\[1-1] )?SSL reusing session.*', line): reused_session = True assert reused_session, 'session was not reused for 2nd transfer' assert earlydata[0] == 0, f'{earlydata}' From c9ecd8f8df3f85799732e16cbbd80b941eb3bbd8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 13:54:14 +0000 Subject: [PATCH 0963/1062] GHA: update openssl/openssl to v4.0.2 Closes #22670 --- .github/workflows/http3-linux.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 0f16d1c006a9..23d955123734 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -47,7 +47,7 @@ env: # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.1 + OPENSSL_VERSION: 4.0.2 # manually bumped OPENSSL_PREV_VERSION: 3.6.2 OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3098853def86..bb6d1e8d377e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -53,7 +53,7 @@ env: # handled in renovate.json OPENLDAP_VERSION: 2.6.10 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.1 + OPENSSL_VERSION: 4.0.2 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com RUSTLS_VERSION: 0.15.3 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com From 7ea37abc6ac0120ba5f6d94be8d196f7cf1506bb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 25 Aug 2026 11:14:30 +0200 Subject: [PATCH 0964/1062] openssl: avoid conn reuse if provider is used Reported-by: Stanislav Fort Closes #22665 --- lib/vtls/openssl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 271643e46dd8..604478cce64a 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3756,6 +3756,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } +#ifdef OPENSSL_HAS_PROVIDERS + if(data->state.libctx) + /* forbid connection reuse with provider/engine use */ + connclose(data->conn); +#endif if(cb_setup) { result = cb_setup(cf, data, cb_user_data); From 7be1e70cb6bcd83e130ecfe8cb91b6a7dcdeff42 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 25 Aug 2026 13:47:11 +0200 Subject: [PATCH 0965/1062] vtls: move 'native_ca_store' ssl_config_data => ssl_primary_config And include it as key for connection reuse matching. Reported-by: Stanislav Fort Closes #22668 --- lib/vtls/gtls.c | 6 +++--- lib/vtls/openssl.c | 9 ++++----- lib/vtls/rustls.c | 2 +- lib/vtls/vtls_config.c | 8 +++++--- lib/vtls/vtls_config.h | 2 +- lib/vtls/wolfssl.c | 5 ++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index ba9da769df3e..9d17573dc5d1 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -457,7 +457,7 @@ static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, } infof(data, "SSL Trust Anchors:"); - if(ssl_config->native_ca_store) { + if(config->native_ca_store) { #ifdef USE_APPLE_SECTRUST infof(data, " Native: Apple SecTrust"); creds_are_empty = FALSE; @@ -661,7 +661,7 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, !conn_config->CApath && !conn_config->ca_info_blob && !ssl_config->primary.CRLfile && - !ssl_config->native_ca_store && + !conn_config->native_ca_store && !conn_config->clientcert; /* GnuTLS adds client cert to its credentials! */ if(cache_criteria_met) @@ -1604,7 +1604,7 @@ static CURLcode gtls_verify_cert(struct Curl_easy *data, infof(data, " SSL certificate verified by GnuTLS"); #ifdef USE_APPLE_SECTRUST - if(!verified && ssl_config->native_ca_store) { + if(!verified && config->native_ca_store) { CURLcode result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, gtls_chain_get_der, chain, NULL, 0); diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 604478cce64a..3c3e6df0e331 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2999,7 +2999,6 @@ static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = CURLE_OK; const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ @@ -3008,7 +3007,7 @@ static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, bool have_native_check = FALSE; octx->store_is_empty = TRUE; - if(ssl_config->native_ca_store) { + if(conn_config->native_ca_store) { #ifdef USE_WIN32_CRYPTO bool added = FALSE; result = ossl_windows_load_anchors(cf, data, store, &added); @@ -3316,7 +3315,7 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, !conn_config->CApath && !conn_config->ca_info_blob && !ssl_config->primary.CRLfile && - !ssl_config->native_ca_store; + !conn_config->native_ca_store; ERR_set_mark(); @@ -3370,7 +3369,7 @@ static bool ossl_apply_session( (SSL_get_verify_result(octx->ssl) != X509_V_OK) #ifdef USE_APPLE_SECTRUST /* if sectrust is used and verified the session before */ - && (!ssl_config->native_ca_store || !scs->sectrust_verified) + && (!conn_cfg->native_ca_store || !scs->sectrust_verified) #endif ) { /* Session was from unverified connection, cannot reuse here */ @@ -4835,7 +4834,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, infof(data, "SSL certificate verified via OpenSSL."); #ifdef USE_APPLE_SECTRUST - if(!verified && conn_config->verifypeer && ssl_config->native_ca_store) { + if(!verified && conn_config->verifypeer && conn_config->native_ca_store) { /* we verify using Apple SecTrust *unless* OpenSSL already verified. * This may happen if the application intercepted the OpenSSL callback * and installed its own. */ diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 31e7a5f35130..9c0b0d7be3b3 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1049,7 +1049,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, rustls_client_config_builder_dangerous_set_certificate_verifier( config_builder, cr_verify_none); } - else if(ssl_config->native_ca_store) { + else if(conn_config->native_ca_store) { if(conn_config->CRLfile) { failf(data, "rustls: CRL file not supported with native CA store; " "the platform verifier has no CRL attachment API"); diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c index afd66d475f2f..080d513bb598 100644 --- a/lib/vtls/vtls_config.c +++ b/lib/vtls/vtls_config.c @@ -141,6 +141,7 @@ static bool match_ssl_primary_config(struct Curl_easy *data, if((c1->version == c2->version) && (c1->version_max == c2->version_max) && (c1->ssl_options == c2->ssl_options) && + (c1->native_ca_store == c2->native_ca_store) && (c1->verifypeer == c2->verifypeer) && (c1->verifyhost == c2->verifyhost) && (c1->verifystatus == c2->verifystatus) && @@ -192,6 +193,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, dest->verifypeer = source->verifypeer; dest->verifyhost = source->verifyhost; dest->verifystatus = source->verifystatus; + dest->native_ca_store = source->native_ca_store; dest->cache_session = source->cache_session; dest->ssl_options = source->ssl_options; @@ -225,7 +227,7 @@ static void ssl_easy_config_compl_options(struct Curl_peer *origin, /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it. * If not, we switch it on for supported backends if no custom * CA settings exist. */ - sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); + sslc->primary.native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST); sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN); sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE); @@ -256,7 +258,7 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; + sslc->primary.native_ca_store = TRUE; #endif #ifdef CURL_CA_PATH if(!sslc->custom_capath && !CURL_EASY_STR(data, STRING_SSL_CAPATH)) { @@ -317,7 +319,7 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; + sslc->primary.native_ca_store = TRUE; #endif #ifdef CURL_CA_PATH if(!sslc->custom_capath && diff --git a/lib/vtls/vtls_config.h b/lib/vtls/vtls_config.h index 4372743fa2f7..8296604259bb 100644 --- a/lib/vtls/vtls_config.h +++ b/lib/vtls/vtls_config.h @@ -54,6 +54,7 @@ struct ssl_primary_config { BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ + BIT(native_ca_store); /* use the native CA store of operating system */ BIT(cache_session); /* cache session or not */ BIT(deep_copy); /* members are deep copies, eg. owned here */ }; @@ -70,7 +71,6 @@ struct ssl_config_data { BIT(no_partialchain); /* do not accept partial certificate chains */ BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation list errors */ - BIT(native_ca_store); /* use the native CA store of operating system */ BIT(auto_client_cert); /* automatically locate and use a client certificate for authentication (Schannel) */ BIT(custom_cafile); /* application has set custom CA file */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 8b4d8a18f40d..b215d44e4c4b 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -592,7 +592,6 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const char * const ssl_capath = conn_config->CApath; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); bool imported_native_ca = FALSE; bool imported_ca_info_blob = FALSE; @@ -601,7 +600,7 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, #ifndef NO_FILESYSTEM /* load native CA certificates */ - if(ssl_config->native_ca_store) { + if(conn_config->native_ca_store) { #ifdef WOLFSSL_SYS_CA_CERTS if(wolfSSL_CTX_load_system_CA_certs(wssl->ssl_ctx) != WOLFSSL_SUCCESS) { infof(data, "error importing native CA store, continuing anyway"); @@ -809,7 +808,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, !conn_config->CApath && !conn_config->ca_info_blob && !ssl_config->primary.CRLfile && - !ssl_config->native_ca_store; + !conn_config->native_ca_store; cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data) : NULL; From bb8ec5eb650678ca75e23b23729f25fc10825923 Mon Sep 17 00:00:00 2001 From: Ron Kuper Date: Tue, 25 Aug 2026 12:12:35 -0400 Subject: [PATCH 0966/1062] strcase: inline the raw case conversions Curl_raw_toupper() and Curl_raw_tolower() were out-of-line functions doing a single table lookup, and casecompare() invokes one of them twice per byte compared, so every case-insensitive comparison in the library paid two calls per byte: header matching, scheme and host compares, cookie domains. Make them static CURL_INLINE functions in strcase.h and give the two mapping tables external linkage so the lookup inlines in every translation unit without relying on LTO or unity mode. The tables and the mapping they perform are unchanged, so this is not a behavior change. curl_strequal() on identical header names: -49% on a Cortex-A55, -51% on a 250MHz e300c3. Closes #22672 --- lib/strcase.c | 18 ++---------------- lib/strcase.h | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/strcase.c b/lib/strcase.c index 9f70f41bd63c..6c3597cc3771 100644 --- a/lib/strcase.c +++ b/lib/strcase.c @@ -26,7 +26,7 @@ #include "strcase.h" /* Mapping table to go from lowercase to uppercase for plain ASCII.*/ -static const unsigned char touppermap[256] = { +const unsigned char Curl_touppermap[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, @@ -48,7 +48,7 @@ static const unsigned char touppermap[256] = { }; /* Mapping table to go from uppercase to lowercase for plain ASCII.*/ -static const unsigned char tolowermap[256] = { +const unsigned char Curl_tolowermap[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, @@ -69,20 +69,6 @@ static const unsigned char tolowermap[256] = { 255 }; -/* Portable, consistent toupper. Do not use toupper() because its behavior is - altered by the current locale. */ -char Curl_raw_toupper(char in) -{ - return (char)touppermap[(unsigned char)in]; -} - -/* Portable, consistent tolower. Do not use tolower() because its behavior is - altered by the current locale. */ -char Curl_raw_tolower(char in) -{ - return (char)tolowermap[(unsigned char)in]; -} - /* Copy an upper case version of the string from src to dest. The * strings may overlap. No more than n characters of the string are copied * (including any NUL) and the destination string will NOT be diff --git a/lib/strcase.h b/lib/strcase.h index 54299812beae..559db875bdfe 100644 --- a/lib/strcase.h +++ b/lib/strcase.h @@ -25,8 +25,24 @@ ***************************************************************************/ #include "curl_setup.h" -char Curl_raw_toupper(char in); -char Curl_raw_tolower(char in); +/* Mapping tables for plain ASCII case conversion, defined in strcase.c. + Declared here so the conversions below inline at every call site without + relying on LTO or a unity build: casecompare() invokes one of them twice + per byte compared, where the call costs more than the lookup itself. */ +extern const unsigned char Curl_touppermap[256]; +extern const unsigned char Curl_tolowermap[256]; + +/* Portable, consistent toupper/tolower. Do not use toupper()/tolower() from + , whose behavior is altered by the current locale. */ +static CURL_INLINE char Curl_raw_toupper(char in) +{ + return (char)Curl_touppermap[(unsigned char)in]; +} + +static CURL_INLINE char Curl_raw_tolower(char in) +{ + return (char)Curl_tolowermap[(unsigned char)in]; +} /* checkprefix() is a shorter version of the above, used when the first argument is the string literal */ From 81ade541200869e07880c59183a578d7102517d2 Mon Sep 17 00:00:00 2001 From: Arham Wani Date: Sun, 23 Aug 2026 14:42:03 +0530 Subject: [PATCH 0967/1062] altsvc: continue after unknown parameters Parse extension names and values before deciding whether a parameter is known. This prevents long names and token or quoted-string values from hiding later ma or persist parameters, as required by RFC 7838 section 3. Add unit coverage for long extension names and both value forms, including an escaped quote and semicolon inside a quoted string. Fixes #22644 Closes #22676 --- lib/altsvc.c | 50 ++++++++++++++++++++++++++----------------- tests/unit/unit1654.c | 24 +++++++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index 11c75d45dfd3..c57d3f6b40a5 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -471,6 +471,8 @@ static void altsvc_flush(struct altsvcinfo *asi, } } +#define ALTSVC_MA 1 +#define ALTSVC_PERSIST 2 static void altsvc_parse_params(const char **pp, time_t *pmaxage, bool *ppersist) @@ -482,36 +484,44 @@ static void altsvc_parse_params(const char **pp, for(;;) { struct Curl_str name; struct Curl_str val; - const char *vp; curl_off_t num; - bool quoted; + int keyword = 0; /* allow some extra whitespaces around name and value */ - if(curlx_str_until(pp, &name, 20, '=') || - curlx_str_single(pp, '=') || - curlx_str_cspn(pp, &val, ",;")) + if(curlx_str_until(pp, &name, MAX_ALTSVC_LINE, '=') || + curlx_str_single(pp, '=')) break; /* skip further parameter parsing */ curlx_str_trimblanks(&name); + curlx_str_passblanks(pp); + if(**pp == '\"') { + if(curlx_str_quotedword(pp, &val, MAX_ALTSVC_LINE)) + break; + } + else { + if(curlx_str_cspn(pp, &val, ",;\r\n")) + break; + } curlx_str_trimblanks(&val); - /* the value might be quoted */ - vp = curlx_str(&val); - quoted = (*vp == '\"'); - if(quoted) - vp++; - /* we process 2 number value parameters: 'ma' and 'persist' */ - if(curlx_str_number(&vp, &num, TIME_T_MAX)) - break; /* not a number, skip further parameter parsing */ if(curlx_str_casecompare(&name, "ma")) - *pmaxage = (time_t)num; - else if(curlx_str_casecompare(&name, "persist") && (num == 1)) - *ppersist = TRUE; + keyword = ALTSVC_MA; + else if(curlx_str_casecompare(&name, "persist")) + keyword = ALTSVC_PERSIST; + + if(keyword) { + const char *vp = curlx_str(&val); + const char *vend = vp + curlx_strlen(&val); + if(curlx_str_number(&vp, &num, TIME_T_MAX)) + break; /* not a number, skip further parameter parsing */ + if(vp != vend) + break; /* not entirely a number, skip further parameter parsing */ + if(keyword == ALTSVC_MA) + *pmaxage = (time_t)num; + else if(num == 1) + *ppersist = TRUE; + } - *pp = vp; /* point to the byte ending the value */ - curlx_str_passblanks(pp); - if(quoted && curlx_str_single(pp, '\"')) - break; /* was quoted but not ended in quote, skip */ curlx_str_passblanks(pp); if(curlx_str_single(pp, ';')) break; /* no further parameters */ diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index 3f851d1bc752..706f9a0169da 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -34,8 +34,10 @@ static CURLcode test_unit1654(const char *arg) CURL *curl = NULL; CURLcode result; struct altsvcinfo *asi = Curl_altsvc_init(); + struct altsvc *dstentry = NULL; struct Curl_peer *origin = NULL; const struct Curl_scheme *scheme = &Curl_scheme_https; + bool same_destination = FALSE; abort_if(!asi, "Curl_altsvc_i"); result = Curl_altsvc_load(asi, arg); @@ -160,6 +162,28 @@ static CURLcode test_unit1654(const char *arg) origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(12) failed!"); + /* Unknown extension parameters must not hide later known parameters. */ + if(Curl_peer_create(curl, scheme, "params.example", 443, &origin)) + goto fail; + result = Curl_altsvc_parse(curl, asi, + "h2=\":443\"; long-extension-parameter=token; " + "ext=token; " + "quoted=\"a;\\\"b\"; ma=120; persist=1\r\n", + origin, ALPN_h1); + fail_if(result, "Curl_altsvc_parse(13) failed!"); + if(!Curl_altsvc_lookup(asi, origin, ALPN_h1, &dstentry, + CURLALTSVC_H2, &same_destination)) { + fail_unless(FALSE, "alt-svc entry not found"); + goto fail; + } + fail_unless(dstentry->expires == 1548369261 + 120, + "unknown parameters hid ma"); + fail_unless(dstentry->persist, "unknown parameters hid persist"); + fail_unless(same_destination, "wrong alt-svc destination"); + + result = Curl_altsvc_parse(curl, asi, "clear\r\n", origin, ALPN_h1); + fail_if(result, "Curl_altsvc_parse(14) failed!"); + Curl_altsvc_save(curl, asi, outname); fail: From ec0d5bb696788b675f05fbe7d7fc31b7206cdce5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 00:08:51 +0200 Subject: [PATCH 0968/1062] urldata: sort the connectdata struct fields by size To minimize holes Closes #22678 --- lib/urldata.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/urldata.h b/lib/urldata.h index fe842b2c99fc..65f02af0ffd8 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -263,13 +263,6 @@ struct connectdata { curl_off_t connection_id; /* Contains a unique number to make it easier to track the connections in the log output */ - /* This is used by the connection pool logic. If this returns TRUE, this - handle is still used by one or more easy handles and can only used by any - other easy handle without careful consideration (== only for - multiplexing) and it cannot be used by another multi handle! */ -#define CONN_INUSE(c) (!!(c)->attached_xfers) - uint32_t attached_xfers; /* # of attached easy handles */ - /* A connection cache from a SHARE might be used in several multi handles. * We MUST not reuse connections that are running in another multi, * for concurrency reasons. That multi might run in another thread. @@ -352,10 +345,18 @@ struct connectdata { that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; + struct ConnectBits bits; /* various state-flags for this connection */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif + /* This is used by the connection pool logic. If this returns TRUE, this + handle is still used by one or more easy handles and can only used by any + other easy handle without careful consideration (== only for + multiplexing) and it cannot be used by another multi handle! */ +#define CONN_INUSE(c) (!!(c)->attached_xfers) + uint32_t attached_xfers; /* # of attached easy handles */ + #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif @@ -373,7 +374,6 @@ struct connectdata { uint8_t httpversion_seen; uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */ - struct ConnectBits bits; /* various state-flags for this connection */ }; #ifndef CURL_DISABLE_PROXY From 89c706665790690ddb847408f4b41f52f033196e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 08:51:48 +0200 Subject: [PATCH 0969/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 64 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 235633c4ed60..452a8fbfaa94 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1511 - Contributors: 3776 + Authors: 1514 + Contributors: 3781 This release includes the following changes: @@ -18,6 +18,7 @@ This release includes the following changes: This release includes the following bugfixes: + o altsvc: continue after unknown parameters [198] o asyn-thrdd: retry link-local ipv6 if missing scope id [118] o autotools: minor fixes and improvements [33] o build: always use local `inet_pton()`/`inet_ntop()` implementations [56] @@ -37,10 +38,12 @@ This release includes the following bugfixes: o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] o cf-socket: disable TCP SYN retransmissions for localhost on Windows [164] o cfilters: fix event-based connection shutdown [91] + o cmake/FindLibgsasl: fix to set `LIBGSASL_VERSION` with pkg-config detection [229] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] o cmake: flatten build tree, tidy up base dir variables [12] o cmake: minor improvements to `cmake_uninstall.in.cmake` [54] + o cmake: optimize OpenSSL fork detection [228] o cmake: replace `remove` command with `rm` and pass arg safely [11] o cmake: robustify base path in local file reference [15] o cmake: stop probing unused `float.h` for `STDC_HEADERS` [10] @@ -124,6 +127,7 @@ This release includes the following bugfixes: o lib: new easy option string storage [215] o lib: optimize struct layouts for reduced memory usage [212] o lib: ratelimit timestamps [14] + o lib: silence gcc-16 compiler warnings `-Wmaybe-uninitialized` [243] o lib: update mentions of the legacy "sessionhandle" [157] o libcurl.pc: add `License` tag [190] o libcurl.pc: add Copyright tag to the pkgconf file @@ -143,16 +147,20 @@ This release includes the following bugfixes: o multi: timeout improvements [209] o multi: use index list for expire timeouts [197] o multihandle: move two struct fields [163] + o ngtcp2+openssL: fix early data [225] o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] o openssl+sectrust: fix session reuse [4] o openssl+sectrust: move session verified set into result check [82] + o openssl: avoid conn reuse if provider is used [214] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: fix DER buffer leak in Apple SecTrust verification [217] + o openssl: no server cert is only okay if also not pinned [226] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] o progress: cleanup, less memory [179] + o protocol: simpler Curl_getn_scheme runs faster [239] o psl: update a comment to understandable English [162] o pytest: update two H3 tests for nghttp3 1.18.0+ [158] o quiche: set the max field section size [100] @@ -175,9 +183,11 @@ This release includes the following bugfixes: o setopt: return OK earlier for the deprecated h2 dep options [77] o smtp: reject CR and LF in the envelope address [37] o spacecheck: cap number of lines per file [111] + o spnego_sspi: pass channel bindings on initial context [230] o src: safely clear certain buffers [125] o sshserver.pl: bump an sshd config to use its modern name [160] o ssls: fix potential memory leak on import [96] + o strcase: inline the raw case conversions [207] o sws: allow connection-monitor to log all disconnects [2] o sws: log the exact closing reason better, to help debugging tests [85] o terminal: Enhance terminal size detection for multiple outputs [115] @@ -215,6 +225,7 @@ This release includes the following bugfixes: o tool_cb_hdr: de-duplicate filename setter [24] o tool_cb_prg: avoid integer overflows [93] o tool_doswin: add stdin relay auth [130] + o tool_doswin: don't use `TerminateThread` in stdin relay [238] o tool_msgs: make notef() respect --silent [220] o tool_operate: limit `is_using_schannel()` call to Windows [140] o tool_operate: only check for schannel if on windows [187] @@ -234,6 +245,7 @@ This release includes the following bugfixes: o urlapi: preserve empty markers in relative URLs [61] o urldata: cleanups [175] o urldata: drop four strings from the aptr struct [136] + o urldata: sort the connectdata struct fields by size [177] o VERSIONS.md: document Rock-solid curl releases [201] o vms: fix symbol typo and missing closing quotes in `config_h.com` [124] o vquic: add Curl_ prefix to some global functions [76] @@ -241,6 +253,9 @@ This release includes the following bugfixes: o vquic: silence `-Wmissing-field-initializers` for nghttp3/ngtcp2 callback tables [159] o vquic: use ngtcp2 v1.25.0 new close2 callback [154] o vssh: keyfile use cleanups [83] + o vssh: silence gcc-11 `-Wnull-dereference`, dedupe `CURL_EASY_STR()` calls [240] + o vtls: move 'native_ca_store' ssl_config_data => ssl_primary_config [211] + o vtls_scache: use case sensitive path match o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] o websocket: pause writing and meta data fix [135] o wolfssl: fix build for wolfssl without bio chain support [75] @@ -268,24 +283,25 @@ This release would not have looked like this without help, code, reports and advice from friends like these: 11soda11, 1rhino2 on hackerone, accl on hackerone, AlanKingPL, Alb3e3, - Alhuda Khan, Anton Karpov, anupamme, Athos Ribeiro, Bartel Sielski, - Bigtang on hackerone, Bill Mill, Bryan Henderson, + Alhuda Khan, Anton Karpov, anupamme, Arham Wani, Athos Ribeiro, + Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, Christoph Reiter, claudex on github, Collin Funk, cybertron10 on github, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], - ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, firexinghe on github, - Fred Klassen, GLaDOS-418 on github, Graham Campbell, Hendrik Hübner, - HwangRock, itzTanos29, Jace Laquerre, Jayanth Vennamreddy, Joel Depooter, - Johannes Schindelin, Keng-Yu Lin, Kieran Gannon, kit-ty-kate on github, - Laurent Sabourin, Marcel Jamin, Martin Dukek, Matthew John Cheetham, - Max Dymond, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, - Ralf Mueller, Ramesh Adhikari, Ray Satiro, renovate[bot], Rito Rhymes, - RMMoreton on github, Roger Leigh, Ross Burton, Sameeh Jubran, Sam James, - Samuel Dainard, Samuel Henrique, Scott Talbert, sdgh179 on github, - Sergei Zimmerman, smaeljaish on hackerone, Stefan Eissing, Stephan Zeisberg, - stze on hackerone, Thomas Chauchefoin, Viktor Szakats, xmoezzz on github, - Yoshiro Yoneya, zaveshaa - (74 contributors) + ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, felix h, + firexinghe on github, Fred Klassen, GLaDOS-418 on github, Graham Campbell, + Hendrik Hübner, HwangRock, itzTanos29, Jace Laquerre, Jayanth Vennamreddy, + Joel Depooter, Johannes Schindelin, Keng-Yu Lin, Kieran Gannon, + kit-ty-kate on github, Laurent Sabourin, Marcel Jamin, Martin Dukek, + martin-fzi on github, Matthew John Cheetham, Max Dymond, Memduh Çelik, + Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, + Ramesh Adhikari, Rarylson Freitas, Ray Satiro, renovate[bot], Rito Rhymes, + RMMoreton on github, Roger Leigh, Ron Kuper, Ross Burton, Sameeh Jubran, + Sam James, Samuel Dainard, Samuel Henrique, Scott Talbert, sdgh179 on github, + Sergei Zimmerman, smaeljaish on hackerone, Stanislav Fort, Stefan Eissing, + Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, Viktor Szakats, + xmoezzz on github, Yoshiro Yoneya, zaveshaa + (80 contributors) References to bug reports and discussions on issues: @@ -465,6 +481,7 @@ References to bug reports and discussions on issues: [174] = https://curl.se/bug/?i=22490 [175] = https://curl.se/bug/?i=22604 [176] = https://curl.se/bug/?i=22528 + [177] = https://curl.se/bug/?i=22678 [178] = https://curl.se/bug/?i=22598 [179] = https://curl.se/bug/?i=22547 [180] = https://curl.se/bug/?i=22560 @@ -485,6 +502,7 @@ References to bug reports and discussions on issues: [195] = https://curl.se/bug/?i=22540 [196] = https://curl.se/bug/?i=22524 [197] = https://curl.se/bug/?i=22473 + [198] = https://curl.se/bug/?i=22644 [199] = https://curl.se/bug/?i=22591 [200] = https://curl.se/bug/?i=22532 [201] = https://curl.se/bug/?i=22636 @@ -493,11 +511,14 @@ References to bug reports and discussions on issues: [204] = https://curl.se/bug/?i=22627 [205] = https://curl.se/bug/?i=22577 [206] = https://curl.se/bug/?i=22587 + [207] = https://curl.se/bug/?i=22672 [208] = https://curl.se/bug/?i=22475 [209] = https://curl.se/bug/?i=22584 [210] = https://curl.se/bug/?i=22519 + [211] = https://curl.se/bug/?i=22668 [212] = https://curl.se/bug/?i=22585 [213] = https://curl.se/bug/?i=22567 + [214] = https://curl.se/bug/?i=22665 [215] = https://curl.se/bug/?i=22628 [216] = https://curl.se/bug/?i=22579 [217] = https://curl.se/bug/?i=22631 @@ -507,6 +528,11 @@ References to bug reports and discussions on issues: [221] = https://curl.se/bug/?i=22629 [223] = https://curl.se/bug/?i=22610 [224] = https://curl.se/bug/?i=22621 + [225] = https://curl.se/bug/?i=22649 + [226] = https://curl.se/bug/?i=22660 + [228] = https://curl.se/bug/?i=22664 + [229] = https://curl.se/bug/?i=22662 + [230] = https://curl.se/bug/?i=22466 [231] = https://curl.se/bug/?i=22613 [232] = https://curl.se/bug/?i=22612 [233] = https://curl.se/bug/?i=22611 @@ -514,3 +540,7 @@ References to bug reports and discussions on issues: [235] = https://curl.se/bug/?i=22587 [236] = https://curl.se/bug/?i=22607 [237] = https://curl.se/bug/?i=22229 + [238] = https://curl.se/bug/?i=22383 + [239] = https://curl.se/bug/?i=22658 + [240] = https://curl.se/bug/?i=22656 + [243] = https://curl.se/bug/?i=22651 From 8be35a48bfe787b7d126cb4c74ee1779526df559 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 08:48:10 +0200 Subject: [PATCH 0970/1062] FAQ: correct an option typo Closes #22679 --- docs/FAQ.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 6cdf97d32725..6cbba7d81110 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -1230,9 +1230,9 @@ wildcard to download multiple files from an FTP directory. ## I want a different time-out -Sometimes users realize that `CURLOPT_TIMEOUT` and `CURLOPT_CONNECTIMEOUT` are -not sufficiently advanced or flexible to cover all the various use cases and -scenarios applications end up with. +Sometimes users realize that `CURLOPT_TIMEOUT` and `CURLOPT_CONNECTTIMEOUT` +are not sufficiently advanced or flexible to cover all the various use cases +and scenarios applications end up with. libcurl offers many more ways to time-out operations. A common alternative is to use the `CURLOPT_LOW_SPEED_LIMIT` and `CURLOPT_LOW_SPEED_TIME` options to From 72dfda5f5ac3825fc26b82fa404225f3e7a0fc31 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 25 Aug 2026 10:38:11 +0200 Subject: [PATCH 0971/1062] cmake: check libgsasl version at configure time To match other dependencies with version requirements. Follow-up to 3da249e1f0716c06644ed3522a37a8bf81808012 #21609 Cherry-picked from #21404 Closes #22667 --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f92d6f050fbc..fc38942e0ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1336,6 +1336,9 @@ option(CURL_USE_GSASL "Use libgsasl" OFF) mark_as_advanced(CURL_USE_GSASL) if(CURL_USE_GSASL) find_package(Libgsasl MODULE REQUIRED) + if(LIBGSASL_VERSION VERSION_LESS 1.6.0) + message(FATAL_ERROR "libgsasl 1.6.0 or greater required.") + endif() list(APPEND CURL_LIBS CURL::libgsasl) set(USE_GSASL ON) endif() From f2c03b1c40a5ba819f90cda236d77b1511832223 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 25 Aug 2026 14:28:07 +0200 Subject: [PATCH 0972/1062] quic: upload improvements - set sockopt to receive ECN - set socktop to dontfrag - vquic: provide ECN on receive to callbacks - ngtcp2: manage sendbuf more carefully, do not add more than QUIC can keep in flight Closes #22669 --- CMake/unix-cache.cmake | 1 + CMake/win32-cache.cmake | 1 + CMakeLists.txt | 1 + configure.ac | 1 + lib/cf-socket.c | 47 ++++++++++++++++++ lib/curl_config-cmake.h.in | 3 ++ lib/vquic/cf-ngtcp2-cmn.c | 7 +-- lib/vquic/cf-ngtcp2-cmn.h | 6 ++- lib/vquic/cf-ngtcp2-proxy.c | 18 +++---- lib/vquic/cf-ngtcp2.c | 49 ++++++++++++++----- lib/vquic/cf-quiche.c | 2 +- lib/vquic/vquic.c | 98 ++++++++++++++++++++++++++++++++----- lib/vquic/vquic_int.h | 2 +- 13 files changed, 198 insertions(+), 38 deletions(-) diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index e6720545e862..f37ef868bb62 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -202,6 +202,7 @@ else() set(HAVE_NETINET_IN6_H 0) endif() set(HAVE_NETINET_IN_H 1) +set(HAVE_NETINET_IP_H 1) set(HAVE_NETINET_TCP_H 1) set(HAVE_NETINET_UDP_H 1) set(HAVE_NET_IF_H 1) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index 6d0cb076e874..cc4779a855b9 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -112,6 +112,7 @@ set(HAVE_MEMRCHR 0) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN6_H 0) set(HAVE_NETINET_IN_H 0) +set(HAVE_NETINET_IP_H 0) set(HAVE_NETINET_TCP_H 0) set(HAVE_NETINET_UDP_H 0) set(HAVE_NET_IF_H 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc38942e0ad7..efa4b4d12d52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1567,6 +1567,7 @@ check_include_file_concat_curl("net/if.h" HAVE_NET_IF_H) # sys/select.h check_include_file_concat_curl("netdb.h" HAVE_NETDB_H) check_include_file_concat_curl("netinet/in.h" HAVE_NETINET_IN_H) check_include_file("netinet/in6.h" HAVE_NETINET_IN6_H) +check_include_file("netinet/ip.h" HAVE_NETINET_IP_H) check_include_file_concat_curl("netinet/tcp.h" HAVE_NETINET_TCP_H) # sys/types.h (e.g. Cygwin) netinet/in.h check_include_file_concat_curl("netinet/udp.h" HAVE_NETINET_UDP_H) # sys/types.h (e.g. Cygwin) check_include_file("poll.h" HAVE_POLL_H) diff --git a/configure.ac b/configure.ac index f130bc558bb2..503cf66a1b09 100644 --- a/configure.ac +++ b/configure.ac @@ -4035,6 +4035,7 @@ AC_CHECK_HEADERS( linux/tcp.h \ netinet/tcp.h \ netinet/udp.h \ + netinet/ip.h \ netdb.h \ sys/sockio.h \ sys/param.h \ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 73b4934f4ee2..ec3c5c9ce882 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -37,6 +37,9 @@ #ifdef HAVE_NETINET_UDP_H #include #endif +#ifdef HAVE_NETINET_IP_H +#include +#endif #ifdef HAVE_SYS_IOCTL_H #include #endif @@ -1944,6 +1947,48 @@ static void linux_quic_gro(struct cf_socket_ctx *ctx) #define linux_quic_gro(x) #endif +#if (defined(__linux__) || defined(__APPLE__)) && defined(IP_RECVTOS) +static void linux_quic_ecn(struct cf_socket_ctx *ctx) +{ + unsigned int tos = 1; + switch(ctx->addr.family) { + case AF_INET: + (void)setsockopt(ctx->sock, IPPROTO_IP, IP_RECVTOS, &tos, sizeof(tos)); + break; +#ifdef IPV6_RECVTCLASS + case AF_INET6: + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_RECVTCLASS, + &tos, sizeof(tos)); + break; +#endif + } +} +#else +#define linux_quic_ecn(x) +#endif + +#if (defined(__linux__) || defined(__APPLE__)) && defined(IP_DONTFRAG) +static void linux_ip_dontfrag(struct cf_socket_ctx *ctx) +{ + int val = 1; + + switch(ctx->addr.family) { + case AF_INET: + (void)setsockopt(ctx->sock, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val)); + break; +#ifdef IPV6_DONTFRAG + case AF_INET6: + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_DONTFRAG, + &val, sizeof(val)); + break; +#endif + } +} +#else +#define linux_ip_dontfrag(x) +#endif + + static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1973,7 +2018,9 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, * non-blocking socket created by cf_socket_open() to it. Thus, we * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore. */ + linux_quic_ecn(ctx); linux_quic_mtu(ctx); + linux_ip_dontfrag(ctx); linux_quic_gro(ctx); return CURLE_OK; diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 7b0dd9a28236..d25eab14ecca 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -424,6 +424,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_UDP_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IP_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_TCP_H 1 diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index e0cd384b0923..f6d273a92c93 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -1640,7 +1640,7 @@ struct cf_ngtcp2_recv_ctx { static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, size_t gso_size, struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, + socklen_t remote_addrlen, uint8_t ecn, void *userp) { struct cf_ngtcp2_recv_ctx *rctx = userp; @@ -1658,7 +1658,7 @@ static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, if(ecn) CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", - buflen, gso_size, (unsigned int)ecn); + buflen, gso_size, (unsigned)ecn); ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, @@ -1890,11 +1890,12 @@ CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, stream->id = -1; stream->rx_offset = 0; stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; + stream->tx_in_flight_ideal = H3_STREAM_SEND_BUF_INITIAL; /* on send, we control how much we put into the buffer */ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; + stream->tx_in_flight_size = 0; stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); diff --git a/lib/vquic/cf-ngtcp2-cmn.h b/lib/vquic/cf-ngtcp2-cmn.h index f4dd9158ec90..7395234af339 100644 --- a/lib/vquic/cf-ngtcp2-cmn.h +++ b/lib/vquic/cf-ngtcp2-cmn.h @@ -93,6 +93,9 @@ struct cf_quic_ctx; #define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) #define H3_STREAM_SEND_CHUNKS \ (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +/* How much data we initially want to buffer un-acked */ +#define H3_STREAM_SEND_BUF_INITIAL (32 * 1024) + #define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 @@ -165,13 +168,14 @@ struct h3_stream_ctx { int64_t id; /* HTTP/3 stream identifier */ struct bufq sendbuf; /* h3 request body */ struct h1_req_parser h1; /* h1 request parsing */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ curl_off_t rx_total; /* total number of bytes received */ uint64_t rx_offset; /* current receive offset */ uint64_t rx_offset_max; /* allowed receive offset */ uint64_t window_size_max; /* max flow control window set for stream */ + size_t tx_in_flight_size; /* sendbuf data "in flight" */ + size_t tx_in_flight_ideal; /* ideal amount of un-acked send data */ int status_code; /* HTTP status code */ CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ BIT(resp_hds_complete); /* we have a complete, final response */ diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c index 5e85c805948b..89bf19cc2782 100644 --- a/lib/vquic/cf-ngtcp2-proxy.c +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -214,15 +214,15 @@ static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, /* The server acknowledged `datalen` of bytes from our request body. * This is a delta. We have kept this data in `sendbuf` for * re-transmissions and can free it now. */ - if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) - skiplen = stream->sendbuf_len_in_flight; + if(datalen >= (uint64_t)stream->tx_in_flight_size) + skiplen = stream->tx_in_flight_size; else skiplen = (size_t)datalen; Curl_bufq_skip(&stream->sendbuf, skiplen); - stream->sendbuf_len_in_flight -= skiplen; + stream->tx_in_flight_size -= skiplen; /* Resume upload processing if we have more data to send */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + if(stream->tx_in_flight_size < Curl_bufq_len(&stream->sendbuf)) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -568,21 +568,21 @@ static nghttp3_ssize cb_h3_tunnel_read_data(nghttp3_conn *conn, /* nghttp3 keeps references to the sendbuf data until it is ACKed * by the server (see `cb_h3_proxy_acked_req_body()` for updates). - * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * `tx_in_flight_size` is the amount of bytes in `sendbuf` * that we have already passed to nghttp3, but which have not been * ACKed yet. - * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * Any amount beyond `tx_in_flight_size` we need still to pass * to nghttp3. Do that now, if we can. */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + if(stream->tx_in_flight_size < Curl_bufq_len(&stream->sendbuf)) { nvecs = 0; while(nvecs < veccnt) { if(!Curl_bufq_peek_at(&stream->sendbuf, - stream->sendbuf_len_in_flight, + stream->tx_in_flight_size, &buf_base, &vec[nvecs].len)) break; vec[nvecs].base = (uint8_t *)(uintptr_t)buf_base; - stream->sendbuf_len_in_flight += vec[nvecs].len; + stream->tx_in_flight_size += vec[nvecs].len; nwritten += vec[nvecs].len; ++nvecs; } diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index 8c7c84928b98..a80c4cd97a34 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -78,7 +78,8 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || !ngtcp2_conn_get_max_data_left(ctx->qconn)); s_exhaust = want_send && stream && stream->id >= 0 && - stream->quic_flow_blocked; + (stream->quic_flow_blocked || + stream->tx_in_flight_size >= stream->tx_in_flight_ideal); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); @@ -577,15 +578,15 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, /* The server acknowledged `datalen` of bytes from our request body. * This is a delta. We have kept this data in `sendbuf` for * re-transmissions and can free it now. */ - if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) - skiplen = stream->sendbuf_len_in_flight; + if(datalen >= (uint64_t)stream->tx_in_flight_size) + skiplen = stream->tx_in_flight_size; else skiplen = (size_t)datalen; Curl_bufq_skip(&stream->sendbuf, skiplen); - stream->sendbuf_len_in_flight -= skiplen; + stream->tx_in_flight_size -= skiplen; /* Resume upload processing if we have more data to send */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + if(stream->tx_in_flight_size < Curl_bufq_len(&stream->sendbuf)) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -615,19 +616,19 @@ static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, return NGHTTP3_ERR_CALLBACK_FAILURE; /* nghttp3 keeps references to the sendbuf data until it is ACKed * by the server (see `cb_h3_acked_req_body()` for updates). - * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * `tx_in_flight_size` is the amount of bytes in `sendbuf` * that we have already passed to nghttp3, but which have not been * ACKed yet. - * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * Any amount beyond `tx_in_flight_size` we need still to pass * to nghttp3. Do that now, if we can. */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + if(stream->tx_in_flight_size < Curl_bufq_len(&stream->sendbuf)) { nvecs = 0; while(nvecs < veccnt && Curl_bufq_peek_at(&stream->sendbuf, - stream->sendbuf_len_in_flight, + stream->tx_in_flight_size, CURL_UNCONST(&vec[nvecs].base), &vec[nvecs].len)) { - stream->sendbuf_len_in_flight += vec[nvecs].len; + stream->tx_in_flight_size += vec[nvecs].len; nwritten += vec[nvecs].len; ++nvecs; } @@ -862,6 +863,10 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = CURLE_SEND_ERROR; goto out; } + else if(stream->tx_in_flight_size >= stream->tx_in_flight_ideal) { + result = CURLE_AGAIN; + goto out; + } else { result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " @@ -878,6 +883,28 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(!result); result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(*pnwritten > 0) { + const size_t delta = (2 * 1024); + if(stream->tx_in_flight_size > (stream->tx_in_flight_ideal + delta)) { + /* ngtcp2 keeps more in flight than we try to provide for, + * increase our "ideal" tx buffer length */ + stream->tx_in_flight_ideal += delta; + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(%zu), added %zu, " + "inflight=%zu, increase tx_win=%zu", + stream->id, len, *pnwritten, + stream->tx_in_flight_size, stream->tx_in_flight_ideal); + } + else if((stream->tx_in_flight_size + delta) < stream->tx_in_flight_ideal) { + /* ngtcp2 keeps less in flight than we try to provide for, + * decrease our "ideal" tx buffer length */ + stream->tx_in_flight_ideal -= delta; + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(%zu), added %zu, " + "inflight=%zu, reduce tx_win=%zu", + stream->id, len, *pnwritten, + stream->tx_in_flight_size, stream->tx_in_flight_ideal); + } + } + out: result = Curl_1st_fatal(result, Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); @@ -925,7 +952,7 @@ static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, if(stream && !stream->send_closed) { stream->send_closed = TRUE; stream->upload_left = Curl_bufq_len(&stream->sendbuf) - - stream->sendbuf_len_in_flight; + stream->tx_in_flight_size; (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } break; diff --git a/lib/vquic/cf-quiche.c b/lib/vquic/cf-quiche.c index c269926dca53..934a56cadaf1 100644 --- a/lib/vquic/cf-quiche.c +++ b/lib/vquic/cf-quiche.c @@ -652,7 +652,7 @@ struct recv_ctx { static CURLcode cf_quiche_recv_pkts(const unsigned char *buf, size_t buflen, size_t gso_size, struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, + socklen_t remote_addrlen, uint8_t ecn, void *userp) { struct recv_ctx *r = userp; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 2603f6b7b1a2..0d3194d01f23 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -33,6 +33,9 @@ #ifdef HAVE_NETINET_UDP_H #include #endif +#ifdef HAVE_NETINET_IP_H +#include +#endif #ifdef USE_NGHTTP3 #include @@ -66,6 +69,8 @@ #define NW_SEND_CHUNKS 1 #ifdef HAVE_APPLE_MSG_X + +/* this is `struct msghdr` with an additional field at the end */ struct msghdr_x { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ @@ -552,14 +557,72 @@ static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) break; } } -#endif +#endif /* linux && UDP_GRO */ (void)msg; return (size_t)gso_size; } +#endif /* (HAVE_SENDMMSG || HAVE_SENDMSG) && !HAVE_APPLE_MSG_X */ + +#if (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) || \ + defined(HAVE_APPLE_MSG_X)) && \ + (defined(IP_RECVTOS) || defined(IP_TOS)) && defined(IPTOS_ECN_MASK) +static uint8_t vquic_msghdr_get_ecn(struct msghdr *msg, int family) +{ + struct cmsghdr *cmsg; + switch(family) { + case AF_INET: + /* Workaround musl CMSG_NXTHDR issue */ +#if defined(__clang__) && !defined(__GLIBC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { +#if defined(__clang__) && !defined(__GLIBC__) +#pragma clang diagnostic pop #endif + if(cmsg->cmsg_level == IPPROTO_IP && +#ifdef __APPLE__ + cmsg->cmsg_type == IP_RECVTOS +#else + cmsg->cmsg_type == IP_TOS +#endif + && cmsg->cmsg_len) { + return *(uint8_t *)(CMSG_DATA(cmsg)) & IPTOS_ECN_MASK; + } + } + break; + case AF_INET6: + /* Workaround musl CMSG_NXTHDR issue */ +#if defined(__clang__) && !defined(__GLIBC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { +#if defined(__clang__) && !defined(__GLIBC__) +#pragma clang diagnostic pop +#endif + if(cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS && + cmsg->cmsg_len) { + unsigned int tos; + + memcpy(&tos, CMSG_DATA(cmsg), sizeof(int)); + + return (uint8_t)(tos & IPTOS_ECN_MASK); + } + } + break; + } + return 0; +} +#else +#define vquic_msghdr_get_ecn(a,b) 0 +#endif /* HAVE_SENDMMSG || HAVE_SENDMSG || HAVE_APPLE_MSG_X ... */ #ifdef HAVE_SENDMMSG + static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -569,14 +632,16 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, #if defined(__linux__) && defined(UDP_GRO) #define MMSG_NUM 16 #define UDP_GRO_CNT_MAX 64 +#define CMSG_PER_MSG_SIZE (2 * CMSG_SPACE(sizeof(int))) #else #define MMSG_NUM 64 #define UDP_GRO_CNT_MAX 1 +#define CMSG_PER_MSG_SIZE CMSG_SPACE(sizeof(int)) #endif #define MSG_BUF_SIZE (UDP_GRO_CNT_MAX * 1500) struct iovec msg_iov[MMSG_NUM]; struct mmsghdr mmsg[MMSG_NUM]; - uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))]; + uint8_t msg_ctrl[MMSG_NUM * CMSG_PER_MSG_SIZE]; struct sockaddr_storage remote_addr[MMSG_NUM]; size_t total_nread = 0, pkts = 0; #ifdef CURLVERBOSE @@ -588,6 +653,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, size_t gso_size; char *sockbuf = NULL; uint8_t (*bufs)[MSG_BUF_SIZE] = NULL; + uint8_t ecn = 0; DEBUGASSERT(max_pkts > 0); result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE, @@ -607,8 +673,8 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); - mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))]; - mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int)); + mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_PER_MSG_SIZE]; + mmsg[i].msg_hdr.msg_controllen = CMSG_PER_MSG_SIZE; } while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && @@ -643,13 +709,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } total_nread += mmsg[i].msg_len; + ecn = vquic_msghdr_get_ecn(&mmsg[i].msg_hdr, remote_addr[i].ss_family); gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); if(gso_size == 0) gso_size = mmsg[i].msg_len; result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size, mmsg[i].msg_hdr.msg_name, - mmsg[i].msg_hdr.msg_namelen, 0, userp); + mmsg[i].msg_hdr.msg_namelen, ecn, userp); if(result) goto out; pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size; @@ -676,9 +743,10 @@ static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf, { #define MSG_X_NUM 64 #define MSG_BUF_SIZE (2048) +#define CMSG_PER_MSG_SIZE CMSG_SPACE(sizeof(int)) struct iovec msg_iov[MSG_X_NUM]; struct msghdr_x mmsg[MSG_X_NUM]; - uint8_t msg_ctrl[MSG_X_NUM * CMSG_SPACE(sizeof(int))]; + uint8_t msg_ctrl[MSG_X_NUM * CMSG_PER_MSG_SIZE]; struct sockaddr_storage remote_addr[MSG_X_NUM]; size_t total_nread = 0, pkts = 0; #ifdef CURLVERBOSE @@ -690,6 +758,7 @@ static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf, size_t gso_size; char *sockbuf = NULL; uint8_t (*bufs)[MSG_BUF_SIZE] = NULL; + uint8_t ecn = 0; DEBUGASSERT(max_pkts > 0); result = Curl_multi_xfer_sockbuf_borrow(data, MSG_X_NUM * MSG_BUF_SIZE, @@ -709,8 +778,8 @@ static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf, mmsg[i].msg_iovlen = 1; mmsg[i].msg_name = &remote_addr[i]; mmsg[i].msg_namelen = sizeof(remote_addr[i]); - mmsg[i].msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))]; - mmsg[i].msg_controllen = CMSG_SPACE(sizeof(int)); + mmsg[i].msg_control = &msg_ctrl[i * CMSG_PER_MSG_SIZE]; + mmsg[i].msg_controllen = CMSG_PER_MSG_SIZE; } #if defined(CURL_HAVE_DIAG) && defined(__APPLE__) @@ -751,11 +820,13 @@ static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf, continue; } total_nread += mmsg[i].msg_datalen; + ecn = vquic_msghdr_get_ecn((struct msghdr *)&mmsg[i], + remote_addr[i].ss_family); gso_size = mmsg[i].msg_datalen; result = recv_cb(bufs[i], mmsg[i].msg_datalen, gso_size, mmsg[i].msg_name, - mmsg[i].msg_namelen, 0, userp); + mmsg[i].msg_namelen, ecn, userp); if(result) goto out; pkts += (mmsg[i].msg_datalen + gso_size - 1) / gso_size; @@ -778,6 +849,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, size_t max_pkts, Curl_vquic_recv_pkts_cb *recv_cb, void *userp) { +#define CMSG_PER_MSG_SIZE CMSG_SPACE(sizeof(int)) struct iovec msg_iov; struct msghdr msg; uint8_t buf[64 * 1024]; @@ -787,8 +859,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, size_t nread; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; - uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))]; + uint8_t msg_ctrl[CMSG_PER_MSG_SIZE]; size_t gso_size; + uint8_t ecn = 0; DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) { @@ -802,7 +875,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, msg.msg_control = msg_ctrl; msg.msg_name = &remote_addr; msg.msg_namelen = sizeof(remote_addr); - msg.msg_controllen = sizeof(msg_ctrl); + msg.msg_controllen = CMSG_PER_MSG_SIZE; while((rc = recvmsg(qctx->sockfd, &msg, 0)) == -1 && (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) @@ -835,12 +908,13 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, continue; } + ecn = vquic_msghdr_get_ecn(&msg, remote_addr.ss_family); gso_size = vquic_msghdr_get_udp_gro(&msg); if(gso_size == 0) gso_size = nread; result = recv_cb(buf, nread, gso_size, - msg.msg_name, msg.msg_namelen, 0, userp); + msg.msg_name, msg.msg_namelen, ecn, userp); if(result) goto out; pkts += (nread + gso_size - 1) / gso_size; diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 7d56ccdeb16e..23e1102eb0d9 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -105,7 +105,7 @@ typedef CURLcode Curl_vquic_recv_pkts_cb(const unsigned char *buf, size_t buflen, size_t gso_size, struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, + socklen_t remote_addrlen, uint8_t ecn, void *userp); CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf, From ce165cda4f54e832b35d29be2ff86cfeaaea90a3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 11:30:12 +0200 Subject: [PATCH 0973/1062] tests: support switching off the stdout redirect per-test The `` tag now supports `option="no-stdout"` for this purpose. Closes #22684 --- docs/tests/FILEFORMAT.md | 5 ++++- tests/runner.pm | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 447593d523a7..cb4743b89153 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -589,7 +589,7 @@ command has been run. If the variable name has no assignment, no `=`, then that variable is deleted. -### `` +### `` Command line to run. If the command spans multiple lines, they are concatenated with a space added @@ -605,6 +605,9 @@ Set `option="no-output"` to prevent the test script to slap on the `--output` argument that directs the output to a file. The `--output` is also not added if the verify/stdout section is used. +Set `option="no-stdout"` to prevent the normal stdout redirect template to be +applied on the command line to run. + Set `option="force-output"` to make use of `--output` even when the test is otherwise written to verify stdout. diff --git a/tests/runner.pm b/tests/runner.pm index 230a688bd081..5187fc094f56 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -991,8 +991,12 @@ sub singletest_run { $CMDLINE = "$valgrindcmd $CMDLINE"; } - $CMDLINE .= "$cmdargs > " . stdoutfilename($LOGDIR, $testnum) . - " 2> " . stderrfilename($LOGDIR, $testnum); + my $redirstdout = "> " . stdoutfilename($LOGDIR, $testnum); + if($cmdhash{'option'} && ($cmdhash{'option'} =~ /no-stdout/)) { + $redirstdout = ""; + } + $CMDLINE .= "$cmdargs $redirstdout". + " 2> " . stderrfilename($LOGDIR, $testnum); if($verbose) { logmsg "$CMDLINE\n"; From 513869744f520c8068af81b3089a7fe4bdd40467 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 22 Aug 2026 18:20:05 +0200 Subject: [PATCH 0974/1062] GHA/checkdocs: verify examples with picky warnings enables, also with clang Verify example snippets with the picky warning options used when compiling curl core code and standalone example code. Also test with clang. Extract picky warnings available via a new, internal, CMake option. Also: - fix issues found. - tried reproducing #22638 in CI (specifically the casts for `%p`), turns out it needs apple clang 17, and no longer reproducing with apple clang 21. Follow-up to 5c61e168698a72b87437d58ed728bcdea6d5db42 #22638 Closes #22639 --- .github/scripts/verify-examples.pl | 7 +++++- .github/workflows/checkdocs.yml | 16 ++++++++++++-- CMake/PickyWarnings.cmake | 3 +++ docs/libcurl/curl_easy_nextheader.md | 4 +++- docs/libcurl/curl_easy_option_by_name.md | 2 +- docs/libcurl/curl_easy_ssls_export.md | 22 +++++++++---------- docs/libcurl/curl_easy_ssls_import.md | 3 ++- docs/libcurl/curl_formadd.md | 8 +++---- docs/libcurl/curl_formget.md | 3 ++- docs/libcurl/curl_global_sslset.md | 4 ++-- docs/libcurl/curl_mime_data_cb.md | 9 +++++--- docs/libcurl/curl_mime_subparts.md | 4 ++-- docs/libcurl/curl_mprintf.md | 2 +- docs/libcurl/curl_multi_assign.md | 4 ++-- docs/libcurl/curl_multi_get_handles.md | 4 +++- docs/libcurl/curl_multi_wakeup.md | 2 +- docs/libcurl/curl_pushheader_bynum.md | 2 +- docs/libcurl/curl_strequal.md | 2 +- docs/libcurl/curl_strnequal.md | 2 +- docs/libcurl/curl_ws_meta.md | 2 +- docs/libcurl/curl_ws_send.md | 2 +- docs/libcurl/curl_ws_start_frame.md | 4 ++-- docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md | 8 +++---- docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md | 8 +++---- docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md | 5 +++-- .../opts/CURLMOPT_PIPELINING_SERVER_BL.md | 3 ++- .../opts/CURLMOPT_PIPELINING_SITE_BL.md | 2 +- docs/libcurl/opts/CURLMOPT_PUSHDATA.md | 10 ++++----- docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md | 10 ++++----- docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md | 5 +++-- .../opts/CURLOPT_CHUNK_BGN_FUNCTION.md | 2 +- docs/libcurl/opts/CURLOPT_CHUNK_DATA.md | 2 +- .../opts/CURLOPT_CHUNK_END_FUNCTION.md | 2 +- docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md | 7 +++--- docs/libcurl/opts/CURLOPT_ECH.md | 4 ++-- docs/libcurl/opts/CURLOPT_ERRORBUFFER.md | 2 +- docs/libcurl/opts/CURLOPT_HEADERDATA.md | 2 +- docs/libcurl/opts/CURLOPT_HSTSREADDATA.md | 4 ++-- docs/libcurl/opts/CURLOPT_HSTSWRITEDATA.md | 4 ++-- docs/libcurl/opts/CURLOPT_HTTPPOST.md | 3 ++- docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md | 3 ++- .../opts/CURLOPT_OPENSOCKETFUNCTION.md | 3 ++- docs/libcurl/opts/CURLOPT_POSTFIELDS.md | 4 ++-- docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md | 2 +- docs/libcurl/opts/CURLOPT_POSTQUOTE.md | 3 ++- docs/libcurl/opts/CURLOPT_PREQUOTE.md | 3 ++- docs/libcurl/opts/CURLOPT_PRIVATE.md | 10 ++++----- docs/libcurl/opts/CURLOPT_PROGRESSDATA.md | 4 ++-- docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md | 4 ++-- .../opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md | 4 ++-- docs/libcurl/opts/CURLOPT_QUOTE.md | 3 ++- docs/libcurl/opts/CURLOPT_READDATA.md | 4 ++-- docs/libcurl/opts/CURLOPT_READFUNCTION.md | 3 ++- docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md | 3 ++- .../opts/CURLOPT_SSH_HOSTKEYFUNCTION.md | 9 ++++---- docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md | 2 +- docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md | 2 +- docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md | 2 +- docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md | 4 ++-- docs/libcurl/opts/CURLOPT_XFERINFODATA.md | 4 ++-- docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md | 4 ++-- 61 files changed, 158 insertions(+), 116 deletions(-) diff --git a/.github/scripts/verify-examples.pl b/.github/scripts/verify-examples.pl index a734eefc9625..8aeda5ef7d03 100755 --- a/.github/scripts/verify-examples.pl +++ b/.github/scripts/verify-examples.pl @@ -37,7 +37,12 @@ } sub testcompile { - my $rc = system('gcc -c test.c -I include -W -Wall -pedantic -Werror ' . + my $cc = defined $ENV{'CC'} ? $ENV{'CC'} : 'gcc'; + my $cflags = ''; + if(defined $ENV{'CFLAGS'}) { + $cflags .= ' ' . $ENV{'CFLAGS'}; + } + my $rc = system($cc . ' -c test.c -I include -W -Wall -pedantic -Werror ' . $cflags . ' ' . '-Wno-unused-parameter -Wno-unused-but-set-variable ' . '-DCURL_ALLOW_OLD_MULTI_SOCKET -DCURL_DISABLE_DEPRECATION') >> 8; return $rc; diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 2222dd9027fb..634957af9b61 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -131,5 +131,17 @@ jobs: - name: 'verify synopsis' run: .github/scripts/verify-synopsis.pl docs/libcurl/curl*.md - - name: 'verify examples' - run: .github/scripts/verify-examples.pl docs/libcurl/curl*.md docs/libcurl/opts/*.md + - name: 'verify examples (gcc)' + run: | + cmake -B bld-gcc -G Ninja -D_CURL_PREFILL=ON -DCURL_USE_LIBPSL=OFF -DCURL_WERROR=ON -D_CURL_SAVE_PICKY_OPTIONS=ON + export CFLAGS; CFLAGS="$(cat bld-gcc/picky-options.txt)" + echo "${CFLAGS}" + .github/scripts/verify-examples.pl docs/libcurl/curl*.md docs/libcurl/opts/*.md + + - name: 'verify examples (clang)' + run: | + export CC=clang + cmake -B bld-clang -G Ninja -D_CURL_PREFILL=ON -DCURL_USE_LIBPSL=OFF -DCURL_WERROR=ON -D_CURL_SAVE_PICKY_OPTIONS=ON + export CFLAGS; CFLAGS="$(cat bld-clang/picky-options.txt)" + echo "${CFLAGS}" + .github/scripts/verify-examples.pl docs/libcurl/curl*.md docs/libcurl/opts/*.md diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index 1ce5f7bda620..4317f8a442ea 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -451,6 +451,9 @@ if(_picky_nocheck OR _picky) string(REPLACE ";" " " _picky_tmp "${_picky_tmp}") string(STRIP "${_picky_tmp}" _picky_tmp) message(STATUS "Picky compiler options: ${_picky_tmp}") + if(_CURL_SAVE_PICKY_OPTIONS) + file(WRITE "${PROJECT_BINARY_DIR}/picky-options.txt" "${_picky_tmp}") + endif() set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "${_picky_nocheck}" "${_picky}") # Apply to all feature checks diff --git a/docs/libcurl/curl_easy_nextheader.md b/docs/libcurl/curl_easy_nextheader.md index 42314996b939..1a07f1fe31d8 100644 --- a/docs/libcurl/curl_easy_nextheader.md +++ b/docs/libcurl/curl_easy_nextheader.md @@ -77,6 +77,8 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { + unsigned int origin; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_perform(curl); @@ -87,7 +89,7 @@ int main(void) } /* extract the normal headers + 1xx + trailers from the last request */ - unsigned int origin = CURLH_HEADER | CURLH_1XX | CURLH_TRAILER; + origin = CURLH_HEADER | CURLH_1XX | CURLH_TRAILER; while((h = curl_easy_nextheader(curl, origin, -1, prev))) { printf("%s: %s\n", h->name, h->value); prev = h; diff --git a/docs/libcurl/curl_easy_option_by_name.md b/docs/libcurl/curl_easy_option_by_name.md index 12b460630792..8ba6ce0d27d1 100644 --- a/docs/libcurl/curl_easy_option_by_name.md +++ b/docs/libcurl/curl_easy_option_by_name.md @@ -43,7 +43,7 @@ int main(void) { const struct curl_easyoption *opt = curl_easy_option_by_name("URL"); if(opt) { - printf("This option wants CURLoption %x\n", (int)opt->id); + printf("This option wants CURLoption %x\n", (unsigned int)opt->id); } } ~~~ diff --git a/docs/libcurl/curl_easy_ssls_export.md b/docs/libcurl/curl_easy_ssls_export.md index cb516254a25c..ce0363acea3e 100644 --- a/docs/libcurl/curl_easy_ssls_export.md +++ b/docs/libcurl/curl_easy_ssls_export.md @@ -117,17 +117,17 @@ The maximum amount of bytes the server supports to receive in early data # EXAMPLE ~~~c -CURLcode my_export_cb(CURL *handle, - void *userptr, - const char *session_key, - const unsigned char *shmac, - size_t shmac_len, - const unsigned char *sdata, - size_t sdata_len, - curl_off_t valid_until, - int ietf_tls_id, - const char *alpn, - size_t earlydata_max) +static CURLcode my_export_cb(CURL *handle, + void *userptr, + const char *session_key, + const unsigned char *shmac, + size_t shmac_len, + const unsigned char *sdata, + size_t sdata_len, + curl_off_t valid_until, + int ietf_tls_id, + const char *alpn, + size_t earlydata_max) { /* persist sdata */ return CURLE_OK; diff --git a/docs/libcurl/curl_easy_ssls_import.md b/docs/libcurl/curl_easy_ssls_import.md index 178c10654c73..3e43972f7053 100644 --- a/docs/libcurl/curl_easy_ssls_import.md +++ b/docs/libcurl/curl_easy_ssls_import.md @@ -49,6 +49,8 @@ already expired is silently discarded. # EXAMPLE ~~~c +extern unsigned char *shmac, *sdata; + int main(void) { CURLSHcode sh; @@ -62,7 +64,6 @@ int main(void) curl = curl_easy_init(); if(curl) { - extern unsigned char *shmac, *sdata; size_t hlen = 4, slen = 5; curl_easy_setopt(curl, CURLOPT_SHARE, share); diff --git a/docs/libcurl/curl_formadd.md b/docs/libcurl/curl_formadd.md index fadb1b95a5dc..354957bfbec6 100644 --- a/docs/libcurl/curl_formadd.md +++ b/docs/libcurl/curl_formadd.md @@ -222,10 +222,10 @@ int main(void) struct curl_httppost *post = NULL; struct curl_httppost *last = NULL; char namebuffer[] = "name buffer"; - long namelength = strlen(namebuffer); + size_t namelength = strlen(namebuffer); char buffer[] = "test buffer"; char htmlbuffer[] = "test buffer"; - long htmlbufferlength = strlen(htmlbuffer); + size_t htmlbufferlength = strlen(htmlbuffer); struct curl_forms forms[3]; char file1[] = "my-face.jpg"; char file2[] = "your-face.jpg"; @@ -250,12 +250,12 @@ int main(void) /* Add ptrname/ptrcontent section */ curl_formadd(&post, &last, CURLFORM_PTRNAME, namebuffer, CURLFORM_PTRCONTENTS, buffer, CURLFORM_NAMELENGTH, - namelength, CURLFORM_END); + (long)namelength, CURLFORM_END); /* Add name/ptrcontent/contenttype section */ curl_formadd(&post, &last, CURLFORM_COPYNAME, "html_code_with_hole", CURLFORM_PTRCONTENTS, htmlbuffer, - CURLFORM_CONTENTSLENGTH, htmlbufferlength, + CURLFORM_CONTENTSLENGTH, (long)htmlbufferlength, CURLFORM_CONTENTTYPE, "text/html", CURLFORM_END); /* Add simple file section */ diff --git a/docs/libcurl/curl_formget.md b/docs/libcurl/curl_formget.md index 0da0150a90a5..a6afea5a8208 100644 --- a/docs/libcurl/curl_formget.md +++ b/docs/libcurl/curl_formget.md @@ -51,13 +51,14 @@ This, because first then does libcurl known which actual read callback to use. # EXAMPLE ~~~c -size_t print_httppost_callback(void *arg, const char *buf, size_t len) +static size_t print_httppost_callback(void *arg, const char *buf, size_t len) { fwrite(buf, len, 1, stdout); *((size_t *)arg) += len; return len; } +size_t print_httppost(struct curl_httppost *post); size_t print_httppost(struct curl_httppost *post) { size_t total_size = 0; diff --git a/docs/libcurl/curl_global_sslset.md b/docs/libcurl/curl_global_sslset.md index ccc7b7aa616c..fa80a8cc411b 100644 --- a/docs/libcurl/curl_global_sslset.md +++ b/docs/libcurl/curl_global_sslset.md @@ -109,16 +109,16 @@ typedef enum { ~~~c int main(void) { + const curl_ssl_backend **list; int i; /* choose a specific backend */ curl_global_sslset(CURLSSLBACKEND_WOLFSSL, NULL, NULL); /* list the available ones */ - const curl_ssl_backend **list; curl_global_sslset(CURLSSLBACKEND_NONE, NULL, &list); for(i = 0; list[i]; i++) - printf("SSL backend #%d: '%s' (ID: %d)\n", + printf("SSL backend #%d: '%s' (ID: %u)\n", i, list[i]->name, list[i]->id); } ~~~ diff --git a/docs/libcurl/curl_mime_data_cb.md b/docs/libcurl/curl_mime_data_cb.md index 199e4f7037d0..d5965c86c769 100644 --- a/docs/libcurl/curl_mime_data_cb.md +++ b/docs/libcurl/curl_mime_data_cb.md @@ -110,7 +110,7 @@ source to avoid data duplication. In this case, original data must be retained until after the transfer terminates. ~~~c #include /* for memcpy */ -char hugedata[512000]; +static char hugedata[512000]; struct ctl { char *buffer; @@ -118,7 +118,8 @@ struct ctl { curl_off_t position; }; -size_t read_callback(char *buffer, size_t size, size_t nitems, void *arg) +static size_t read_callback(char *buffer, size_t size, size_t nitems, + void *arg) { struct ctl *p = (struct ctl *)arg; size_t sz = (size_t)(p->size - p->position); @@ -132,7 +133,7 @@ size_t read_callback(char *buffer, size_t size, size_t nitems, void *arg) return sz; } -int seek_callback(void *arg, curl_off_t offset, int origin) +static int seek_callback(void *arg, curl_off_t offset, int origin) { struct ctl *p = (struct ctl *) arg; @@ -143,6 +144,8 @@ int seek_callback(void *arg, curl_off_t offset, int origin) case SEEK_CUR: offset += p->position; break; + default: + break; } if(offset < 0) diff --git a/docs/libcurl/curl_mime_subparts.md b/docs/libcurl/curl_mime_subparts.md index 3a8c18bd22bc..f705a276e3d2 100644 --- a/docs/libcurl/curl_mime_subparts.md +++ b/docs/libcurl/curl_mime_subparts.md @@ -48,8 +48,8 @@ setting *subparts* to NULL. ~~~c -static char *inline_html = "example"; -static char *inline_text = "once upon the time"; +static const char *inline_html = "example"; +static const char *inline_text = "once upon the time"; int main(void) { diff --git a/docs/libcurl/curl_mprintf.md b/docs/libcurl/curl_mprintf.md index 7c9def535c8a..a5a6c9a2c5b6 100644 --- a/docs/libcurl/curl_mprintf.md +++ b/docs/libcurl/curl_mprintf.md @@ -269,7 +269,7 @@ A '%' symbol is written. No argument is converted. # EXAMPLE ~~~c -const char *name = "John"; +static const char *name = "John"; int main(void) { diff --git a/docs/libcurl/curl_multi_assign.md b/docs/libcurl/curl_multi_assign.md index 784f5ad9a9cc..7d1eaac4ed3d 100644 --- a/docs/libcurl/curl_multi_assign.md +++ b/docs/libcurl/curl_multi_assign.md @@ -53,11 +53,11 @@ It is acceptable to call this function from your multi callback functions. int main(void) { CURLM *multi = curl_multi_init(); - int private = 123; + int private_data = 123; curl_socket_t fd = 0; /* file descriptor to associate our data with */ /* make our struct pointer associated with socket fd */ - CURLMcode mresult = curl_multi_assign(multi, fd, &private); + CURLMcode mresult = curl_multi_assign(multi, fd, &private_data); if(mresult) printf("error: %s\n", curl_multi_strerror(mresult)); } diff --git a/docs/libcurl/curl_multi_get_handles.md b/docs/libcurl/curl_multi_get_handles.md index 56da272b8abf..ad472970c9fb 100644 --- a/docs/libcurl/curl_multi_get_handles.md +++ b/docs/libcurl/curl_multi_get_handles.md @@ -56,11 +56,13 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { + CURL **list; + /* add the transfer */ curl_multi_add_handle(multi, curl); /* extract all added handles */ - CURL **list = curl_multi_get_handles(multi); + list = curl_multi_get_handles(multi); if(list) { int i; diff --git a/docs/libcurl/curl_multi_wakeup.md b/docs/libcurl/curl_multi_wakeup.md index d95f9eb6b874..97cb4b2b9abf 100644 --- a/docs/libcurl/curl_multi_wakeup.md +++ b/docs/libcurl/curl_multi_wakeup.md @@ -46,7 +46,7 @@ This function has no effect on curl_multi_wait(3) calls. ~~~c extern int time_to_die(void); extern int set_something_to_signal_thread_1_to_exit(void); -extern int decide_to_stop_thread1(); +extern int decide_to_stop_thread1(void); int main(void) { diff --git a/docs/libcurl/curl_pushheader_bynum.md b/docs/libcurl/curl_pushheader_bynum.md index 429710f00127..256098e03032 100644 --- a/docs/libcurl/curl_pushheader_bynum.md +++ b/docs/libcurl/curl_pushheader_bynum.md @@ -48,7 +48,7 @@ static int push_cb(CURL *parent, struct curl_pushheaders *headers, void *clientp) { - int i = 0; + size_t i = 0; char *field; do { field = curl_pushheader_bynum(headers, i); diff --git a/docs/libcurl/curl_strequal.md b/docs/libcurl/curl_strequal.md index 0d9689acf782..1c29a8770387 100644 --- a/docs/libcurl/curl_strequal.md +++ b/docs/libcurl/curl_strequal.md @@ -46,7 +46,7 @@ string comparison functions. This function works on all platforms. ~~~c int main(int argc, char *argv[]) { - const char *name = "compare"; + static const char *name = "compare"; if(curl_strequal(name, argv[1])) printf("Name and input matches\n"); } diff --git a/docs/libcurl/curl_strnequal.md b/docs/libcurl/curl_strnequal.md index 7e0d40461d50..e6ce1eb3e7d1 100644 --- a/docs/libcurl/curl_strnequal.md +++ b/docs/libcurl/curl_strnequal.md @@ -49,7 +49,7 @@ string comparison functions. This function works on all platforms. ~~~c int main(int argc, char *argv[]) { - const char *name = "compare"; + static const char *name = "compare"; if(curl_strnequal(name, argv[1], 5)) printf("Name and input matches in the 5 first bytes\n"); } diff --git a/docs/libcurl/curl_ws_meta.md b/docs/libcurl/curl_ws_meta.md index 7fb2551a5ed3..6fd44ac6bfda 100644 --- a/docs/libcurl/curl_ws_meta.md +++ b/docs/libcurl/curl_ws_meta.md @@ -152,7 +152,7 @@ static size_t writecb(char *buffer, size_t size, size_t nitems, void *p) struct customdata *c = (struct customdata *)p; const struct curl_ws_frame *m = curl_ws_meta(c->easy); - printf("flags: %x\n", m->flags); + printf("flags: %x\n", (unsigned int)m->flags); return 0; } diff --git a/docs/libcurl/curl_ws_send.md b/docs/libcurl/curl_ws_send.md index dc6e927ca848..74d562e86184 100644 --- a/docs/libcurl/curl_ws_send.md +++ b/docs/libcurl/curl_ws_send.md @@ -86,7 +86,7 @@ calls. int main(void) { - const char *buffer = "PAYLOAD"; + static const char *buffer = "PAYLOAD"; size_t offset = 0; CURLcode result = CURLE_OK; CURL *curl = curl_easy_init(); diff --git a/docs/libcurl/curl_ws_start_frame.md b/docs/libcurl/curl_ws_start_frame.md index e02487759c80..9fcbae027454 100644 --- a/docs/libcurl/curl_ws_start_frame.md +++ b/docs/libcurl/curl_ws_start_frame.md @@ -66,7 +66,7 @@ Supports all flags documented in curl_ws_meta(3). struct read_ctx { CURL *easy; - char *message; + const char *message; size_t msg_len; size_t nsent; }; @@ -83,7 +83,7 @@ static size_t readcb(char *buf, size_t nitems, size_t buflen, void *p) result = curl_ws_start_frame(ctx->easy, CURLWS_TEXT, (curl_off_t)ctx->msg_len); if(result != CURLE_OK) { - fprintf(stderr, "error starting frame: %d\n", result); + fprintf(stderr, "error starting frame: %d\n", (int)result); return CURL_READFUNC_ABORT; } } diff --git a/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md b/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md index 2a65509ede58..b9d18805c3af 100644 --- a/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md +++ b/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md @@ -55,10 +55,10 @@ int main(void) printf("No auth available, perhaps no 401?\n"); else { printf("%s%s%s%s\n", - auth & CURLAUTH_BASIC ? "Basic " : "", - auth & CURLAUTH_DIGEST ? "Digest " : "", - auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", - auth % CURLAUTH_NTLM ? "NTLM " : ""); + (unsigned long)auth & CURLAUTH_BASIC ? "Basic " : "", + (unsigned long)auth & CURLAUTH_DIGEST ? "Digest " : "", + (unsigned long)auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", + (unsigned long)auth & CURLAUTH_NTLM ? "NTLM " : ""); } } } diff --git a/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md b/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md index c5110ef3771b..7ae437d18d57 100644 --- a/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md +++ b/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md @@ -56,10 +56,10 @@ int main(void) printf("No proxy auth available, perhaps no 407?\n"); else { printf("%s%s%s%s\n", - auth & CURLAUTH_BASIC ? "Basic " : "", - auth & CURLAUTH_DIGEST ? "Digest " : "", - auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", - auth % CURLAUTH_NTLM ? "NTLM " : ""); + (unsigned long)auth & CURLAUTH_BASIC ? "Basic " : "", + (unsigned long)auth & CURLAUTH_DIGEST ? "Digest " : "", + (unsigned long)auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", + (unsigned long)auth & CURLAUTH_NTLM ? "NTLM " : ""); } } } diff --git a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md index 5dd44f48e63f..f5c979b57177 100644 --- a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md +++ b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md @@ -127,7 +127,8 @@ https://github.com/curl/curl/issues/685 #include #include -CURL *curl; +static CURL *curl; + static size_t wf(char *ptr, size_t size, size_t nmemb, void *stream) { const struct curl_tlssessioninfo *info = NULL; @@ -150,7 +151,7 @@ int main(int argc, char *argv[]) result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return result; + return (int)result; } ~~~ diff --git a/docs/libcurl/opts/CURLMOPT_PIPELINING_SERVER_BL.md b/docs/libcurl/opts/CURLMOPT_PIPELINING_SERVER_BL.md index 6d3764ef90ce..c57020b01ec3 100644 --- a/docs/libcurl/opts/CURLMOPT_PIPELINING_SERVER_BL.md +++ b/docs/libcurl/opts/CURLMOPT_PIPELINING_SERVER_BL.md @@ -49,12 +49,13 @@ NULL, which means that there is no block list. # EXAMPLE ~~~c -static char *server_block_list[] = +static const char *server_block_list[] = { "Microsoft-IIS/6.0", "nginx/0.8.54", NULL }; + int main(void) { CURLM *m = curl_multi_init(); diff --git a/docs/libcurl/opts/CURLMOPT_PIPELINING_SITE_BL.md b/docs/libcurl/opts/CURLMOPT_PIPELINING_SITE_BL.md index 19fd0fdba919..b9d27cb3394e 100644 --- a/docs/libcurl/opts/CURLMOPT_PIPELINING_SITE_BL.md +++ b/docs/libcurl/opts/CURLMOPT_PIPELINING_SITE_BL.md @@ -44,7 +44,7 @@ NULL, which means that there is no block list. # EXAMPLE ~~~c -static char *site_block_list[] = +static const char *site_block_list[] = { "www.haxx.se", "www.example.com:1234", diff --git a/docs/libcurl/opts/CURLMOPT_PUSHDATA.md b/docs/libcurl/opts/CURLMOPT_PUSHDATA.md index f3383d3ece5c..4204edcecf68 100644 --- a/docs/libcurl/opts/CURLMOPT_PUSHDATA.md +++ b/docs/libcurl/opts/CURLMOPT_PUSHDATA.md @@ -44,11 +44,11 @@ NULL #include /* only allow pushes for filenames starting with "push-" */ -int push_callback(CURL *parent, - CURL *easy, - size_t num_headers, - struct curl_pushheaders *headers, - void *clientp) +static int push_callback(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *clientp) { char *headp; int *transfers = (int *)clientp; diff --git a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md index 4bead2b94fdd..1336133cde0b 100644 --- a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md @@ -105,11 +105,11 @@ NULL, no callback #include /* only allow pushes for filenames starting with "push-" */ -int push_callback(CURL *parent, - CURL *easy, - size_t num_headers, - struct curl_pushheaders *headers, - void *clientp) +static int push_callback(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *clientp) { char *headp; int *transfers = (int *)clientp; diff --git a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md index ffced772b4d8..0b243da5a76d 100644 --- a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md @@ -58,17 +58,18 @@ NULL # EXAMPLE ~~~c +#include /* for uintptr_t */ #include int main(void) { - char *strpem = "PEMDATA"; /* strpem must point to a PEM string */ + static const char *strpem = "PEMDATA"; /* must point to a PEM string */ CURL *curl = curl_easy_init(); if(curl) { CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - blob.data = strpem; + blob.data = (void *)(uintptr_t)(const void *)strpem; /* strip const */ blob.len = strlen(strpem); blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md index c04ebe305156..b632e1a3a759 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md @@ -132,7 +132,7 @@ static long file_is_coming(struct curl_fileinfo *finfo, return CURL_CHUNK_BGN_FUNC_OK; } -int main() +int main(void) { /* data for callback */ struct callback_data callback_info; diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md b/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md index fb1877e49174..e571b7c4962e 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md @@ -81,7 +81,7 @@ static long file_is_coming(struct curl_fileinfo *finfo, return CURL_CHUNK_BGN_FUNC_OK; } -int main() +int main(void) { /* data for callback */ struct callback_data callback_info; diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md index 82bf9ad5baa6..3f365941bfb5 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md @@ -63,7 +63,7 @@ static long file_is_downloaded(void *ptr) return CURL_CHUNK_END_FUNC_OK; } -int main() +int main(void) { /* data for callback */ struct callback_data callback_info; diff --git a/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md b/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md index b1d0dd872c46..e66a470f6ada 100644 --- a/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md @@ -119,10 +119,10 @@ static void dump(const char *text, unsigned int width = 0x10; fprintf(stream, "%s, %lu bytes (0x%lx)\n", - text, (long)size, (long)size); + text, (unsigned long)size, (unsigned long)size); for(i = 0; i < size; i += width) { - fprintf(stream, "%4.4lx: ", (long)i); + fprintf(stream, "%4.4lx: ", (unsigned long)i); /* show hex to the left */ for(c = 0; c < width; c++) { @@ -134,7 +134,8 @@ static void dump(const char *text, /* show data on the right */ for(c = 0; (c < width) && (i + c < size); c++) { - char x = (ptr[i + c] >= 0x20 && ptr[i + c] < 0x80) ? ptr[i + c] : '.'; + char x = (ptr[i + c] >= 0x20 && ptr[i + c] < 0x80) + ? (char)ptr[i + c] : '.'; fputc(x, stream); } diff --git a/docs/libcurl/opts/CURLOPT_ECH.md b/docs/libcurl/opts/CURLOPT_ECH.md index 99d3ddaefc33..660102c4ce43 100644 --- a/docs/libcurl/opts/CURLOPT_ECH.md +++ b/docs/libcurl/opts/CURLOPT_ECH.md @@ -88,8 +88,8 @@ int main(void) { CURL *curl = curl_easy_init(); - const char *config = \ - "ecl:AED+DQA87wAgACB/RuzUCsW3uBbSFI7mzD63TUXpI8sGDTnFTbFCDpa+" \ + static const char *config = + "ecl:AED+DQA87wAgACB/RuzUCsW3uBbSFI7mzD63TUXpI8sGDTnFTbFCDpa+" "CAAEAAEAAQANY292ZXIuZGVmby5pZQAA"; if(curl) { CURLcode result; diff --git a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md index fa3a7c7994ec..ff5ee86c0044 100644 --- a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md +++ b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md @@ -86,7 +86,7 @@ int main(void) show the more generic information from curl_easy_strerror instead. */ if(result != CURLE_OK) { size_t len = strlen(errbuf); - fprintf(stderr, "\nlibcurl: (%d) ", result); + fprintf(stderr, "\nlibcurl: (%d) ", (int)result); if(len) fprintf(stderr, "%s%s", errbuf, ((errbuf[len - 1] != '\n') ? "\n" : "")); diff --git a/docs/libcurl/opts/CURLOPT_HEADERDATA.md b/docs/libcurl/opts/CURLOPT_HEADERDATA.md index 7df0f0d1e6ef..39770989b91f 100644 --- a/docs/libcurl/opts/CURLOPT_HEADERDATA.md +++ b/docs/libcurl/opts/CURLOPT_HEADERDATA.md @@ -51,7 +51,7 @@ NULL ~~~c struct my_info { int shoesize; - char *secret; + const char *secret; }; static size_t header_callback(char *buffer, size_t size, diff --git a/docs/libcurl/opts/CURLOPT_HSTSREADDATA.md b/docs/libcurl/opts/CURLOPT_HSTSREADDATA.md index 0b5174d8e19a..21397c490944 100644 --- a/docs/libcurl/opts/CURLOPT_HSTSREADDATA.md +++ b/docs/libcurl/opts/CURLOPT_HSTSREADDATA.md @@ -58,14 +58,14 @@ static CURLSTScode hsts_cb(CURL *easy, struct curl_hstsentry *sts, int main(void) { CURL *curl = curl_easy_init(); - struct MyData this; + struct MyData my_data; if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); /* pass pointer that gets passed in to the CURLOPT_HSTSREADFUNCTION callback */ - curl_easy_setopt(curl, CURLOPT_HSTSREADDATA, &this); + curl_easy_setopt(curl, CURLOPT_HSTSREADDATA, &my_data); /* set HSTS read callback */ curl_easy_setopt(curl, CURLOPT_HSTSREADFUNCTION, hsts_cb); diff --git a/docs/libcurl/opts/CURLOPT_HSTSWRITEDATA.md b/docs/libcurl/opts/CURLOPT_HSTSWRITEDATA.md index cc078539dd24..d84a810f2564 100644 --- a/docs/libcurl/opts/CURLOPT_HSTSWRITEDATA.md +++ b/docs/libcurl/opts/CURLOPT_HSTSWRITEDATA.md @@ -51,14 +51,14 @@ struct MyData { int main(void) { CURL *curl = curl_easy_init(); - struct MyData this; + struct MyData my_data; if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); /* pass pointer that gets passed in to the CURLOPT_HSTSWRITEFUNCTION callback */ - curl_easy_setopt(curl, CURLOPT_HSTSWRITEDATA, &this); + curl_easy_setopt(curl, CURLOPT_HSTSWRITEDATA, &my_data); result = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_HTTPPOST.md b/docs/libcurl/opts/CURLOPT_HTTPPOST.md index 8ae91095e4e0..4f07eacdcde7 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPPOST.md +++ b/docs/libcurl/opts/CURLOPT_HTTPPOST.md @@ -57,6 +57,7 @@ NULL ~~~c int main(void) { + CURL *curl; struct curl_httppost *formpost; struct curl_httppost *lastptr; @@ -82,7 +83,7 @@ int main(void) CURLFORM_COPYCONTENTS, "send", CURLFORM_END); - CURL *curl = curl_easy_init(); + curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); diff --git a/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md b/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md index 2bcdee3b6650..0d0b28fd88c5 100644 --- a/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md +++ b/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md @@ -59,12 +59,13 @@ static int sockopt_callback(void *clientp, curl_socket_t curlfd, return CURL_SOCKOPT_ALREADY_CONNECTED; } +extern int sockfd; /* the already connected one */ + int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; - extern int sockfd; /* the already connected one */ /* libcurl thinks that you connect to the host * and port that you specify in the URL option. */ diff --git a/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md b/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md index 568fb11b9402..4978d65f987f 100644 --- a/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md @@ -107,12 +107,13 @@ static int sockopt_callback(void *clientp, curl_socket_t curlfd, return CURL_SOCKOPT_ALREADY_CONNECTED; } +extern int sockfd; /* the already connected one */ + int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; - extern int sockfd; /* the already connected one */ /* libcurl thinks that you connect to the host * and port that you specify in the URL option. */ curl_easy_setopt(curl, CURLOPT_URL, "http://99.99.99.99:9999"); diff --git a/docs/libcurl/opts/CURLOPT_POSTFIELDS.md b/docs/libcurl/opts/CURLOPT_POSTFIELDS.md index 6258e53f209f..49d955966a3f 100644 --- a/docs/libcurl/opts/CURLOPT_POSTFIELDS.md +++ b/docs/libcurl/opts/CURLOPT_POSTFIELDS.md @@ -87,7 +87,7 @@ int main(void) CURLcode result = CURLE_OK; CURL *curl = curl_easy_init(); if(curl) { - const char *data = "data to send"; + static const char *data = "data to send"; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); @@ -104,7 +104,7 @@ int main(void) /* send an application/json POST */ curl = curl_easy_init(); if(curl) { - const char *json = "{\"name\": \"daniel\"}"; + static const char *json = "{\"name\": \"daniel\"}"; struct curl_slist *slist1 = NULL; slist1 = curl_slist_append(slist1, "Content-Type: application/json"); slist1 = curl_slist_append(slist1, "Accept: application/json"); diff --git a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md index 4dbc324e2d68..d45121b53f1d 100644 --- a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md +++ b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md @@ -52,7 +52,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { CURLcode result; - const char *data = "data to send"; + static const char *data = "data to send"; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); diff --git a/docs/libcurl/opts/CURLOPT_POSTQUOTE.md b/docs/libcurl/opts/CURLOPT_POSTQUOTE.md index 55ef81aabc02..f53e20109cff 100644 --- a/docs/libcurl/opts/CURLOPT_POSTQUOTE.md +++ b/docs/libcurl/opts/CURLOPT_POSTQUOTE.md @@ -50,11 +50,12 @@ NULL ~~~c int main(void) { + CURL *curl; struct curl_slist *cmdlist = NULL; cmdlist = curl_slist_append(cmdlist, "RNFR source-name"); cmdlist = curl_slist_append(cmdlist, "RNTO new-name"); - CURL *curl = curl_easy_init(); + curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); diff --git a/docs/libcurl/opts/CURLOPT_PREQUOTE.md b/docs/libcurl/opts/CURLOPT_PREQUOTE.md index 79cf9fee80e4..21ae822b5d71 100644 --- a/docs/libcurl/opts/CURLOPT_PREQUOTE.md +++ b/docs/libcurl/opts/CURLOPT_PREQUOTE.md @@ -52,10 +52,11 @@ NULL ~~~c int main(void) { + CURL *curl; struct curl_slist *cmdlist = NULL; cmdlist = curl_slist_append(cmdlist, "SYST"); - CURL *curl = curl_easy_init(); + curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); diff --git a/docs/libcurl/opts/CURLOPT_PRIVATE.md b/docs/libcurl/opts/CURLOPT_PRIVATE.md index e59016bf8c79..5b7443072c5a 100644 --- a/docs/libcurl/opts/CURLOPT_PRIVATE.md +++ b/docs/libcurl/opts/CURLOPT_PRIVATE.md @@ -41,25 +41,25 @@ NULL # EXAMPLE ~~~c -struct private { +struct private_data { void *custom; }; int main(void) { CURL *curl = curl_easy_init(); - struct private secrets; + struct private_data secrets; if(curl) { CURLcode result; - struct private *extracted; + struct private_data *extracted; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - /* store a pointer to our private struct */ + /* store a pointer to our private_data struct */ curl_easy_setopt(curl, CURLOPT_PRIVATE, &secrets); result = curl_easy_perform(curl); - /* we can extract the private pointer again too */ + /* we can extract the private_data pointer again too */ curl_easy_getinfo(curl, CURLINFO_PRIVATE, &extracted); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md index 621a246d1a0c..918ae9fd891d 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md @@ -39,7 +39,7 @@ NULL ~~~c struct progress { - char *private; + char *private_data; size_t size; }; @@ -50,7 +50,7 @@ static int progress_callback(void *clientp, double ulnow) { struct progress *memory = clientp; - printf("private: %p\n", (void *)memory->private); + printf("private: %p\n", (void *)memory->private_data); /* use the values */ diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md index 82cf3585f76d..41fb91ae9ad8 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md @@ -85,7 +85,7 @@ NULL. libcurl has an internal progress meter. That is rarely wanted by users. ~~~c struct progress { - char *private; + char *private_data; size_t size; }; @@ -96,7 +96,7 @@ static int progress_callback(void *clientp, double ulnow) { struct progress *memory = clientp; - printf("private: %p\n", (void *)memory->private); + printf("private: %p\n", (void *)memory->private_data); /* use the values */ diff --git a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md index 180a7b1808bf..0885ad96e0c9 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md @@ -66,8 +66,8 @@ NULL ~~~c -extern char *certificateData; /* point to the data */ -size_t filesize; /* size of the data */ +static char *certificateData; /* point to the data */ +static size_t filesize; /* size of the data */ int main(void) { diff --git a/docs/libcurl/opts/CURLOPT_QUOTE.md b/docs/libcurl/opts/CURLOPT_QUOTE.md index d4365f7b4185..020d5ed95ae3 100644 --- a/docs/libcurl/opts/CURLOPT_QUOTE.md +++ b/docs/libcurl/opts/CURLOPT_QUOTE.md @@ -141,11 +141,12 @@ NULL ~~~c int main(void) { + CURL *curl; struct curl_slist *cmdlist = NULL; cmdlist = curl_slist_append(cmdlist, "RNFR source-name"); cmdlist = curl_slist_append(cmdlist, "RNTO new-name"); - CURL *curl = curl_easy_init(); + curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); diff --git a/docs/libcurl/opts/CURLOPT_READDATA.md b/docs/libcurl/opts/CURLOPT_READDATA.md index fb18753363dd..81055cd0748c 100644 --- a/docs/libcurl/opts/CURLOPT_READDATA.md +++ b/docs/libcurl/opts/CURLOPT_READDATA.md @@ -55,14 +55,14 @@ struct MyData { int main(void) { CURL *curl = curl_easy_init(); - struct MyData this; + struct MyData my_data; if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* pass pointer that gets passed in to the CURLOPT_READFUNCTION callback */ - curl_easy_setopt(curl, CURLOPT_READDATA, &this); + curl_easy_setopt(curl, CURLOPT_READDATA, &my_data); result = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_READFUNCTION.md b/docs/libcurl/opts/CURLOPT_READFUNCTION.md index 0c8304ce3c77..e0345e832ae3 100644 --- a/docs/libcurl/opts/CURLOPT_READFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_READFUNCTION.md @@ -84,7 +84,8 @@ fread(3) # EXAMPLE ~~~c -size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +static size_t read_callback(char *ptr, size_t size, size_t nmemb, + void *userdata) { FILE *readhere = (FILE *)userdata; curl_off_t nread; diff --git a/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md b/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md index 39d6ae791307..061e8228f73d 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md @@ -56,7 +56,8 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { CURLcode result; - char *prev_id = "old"; /* previously retrieved RTSP session ID */ + static const char *prev_id = "old"; /* previously retrieved + RTSP session ID */ curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, prev_id); result = curl_easy_perform(curl); diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md index 1125ec392e25..6ff15427a8fe 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md @@ -71,15 +71,16 @@ struct mine { void *custom; }; -int hostkeycb(void *clientp, /* passed with CURLOPT_SSH_HOSTKEYDATA */ - int keytype, /* CURLKHTYPE */ - const char *key, /* host key to check */ - size_t keylen) /* length of the key */ +static int hostkeycb(void *clientp, /* passed with CURLOPT_SSH_HOSTKEYDATA */ + int keytype, /* CURLKHTYPE */ + const char *key, /* host key to check */ + size_t keylen) /* length of the key */ { /* 'clientp' points to the callback_data struct */ /* investigate the situation and return the correct value */ return CURLKHMATCH_OK; } + int main(void) { struct mine callback_data; diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md index 73a16ae41204..c6521622b9fd 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md @@ -84,7 +84,7 @@ int main(void) CURL *curl; CURLcode result; /* CA cert in PEM format, replace the XXXs */ - char *mypem = + const char *mypem = "-----BEGIN CERTIFICATE-----\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md index f0460e53577a..c834cc8bf523 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md @@ -144,7 +144,7 @@ int main(void) CURL *curl; CURLcode result; /* CA cert in PEM format, replace the XXXs */ - char *mypem = + const char *mypem = "-----BEGIN CERTIFICATE-----\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" diff --git a/docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md b/docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md index 43f3317c01a5..63b9444168cc 100644 --- a/docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md @@ -41,7 +41,7 @@ CURLOPT_TFTP_BLKSIZE(3) is ignored. # EXAMPLE ~~~c -size_t write_callback(char *ptr, size_t size, size_t nmemb, void *fp) +static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *fp) { return fwrite(ptr, size, nmemb, (FILE *)fp); } diff --git a/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md b/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md index 716967d88adf..3d9e2d096a6b 100644 --- a/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md @@ -79,6 +79,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { CURLcode result; + struct curl_slist *headers; /* Set the URL of the request */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); @@ -89,8 +90,7 @@ int main(void) Let that function be read_cb */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); - struct curl_slist *headers = NULL; - headers = curl_slist_append(headers, "Trailer: My-super-awesome-trailer"); + headers = curl_slist_append(NULL, "Trailer: My-super-awesome-trailer"); result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* Set the trailers filling callback */ diff --git a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md index 01e46918b848..78fc0274257a 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md @@ -42,7 +42,7 @@ NULL ~~~c struct progress { - char *private; + char *private_data; size_t size; }; @@ -53,7 +53,7 @@ static int progress_cb(void *clientp, curl_off_t ulnow) { struct progress *memory = clientp; - printf("private ptr: %p\n", (void *)memory->private); + printf("private ptr: %p\n", (void *)memory->private_data); /* use the values */ return 0; /* all is good */ diff --git a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md index c9c6cd6e2aa3..9c074b60f66c 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md @@ -81,7 +81,7 @@ NULL - use the internal progress meter. That is rarely wanted by users. ~~~c struct progress { - char *private; + char *private_data; size_t size; }; @@ -92,7 +92,7 @@ static int xferinfo_callback(void *clientp, curl_off_t ulnow) { struct progress *memory = clientp; - printf("my ptr: %p\n", (void *)memory->private); + printf("my ptr: %p\n", (void *)memory->private_data); /* use the values */ From 6fafe470822f1b4b5db38d0d57206e10df96ad0b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 11:45:42 +0200 Subject: [PATCH 0975/1062] tool_cb_hdr: do not truncate etags output to stdout Verified by test 321 Reported-by: Stanislav Fort Closes #22685 --- src/tool_cb_hdr.c | 1 + tests/data/Makefile.am | 2 +- tests/data/test321 | 50 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/data/test321 diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index c54037f130fc..014e4b832ed1 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -268,6 +268,7 @@ static size_t save_etag(const char *etag_h, const char *endp, /* Truncate regular files to avoid stale etag content */ if((fd != -1) && + etag_save->regular_file && !curlx_fstat(fd, &file) && (S_ISREG(file.st_mode) && toolx_ftruncate(fd, 0))) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 1b6fd78272a8..f6456d8a4d81 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -60,7 +60,7 @@ test280 test281 test282 test283 test284 test285 test286 test287 test288 \ test289 test290 test291 test292 test293 test294 test295 test296 test297 \ test298 test299 test300 test301 test302 test303 test304 test305 test306 \ test307 test308 test309 test310 test311 test312 test313 test314 test315 \ -test316 test317 test318 test319 test320 \ +test316 test317 test318 test319 test320 test321 \ test325 test326 test327 test328 test329 test330 test331 test332 test333 \ test334 test335 test336 test337 test338 test339 test340 test341 test342 \ test343 test344 test345 test346 test347 test348 test349 test350 test351 \ diff --git a/tests/data/test321 b/tests/data/test321 new file mode 100644 index 000000000000..47aa48d66ee8 --- /dev/null +++ b/tests/data/test321 @@ -0,0 +1,50 @@ + + + + +etags + + + +# Server-side + + +HTTP/1.1 200 OK +ETag: "remote" +Content-Length: 0 + + + + +# Client-side + + +http + + +--etag-save to stdout append to file + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --etag-save - >> %LOGDIR/out%TESTNUMBER + + + +initial content + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +initial content +"remote" + + + From cdab112af31c64863cf0a156e17ccc819249003a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 21 Apr 2026 23:26:33 +0200 Subject: [PATCH 0976/1062] tidy-up: minimum version comments and messages - avoid 'upper'. - if it improves consistency across builds and sources, prefer 'greater' or newer/later/+, prefer no 'v' prefix. - INSTALL-CMAKE.md: drop redundant version requirement. - make `mingw-w64` lowercase where missing. Closes #21404 --- .clang-tidy.yml | 4 ++-- CMake/FindZstd.cmake | 4 ++-- CMake/Macros.cmake | 2 +- CMake/PickyWarnings.cmake | 2 +- CMake/curl-config.in.cmake | 2 +- CMake/unix-cache.cmake | 8 ++++---- CMakeLists.txt | 16 ++++++++-------- configure.ac | 2 +- docs/HTTPSRR.md | 2 +- docs/INSTALL-CMAKE.md | 8 ++++---- docs/INSTALL.md | 6 +++--- lib/content_encoding.c | 2 +- lib/curl_ntlm_core.c | 2 +- lib/curl_setup.h | 12 ++++++------ lib/md5.c | 2 +- lib/sha256.c | 2 +- lib/vauth/gsasl.c | 2 +- lib/vdns/asyn-ares.c | 4 ++-- lib/vdns/asyn-base.c | 2 +- lib/vssh/ssh.h | 2 +- lib/vtls/mbedtls.c | 2 +- lib/vtls/openssl.c | 4 ++-- lib/vtls/schannel.c | 10 +++++----- lib/vtls/wolfssl.c | 4 ++-- m4/curl-amissl.m4 | 4 ++-- m4/curl-openssl.m4 | 4 ++-- tests/certs/genserv.pl | 2 +- 27 files changed, 58 insertions(+), 58 deletions(-) diff --git a/.clang-tidy.yml b/.clang-tidy.yml index 5f523fb50ba0..0476ba5d56b6 100644 --- a/.clang-tidy.yml +++ b/.clang-tidy.yml @@ -8,7 +8,7 @@ Checks: - clang-analyzer-* - -clang-analyzer-optin.performance.Padding - - -clang-analyzer-security.ArrayBound # due to false positives with clang-tidy v21.1.0+ + - -clang-analyzer-security.ArrayBound # due to false positives with clang-tidy 21.1.0+ - -clang-analyzer-security.insecureAPI.bzero # for FD_ZERO() (seen on macOS) - -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling - -clang-diagnostic-nullability-extension @@ -56,4 +56,4 @@ CheckOptions: misc-header-include-cycle.IgnoredFilesList: 'curl/curl.h' readability-inconsistent-declaration-parameter-name.Strict: true -HeaderFilterRegex: '.*' # Default in v22.1.0+ +HeaderFilterRegex: '.*' # Default in 22.1.0+ diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 176645d97bab..5a2db76bb1a2 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -54,7 +54,7 @@ if(NOT DEFINED ZSTD_INCLUDE_DIR AND endif() if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) find_package(zstd CONFIG QUIET) - # Skip using if older than v1.4.5 + # Only use if 1.4.5 or greater if(zstd_CONFIG AND NOT TARGET zstd::libzstd_static AND NOT TARGET zstd::libzstd_shared) @@ -81,7 +81,7 @@ elseif(zstd_CONFIG) if(ZSTD_USE_STATIC_LIBS) set(_zstd_LIBRARIES zstd::libzstd_static) elseif(TARGET zstd::libzstd) - set(_zstd_LIBRARIES zstd::libzstd) # v1.5.6+ + set(_zstd_LIBRARIES zstd::libzstd) # 1.5.6+ else() set(_zstd_LIBRARIES zstd::libzstd_shared) endif() diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 40968156d241..503074a0a02b 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -255,7 +255,7 @@ macro(curl_collect_target_link_options _target) get_target_property(_val ${_target} IMPORTED) if(_val) # LOCATION is empty for interface library targets and safe to ignore. - # Explicitly skip this query to avoid CMake v3.18 and older erroring out. + # Explicitly skip this query to avoid CMake 3.18 and older erroring out. get_target_property(_val ${_target} TYPE) if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY") get_target_property(_val ${_target} LOCATION) diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index 4317f8a442ea..aeb72564dbdb 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -97,7 +97,7 @@ if(PICKY_COMPILER) # to suppress undesired warnings in case -Weverything is passed as a custom option. # Assume these options always exist with both clang and gcc. - # Require clang 3.0 / gcc 2.95 or later. + # Require clang 3.0 / gcc 2.95 or greater. list(APPEND _picky_enable -Wbad-function-cast # clang 2.7 gcc 2.95 -Wconversion # clang 2.7 gcc 2.95 diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake index e782cb5e9274..ee49478d5fbc 100644 --- a/CMake/curl-config.in.cmake +++ b/CMake/curl-config.in.cmake @@ -30,7 +30,7 @@ option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependenci if(CMAKE_VERSION VERSION_LESS @CMAKE_MINIMUM_REQUIRED_VERSION@) message(STATUS "@PROJECT_NAME@: @PROJECT_NAME@-specific Find modules require " - "CMake @CMAKE_MINIMUM_REQUIRED_VERSION@ or upper, found: ${CMAKE_VERSION}.") + "CMake @CMAKE_MINIMUM_REQUIRED_VERSION@ or greater, found: ${CMAKE_VERSION}.") endif() include(CMakeFindDependencyMacro) diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index f37ef868bb62..8ec2cd48c193 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -73,9 +73,9 @@ if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34) set(HAVE_MEMSET_EXPLICIT 1) endif() if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR - CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+ - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # v11.2+ - CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") # v1.3+ + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # 6+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # 11.2+ + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") # 1.3+ set(HAVE_MEMSET_S 1) elseif(NOT APPLE) set(HAVE_MEMSET_S 0) @@ -260,7 +260,7 @@ set(HAVE_STRINGS_H 1) if(_CURL_OLD_LINUX) set(HAVE_STROPTS_H 1) else() - set(HAVE_STROPTS_H 0) # glibc 2.30 or newer. https://sourceware.org/legacy-ml/libc-alpha/2019-08/msg00029.html + set(HAVE_STROPTS_H 0) # glibc 2.30 or greater. https://sourceware.org/legacy-ml/libc-alpha/2019-08/msg00029.html endif() set(HAVE_STRUCT_SOCKADDR_STORAGE 1) set(HAVE_STRUCT_TIMEVAL 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index efa4b4d12d52..2e9589a47576 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ project(CURL # CMake does not recognize some targets accurately. Touch up configuration manually as a workaround. if(WINDOWS_STORE AND MINGW) # MinGW UWP build - # CMake (as of v3.31.2) gets confused and applies the MSVC rc.exe command-line + # CMake (as of 3.31.2) gets confused and applies the MSVC rc.exe command-line # template to windres. Reset it to the windres template as in 'Modules/Platform/Windows-windres.cmake': set(CMAKE_RC_COMPILE_OBJECT " -O coff ") elseif(DOS AND CMAKE_C_COMPILER_ID STREQUAL "GNU") # DJGPP @@ -230,7 +230,7 @@ if(WIN32) if(MINGW64_VERSION) message(STATUS "Found MINGW64_VERSION=${MINGW64_VERSION}") if(MINGW64_VERSION VERSION_LESS 3.0) - message(FATAL_ERROR "mingw-w64 3.0 or upper is required") + message(FATAL_ERROR "mingw-w64 3.0 or greater required") endif() endif() endif() @@ -895,7 +895,7 @@ endif() if(CURL_USE_MBEDTLS) find_package(MbedTLS MODULE REQUIRED) if(MBEDTLS_VERSION VERSION_LESS 3.2.0) - message(FATAL_ERROR "mbedTLS v3.2.0 or newer is required.") + message(FATAL_ERROR "mbedTLS 3.2.0 or greater required.") endif() set(_ssl_enabled ON) set(USE_MBEDTLS ON) @@ -929,7 +929,7 @@ if(CURL_USE_WOLFSSL) set(_curl_ca_bundle_supported TRUE) if(USE_OPENSSL AND WOLFSSL_VERSION VERSION_LESS 5.7.6) - message(FATAL_ERROR "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL.") + message(FATAL_ERROR "wolfSSL 5.7.6 or greater required to coexist with OpenSSL.") endif() set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "WOLFSSL_OPTIONS_IGNORE_SYS") @@ -966,7 +966,7 @@ if(CURL_USE_RUSTLS) endif() endif() if(NOT HAVE_RUSTLS_SUPPORTED_HPKE) - message(FATAL_ERROR "rustls-ffi library does not provide rustls_supported_hpke function. Required version is 0.15 or newer.") + message(FATAL_ERROR "rustls-ffi library does not provide rustls_supported_hpke function. Required version is 0.15 or greater.") endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls") @@ -1005,7 +1005,7 @@ if(ZSTD_FOUND) set(HAVE_ZSTD ON) list(APPEND CURL_LIBS CURL::zstd) else() - message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.") + message(WARNING "zstd 1.0.0 or greater required, disabling zstd support.") endif() endif() @@ -1027,7 +1027,7 @@ macro(curl_openssl_check_exists) if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) # Public wolfSSL headers also require zlib headers endif() - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T") # to pull in stdint.h (as of wolfSSL v5.5.4) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T") # to pull in stdint.h (as of wolfSSL 5.5.4) endif() if(WIN32) list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32") # for OpenSSL/wolfSSL @@ -1147,7 +1147,7 @@ if(USE_NGTCP2) elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0) find_package(NGTCP2 MODULE REQUIRED COMPONENTS "ossl") if(NGTCP2_VERSION VERSION_LESS 1.12.0) - message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL") + message(FATAL_ERROR "ngtcp2 1.12.0 or greater required for OpenSSL") endif() set(OPENSSL_QUIC_API2 1) elseif(HAVE_LIBRESSL) diff --git a/configure.ac b/configure.ac index 503cf66a1b09..366c7aa88878 100644 --- a/configure.ac +++ b/configure.ac @@ -2495,7 +2495,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then CPPFLAGS="$CPPFLAGS $CPP_SSH2" LIBS="$LIB_SSH2 $LIBS" - dnl check for function added in libssh2 v1.9.0 + dnl check for function added in libssh2 1.9.0 AC_CHECK_LIB(ssh2, libssh2_agent_get_identity_path) AC_CHECK_HEADER(libssh2.h, diff --git a/docs/HTTPSRR.md b/docs/HTTPSRR.md index fbdebc7fde16..e046e512ea0c 100644 --- a/docs/HTTPSRR.md +++ b/docs/HTTPSRR.md @@ -65,7 +65,7 @@ returned, curl parses it and stores the retrieved information. If DoH is not used for name resolving in an HTTPS RR enabled build, we must provide the ability using the regular resolver backends. We use the c-ares DNS -library for the HTTPS RR lookup. Version 1.28.0 or later. +library for the HTTPS RR lookup. Version 1.28.0 or greater. ### c-ares diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 2d1390373259..16a3580634ef 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -46,7 +46,7 @@ For the full list of CMake build configuration variables see ### Build system generator selection You can override CMake's default by using `-G `. For example -on Windows with multiple build systems if you have MinGW-w64 then you could use +on Windows with multiple build systems if you have mingw-w64 then you could use `-G "MinGW Makefiles"`. [List of generator names](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). @@ -227,7 +227,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `IMPORT_LIB_SUFFIX`: Import library suffix. Default: `_imp` for MSVC-like toolchains, otherwise empty. - `LIBCURL_OUTPUT_NAME`: Basename of the curl library. Default: `libcurl` - `PICKY_COMPILER`: Enable picky compiler options. Default: `ON` -- `SHARE_LIB_OBJECT`: Build shared and static libcurl in a single pass (requires CMake 3.12 or newer). Default: `ON` for Windows +- `SHARE_LIB_OBJECT`: Build shared and static libcurl in a single pass. Default: `ON` for Windows - `STATIC_LIB_SUFFIX`: Static library suffix. Default: (empty) ## Root CA options @@ -387,7 +387,7 @@ Details via CMake - `ZLIB_INCLUDE_DIR`: Absolute path to zlib include directory. - `ZLIB_LIBRARY`: Absolute path to `zlib` library. - `ZLIB_ROOT`: Absolute path to the installation root of zlib. -- `ZLIB_USE_STATIC_LIBS`: Look for static `zlib` library (requires CMake v3.24). +- `ZLIB_USE_STATIC_LIBS`: Look for static `zlib` library (requires CMake 3.24+). - `_DIR`: Absolute path to `` CMake Config directory where `*.cmake` files reside. Used when `CURL_USE_CMAKECONFIG` is enabled. `` may be: @@ -590,7 +590,7 @@ We recommend using CMake to build curl with MSVC. The project build files reside in project/Windows/VC\* for VS2010, VS2012 and VS2013. -These CMake Visual Studio generators require CMake v3.24 or older. You can +These CMake Visual Studio generators require CMake 3.24 or older. You can download them from . You can also use `-G "NMake Makefiles"`, which is supported by all CMake diff --git a/docs/INSTALL.md b/docs/INSTALL.md index a2f233b5d091..ce1b63040555 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -224,8 +224,8 @@ Building for Windows Vista/Server 2008 is required as a minimum. You can build curl with: -- Microsoft Visual Studio 2010 v10.0 or later (`_MSC_VER >= 1600`) -- MinGW-w64 3.0 or later (`__MINGW64_VERSION_MAJOR >= 3`) +- Microsoft Visual Studio 2010 10.0 or greater (`_MSC_VER >= 1600`) +- mingw-w64 3.0 or greater (`__MINGW64_VERSION_MAJOR >= 3`) ## Building Windows DLLs and C runtime (CRT) linkage issues @@ -352,7 +352,7 @@ cmake . \ Notes: -- Requires DJGPP 2.04 or upper. +- Requires DJGPP 2.04 or greater. - Compile Watt-32 (and OpenSSL) with the same version of DJGPP. Otherwise things go wrong because things like FS-extensions and `errno` values have diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 88446cfa0c08..0feecceedc7a 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -65,7 +65,7 @@ #ifdef HAVE_LIBZ #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252) -#error "requires zlib 1.2.5.2 or newer" +#error "zlib 1.2.5.2 or greater required" #endif typedef enum { diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 6978d42d93dd..7aceb65e9626 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -76,7 +76,7 @@ #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) # include # if MBEDTLS_VERSION_NUMBER < 0x03020000 -# error "mbedTLS 3.2.0 or later required" +# error "mbedTLS 3.2.0 or greater required" # endif # include # define USE_MBEDTLS_DES diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 5924d54c05b3..f9063b68976c 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -77,7 +77,7 @@ #if defined(__MINGW32__) && \ (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 3)) -#error "Building curl requires mingw-w64 3.0 or later" +#error "mingw-w64 3.0 or greater required" #endif /* Visual Studio 2010 is the minimum Visual Studio version we support. @@ -178,7 +178,7 @@ #ifdef HAVE_LIBZ # ifndef ZLIB_CONST -# define ZLIB_CONST /* Use z_const. Supported by v1.2.5.2 and upper. */ +# define ZLIB_CONST /* Use z_const. Supported by v1.2.5.2 or greater. */ # endif #endif @@ -1642,11 +1642,11 @@ typedef struct sockaddr_un { (defined(__NEWLIB__) && !defined(__CLIB2__)) || \ (defined(__GLIBC__) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \ - (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ - (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ - (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) + (defined(__DragonFly__) && __DragonFly_version >= 500600 /* 5.6+ */) || \ + (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* 11.0+ */) || \ + (defined(__OpenBSD__) && OpenBSD >= 201405 /* 5.5+ */) #define curlx_memzero_low(buf, size) explicit_bzero(buf, size) -#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ +#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* 7.2+ */ #define curlx_memzero_low(buf, size) (void)explicit_memset(buf, 0, size) #endif #endif /* !_CURL_LOCAL_MEMZERO */ diff --git a/lib/md5.c b/lib/md5.c index 358f0744ea32..f842c7ae63b4 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -40,7 +40,7 @@ #ifdef USE_MBEDTLS #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 -#error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or greater required" #endif #include #endif diff --git a/lib/sha256.c b/lib/sha256.c index e2278d366413..89b96017bdc2 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -32,7 +32,7 @@ #ifdef USE_MBEDTLS #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 -#error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or greater required" #endif #include #endif diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 10a83fdb0998..37adba593dd5 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -33,7 +33,7 @@ #include #if GSASL_VERSION_NUMBER < 0x010600 -#error "requires libgsasl 1.6.0+" +#error "libgsasl 1.6.0 or greater required" #endif bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, diff --git a/lib/vdns/asyn-ares.c b/lib/vdns/asyn-ares.c index a98c4de756c5..dfb087599527 100644 --- a/lib/vdns/asyn-ares.c +++ b/lib/vdns/asyn-ares.c @@ -61,12 +61,12 @@ #include #if ARES_VERSION < 0x011000 -#error "requires c-ares 1.16.0 or newer" +#error "c-ares 1.16.0 or greater required" #endif #ifdef USE_HTTPSRR #if ARES_VERSION < 0x011c00 -#error "requires c-ares 1.28.0 or newer for HTTPSRR" +#error "c-ares 1.28.0 or greater required for HTTPSRR" #endif #define HTTPSRR_WORKS #endif diff --git a/lib/vdns/asyn-base.c b/lib/vdns/asyn-base.c index fafc39485065..7a38c0d62e0b 100644 --- a/lib/vdns/asyn-base.c +++ b/lib/vdns/asyn-base.c @@ -71,7 +71,7 @@ timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, #ifdef USE_ARES #if ARES_VERSION < 0x011000 -#error "requires c-ares 1.16.0 or newer" +#error "c-ares 1.16.0 or greater required" #endif /* diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index a299c3c4764c..52bc1399901a 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -220,7 +220,7 @@ struct ssh_conn { #ifdef USE_LIBSSH #if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 9, 0) -#error "SCP/SFTP protocols require libssh 0.9.0 or later" +#error "SCP/SFTP protocols require libssh 0.9.0 or greater" #endif #endif diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index d10c4b27f901..dbee473d7dbb 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -36,7 +36,7 @@ #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 -#error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or greater required" #endif #include #include diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 3c3e6df0e331..429b1a81878a 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -87,11 +87,11 @@ #ifdef LIBRESSL_VERSION_NUMBER /* As of LibreSSL 2.0.0-4.0.0: OPENSSL_VERSION_NUMBER == 0x20000000L */ # if LIBRESSL_VERSION_NUMBER < 0x2090100fL /* 2019-04-13 */ -# error "LibreSSL 2.9.1 or later required" +# error "LibreSSL 2.9.1 or greater required" # endif #elif !defined(HAVE_BORINGSSL_LIKE) # ifndef HAVE_OPENSSL3 /* 2021-09-07 */ -# error "OpenSSL 3.0.0 or later required" +# error "OpenSSL 3.0.0 or greater required" # endif #endif diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 1162c9c2c231..581fa1979bd5 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -122,7 +122,7 @@ /* ALPN requires version 8.1 of the Windows SDK, which was shipped with Visual Studio 2013, aka _MSC_VER 1800: https://learn.microsoft.com/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831771 - Or mingw-w64 9.0 or upper. */ + Or mingw-w64 9.0+ */ #if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \ (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)) #define HAS_ALPN_SCHANNEL @@ -184,7 +184,7 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, break; case CURL_SSLVERSION_TLSv1_3: - /* Windows Server 2022 and newer */ + /* Windows Server 2022 or newer */ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; @@ -502,7 +502,7 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, } /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server - 2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */ + 2012, (NT 6.2). For older versions we use CURL_FIND_ANY. */ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY; @@ -2605,13 +2605,13 @@ static int schannel_init(void) if(p_wine_get_version) { /* WINE detected */ curl_off_t ver = 0; const char *wine_version = p_wine_get_version(); /* e.g. "6.0.2" */ - /* Assume ALPN support with WINE 6.0 or upper */ + /* Assume ALPN support with WINE 6.0 or greater */ if(wine_version) curlx_str_number(&wine_version, &ver, 20); s_win_has_alpn = (ver >= 6); } else { - /* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */ + /* ALPN is supported on Windows 8.1 / Server 2012 R2 or newer. */ s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL); } diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index b215d44e4c4b..2f4421cc9768 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -34,10 +34,10 @@ #include #if LIBWOLFSSL_VERSION_HEX < 0x05000000 /* wolfSSL 5.0.0 (2021-11-01) */ -#error "wolfSSL version should be at least 5.0.0" +#error "wolfSSL 5.0.0 or greater required" #endif #if defined(OPENSSL_COEXIST) && LIBWOLFSSL_VERSION_HEX < 0x05007006 -#error "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL" +#error "wolfSSL 5.7.6 or greater required to coexist with OpenSSL" #endif /* To determine what functions are available we rely on one or both of: diff --git a/m4/curl-amissl.m4 b/m4/curl-amissl.m4 index e32861e9ef12..70239a3ac695 100644 --- a/m4/curl-amissl.m4 +++ b/m4/curl-amissl.m4 @@ -23,7 +23,7 @@ #*************************************************************************** AC_DEFUN([CURL_WITH_AMISSL], [ -AC_MSG_CHECKING([whether to enable Amiga native SSL/TLS (AmiSSL v5)]) +AC_MSG_CHECKING([whether to enable Amiga native SSL/TLS (AmiSSL 5)]) if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then if test "x$OPT_AMISSL" != "xno"; then ssl_msg= @@ -36,7 +36,7 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then (OPENSSL_VERSION_NUMBER >= 0x30000000L) && defined(PROTO_AMISSL_H) return 0; #else - #error not AmiSSL v5 / OpenSSL 3 + #error not AmiSSL 5 / OpenSSL 3 #endif ]]) ],[ diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index d6cb8a67e382..8a1d07b02a4d 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -277,7 +277,7 @@ if test "x$OPT_OPENSSL" != "xno"; then ]) if test "$ssl_msg" = 'OpenSSL'; then - AC_MSG_CHECKING([for OpenSSL >= v3]) + AC_MSG_CHECKING([for OpenSSL >= 3]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include @@ -289,7 +289,7 @@ if test "x$OPT_OPENSSL" != "xno"; then #endif ]]) ],[],[ - AC_MSG_ERROR([OpenSSL 3.0.0 or later required.]) + AC_MSG_ERROR([OpenSSL 3.0.0 or greater required.]) ]) fi fi diff --git a/tests/certs/genserv.pl b/tests/certs/genserv.pl index 3de4071ccf24..0eb6c713f456 100755 --- a/tests/certs/genserv.pl +++ b/tests/certs/genserv.pl @@ -32,7 +32,7 @@ use Symbol 'gensym'; sub opensslfail { - die "Missing or broken 'openssl' tool. openssl 1.0.2+ is required. ". + die "Missing or unsupported 'openssl' tool. openssl 1.0.2+ required. ". "Without it, this script cannot generate the necessary certificates ". "the curl test suite needs for all its TLS related tests."; } From 60870831a825f627545836884466b0179701d23c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 25 Aug 2026 11:25:41 +0200 Subject: [PATCH 0977/1062] build: fold lists to use one source filename per line To reduce merge conflicts. Closes #22666 --- Makefile.am | 9 +- lib/Makefile.am | 16 +- tests/data/Makefile.am | 2393 +++++++++++++++++++++++++++++++----- tests/libtest/Makefile.am | 7 +- tests/libtest/Makefile.inc | 301 ++++- tests/unit/Makefile.inc | 91 +- 6 files changed, 2463 insertions(+), 354 deletions(-) diff --git a/Makefile.am b/Makefile.am index 08828fd4093e..c2f229a6c4f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,7 +64,14 @@ CMAKE_DIST = \ tests/cmake/test.cpp \ tests/cmake/test.sh -EXTRA_DIST = CHANGES.md COPYING RELEASE-NOTES Dockerfile .clang-tidy.yml .editorconfig $(CMAKE_DIST) +EXTRA_DIST = \ + .clang-tidy.yml \ + .editorconfig \ + CHANGES.md \ + COPYING \ + Dockerfile \ + RELEASE-NOTES \ + $(CMAKE_DIST) DISTCLEANFILES = buildinfo.txt diff --git a/lib/Makefile.am b/lib/Makefile.am index b2e64c788e0c..9a3189ce8186 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -26,11 +26,17 @@ AUTOMAKE_OPTIONS = foreign nostdinc # Get CSOURCES, HHEADERS, LIB_RCFILES variables include Makefile.inc -CMAKE_DIST = CMakeLists.txt curl_config-cmake.h.in - -EXTRA_DIST = config-mac.h config-os400.h config-win32.h \ - curl_config.h.in $(LIB_RCFILES) libcurl.def $(CMAKE_DIST) Makefile.soname \ - optiontable.pl +EXTRA_DIST = \ + CMakeLists.txt \ + config-mac.h \ + config-os400.h \ + config-win32.h \ + curl_config-cmake.h.in \ + curl_config.h.in \ + libcurl.def \ + Makefile.soname \ + optiontable.pl \ + $(LIB_RCFILES) lib_LTLIBRARIES = libcurl.la diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f6456d8a4d81..65b0c15b45d0 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -26,282 +26,2119 @@ install: test: # this list is in numerical order -TESTCASES = test1 test2 test3 test4 test5 test6 test7 test8 test9 \ -test10 test11 test12 test13 test14 test15 test16 test17 test18 test19 \ -test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \ -test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 \ -test40 test41 test42 test43 test44 test45 test46 test47 test48 test49 \ -test50 test51 test52 test53 test54 test55 test56 test57 test58 test59 \ -test60 test61 test62 test63 test64 test65 test66 test67 test68 test69 \ -test70 test71 test72 test73 test74 test75 test76 test77 test78 test79 \ -test80 test81 test82 test83 test84 test85 test86 test87 test88 test89 \ -test90 test91 test92 test93 test94 test95 test96 test97 test98 test99 \ -test100 test101 test102 test103 test104 test105 test106 test107 test108 \ -test109 test110 test111 test112 test113 test114 test115 test116 test117 \ -test118 test119 test120 test121 test122 test123 test124 test125 test126 \ -test127 test128 test129 test130 test131 test132 test133 test134 test135 \ -test136 test137 test138 test139 test140 test141 test142 test143 test144 \ -test145 test146 test147 test148 test149 test150 test151 test152 test153 \ -test154 test155 test156 test157 test158 test159 test160 test161 test162 \ -test163 test164 test165 test166 test167 test168 test169 test170 test171 \ -test172 test173 test174 test175 test176 test177 test178 test179 test180 \ -test181 test182 test183 test184 test185 test186 test187 test188 test189 \ -test190 test191 test192 test193 test194 test195 test196 test197 test198 \ -test199 test200 test201 test202 test203 test204 test205 test206 test207 \ -test208 test209 test210 test211 test212 test213 test214 test215 test216 \ -test217 test218 test219 test220 test221 test222 test223 test224 test225 \ -test226 test227 test228 test229 test230 test231 test232 test233 test234 \ -test235 test236 test237 test238 test239 test240 test241 test242 test243 \ -test244 test245 test246 test247 test248 test249 test250 test251 test252 \ -test253 test254 test255 test256 test257 test258 test259 test260 test261 \ -test262 test263 test264 test265 test266 test267 test268 test269 test270 \ -test271 test272 test273 test274 test275 test276 test277 test278 test279 \ -test280 test281 test282 test283 test284 test285 test286 test287 test288 \ -test289 test290 test291 test292 test293 test294 test295 test296 test297 \ -test298 test299 test300 test301 test302 test303 test304 test305 test306 \ -test307 test308 test309 test310 test311 test312 test313 test314 test315 \ -test316 test317 test318 test319 test320 test321 \ -test325 test326 test327 test328 test329 test330 test331 test332 test333 \ -test334 test335 test336 test337 test338 test339 test340 test341 test342 \ -test343 test344 test345 test346 test347 test348 test349 test350 test351 \ -test352 test353 test354 test355 test356 test357 test358 test359 test360 \ -test361 test362 test363 test364 test365 test366 test367 test368 test369 \ -test370 test371 test372 test373 test374 test375 test376 test378 test379 \ -test380 test381 test383 test384 test385 test386 test387 test388 test389 \ -test390 test391 test392 test393 test394 test395 test396 test397 test398 \ -test399 test400 test401 test402 test403 test404 test405 test406 test407 \ -test408 test409 test410 test411 test412 test413 test414 test415 test416 \ -test417 test418 test419 test420 test421 test422 test423 test424 test425 \ -test426 test427 test428 test429 test430 test431 test432 test433 test434 \ -test435 test436 test437 test438 test439 test440 test441 test442 test443 \ -test444 test445 test446 test447 test448 test449 test450 test451 test452 \ -test453 test454 test455 test456 test457 test458 test459 test460 test461 \ -test462 test463 test467 test468 test469 test470 test471 test472 test473 \ -test474 test475 test476 test477 test478 test479 test480 test481 test482 \ -test483 test484 test485 test486 test487 test488 test489 test490 test491 \ -test492 test493 test494 test495 test496 test497 test498 test499 test500 \ -test501 test502 test503 test504 test505 test506 test507 test508 test509 \ -test510 test511 test512 test513 test514 test515 test516 test517 test518 \ -test519 test520 test521 test522 test523 test524 test525 test526 test527 \ -test528 test529 test530 test531 test532 test533 test534 test535 test536 \ -test537 test538 test539 test540 test541 test542 test543 test544 test545 \ -test546 test547 test548 test549 test550 test551 test552 test553 test554 \ -test555 test556 test557 test558 test559 test560 test561 test562 test563 \ -test564 test565 test566 test567 test568 test569 test570 test571 test572 \ -test573 test574 test575 test576 test577 test578 test579 test580 test581 \ -test582 test583 test584 test585 test586 test587 test588 test589 test590 \ -test591 test592 test593 test594 test595 test596 test597 test598 test599 \ -test600 test601 test602 test603 test604 test605 test606 test607 test608 \ -test609 test610 test611 test612 test613 test614 test615 test616 test617 \ -test618 test619 test620 test621 test622 test623 test624 test625 test626 \ -test627 test628 test629 test630 test631 test632 test633 test634 test635 \ -test636 test637 test638 test639 test640 test641 test642 test643 test644 \ -test645 test646 test647 test648 test649 test650 test651 test652 test653 \ -test654 test655 test656 test658 test659 test660 test661 test662 test663 \ -test664 test665 test666 test667 test668 test669 test670 test671 test672 \ -test673 test674 test675 test676 test677 test678 test679 test680 test681 \ -test682 test683 test684 test685 test686 test687 test688 test689 test690 \ -test691 test692 test693 test694 test695 test696 test697 test698 test699 \ -test700 test701 test702 test703 test704 test705 test706 test707 test708 \ -test709 test710 test711 test712 test713 test714 test715 test716 test717 \ -test718 test719 test720 test721 test722 test723 test724 test725 test726 \ -test727 test728 test729 test730 test731 test732 test733 test734 test735 \ -test736 test737 test738 test739 test740 test741 test742 test743 test744 \ -test745 test746 test747 test748 test749 test750 test751 test752 test753 \ -test754 test755 test756 test757 test758 test759 test760 test761 test762 \ -test763 test764 test765 test766 test767 test768 test769 test770 test771 \ -test772 test773 test774 test775 test776 test777 test778 test779 test780 \ -test781 test782 test783 test784 test785 test786 test787 test788 test789 \ -test790 test791 test792 test793 test794 test795 test796 test797 test798 \ -test799 test800 test801 test802 test803 test804 test805 test806 test807 \ -test808 test809 test810 test811 test812 test813 test814 test815 test816 \ -test817 test818 test819 test820 test821 test822 test823 test824 test825 \ -test826 test827 test828 test829 test830 test831 test832 test833 test834 \ -test835 test836 test837 test838 test839 test840 test841 test842 test843 \ -test844 test845 test846 test847 test848 test849 test850 test851 test852 \ -test853 test854 test855 test856 test857 test858 test859 test860 test861 \ -test862 test863 test864 test865 test866 test867 test868 test869 test870 \ -test871 test872 test873 test874 test875 test876 test877 test878 test879 \ -test880 test881 test882 test883 test884 test885 test886 test887 test888 \ -test889 test890 test891 test892 test893 test894 test895 test896 test897 \ -test898 test899 test900 test901 test902 test903 test904 test905 test906 \ -test907 test908 test909 test910 test911 test912 test913 test914 test915 \ -test916 test917 test918 test919 test920 test921 test922 test923 test924 \ -test925 test926 test927 test928 test929 test930 test931 test932 test933 \ -test934 test935 test936 test937 test938 test939 test940 test941 test942 \ -test943 test944 test945 test946 test947 test948 test949 test950 test951 \ -test952 test953 test954 test955 test956 test957 test958 test959 test960 \ -test961 test962 test963 test964 test965 test966 test967 test968 test969 \ -test970 test971 test972 test973 test974 test975 test976 test977 test978 \ -test979 test980 test981 test982 test983 test984 test985 test986 test987 \ -test988 test989 test990 test991 test992 test993 test994 test995 test996 \ -test997 test998 test999 test1000 test1001 test1002 test1003 test1004 \ -test1005 test1006 test1007 test1008 test1009 test1010 test1011 test1012 \ -test1013 test1014 test1015 test1016 test1017 test1018 test1019 test1020 \ -test1021 test1022 test1023 test1024 test1025 test1026 test1027 test1028 \ -test1029 test1030 test1031 test1032 test1033 test1034 test1035 test1036 \ -test1037 test1038 test1039 test1040 test1041 test1042 test1043 test1044 \ -test1045 test1046 test1047 test1048 test1049 test1050 test1051 test1052 \ -test1053 test1054 test1055 test1056 test1057 test1058 test1059 test1060 \ -test1061 test1062 test1063 test1064 test1065 test1066 test1067 test1068 \ -test1069 test1070 test1071 test1072 test1073 test1074 test1075 test1076 \ -test1077 test1078 test1079 test1080 test1081 test1082 test1083 test1084 \ -test1085 test1086 test1087 test1088 test1089 test1090 test1091 test1092 \ -test1093 test1094 test1095 test1096 test1097 test1098 test1099 test1100 \ -test1101 test1102 test1103 test1104 test1105 test1106 test1107 test1108 \ -test1109 test1110 test1111 test1112 test1113 test1114 test1115 test1116 \ -test1117 test1118 test1119 test1120 test1121 test1122 test1123 test1124 \ -test1125 test1126 test1127 test1128 test1129 test1130 test1131 test1132 \ -test1133 test1134 test1135 test1136 test1137 test1138 test1139 test1140 \ -test1141 test1142 test1143 test1144 test1145 test1146 test1147 test1148 \ -test1149 test1150 test1151 test1152 test1153 test1154 test1155 test1156 \ -test1157 test1158 test1159 test1160 test1161 test1162 test1163 test1164 \ -test1165 test1166 test1167 test1168 test1169 test1170 test1171 test1172 \ -test1173 test1174 test1175 test1176 test1177 test1178 test1179 test1180 \ -test1181 test1182 test1183 test1184 test1185 test1186 test1187 test1188 \ -test1189 test1190 test1191 test1192 test1193 test1194 test1195 test1196 \ -test1197 test1198 test1199 test1200 test1201 test1202 test1203 test1204 \ -test1205 test1206 test1207 test1208 test1209 test1210 test1211 test1212 \ -test1213 test1214 test1215 test1216 test1217 test1218 test1219 test1220 \ -test1221 test1222 test1223 test1224 test1225 test1226 test1227 test1228 \ -test1229 test1230 test1231 test1232 test1233 test1234 test1235 test1236 \ -test1237 test1238 test1239 test1240 test1241 test1242 test1243 test1244 \ -test1245 test1246 test1247 test1248 test1249 test1250 test1251 test1252 \ -test1253 test1254 test1255 test1256 test1257 test1258 test1259 test1260 \ -test1261 test1262 test1263 test1264 test1265 test1266 test1267 test1268 \ -test1269 test1270 test1271 test1272 test1273 test1274 test1275 test1276 \ -test1277 test1278 test1279 test1280 test1281 test1282 test1283 test1284 \ -test1285 test1286 test1287 test1288 test1289 test1290 test1291 test1292 \ -test1293 test1294 test1295 test1296 test1297 test1298 test1299 test1300 \ -test1301 test1302 test1303 test1304 test1305 test1306 test1307 test1308 \ -test1309 test1310 test1311 test1312 test1313 test1314 test1315 test1316 \ -test1317 test1318 test1319 test1320 test1321 test1322 test1323 test1324 \ -test1325 test1326 test1327 test1328 test1329 test1330 test1331 test1332 \ -test1333 test1334 test1335 test1336 test1337 test1338 test1339 test1340 \ -test1341 test1342 test1343 test1344 test1345 test1346 test1347 test1348 \ -test1349 test1350 test1351 test1352 test1353 test1354 test1355 test1356 \ -test1357 test1358 test1359 test1360 test1361 test1362 test1363 test1364 \ -test1365 test1366 test1367 test1368 test1369 test1370 test1371 test1372 \ -test1373 test1374 test1375 test1376 test1377 test1378 test1379 test1380 \ -test1381 test1382 test1383 test1384 test1385 test1386 test1387 test1388 \ -test1389 test1390 test1391 test1392 test1393 test1394 test1395 test1396 \ -test1397 test1398 test1399 test1400 test1401 test1402 test1403 test1404 \ -test1405 test1406 test1407 test1408 test1409 test1410 test1411 test1412 \ -test1413 test1414 test1415 test1416 test1417 test1418 test1419 test1420 \ -test1421 test1422 test1423 test1424 test1425 test1426 test1427 test1428 \ -test1429 test1430 test1431 test1432 test1433 test1434 test1435 test1436 \ -test1437 test1438 test1439 test1440 test1441 test1442 test1443 test1444 \ -test1445 test1446 test1447 test1448 test1449 test1450 test1451 test1452 \ -test1453 test1454 test1455 test1456 test1457 test1458 test1459 test1460 \ -test1461 test1462 test1463 test1464 test1465 test1466 test1467 test1468 \ -test1469 test1470 test1471 test1472 test1473 test1474 test1475 test1476 \ -test1477 test1478 test1479 test1480 test1481 test1482 test1483 test1484 \ -test1485 test1486 test1487 test1488 test1489 test1490 test1491 test1492 \ -test1493 test1494 test1495 test1496 test1497 test1498 test1499 test1500 \ -test1501 test1502 test1503 test1504 test1505 test1506 test1507 test1508 \ -test1509 test1510 test1511 test1512 test1513 test1514 test1515 test1516 \ -test1517 test1518 test1519 test1520 test1521 test1522 test1523 test1524 \ -test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \ -test1533 test1534 test1535 test1536 test1537 test1538 test1539 test1540 \ -test1541 test1542 test1543 test1544 test1545 test1546 test1547 test1548 \ -test1549 test1550 test1551 test1552 test1553 test1554 test1555 test1556 \ -test1557 test1558 test1559 test1560 test1561 test1562 test1563 test1564 \ -test1565 test1566 test1567 test1568 test1569 test1570 test1571 test1572 \ -test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 \ -test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 \ -test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ -test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 \ -test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 \ -test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 \ -test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 \ -test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ -test1637 test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ -test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 \ -test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ -test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ -test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ -test1677 test1678 test1680 test1681 test1682 test1683 test1684 \ -test1685 test1686 \ -\ -test1700 test1702 test1703 test1704 test1705 test1706 test1707 \ -test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ -test1720 test1721 test1722 test1723 test1724 test1725 test1740 test1741 \ -\ -test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ -\ -test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \ -test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \ -test1916 test1917 test1918 test1919 test1920 test1921 test1922 \ -\ -test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ -test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ -test1955 test1956 test1957 test1958 test1959 test1960 test1961 \ - test1964 test1965 test1966 test1967 test1970 \ -test1971 test1972 test1973 test1974 test1975 test1976 test1977 test1978 \ -test1979 test1980 test1981 test1982 test1983 test1984 \ -\ -test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ -test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ -\ - test2023 \ -test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ -test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 \ -test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 \ -test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 \ -test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \ -test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ -test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ -test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ -test2088 test2089 test2090 test2091 test2092 test2093 test2094 \ -test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ -test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \ -test2118 test2119 \ -\ -test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ -test2208 \ -\ -test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 test2308 \ -test2309 test2310 test2311 \ -\ -test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ -test2408 test2409 test2410 test2411 test2412 test2413 test2414 \ -\ -test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ -\ -test2600 test2601 test2602 test2603 test2604 test2605 test2606 \ -\ -test2700 test2701 test2702 test2703 test2704 test2705 test2706 test2707 \ -test2708 test2709 test2710 test2711 test2712 test2713 test2714 test2715 \ -test2716 test2717 test2718 test2719 test2720 test2721 test2722 test2723 \ -\ -test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \ -test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \ -test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \ -test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 \ -test3032 test3033 test3034 test3035 test3036 \ -\ -test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ -\ -test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ -test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ -test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ -test3223 test3224 test3225 test3226 test3227 test3228 test3229 \ -\ -test3300 test3301 test3302 test3303 test3304 test3305 test3306 \ -\ -test3400 test3401 \ -\ -test4000 test4001 \ -\ -test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ -test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ -test5016 test5017 test5018 test5019 test5020 test5021 test5022 test5023 \ -test5024 test5025 test5026 test5027 +TESTCASES = \ + test1 \ + test2 \ + test3 \ + test4 \ + test5 \ + test6 \ + test7 \ + test8 \ + test9 \ + test10 \ + test11 \ + test12 \ + test13 \ + test14 \ + test15 \ + test16 \ + test17 \ + test18 \ + test19 \ + test20 \ + test21 \ + test22 \ + test23 \ + test24 \ + test25 \ + test26 \ + test27 \ + test28 \ + test29 \ + test30 \ + test31 \ + test32 \ + test33 \ + test34 \ + test35 \ + test36 \ + test37 \ + test38 \ + test39 \ + test40 \ + test41 \ + test42 \ + test43 \ + test44 \ + test45 \ + test46 \ + test47 \ + test48 \ + test49 \ + test50 \ + test51 \ + test52 \ + test53 \ + test54 \ + test55 \ + test56 \ + test57 \ + test58 \ + test59 \ + test60 \ + test61 \ + test62 \ + test63 \ + test64 \ + test65 \ + test66 \ + test67 \ + test68 \ + test69 \ + test70 \ + test71 \ + test72 \ + test73 \ + test74 \ + test75 \ + test76 \ + test77 \ + test78 \ + test79 \ + test80 \ + test81 \ + test82 \ + test83 \ + test84 \ + test85 \ + test86 \ + test87 \ + test88 \ + test89 \ + test90 \ + test91 \ + test92 \ + test93 \ + test94 \ + test95 \ + test96 \ + test97 \ + test98 \ + test99 \ + test100 \ + test101 \ + test102 \ + test103 \ + test104 \ + test105 \ + test106 \ + test107 \ + test108 \ + test109 \ + test110 \ + test111 \ + test112 \ + test113 \ + test114 \ + test115 \ + test116 \ + test117 \ + test118 \ + test119 \ + test120 \ + test121 \ + test122 \ + test123 \ + test124 \ + test125 \ + test126 \ + test127 \ + test128 \ + test129 \ + test130 \ + test131 \ + test132 \ + test133 \ + test134 \ + test135 \ + test136 \ + test137 \ + test138 \ + test139 \ + test140 \ + test141 \ + test142 \ + test143 \ + test144 \ + test145 \ + test146 \ + test147 \ + test148 \ + test149 \ + test150 \ + test151 \ + test152 \ + test153 \ + test154 \ + test155 \ + test156 \ + test157 \ + test158 \ + test159 \ + test160 \ + test161 \ + test162 \ + test163 \ + test164 \ + test165 \ + test166 \ + test167 \ + test168 \ + test169 \ + test170 \ + test171 \ + test172 \ + test173 \ + test174 \ + test175 \ + test176 \ + test177 \ + test178 \ + test179 \ + test180 \ + test181 \ + test182 \ + test183 \ + test184 \ + test185 \ + test186 \ + test187 \ + test188 \ + test189 \ + test190 \ + test191 \ + test192 \ + test193 \ + test194 \ + test195 \ + test196 \ + test197 \ + test198 \ + test199 \ + test200 \ + test201 \ + test202 \ + test203 \ + test204 \ + test205 \ + test206 \ + test207 \ + test208 \ + test209 \ + test210 \ + test211 \ + test212 \ + test213 \ + test214 \ + test215 \ + test216 \ + test217 \ + test218 \ + test219 \ + test220 \ + test221 \ + test222 \ + test223 \ + test224 \ + test225 \ + test226 \ + test227 \ + test228 \ + test229 \ + test230 \ + test231 \ + test232 \ + test233 \ + test234 \ + test235 \ + test236 \ + test237 \ + test238 \ + test239 \ + test240 \ + test241 \ + test242 \ + test243 \ + test244 \ + test245 \ + test246 \ + test247 \ + test248 \ + test249 \ + test250 \ + test251 \ + test252 \ + test253 \ + test254 \ + test255 \ + test256 \ + test257 \ + test258 \ + test259 \ + test260 \ + test261 \ + test262 \ + test263 \ + test264 \ + test265 \ + test266 \ + test267 \ + test268 \ + test269 \ + test270 \ + test271 \ + test272 \ + test273 \ + test274 \ + test275 \ + test276 \ + test277 \ + test278 \ + test279 \ + test280 \ + test281 \ + test282 \ + test283 \ + test284 \ + test285 \ + test286 \ + test287 \ + test288 \ + test289 \ + test290 \ + test291 \ + test292 \ + test293 \ + test294 \ + test295 \ + test296 \ + test297 \ + test298 \ + test299 \ + test300 \ + test301 \ + test302 \ + test303 \ + test304 \ + test305 \ + test306 \ + test307 \ + test308 \ + test309 \ + test310 \ + test311 \ + test312 \ + test313 \ + test314 \ + test315 \ + test316 \ + test317 \ + test318 \ + test319 \ + test320 \ + test321 \ + test325 \ + test326 \ + test327 \ + test328 \ + test329 \ + test330 \ + test331 \ + test332 \ + test333 \ + test334 \ + test335 \ + test336 \ + test337 \ + test338 \ + test339 \ + test340 \ + test341 \ + test342 \ + test343 \ + test344 \ + test345 \ + test346 \ + test347 \ + test348 \ + test349 \ + test350 \ + test351 \ + test352 \ + test353 \ + test354 \ + test355 \ + test356 \ + test357 \ + test358 \ + test359 \ + test360 \ + test361 \ + test362 \ + test363 \ + test364 \ + test365 \ + test366 \ + test367 \ + test368 \ + test369 \ + test370 \ + test371 \ + test372 \ + test373 \ + test374 \ + test375 \ + test376 \ + test378 \ + test379 \ + test380 \ + test381 \ + test383 \ + test384 \ + test385 \ + test386 \ + test387 \ + test388 \ + test389 \ + test390 \ + test391 \ + test392 \ + test393 \ + test394 \ + test395 \ + test396 \ + test397 \ + test398 \ + test399 \ + test400 \ + test401 \ + test402 \ + test403 \ + test404 \ + test405 \ + test406 \ + test407 \ + test408 \ + test409 \ + test410 \ + test411 \ + test412 \ + test413 \ + test414 \ + test415 \ + test416 \ + test417 \ + test418 \ + test419 \ + test420 \ + test421 \ + test422 \ + test423 \ + test424 \ + test425 \ + test426 \ + test427 \ + test428 \ + test429 \ + test430 \ + test431 \ + test432 \ + test433 \ + test434 \ + test435 \ + test436 \ + test437 \ + test438 \ + test439 \ + test440 \ + test441 \ + test442 \ + test443 \ + test444 \ + test445 \ + test446 \ + test447 \ + test448 \ + test449 \ + test450 \ + test451 \ + test452 \ + test453 \ + test454 \ + test455 \ + test456 \ + test457 \ + test458 \ + test459 \ + test460 \ + test461 \ + test462 \ + test463 \ + test467 \ + test468 \ + test469 \ + test470 \ + test471 \ + test472 \ + test473 \ + test474 \ + test475 \ + test476 \ + test477 \ + test478 \ + test479 \ + test480 \ + test481 \ + test482 \ + test483 \ + test484 \ + test485 \ + test486 \ + test487 \ + test488 \ + test489 \ + test490 \ + test491 \ + test492 \ + test493 \ + test494 \ + test495 \ + test496 \ + test497 \ + test498 \ + test499 \ + test500 \ + test501 \ + test502 \ + test503 \ + test504 \ + test505 \ + test506 \ + test507 \ + test508 \ + test509 \ + test510 \ + test511 \ + test512 \ + test513 \ + test514 \ + test515 \ + test516 \ + test517 \ + test518 \ + test519 \ + test520 \ + test521 \ + test522 \ + test523 \ + test524 \ + test525 \ + test526 \ + test527 \ + test528 \ + test529 \ + test530 \ + test531 \ + test532 \ + test533 \ + test534 \ + test535 \ + test536 \ + test537 \ + test538 \ + test539 \ + test540 \ + test541 \ + test542 \ + test543 \ + test544 \ + test545 \ + test546 \ + test547 \ + test548 \ + test549 \ + test550 \ + test551 \ + test552 \ + test553 \ + test554 \ + test555 \ + test556 \ + test557 \ + test558 \ + test559 \ + test560 \ + test561 \ + test562 \ + test563 \ + test564 \ + test565 \ + test566 \ + test567 \ + test568 \ + test569 \ + test570 \ + test571 \ + test572 \ + test573 \ + test574 \ + test575 \ + test576 \ + test577 \ + test578 \ + test579 \ + test580 \ + test581 \ + test582 \ + test583 \ + test584 \ + test585 \ + test586 \ + test587 \ + test588 \ + test589 \ + test590 \ + test591 \ + test592 \ + test593 \ + test594 \ + test595 \ + test596 \ + test597 \ + test598 \ + test599 \ + test600 \ + test601 \ + test602 \ + test603 \ + test604 \ + test605 \ + test606 \ + test607 \ + test608 \ + test609 \ + test610 \ + test611 \ + test612 \ + test613 \ + test614 \ + test615 \ + test616 \ + test617 \ + test618 \ + test619 \ + test620 \ + test621 \ + test622 \ + test623 \ + test624 \ + test625 \ + test626 \ + test627 \ + test628 \ + test629 \ + test630 \ + test631 \ + test632 \ + test633 \ + test634 \ + test635 \ + test636 \ + test637 \ + test638 \ + test639 \ + test640 \ + test641 \ + test642 \ + test643 \ + test644 \ + test645 \ + test646 \ + test647 \ + test648 \ + test649 \ + test650 \ + test651 \ + test652 \ + test653 \ + test654 \ + test655 \ + test656 \ + test658 \ + test659 \ + test660 \ + test661 \ + test662 \ + test663 \ + test664 \ + test665 \ + test666 \ + test667 \ + test668 \ + test669 \ + test670 \ + test671 \ + test672 \ + test673 \ + test674 \ + test675 \ + test676 \ + test677 \ + test678 \ + test679 \ + test680 \ + test681 \ + test682 \ + test683 \ + test684 \ + test685 \ + test686 \ + test687 \ + test688 \ + test689 \ + test690 \ + test691 \ + test692 \ + test693 \ + test694 \ + test695 \ + test696 \ + test697 \ + test698 \ + test699 \ + test700 \ + test701 \ + test702 \ + test703 \ + test704 \ + test705 \ + test706 \ + test707 \ + test708 \ + test709 \ + test710 \ + test711 \ + test712 \ + test713 \ + test714 \ + test715 \ + test716 \ + test717 \ + test718 \ + test719 \ + test720 \ + test721 \ + test722 \ + test723 \ + test724 \ + test725 \ + test726 \ + test727 \ + test728 \ + test729 \ + test730 \ + test731 \ + test732 \ + test733 \ + test734 \ + test735 \ + test736 \ + test737 \ + test738 \ + test739 \ + test740 \ + test741 \ + test742 \ + test743 \ + test744 \ + test745 \ + test746 \ + test747 \ + test748 \ + test749 \ + test750 \ + test751 \ + test752 \ + test753 \ + test754 \ + test755 \ + test756 \ + test757 \ + test758 \ + test759 \ + test760 \ + test761 \ + test762 \ + test763 \ + test764 \ + test765 \ + test766 \ + test767 \ + test768 \ + test769 \ + test770 \ + test771 \ + test772 \ + test773 \ + test774 \ + test775 \ + test776 \ + test777 \ + test778 \ + test779 \ + test780 \ + test781 \ + test782 \ + test783 \ + test784 \ + test785 \ + test786 \ + test787 \ + test788 \ + test789 \ + test790 \ + test791 \ + test792 \ + test793 \ + test794 \ + test795 \ + test796 \ + test797 \ + test798 \ + test799 \ + test800 \ + test801 \ + test802 \ + test803 \ + test804 \ + test805 \ + test806 \ + test807 \ + test808 \ + test809 \ + test810 \ + test811 \ + test812 \ + test813 \ + test814 \ + test815 \ + test816 \ + test817 \ + test818 \ + test819 \ + test820 \ + test821 \ + test822 \ + test823 \ + test824 \ + test825 \ + test826 \ + test827 \ + test828 \ + test829 \ + test830 \ + test831 \ + test832 \ + test833 \ + test834 \ + test835 \ + test836 \ + test837 \ + test838 \ + test839 \ + test840 \ + test841 \ + test842 \ + test843 \ + test844 \ + test845 \ + test846 \ + test847 \ + test848 \ + test849 \ + test850 \ + test851 \ + test852 \ + test853 \ + test854 \ + test855 \ + test856 \ + test857 \ + test858 \ + test859 \ + test860 \ + test861 \ + test862 \ + test863 \ + test864 \ + test865 \ + test866 \ + test867 \ + test868 \ + test869 \ + test870 \ + test871 \ + test872 \ + test873 \ + test874 \ + test875 \ + test876 \ + test877 \ + test878 \ + test879 \ + test880 \ + test881 \ + test882 \ + test883 \ + test884 \ + test885 \ + test886 \ + test887 \ + test888 \ + test889 \ + test890 \ + test891 \ + test892 \ + test893 \ + test894 \ + test895 \ + test896 \ + test897 \ + test898 \ + test899 \ + test900 \ + test901 \ + test902 \ + test903 \ + test904 \ + test905 \ + test906 \ + test907 \ + test908 \ + test909 \ + test910 \ + test911 \ + test912 \ + test913 \ + test914 \ + test915 \ + test916 \ + test917 \ + test918 \ + test919 \ + test920 \ + test921 \ + test922 \ + test923 \ + test924 \ + test925 \ + test926 \ + test927 \ + test928 \ + test929 \ + test930 \ + test931 \ + test932 \ + test933 \ + test934 \ + test935 \ + test936 \ + test937 \ + test938 \ + test939 \ + test940 \ + test941 \ + test942 \ + test943 \ + test944 \ + test945 \ + test946 \ + test947 \ + test948 \ + test949 \ + test950 \ + test951 \ + test952 \ + test953 \ + test954 \ + test955 \ + test956 \ + test957 \ + test958 \ + test959 \ + test960 \ + test961 \ + test962 \ + test963 \ + test964 \ + test965 \ + test966 \ + test967 \ + test968 \ + test969 \ + test970 \ + test971 \ + test972 \ + test973 \ + test974 \ + test975 \ + test976 \ + test977 \ + test978 \ + test979 \ + test980 \ + test981 \ + test982 \ + test983 \ + test984 \ + test985 \ + test986 \ + test987 \ + test988 \ + test989 \ + test990 \ + test991 \ + test992 \ + test993 \ + test994 \ + test995 \ + test996 \ + test997 \ + test998 \ + test999 \ + test1000 \ + test1001 \ + test1002 \ + test1003 \ + test1004 \ + test1005 \ + test1006 \ + test1007 \ + test1008 \ + test1009 \ + test1010 \ + test1011 \ + test1012 \ + test1013 \ + test1014 \ + test1015 \ + test1016 \ + test1017 \ + test1018 \ + test1019 \ + test1020 \ + test1021 \ + test1022 \ + test1023 \ + test1024 \ + test1025 \ + test1026 \ + test1027 \ + test1028 \ + test1029 \ + test1030 \ + test1031 \ + test1032 \ + test1033 \ + test1034 \ + test1035 \ + test1036 \ + test1037 \ + test1038 \ + test1039 \ + test1040 \ + test1041 \ + test1042 \ + test1043 \ + test1044 \ + test1045 \ + test1046 \ + test1047 \ + test1048 \ + test1049 \ + test1050 \ + test1051 \ + test1052 \ + test1053 \ + test1054 \ + test1055 \ + test1056 \ + test1057 \ + test1058 \ + test1059 \ + test1060 \ + test1061 \ + test1062 \ + test1063 \ + test1064 \ + test1065 \ + test1066 \ + test1067 \ + test1068 \ + test1069 \ + test1070 \ + test1071 \ + test1072 \ + test1073 \ + test1074 \ + test1075 \ + test1076 \ + test1077 \ + test1078 \ + test1079 \ + test1080 \ + test1081 \ + test1082 \ + test1083 \ + test1084 \ + test1085 \ + test1086 \ + test1087 \ + test1088 \ + test1089 \ + test1090 \ + test1091 \ + test1092 \ + test1093 \ + test1094 \ + test1095 \ + test1096 \ + test1097 \ + test1098 \ + test1099 \ + test1100 \ + test1101 \ + test1102 \ + test1103 \ + test1104 \ + test1105 \ + test1106 \ + test1107 \ + test1108 \ + test1109 \ + test1110 \ + test1111 \ + test1112 \ + test1113 \ + test1114 \ + test1115 \ + test1116 \ + test1117 \ + test1118 \ + test1119 \ + test1120 \ + test1121 \ + test1122 \ + test1123 \ + test1124 \ + test1125 \ + test1126 \ + test1127 \ + test1128 \ + test1129 \ + test1130 \ + test1131 \ + test1132 \ + test1133 \ + test1134 \ + test1135 \ + test1136 \ + test1137 \ + test1138 \ + test1139 \ + test1140 \ + test1141 \ + test1142 \ + test1143 \ + test1144 \ + test1145 \ + test1146 \ + test1147 \ + test1148 \ + test1149 \ + test1150 \ + test1151 \ + test1152 \ + test1153 \ + test1154 \ + test1155 \ + test1156 \ + test1157 \ + test1158 \ + test1159 \ + test1160 \ + test1161 \ + test1162 \ + test1163 \ + test1164 \ + test1165 \ + test1166 \ + test1167 \ + test1168 \ + test1169 \ + test1170 \ + test1171 \ + test1172 \ + test1173 \ + test1174 \ + test1175 \ + test1176 \ + test1177 \ + test1178 \ + test1179 \ + test1180 \ + test1181 \ + test1182 \ + test1183 \ + test1184 \ + test1185 \ + test1186 \ + test1187 \ + test1188 \ + test1189 \ + test1190 \ + test1191 \ + test1192 \ + test1193 \ + test1194 \ + test1195 \ + test1196 \ + test1197 \ + test1198 \ + test1199 \ + test1200 \ + test1201 \ + test1202 \ + test1203 \ + test1204 \ + test1205 \ + test1206 \ + test1207 \ + test1208 \ + test1209 \ + test1210 \ + test1211 \ + test1212 \ + test1213 \ + test1214 \ + test1215 \ + test1216 \ + test1217 \ + test1218 \ + test1219 \ + test1220 \ + test1221 \ + test1222 \ + test1223 \ + test1224 \ + test1225 \ + test1226 \ + test1227 \ + test1228 \ + test1229 \ + test1230 \ + test1231 \ + test1232 \ + test1233 \ + test1234 \ + test1235 \ + test1236 \ + test1237 \ + test1238 \ + test1239 \ + test1240 \ + test1241 \ + test1242 \ + test1243 \ + test1244 \ + test1245 \ + test1246 \ + test1247 \ + test1248 \ + test1249 \ + test1250 \ + test1251 \ + test1252 \ + test1253 \ + test1254 \ + test1255 \ + test1256 \ + test1257 \ + test1258 \ + test1259 \ + test1260 \ + test1261 \ + test1262 \ + test1263 \ + test1264 \ + test1265 \ + test1266 \ + test1267 \ + test1268 \ + test1269 \ + test1270 \ + test1271 \ + test1272 \ + test1273 \ + test1274 \ + test1275 \ + test1276 \ + test1277 \ + test1278 \ + test1279 \ + test1280 \ + test1281 \ + test1282 \ + test1283 \ + test1284 \ + test1285 \ + test1286 \ + test1287 \ + test1288 \ + test1289 \ + test1290 \ + test1291 \ + test1292 \ + test1293 \ + test1294 \ + test1295 \ + test1296 \ + test1297 \ + test1298 \ + test1299 \ + test1300 \ + test1301 \ + test1302 \ + test1303 \ + test1304 \ + test1305 \ + test1306 \ + test1307 \ + test1308 \ + test1309 \ + test1310 \ + test1311 \ + test1312 \ + test1313 \ + test1314 \ + test1315 \ + test1316 \ + test1317 \ + test1318 \ + test1319 \ + test1320 \ + test1321 \ + test1322 \ + test1323 \ + test1324 \ + test1325 \ + test1326 \ + test1327 \ + test1328 \ + test1329 \ + test1330 \ + test1331 \ + test1332 \ + test1333 \ + test1334 \ + test1335 \ + test1336 \ + test1337 \ + test1338 \ + test1339 \ + test1340 \ + test1341 \ + test1342 \ + test1343 \ + test1344 \ + test1345 \ + test1346 \ + test1347 \ + test1348 \ + test1349 \ + test1350 \ + test1351 \ + test1352 \ + test1353 \ + test1354 \ + test1355 \ + test1356 \ + test1357 \ + test1358 \ + test1359 \ + test1360 \ + test1361 \ + test1362 \ + test1363 \ + test1364 \ + test1365 \ + test1366 \ + test1367 \ + test1368 \ + test1369 \ + test1370 \ + test1371 \ + test1372 \ + test1373 \ + test1374 \ + test1375 \ + test1376 \ + test1377 \ + test1378 \ + test1379 \ + test1380 \ + test1381 \ + test1382 \ + test1383 \ + test1384 \ + test1385 \ + test1386 \ + test1387 \ + test1388 \ + test1389 \ + test1390 \ + test1391 \ + test1392 \ + test1393 \ + test1394 \ + test1395 \ + test1396 \ + test1397 \ + test1398 \ + test1399 \ + test1400 \ + test1401 \ + test1402 \ + test1403 \ + test1404 \ + test1405 \ + test1406 \ + test1407 \ + test1408 \ + test1409 \ + test1410 \ + test1411 \ + test1412 \ + test1413 \ + test1414 \ + test1415 \ + test1416 \ + test1417 \ + test1418 \ + test1419 \ + test1420 \ + test1421 \ + test1422 \ + test1423 \ + test1424 \ + test1425 \ + test1426 \ + test1427 \ + test1428 \ + test1429 \ + test1430 \ + test1431 \ + test1432 \ + test1433 \ + test1434 \ + test1435 \ + test1436 \ + test1437 \ + test1438 \ + test1439 \ + test1440 \ + test1441 \ + test1442 \ + test1443 \ + test1444 \ + test1445 \ + test1446 \ + test1447 \ + test1448 \ + test1449 \ + test1450 \ + test1451 \ + test1452 \ + test1453 \ + test1454 \ + test1455 \ + test1456 \ + test1457 \ + test1458 \ + test1459 \ + test1460 \ + test1461 \ + test1462 \ + test1463 \ + test1464 \ + test1465 \ + test1466 \ + test1467 \ + test1468 \ + test1469 \ + test1470 \ + test1471 \ + test1472 \ + test1473 \ + test1474 \ + test1475 \ + test1476 \ + test1477 \ + test1478 \ + test1479 \ + test1480 \ + test1481 \ + test1482 \ + test1483 \ + test1484 \ + test1485 \ + test1486 \ + test1487 \ + test1488 \ + test1489 \ + test1490 \ + test1491 \ + test1492 \ + test1493 \ + test1494 \ + test1495 \ + test1496 \ + test1497 \ + test1498 \ + test1499 \ + test1500 \ + test1501 \ + test1502 \ + test1503 \ + test1504 \ + test1505 \ + test1506 \ + test1507 \ + test1508 \ + test1509 \ + test1510 \ + test1511 \ + test1512 \ + test1513 \ + test1514 \ + test1515 \ + test1516 \ + test1517 \ + test1518 \ + test1519 \ + test1520 \ + test1521 \ + test1522 \ + test1523 \ + test1524 \ + test1525 \ + test1526 \ + test1527 \ + test1528 \ + test1529 \ + test1530 \ + test1531 \ + test1532 \ + test1533 \ + test1534 \ + test1535 \ + test1536 \ + test1537 \ + test1538 \ + test1539 \ + test1540 \ + test1541 \ + test1542 \ + test1543 \ + test1544 \ + test1545 \ + test1546 \ + test1547 \ + test1548 \ + test1549 \ + test1550 \ + test1551 \ + test1552 \ + test1553 \ + test1554 \ + test1555 \ + test1556 \ + test1557 \ + test1558 \ + test1559 \ + test1560 \ + test1561 \ + test1562 \ + test1563 \ + test1564 \ + test1565 \ + test1566 \ + test1567 \ + test1568 \ + test1569 \ + test1570 \ + test1571 \ + test1572 \ + test1573 \ + test1574 \ + test1575 \ + test1576 \ + test1577 \ + test1578 \ + test1579 \ + test1580 \ + test1581 \ + test1582 \ + test1583 \ + test1584 \ + test1585 \ + test1586 \ + test1587 \ + test1588 \ + test1589 \ + test1590 \ + test1591 \ + test1592 \ + test1593 \ + test1594 \ + test1595 \ + test1596 \ + test1597 \ + test1598 \ + test1599 \ + test1600 \ + test1601 \ + test1602 \ + test1603 \ + test1604 \ + test1605 \ + test1606 \ + test1607 \ + test1608 \ + test1609 \ + test1610 \ + test1611 \ + test1612 \ + test1613 \ + test1614 \ + test1615 \ + test1616 \ + test1617 \ + test1618 \ + test1619 \ + test1620 \ + test1621 \ + test1622 \ + test1623 \ + test1624 \ + test1625 \ + test1626 \ + test1627 \ + test1628 \ + test1629 \ + test1630 \ + test1631 \ + test1632 \ + test1633 \ + test1634 \ + test1635 \ + test1636 \ + test1637 \ + test1638 \ + test1639 \ + test1640 \ + test1641 \ + test1642 \ + test1643 \ + test1644 \ + test1645 \ + test1646 \ + test1647 \ + test1648 \ + test1649 \ + test1650 \ + test1651 \ + test1652 \ + test1653 \ + test1654 \ + test1655 \ + test1656 \ + test1657 \ + test1658 \ + test1659 \ + test1660 \ + test1661 \ + test1662 \ + test1663 \ + test1664 \ + test1665 \ + test1666 \ + test1667 \ + test1668 \ + test1669 \ + test1670 \ + test1671 \ + test1672 \ + test1673 \ + test1674 \ + test1675 \ + test1676 \ + test1677 \ + test1678 \ + test1680 \ + test1681 \ + test1682 \ + test1683 \ + test1684 \ + test1685 \ + test1686 \ + \ + test1700 \ + test1702 \ + test1703 \ + test1704 \ + test1705 \ + test1706 \ + test1707 \ + test1708 \ + test1709 \ + test1710 \ + test1711 \ + test1712 \ + test1713 \ + test1714 \ + test1715 \ + test1720 \ + test1721 \ + test1722 \ + test1723 \ + test1724 \ + test1725 \ + test1740 \ + test1741 \ + \ + test1800 \ + test1801 \ + test1802 \ + test1847 \ + test1848 \ + test1849 \ + test1850 \ + test1851 \ + \ + test1900 \ + test1901 \ + test1902 \ + test1903 \ + test1904 \ + test1905 \ + test1906 \ + test1907 \ + test1908 \ + test1909 \ + test1910 \ + test1911 \ + test1912 \ + test1913 \ + test1914 \ + test1915 \ + test1916 \ + test1917 \ + test1918 \ + test1919 \ + test1920 \ + test1921 \ + test1922 \ + \ + test1933 \ + test1934 \ + test1935 \ + test1936 \ + test1937 \ + test1938 \ + test1939 \ + test1940 \ + test1941 \ + test1942 \ + test1943 \ + test1944 \ + test1945 \ + test1946 \ + test1947 \ + test1948 \ + test1955 \ + test1956 \ + test1957 \ + test1958 \ + test1959 \ + test1960 \ + test1961 \ + \ + test1964 \ + test1965 \ + test1966 \ + test1967 \ + test1970 \ + test1971 \ + test1972 \ + test1973 \ + test1974 \ + test1975 \ + test1976 \ + test1977 \ + test1978 \ + test1979 \ + test1980 \ + test1981 \ + test1982 \ + test1983 \ + test1984 \ + \ + test2000 \ + test2001 \ + test2002 \ + test2003 \ + test2004 \ + test2005 \ + test2006 \ + test2007 \ + test2008 \ + test2009 \ + test2010 \ + test2011 \ + test2012 \ + test2013 \ + test2014 \ + test2015 \ + \ + test2023 \ + test2024 \ + test2025 \ + test2026 \ + test2027 \ + test2028 \ + test2029 \ + test2030 \ + test2031 \ + test2032 \ + test2033 \ + test2034 \ + test2035 \ + test2036 \ + test2037 \ + test2038 \ + test2039 \ + test2040 \ + test2041 \ + test2042 \ + test2043 \ + test2044 \ + test2045 \ + test2046 \ + test2047 \ + test2048 \ + test2049 \ + test2050 \ + test2051 \ + test2052 \ + test2053 \ + test2054 \ + test2055 \ + test2056 \ + test2057 \ + test2058 \ + test2059 \ + test2060 \ + test2061 \ + test2062 \ + test2063 \ + test2064 \ + test2065 \ + test2066 \ + test2067 \ + test2068 \ + test2069 \ + test2070 \ + test2071 \ + test2072 \ + test2073 \ + test2074 \ + test2075 \ + test2076 \ + test2077 \ + test2078 \ + test2079 \ + test2080 \ + test2081 \ + test2082 \ + test2083 \ + test2084 \ + test2085 \ + test2086 \ + test2087 \ + test2088 \ + test2089 \ + test2090 \ + test2091 \ + test2092 \ + test2093 \ + test2094 \ + test2100 \ + test2101 \ + test2102 \ + test2103 \ + test2104 \ + test2105 \ + test2106 \ + test2107 \ + test2108 \ + test2109 \ + test2110 \ + test2113 \ + test2114 \ + test2115 \ + test2116 \ + test2117 \ + test2118 \ + test2119 \ + \ + test2200 \ + test2201 \ + test2202 \ + test2203 \ + test2204 \ + test2205 \ + test2206 \ + test2207 \ + test2208 \ + \ + test2300 \ + test2301 \ + test2302 \ + test2303 \ + test2304 \ + test2305 \ + test2306 \ + test2307 \ + test2308 \ + test2309 \ + test2310 \ + test2311 \ + \ + test2400 \ + test2401 \ + test2402 \ + test2403 \ + test2404 \ + test2405 \ + test2406 \ + test2407 \ + test2408 \ + test2409 \ + test2410 \ + test2411 \ + test2412 \ + test2413 \ + test2414 \ + \ + test2500 \ + test2501 \ + test2502 \ + test2503 \ + test2504 \ + test2505 \ + test2506 \ + \ + test2600 \ + test2601 \ + test2602 \ + test2603 \ + test2604 \ + test2605 \ + test2606 \ + \ + test2700 \ + test2701 \ + test2702 \ + test2703 \ + test2704 \ + test2705 \ + test2706 \ + test2707 \ + test2708 \ + test2709 \ + test2710 \ + test2711 \ + test2712 \ + test2713 \ + test2714 \ + test2715 \ + test2716 \ + test2717 \ + test2718 \ + test2719 \ + test2720 \ + test2721 \ + test2722 \ + test2723 \ + \ + test3000 \ + test3001 \ + test3002 \ + test3003 \ + test3004 \ + test3005 \ + test3006 \ + test3007 \ + test3008 \ + test3009 \ + test3010 \ + test3011 \ + test3012 \ + test3013 \ + test3014 \ + test3015 \ + test3016 \ + test3017 \ + test3018 \ + test3019 \ + test3020 \ + test3021 \ + test3022 \ + test3023 \ + test3024 \ + test3025 \ + test3026 \ + test3027 \ + test3028 \ + test3029 \ + test3030 \ + test3031 \ + test3032 \ + test3033 \ + test3034 \ + test3035 \ + test3036 \ + \ + test3100 \ + test3101 \ + test3102 \ + test3103 \ + test3104 \ + test3105 \ + test3106 \ + \ + test3200 \ + test3201 \ + test3202 \ + test3203 \ + test3204 \ + test3205 \ + test3206 \ + test3207 \ + test3208 \ + test3209 \ + test3210 \ + test3211 \ + test3212 \ + test3213 \ + test3214 \ + test3215 \ + test3216 \ + test3217 \ + test3218 \ + test3219 \ + test3220 \ + test3221 \ + test3222 \ + test3223 \ + test3224 \ + test3225 \ + test3226 \ + test3227 \ + test3228 \ + test3229 \ + \ + test3300 \ + test3301 \ + test3302 \ + test3303 \ + test3304 \ + test3305 \ + test3306 \ + \ + test3400 \ + test3401 \ + \ + test4000 \ + test4001 \ + \ + test5000 \ + test5001 \ + test5002 \ + test5003 \ + test5004 \ + test5005 \ + test5006 \ + test5007 \ + test5008 \ + test5009 \ + test5010 \ + test5011 \ + test5012 \ + test5013 \ + test5014 \ + test5015 \ + test5016 \ + test5017 \ + test5018 \ + test5019 \ + test5020 \ + test5021 \ + test5022 \ + test5023 \ + test5024 \ + test5025 \ + test5026 \ + test5027 -EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ -data1461.txt data1463.txt \ -data1400.c data1401.c data1402.c data1403.c data1404.c data1405.c data1406.c \ -data1407.c data1420.c data1465.c data1481.c \ -data1705-1.md data1705-2.md data1705-3.md data1705-4.md data1705-stdout.1 \ -data1706-1.md data1706-2.md data1706-3.md data1706-4.md data1706-stdout.txt \ -data-httpsig-ed25519.key data-httpsig-hmac-sha256.key +EXTRA_DIST = \ + $(TESTCASES) \ + data-httpsig-ed25519.key \ + data-httpsig-hmac-sha256.key \ + data-xml1 \ + data1400.c \ + data1401.c \ + data1402.c \ + data1403.c \ + data1404.c \ + data1405.c \ + data1406.c \ + data1407.c \ + data1420.c \ + data1461.txt \ + data1463.txt \ + data1465.c \ + data1481.c \ + data1705-1.md \ + data1705-2.md \ + data1705-3.md \ + data1705-4.md \ + data1705-stdout.1 \ + data1706-1.md \ + data1706-2.md \ + data1706-3.md \ + data1706-4.md \ + data1706-stdout.txt \ + DISABLED diff --git a/tests/libtest/Makefile.am b/tests/libtest/Makefile.am index db7c9d741b3e..1fb110fb7906 100644 --- a/tests/libtest/Makefile.am +++ b/tests/libtest/Makefile.am @@ -44,7 +44,12 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ include Makefile.inc EXTRA_DIST = CMakeLists.txt $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C) \ - test307.pl test610.pl test613.pl test1013.pl test1022.pl mk-lib1521.pl + mk-lib1521.pl \ + test1013.pl \ + test1022.pl \ + test307.pl \ + test610.pl \ + test613.pl CFLAGS += @CURL_CFLAG_EXTRAS@ diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 1554f1b4d22c..deafec9f0d60 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -68,63 +68,260 @@ TESTS_C = \ cli_ws_pingpong.c \ cli_ws_write_err.c \ \ - lib500.c lib501.c lib502.c lib503.c lib504.c lib505.c lib506.c lib507.c \ - lib508.c lib509.c lib510.c lib511.c lib512.c lib513.c lib514.c lib515.c \ - lib516.c lib517.c lib518.c lib519.c lib520.c lib521.c lib523.c lib524.c \ - lib525.c lib526.c lib530.c \ - lib533.c lib536.c lib537.c lib539.c lib540.c lib541.c \ - lib542.c lib543.c lib544.c lib547.c lib549.c \ - lib552.c lib553.c lib554.c lib555.c lib556.c lib557.c lib558.c lib559.c \ - lib560.c lib562.c lib564.c lib566.c lib567.c \ - lib568.c lib569.c lib570.c lib571.c lib572.c lib573.c lib574.c lib575.c \ - lib576.c lib578.c lib579.c lib582.c lib583.c \ - lib586.c lib589.c lib590.c lib591.c \ - lib597.c lib598.c lib599.c \ + lib500.c \ + lib501.c \ + lib502.c \ + lib503.c \ + lib504.c \ + lib505.c \ + lib506.c \ + lib507.c \ + lib508.c \ + lib509.c \ + lib510.c \ + lib511.c \ + lib512.c \ + lib513.c \ + lib514.c \ + lib515.c \ + lib516.c \ + lib517.c \ + lib518.c \ + lib519.c \ + lib520.c \ + lib521.c \ + lib523.c \ + lib524.c \ + lib525.c \ + lib526.c \ + lib530.c \ + lib533.c \ + lib536.c \ + lib537.c \ + lib539.c \ + lib540.c \ + lib541.c \ + lib542.c \ + lib543.c \ + lib544.c \ + lib547.c \ + lib549.c \ + lib552.c \ + lib553.c \ + lib554.c \ + lib555.c \ + lib556.c \ + lib557.c \ + lib558.c \ + lib559.c \ + lib560.c \ + lib562.c \ + lib564.c \ + lib566.c \ + lib567.c \ + lib568.c \ + lib569.c \ + lib570.c \ + lib571.c \ + lib572.c \ + lib573.c \ + lib574.c \ + lib575.c \ + lib576.c \ + lib578.c \ + lib579.c \ + lib582.c \ + lib583.c \ + lib586.c \ + lib589.c \ + lib590.c \ + lib591.c \ + lib597.c \ + lib598.c \ + lib599.c \ lib643.c \ - lib650.c lib651.c lib652.c lib653.c lib654.c lib655.c lib658.c lib659.c \ - lib661.c lib666.c lib667.c lib668.c \ - lib670.c lib674.c lib676.c lib677.c lib678.c \ - lib694.c lib695.c \ - lib751.c lib753.c lib758.c \ + lib650.c \ + lib651.c \ + lib652.c \ + lib653.c \ + lib654.c \ + lib655.c \ + lib658.c \ + lib659.c \ + lib661.c \ + lib666.c \ + lib667.c \ + lib668.c \ + lib670.c \ + lib674.c \ + lib676.c \ + lib677.c \ + lib678.c \ + lib694.c \ + lib695.c \ + lib751.c \ + lib753.c \ lib757.c \ + lib758.c \ lib766.c \ lib1156.c \ - lib1301.c lib1308.c \ - lib1396.c lib1398.c \ + lib1301.c \ + lib1308.c \ + lib1396.c \ + lib1398.c \ lib1485.c \ - lib1500.c lib1501.c lib1502.c lib1506.c \ - lib1507.c lib1508.c lib1509.c lib1510.c lib1511.c lib1512.c lib1513.c \ - lib1514.c lib1515.c lib1517.c lib1518.c lib1520.c \ - lib1522.c lib1523.c lib1525.c lib1526.c lib1527.c lib1528.c \ - lib1529.c lib1530.c lib1531.c lib1532.c lib1533.c lib1534.c lib1535.c \ - lib1536.c lib1537.c lib1538.c lib1540.c lib1541.c lib1542.c \ - lib1545.c lib1549.c lib1550.c lib1551.c \ - lib1552.c lib1553.c lib1554.c lib1555.c lib1556.c lib1557.c lib1558.c \ - lib1559.c lib1560.c lib1564.c lib1565.c \ - lib1567.c lib1568.c lib1569.c lib1571.c \ - lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ - lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ - lib1598.c lib1599.c \ - lib1647.c lib1648.c lib1649.c \ - lib1662.c lib1678.c \ + lib1500.c \ + lib1501.c \ + lib1502.c \ + lib1506.c \ + lib1507.c \ + lib1508.c \ + lib1509.c \ + lib1510.c \ + lib1511.c \ + lib1512.c \ + lib1513.c \ + lib1514.c \ + lib1515.c \ + lib1517.c \ + lib1518.c \ + lib1520.c \ + lib1522.c \ + lib1523.c \ + lib1525.c \ + lib1526.c \ + lib1527.c \ + lib1528.c \ + lib1529.c \ + lib1530.c \ + lib1531.c \ + lib1532.c \ + lib1533.c \ + lib1534.c \ + lib1535.c \ + lib1536.c \ + lib1537.c \ + lib1538.c \ + lib1540.c \ + lib1541.c \ + lib1542.c \ + lib1545.c \ + lib1549.c \ + lib1550.c \ + lib1551.c \ + lib1552.c \ + lib1553.c \ + lib1554.c \ + lib1555.c \ + lib1556.c \ + lib1557.c \ + lib1558.c \ + lib1559.c \ + lib1560.c \ + lib1564.c \ + lib1565.c \ + lib1567.c \ + lib1568.c \ + lib1569.c \ + lib1571.c \ + lib1576.c \ + lib1582.c \ + lib1587.c \ + lib1588.c \ + lib1589.c \ + lib1591.c \ + lib1592.c \ + lib1593.c \ + lib1594.c \ + lib1597.c \ + lib1598.c \ + lib1599.c \ + lib1647.c \ + lib1648.c \ + lib1649.c \ + lib1662.c \ + lib1678.c \ lib1686.c \ - lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ - lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ - lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ + lib1900.c \ + lib1901.c \ + lib1902.c \ + lib1903.c \ + lib1905.c \ + lib1906.c \ + lib1907.c \ + lib1908.c \ + lib1910.c \ + lib1911.c \ + lib1912.c \ + lib1913.c \ + lib1915.c \ + lib1916.c \ + lib1918.c \ + lib1919.c \ + lib1920.c \ + lib1921.c \ lib1922.c \ - lib1933.c lib1934.c lib1935.c lib1936.c lib1937.c lib1938.c lib1939.c \ - lib1940.c lib1945.c \ - lib1947.c lib1948.c \ - lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \ - lib1964.c lib1965.c lib1967.c lib1970.c \ - lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \ - lib2023.c lib2032.c lib2082.c lib2118.c \ - lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ - lib2402.c lib2404.c lib2405.c \ - lib2412.c lib2414.c \ - lib2502.c lib2504.c lib2505.c lib2506.c \ + lib1933.c \ + lib1934.c \ + lib1935.c \ + lib1936.c \ + lib1937.c \ + lib1938.c \ + lib1939.c \ + lib1940.c \ + lib1945.c \ + lib1947.c \ + lib1948.c \ + lib1955.c \ + lib1956.c \ + lib1957.c \ + lib1958.c \ + lib1959.c \ + lib1960.c \ + lib1964.c \ + lib1965.c \ + lib1967.c \ + lib1970.c \ + lib1971.c \ + lib1972.c \ + lib1973.c \ + lib1974.c \ + lib1975.c \ + lib1977.c \ + lib1978.c \ + lib2023.c \ + lib2032.c \ + lib2082.c \ + lib2118.c \ + lib2301.c \ + lib2302.c \ + lib2304.c \ + lib2306.c \ + lib2308.c \ + lib2309.c \ + lib2402.c \ + lib2404.c \ + lib2405.c \ + lib2412.c \ + lib2414.c \ + lib2502.c \ + lib2504.c \ + lib2505.c \ + lib2506.c \ lib2700.c \ - lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ - lib3100.c lib3101.c lib3102.c lib3103.c lib3104.c lib3105.c \ - lib3207.c lib3208.c \ - lib5000.c lib5004.c + lib3010.c \ + lib3025.c \ + lib3026.c \ + lib3027.c \ + lib3033.c \ + lib3034.c \ + lib3100.c \ + lib3101.c \ + lib3102.c \ + lib3103.c \ + lib3104.c \ + lib3105.c \ + lib3207.c \ + lib3208.c \ + lib5000.c \ + lib5004.c diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index a2bfa273d8c5..e0effbcb9af2 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -30,25 +30,82 @@ FIRST_C = ../libtest/first.c # All unit test programs TESTS_C = \ - unit1300.c unit1302.c unit1303.c unit1304.c unit1305.c \ - unit1307.c unit1309.c \ - unit1323.c unit1330.c \ - unit1395.c unit1397.c unit1399.c \ - unit1600.c unit1601.c unit1602.c unit1603.c unit1605.c unit1606.c \ - unit1607.c unit1608.c unit1609.c unit1610.c unit1611.c unit1612.c unit1614.c \ - unit1615.c unit1616.c unit1620.c \ - unit1625.c unit1626.c unit1627.c \ + unit1300.c \ + unit1302.c \ + unit1303.c \ + unit1304.c \ + unit1305.c \ + unit1307.c \ + unit1309.c \ + unit1323.c \ + unit1330.c \ + unit1395.c \ + unit1397.c \ + unit1399.c \ + unit1600.c \ + unit1601.c \ + unit1602.c \ + unit1603.c \ + unit1605.c \ + unit1606.c \ + unit1607.c \ + unit1608.c \ + unit1609.c \ + unit1610.c \ + unit1611.c \ + unit1612.c \ + unit1614.c \ + unit1615.c \ + unit1616.c \ + unit1620.c \ + unit1625.c \ + unit1626.c \ + unit1627.c \ unit1636.c \ - unit1650.c unit1651.c unit1652.c unit1653.c unit1654.c unit1655.c unit1656.c \ - unit1657.c unit1658.c unit1660.c unit1661.c unit1663.c unit1664.c \ - unit1666.c unit1667.c unit1668.c unit1669.c \ - unit1674.c unit1675.c unit1676.c \ - unit1961.c unit1979.c unit1980.c \ + unit1650.c \ + unit1651.c \ + unit1652.c \ + unit1653.c \ + unit1654.c \ + unit1655.c \ + unit1656.c \ + unit1657.c \ + unit1658.c \ + unit1660.c \ + unit1661.c \ + unit1663.c \ + unit1664.c \ + unit1666.c \ + unit1667.c \ + unit1668.c \ + unit1669.c \ + unit1674.c \ + unit1675.c \ + unit1676.c \ + unit1961.c \ + unit1979.c \ + unit1980.c \ unit2413.c \ - unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ + unit2600.c \ + unit2601.c \ + unit2602.c \ + unit2603.c \ + unit2604.c \ + unit2605.c \ unit2606.c \ - unit3200.c unit3205.c \ - unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \ + unit3200.c \ + unit3205.c \ + unit3211.c \ + unit3212.c \ + unit3213.c \ + unit3214.c \ + unit3216.c \ + unit3219.c \ unit3227.c \ - unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c unit3306.c \ + unit3300.c \ + unit3301.c \ + unit3302.c \ + unit3303.c \ + unit3304.c \ + unit3306.c \ unit3400.c From c3c70440e8d9938c0bb732595751e8aebc614f24 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 26 Aug 2026 09:41:03 +0200 Subject: [PATCH 0978/1062] setopt: make NULL `CURLOPT_AWS_SIGV4` disable aws-sigv4 auth Disable `CURLAUTH_AWS_SIGV4` when `CURLOPT_AWS_SIGV4` is set to NULL. Syncing behavior with `CURLAUTH_HTTPSIG`. Reported-by: Stanislav Fort Closes #22681 --- lib/setopt.c | 2 ++ tests/data/test1978 | 19 +++++++++++++++++++ tests/libtest/lib1978.c | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/lib/setopt.c b/lib/setopt.c index fe991a145f61..86897973f77f 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2069,6 +2069,8 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, */ if(CURL_EASY_STR(data, STRING_AWS_SIGV4)) s->httpauth = CURLAUTH_AWS_SIGV4; + else + s->httpauth &= ~(uint32_t)CURLAUTH_AWS_SIGV4; break; #endif #ifndef CURL_DISABLE_HTTPSIG diff --git a/tests/data/test1978 b/tests/data/test1978 index b2a4285b96ae..4113900fca53 100644 --- a/tests/data/test1978 +++ b/tests/data/test1978 @@ -70,6 +70,25 @@ header-no-value: header-some-no-value: header-some-no-value: value +PUT /%TESTNUMBER/testapi/test HTTP/1.1 +Host: 127.0.0.1:9000 +x-amz-meta-test: test2 +some-other-header: value +x-amz-meta-test: test1 +duplicate-header: duplicate +x-amz-meta-test: test3 +X-amz-meta-test2: test2 +x-amz-meta-blah: blah +x-Amz-meta-test2: test1 +x-amz-Meta-test2: test3 +curr-header-no-colon: value +next-header-no-colon: value +duplicate-header: duplicate +header-no-value: +header-no-value: +header-some-no-value: +header-some-no-value: value + diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index e98ce3ca813f..0f51cc47bb75 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -90,6 +90,11 @@ static CURLcode test_lib1978(const char *URL) easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); + if(result) + goto test_cleanup; + + easy_setopt(curl, CURLOPT_AWS_SIGV4, NULL); + result = curl_easy_perform(curl); test_cleanup: From 4080ace060477c8a6b4d6a20f937a3c960c2f25c Mon Sep 17 00:00:00 2001 From: Arham Wani Date: Sun, 23 Aug 2026 16:46:21 +0200 Subject: [PATCH 0979/1062] hsts: only match the exact strings Fixes #22643 Closes #22677 --- lib/hsts.c | 60 ++++++++++++++++++++++--------------------- tests/unit/unit1660.c | 17 ++++++++++++ 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/lib/hsts.c b/lib/hsts.c index b8745a5195e4..73c01db01892 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -223,53 +223,55 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, return CURLE_OK; do { - curlx_str_passblanks(&p); - if(curl_strnequal("max-age", p, 7)) { - bool quoted = FALSE; - int rc; - - if(gotma) - return CURLE_BAD_FUNCTION_ARGUMENT; + struct Curl_str word; + struct Curl_str val = { 0 }; + int rc; + bool assign = FALSE; - p += 7; + do { curlx_str_passblanks(&p); - if(curlx_str_single(&p, '=')) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(*p == ';') + p++; + else + break; + } while(1); + if(curlx_str_cspn(&p, &word, ";=\r\n \t")) + break; + + curlx_str_passblanks(&p); + if(!curlx_str_single(&p, '=')) { + assign = TRUE; curlx_str_passblanks(&p); - if(!curlx_str_single(&p, '\"')) - quoted = TRUE; + if(*p == '\"') { + if(curlx_str_quotedword(&p, &val, MAX_HSTS_LINE)) + break; + } + else { + if(curlx_str_cspn(&p, &val, ", ;\r\n")) + break; + } + } - rc = curlx_str_number(&p, &expires, TIME_T_MAX); + if(assign && curlx_str_casecompare(&word, "max-age")) { + const char *vp = curlx_str(&val); + if(gotma) + return CURLE_BAD_FUNCTION_ARGUMENT; + rc = curlx_str_number(&vp, &expires, TIME_T_MAX); if(rc == STRE_OVERFLOW) expires = CURL_OFF_T_MAX; else if(rc) /* invalid max-age */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(quoted) { - if(*p != '\"') - return CURLE_BAD_FUNCTION_ARGUMENT; - p++; - } gotma = TRUE; } - else if(curl_strnequal("includesubdomains", p, 17)) { + else if(curlx_str_casecompare(&word, "includesubdomains")) { if(gotinc) return CURLE_BAD_FUNCTION_ARGUMENT; subdomains = TRUE; - p += 17; gotinc = TRUE; } - else { - /* unknown directive, do a lame attempt to skip */ - while(*p && (*p != ';')) - p++; - } - - curlx_str_passblanks(&p); - if(*p == ';') - p++; } while(*p); if(!gotma) diff --git a/tests/unit/unit1660.c b/tests/unit/unit1660.c index f2d72705d5d4..2e62b313a836 100644 --- a/tests/unit/unit1660.c +++ b/tests/unit/unit1660.c @@ -142,6 +142,23 @@ static CURLcode test_unit1660(const char *arg) showsts(e, chost); } + result = Curl_hsts_parse(h, "prefix.example", + "max-age=60; includeSubDomainsExtra"); + fail_if(result, "failed to parse unknown HSTS directive"); + e = hsts_check(h, "child.prefix.example", + strlen("child.prefix.example"), TRUE); + fail_if(e, "unknown directive enabled subdomain matching"); + result = Curl_hsts_parse(h, "prefix.example", "max-age=0"); + fail_if(result, "failed to remove HSTS test entry"); + + result = Curl_hsts_parse(h, "unknown.example", + "max-age-extra=1; max-age=60"); + fail_if(result, "failed to ignore unknown HSTS directive"); + e = hsts_check(h, "unknown.example", strlen("unknown.example"), FALSE); + fail_if(!e, "recognized directive after unknown directive was ignored"); + result = Curl_hsts_parse(h, "unknown.example", "max-age=0"); + fail_if(result, "failed to remove HSTS test entry"); + curl_mprintf("Number of entries: %zu\n", Curl_llist_count(&h->list)); /* verify that it is exists for 7 seconds */ From 6017e6ffedff391c5e8ec6f0d3a2ca85b5fda26a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 12:03:11 +0200 Subject: [PATCH 0980/1062] tool_paramhlp: --proto only supports one modifier 1 - clarify the documentation 2 - make curl exit with an error for unknown protocols to make users better notice when they get it wrong Verified in test 322 Closes #22687 --- docs/cmdline-opts/proto.md | 2 +- src/tool_paramhlp.c | 3 ++- tests/data/Makefile.am | 1 + tests/data/test322 | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/data/test322 diff --git a/docs/cmdline-opts/proto.md b/docs/cmdline-opts/proto.md index cf288d456575..85d814a9b23d 100644 --- a/docs/cmdline-opts/proto.md +++ b/docs/cmdline-opts/proto.md @@ -18,7 +18,7 @@ Example: Limit what protocols to allow for transfers. Protocols are evaluated left to right, are comma separated, and are each a protocol name or 'all', optionally -prefixed by zero or more modifiers. Available modifiers are: +prefixed by a modifier. Available modifiers are: ## + Permit this protocol in addition to protocols already permitted (this is diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c index a3be2afcf736..e425b36411b4 100644 --- a/src/tool_paramhlp.c +++ b/src/tool_paramhlp.c @@ -482,7 +482,8 @@ ParameterError proto2num(const char * const *val, char **ostr, const char *str) if no protocols are allowed */ if(action == set) protoset[0] = NULL; - warnf("unrecognized protocol '%s'", buffer); + errorf("unrecognized protocol '%s'", buffer); + return PARAM_BAD_USE; } } if(next) diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 65b0c15b45d0..91d44dffe74d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -348,6 +348,7 @@ TESTCASES = \ test319 \ test320 \ test321 \ + test322 \ test325 \ test326 \ test327 \ diff --git a/tests/data/test322 b/tests/data/test322 new file mode 100644 index 000000000000..587bebe4d1c3 --- /dev/null +++ b/tests/data/test322 @@ -0,0 +1,31 @@ + + + + +--proto + + + + + +http + + +--proto with multiple modifiers + + +http://localhost/wont-get-this --proto +-http + + + + + +2 + + +curl: unrecognized protocol '-http' +curl: option --proto: is badly used here +curl: try 'curl --help' or 'curl --manual' for more information + + + From 71cabc8f7cb3953ce97845a2ce11202e11983841 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 14:28:14 +0200 Subject: [PATCH 0981/1062] scripts/testnum: provide a few available test numbers Closes #22691 --- scripts/Makefile.am | 3 +- scripts/testnum | 68 ++++++++++++++++++++++++++++++++++++++++++ tests/data/Makefile.am | 3 ++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100755 scripts/testnum diff --git a/scripts/Makefile.am b/scripts/Makefile.am index c490c04fe5df..dc3353f03953 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -27,7 +27,8 @@ EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ cdall cd2cd managen dmaketgz maketgz release-tools.sh verify-release \ cmakelint.sh cmakeopts.sh mdlinkcheck CMakeLists.txt perlcheck.sh \ pythonlint.sh spacecheck.pl randdisable wcurl top-complexity \ - extract-unit-protos .checksrc badwords badwords-all badwords.txt top-length + extract-unit-protos .checksrc badwords badwords-all badwords.txt top-length \ + testnum dist_bin_SCRIPTS = wcurl diff --git a/scripts/testnum b/scripts/testnum new file mode 100755 index 000000000000..3050381b114a --- /dev/null +++ b/scripts/testnum @@ -0,0 +1,68 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +use strict; +use warnings; + +my $num = 10; # provide (at least) this many proposals + +my @all; +my $expect = 0; + +open(F, ") { + if(/^TESTCASES/) { + $expect = 1; + } + elsif($expect) { + if(/^ test(\d+)/) { + my $f = $1; + if($f == $expect) { + $expect = $f + 1; + } + else { + if(($f - 1) != $expect) { + # a range + for my $t ($expect .. ($f - 1)) { + push @all, $t; + } + } + else { + # single value + push @all, $expect; + } + } + $expect = $f + 1; + } + } +} + +use List::Util qw(shuffle); + +for my $t (shuffle @all) { + print "test$t\n"; + if(!--$num) { + last; + } +} diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 91d44dffe74d..271518af441a 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -25,6 +25,9 @@ all: install: test: +# The helper script ./scripts/testnum provides a set of available test +# numbers. + # this list is in numerical order TESTCASES = \ test1 \ From f8df560ed9faa2ff3d6198b50f55eaf0315fcb39 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 14:46:46 +0200 Subject: [PATCH 0982/1062] test322: fix for disabled-manual builds Follow-up to 6017e6ffedff391c5e8ec6f0d3a2ca Closes #22693 --- tests/data/test322 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/data/test322 b/tests/data/test322 index 587bebe4d1c3..3f2ec9126144 100644 --- a/tests/data/test322 +++ b/tests/data/test322 @@ -25,7 +25,11 @@ http://localhost/wont-get-this --proto +-http curl: unrecognized protocol '-http' curl: option --proto: is badly used here +%if manual curl: try 'curl --help' or 'curl --manual' for more information +%else +curl: try 'curl --help' for more information +%endif From ed0338befd1d865a8ea1fbaa90013a096dedd07a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 26 Aug 2026 13:46:13 +0200 Subject: [PATCH 0983/1062] wolfssl: do not run Curl_wssl_setup_x509_store() twice The flag preventing a rerun was not set when a cached_store was used. CURL_UNCONST an ech string for my old wolfssl version Closes #22689 --- lib/vtls/wolfssl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 2f4421cc9768..4b9facadf01e 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -595,9 +595,6 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, bool imported_native_ca = FALSE; bool imported_ca_info_blob = FALSE; - /* We do not want to do this again, no matter the outcome */ - wssl->x509_store_setup = TRUE; - #ifndef NO_FILESYSTEM /* load native CA certificates */ if(conn_config->native_ca_store) { @@ -800,6 +797,9 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, WOLFSSL_X509_STORE *cached_store; bool cache_criteria_met; + /* We do not want to do this again, no matter the outcome */ + wssl->x509_store_setup = TRUE; + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, or no source is provided and we are falling back to wolfSSL's built-in default. */ @@ -1273,7 +1273,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, word32 b64len = 0; b64len = (word32)strlen(b64val); - if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, + if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, CURL_UNCONST(b64val), b64len) != WOLFSSL_SUCCESS) { if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; From f30d9ef341de9199bca97f3a3956676931d20e55 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 26 Aug 2026 11:07:43 +0200 Subject: [PATCH 0984/1062] http digest: tie peer/credentials on input Tie the peer/origin already on input handling and flush stale data when it changed. Verified by test 1985 Reported-by: Igor Morgenstern Closes #22683 --- lib/http_digest.c | 82 +++++++++++++++++++++++++------------- tests/data/Makefile.am | 1 + tests/data/test1985 | 79 ++++++++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib1985.c | 77 +++++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 tests/data/test1985 create mode 100644 tests/libtest/lib1985.c diff --git a/lib/http_digest.c b/lib/http_digest.c index 90350a1d1e8d..1ec29bd391a1 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -26,11 +26,41 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) #include "urldata.h" +#include "curl_trc.h" #include "strcase.h" #include "vauth/vauth.h" #include "http_digest.h" #include "curlx/strparse.h" +/* Flush the Digest state if it was created for a different origin or with + different credentials than the ones now in use, then link the current + ones. */ +static void digest_flush_stale(struct Curl_easy *data, + struct digestdata *digest, + struct Curl_peer *peer, + struct Curl_creds *creds) +{ + bool flush = FALSE; + if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) { + CURL_TRC_M(data, "http_digest, reset on peer change to %s:%u", + peer->hostname, peer->port); + flush = TRUE; + } + else if(digest->creds && !Curl_creds_same(creds, digest->creds)) { + CURL_TRC_M(data, "http_digest, reset on creds change to %s", + creds ? creds->user : "-"); + flush = TRUE; + } + + if(flush) { + /* flush Digest state */ + Curl_auth_digest_cleanup(digest); + } + + Curl_peer_link(&digest->origin, peer); + Curl_creds_link(&digest->creds, creds); +} + /* Test example headers: WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" @@ -43,42 +73,37 @@ CURLcode Curl_input_digest(struct Curl_easy *data, { /* Point to the correct struct with this */ struct digestdata *digest; + struct Curl_peer *origin = NULL; + CURLcode result; if(proxy) { digest = &data->state.proxydigest; +#ifdef CURL_DISABLE_PROXY + Curl_auth_digest_cleanup(digest); + return CURLE_OK; /* just ignore such a header without proxy support */ +#else + origin = data->conn->http_proxy.peer; +#endif } else { digest = &data->state.digest; + origin = data->state.origin; } - if(!checkprefix("Digest", header) || !ISBLANK(header[6])) + if(!checkprefix("Digest", header) || !ISBLANK(header[6])) { + Curl_auth_digest_cleanup(digest); return CURLE_AUTH_ERROR; + } header += CURL_CSTRLEN("Digest"); curlx_str_passblanks(&header); - return Curl_auth_decode_digest_http_message(header, digest); -} - -/* Flush the Digest state if it was created for a different origin or with - different credentials than the ones now in use, then link the current - ones. */ -static void digest_flush_stale(struct digestdata *digest, - struct Curl_peer *peer, - struct Curl_creds *creds) -{ - bool flush = FALSE; - if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) - flush = TRUE; - else if(digest->creds && !Curl_creds_same(creds, digest->creds)) - flush = TRUE; - - if(flush) - /* flush Digest state */ - Curl_auth_digest_cleanup(digest); - - Curl_peer_link(&digest->origin, peer); - Curl_creds_link(&digest->creds, creds); + /* This resets the digest struct before decoding */ + result = Curl_auth_decode_digest_http_message(header, digest); + /* Remember only the peer, the data we take in has no relation to creds + * at this time. We can use it even if creds change. */ + Curl_peer_link(&digest->origin, origin); + return result; } CURLcode Curl_output_digest(struct Curl_easy *data, @@ -86,6 +111,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, const unsigned char *request, const unsigned char *uripath) { + struct Curl_peer *origin = NULL; CURLcode result; char *response; size_t len; @@ -107,22 +133,22 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - digest_flush_stale(digest, data->conn->http_proxy.peer, - data->conn->http_proxy.creds); - allocuserpwd = &data->req.hd_proxy_auth; + origin = data->conn->http_proxy.peer; creds = data->conn->http_proxy.creds; + allocuserpwd = &data->req.hd_proxy_auth; authp = &data->state.authproxy; #endif } else { DEBUGASSERT(data->state.origin); digest = &data->state.digest; - digest_flush_stale(digest, data->state.origin, data->state.creds); - allocuserpwd = &data->req.hd_auth; + origin = data->state.origin; creds = data->state.creds; + allocuserpwd = &data->req.hd_auth; authp = &data->state.authhost; } + digest_flush_stale(data, digest, origin, creds); curlx_safefree(*allocuserpwd); #ifdef USE_WINDOWS_SSPI diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 271518af441a..02458d193e36 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1808,6 +1808,7 @@ TESTCASES = \ test1982 \ test1983 \ test1984 \ + test1985 \ \ test2000 \ test2001 \ diff --git a/tests/data/test1985 b/tests/data/test1985 new file mode 100644 index 000000000000..78be4df0ea51 --- /dev/null +++ b/tests/data/test1985 @@ -0,0 +1,79 @@ + + + + +Digest + + + +# Server-side + + +HTTP/1.1 401 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/html +Content-Length: 0 +WWW-Authenticate: Digest realm="host-a-realm", nonce="host-a-nonce-0123456789", algorithm=MD5, qop="auth" + + + +HTTP/1.1 401 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/html +Content-Length: 0 +WWW-Authenticate: Digest realm="host-b-realm", nonce="2" + + + + +# Client-side + + +http + + +Digest response when not asked, then use Digest + + +!SSPI +crypto +digest + + +lib%TESTNUMBER + + + +http://firsthost:%HTTPPORT/%TESTNUMBER http://secondhost:%HTTPPORT/%TESTNUMBER0001 %HTTPPORT %HOSTIP + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: firsthost:%HTTPPORT +Accept: */* + +GET /%TESTNUMBER0001 HTTP/1.1 +Host: secondhost:%HTTPPORT +Accept: */* + +GET /19850001 HTTP/1.1 +Host: secondhost:%HTTPPORT +Authorization: Digest username="alice", realm="host-b-realm", nonce="2", uri="/19850001", response="b3186bc857ad5bb63947c39476d37290" +Accept: */* + +GET /19850001 HTTP/1.1 +Host: secondhost:%HTTPPORT +Authorization: Digest username="alice", realm="host-b-realm", nonce="2", uri="/19850001", response="b3186bc857ad5bb63947c39476d37290" +Accept: */* + + + +52 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index deafec9f0d60..70c6cafbf5f5 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -289,6 +289,7 @@ TESTS_C = \ lib1975.c \ lib1977.c \ lib1978.c \ + lib1985.c \ lib2023.c \ lib2032.c \ lib2082.c \ diff --git a/tests/libtest/lib1985.c b/tests/libtest/lib1985.c new file mode 100644 index 000000000000..3790023a1e92 --- /dev/null +++ b/tests/libtest/lib1985.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +/* URL1, URL2, port, address */ + +static CURLcode test_lib1985(const char *URL) +{ + char host1[80]; + char host2[80]; + CURL *curl = NULL; + struct curl_slist *slist = NULL; + struct curl_slist *slist2 = NULL; + CURLcode r1 = CURLE_OK, r2 = CURLE_OK; + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl_msnprintf(host1, sizeof(host1), "firsthost:%s:%s", + libtest_arg3, libtest_arg4); + curl_msnprintf(host2, sizeof(host2), "secondhost:%s:%s", + libtest_arg3, libtest_arg4); + slist = curl_slist_append(slist, host1); + if(!slist) + goto error; + slist2 = curl_slist_append(slist, host2); + if(!slist2) + goto error; + slist = slist2; + + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_RESOLVE, slist); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + + curl_easy_setopt(curl, CURLOPT_USERPWD, + "alice:correct-horse-battery-staple"); + curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, "unused-token"); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, + (long)(CURLAUTH_BASIC | CURLAUTH_BEARER)); + curl_easy_setopt(curl, CURLOPT_URL, URL); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + r1 = curl_easy_perform(curl); + curl_mprintf("STEP1_CODE=%d\n", (int)r1); + + /* Step 2: reuse handle for HostB with Digest stale state leaks */ + curl_easy_setopt(curl, CURLOPT_URL, libtest_arg2); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST); + r2 = curl_easy_perform(curl); + curl_mprintf("STEP2_CODE=%d\n", (int)r2); + } + +error: + curl_easy_cleanup(curl); + curl_slist_free_all(slist); + curl_global_cleanup(); + return r2; +} From c878bb36f838d054adc0be27a219409842bb8365 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 26 Aug 2026 15:15:14 +0200 Subject: [PATCH 0985/1062] openssl: aws-lc ocsp workaround SSL_get_tlsext_status_ocsp_resp() is documented to return -1 on a missing OCSP stapling from the server, however AWS-LC is breaking that by returning 0. Since it does not semantically make a difference if the data is reported missing or has 0 length, treat 0 also as a missing stapling. Reported-by: Stanislav Fort Closes #22694 --- lib/vtls/openssl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 429b1a81878a..f21778dda029 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4755,8 +4755,9 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, ocsp_len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &ocsp_data); /* SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP - response data or -1 if there is no OCSP response data. */ - if(ocsp_len < 0) { + response data or -1 if there is no OCSP response data. + AWS-LC breaks the API and returns 0 when there is no data. */ + if(ocsp_len <= 0) { ocsp_len = 0; /* no data available */ ocsp_missing = TRUE; } From 0b5759caa299e2f86abc8b140c56e16fd03cc744 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 22:54:00 +0200 Subject: [PATCH 0986/1062] CURLOPT_SOCKOPTFUNCTION.md: ALREADY_CONNECTED does not work for HTTP/3 Reported-by: Stanislav Fort Closes #22696 --- docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md b/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md index 2080aa9483c5..abc8d65f0cc2 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md @@ -73,6 +73,9 @@ then libcurl does no attempt to connect. This allows an application to pass in an already connected socket with CURLOPT_OPENSOCKETFUNCTION(3) and then have this function make libcurl not attempt to connect (again). +The *CURL_SOCKOPT_ALREADY_CONNECTED* feature does not work for HTTP/3 (QUIC) +connections. + # DEFAULT NULL From 72766ecf8ab41a3384f8e1e1dc8b00c554322b78 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 25 Aug 2026 16:24:01 +0200 Subject: [PATCH 0987/1062] connection reuse: age check If a transfer has CURLOPT_MAXLIFETIME_CONN set, verify the connection age of a matched connection before reuse, independent if it is currently in use (for multiplexed connections). Closes #22671 --- lib/url.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/url.c b/lib/url.c index 16e14aaac0af..8e8fb39acbcc 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1095,6 +1095,16 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_multiplex_limits(conn, m)) return FALSE; + if(m->data->set.conn_max_age_ms > 0) { + timediff_t age_ms = curlx_ptimediff_ms(&m->now, &conn->created); + if(age_ms > m->data->set.conn_max_age_ms) { + /* Transfer is looking for a younger connection. */ + if(!CONN_INUSE(conn)) + Curl_conn_close(m->data, conn, FALSE); + return FALSE; + } + } + /* If we are going to pick an idle connection, do an extra * health check before we reuse it. */ if(!CONN_INUSE(conn) && From caff91bf3c8ca6cd0cbe7cfa6c1a155a94a2a0f3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 07:51:34 +0200 Subject: [PATCH 0988/1062] ngtcp2: avoid NULL deref in cf_ngtcp2_send In disable-verbose builds Pointed out by Codex Security Closes #22698 --- lib/vquic/cf-ngtcp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c index a80c4cd97a34..e3bca8774e40 100644 --- a/lib/vquic/cf-ngtcp2.c +++ b/lib/vquic/cf-ngtcp2.c @@ -832,7 +832,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "failed to open stream -> %d", (int)result); goto out; } - VERBOSE(stream = H3_STREAM_CTX(ctx, data)); + stream = H3_STREAM_CTX(ctx, data); } else if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); From 621e507300e6c83966567b6ae0352ca82544df13 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 09:16:11 +0200 Subject: [PATCH 0989/1062] libcurl.md: emphasize that the output needs checking Closes #22700 --- docs/cmdline-opts/libcurl.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/cmdline-opts/libcurl.md b/docs/cmdline-opts/libcurl.md index e37e5aa0fadd..7f1b64a0656b 100644 --- a/docs/cmdline-opts/libcurl.md +++ b/docs/cmdline-opts/libcurl.md @@ -19,3 +19,6 @@ Example: Append this option to any ordinary curl command line, and you get libcurl-using C source code written to the file that does the equivalent of what your command-line operation does. + +The source code output should be considered example code and is not production +ready. You must double-check that the code actually does what you want it do. From 4f6aa41a0145e930e766775dbe860883d350aa0a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 08:33:30 +0200 Subject: [PATCH 0990/1062] cookie: improve TAB handling For entries with a leading tab. Verified in test 2885. Reported-by: Stanislav Fort Closes #22699 --- lib/cookie.c | 2 +- tests/data/Makefile.am | 2 +- tests/data/test2885 | 52 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/data/test2885 diff --git a/lib/cookie.c b/lib/cookie.c index 8c72f6596657..bf3374d04d56 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -611,7 +611,7 @@ parse_cookie_header(struct Curl_easy *data, struct Curl_str name; /* we have a = pair or a stand-alone word here */ - if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) { + if(!curlx_str_cspn(&ptr, &name, ";\r\n=")) { struct Curl_str val; bool sep = FALSE; curlx_str_trimblanks(&name); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 02458d193e36..86ae6bc9c10a 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1997,7 +1997,7 @@ TESTCASES = \ test2721 \ test2722 \ test2723 \ - \ + test2885 \ test3000 \ test3001 \ test3002 \ diff --git a/tests/data/test2885 b/tests/data/test2885 new file mode 100644 index 000000000000..456224fdc51b --- /dev/null +++ b/tests/data/test2885 @@ -0,0 +1,52 @@ + + + + +HTTP +cookies + + + + + +HTTP/1.1 200 OK +Content-Length: 0 +Set-Cookie: thisis=SECRET;%TABSecure +Set-Cookie: also=notab; Secure + + + + + + +cookies + + +https + + +Cookies with TAB before 'secure' + + +https://cookie.example:%HTTPSPORT/%TESTNUMBER -c %LOGDIR/cookies.txt --resolve cookie.example:%HTTPSPORT:%HOSTIP --insecure + + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: cookie.example:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +cookie.example%TABFALSE%TAB/%TABTRUE%TAB0%TABalso%TABnotab +cookie.example%TABFALSE%TAB/%TABTRUE%TAB0%TABthisis%TABSECRET + + + From b73a4f00b2a6748d6f36d737cc3869c9a2e96e1e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 09:33:27 +0200 Subject: [PATCH 0991/1062] Makefile.am: improve etags The Makefile generated with autotools offers a 'tags' target that invokes etags to build a symbol database in 'TAGS'. For some reason it never indexes the symbols in the public include files and this is a somewhat crude way to make it to so. The downside with this take is that it repeats the header files names in the root Makefile. I still consider it valuable. The file set changes very rarely and this tags target is not critical. Closes #22680 --- Makefile.am | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Makefile.am b/Makefile.am index c2f229a6c4f0..430c424e8031 100644 --- a/Makefile.am +++ b/Makefile.am @@ -77,6 +77,24 @@ DISTCLEANFILES = buildinfo.txt bin_SCRIPTS = curl-config +CURL_ETAGS_FILES = \ + $(srcdir)/include/curl/curl.h \ + $(srcdir)/include/curl/curlver.h \ + $(srcdir)/include/curl/easy.h \ + $(srcdir)/include/curl/mprintf.h \ + $(srcdir)/include/curl/stdcheaders.h \ + $(srcdir)/include/curl/multi.h \ + $(srcdir)/include/curl/typecheck-gcc.h \ + $(srcdir)/include/curl/system.h \ + $(srcdir)/include/curl/urlapi.h \ + $(srcdir)/include/curl/options.h \ + $(srcdir)/include/curl/header.h \ + $(srcdir)/include/curl/websockets.h + +ETAGS_ARGS = $(CURL_ETAGS_FILES) + +TAGS_DEPENDENCIES = $(CURL_ETAGS_FILES) + SUBDIRS = lib docs src scripts DIST_SUBDIRS = $(SUBDIRS) tests projects include docs From 3b547de301342b347fb3c6ea6ae7979070793a66 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 27 Aug 2026 09:47:53 +0200 Subject: [PATCH 0992/1062] imap: APPEND CRLF fix Deny an APPEND operation with CRLF conversion requested as we are unable to predict the final upload size and APPEND requires us to announce it before starting the upload. The check for unknown sizes had already been there, but the crlf conversion that renders a known size unknown was not accounted for. Closes #22702 --- lib/imap.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/imap.c b/lib/imap.c index d854a2e8ea75..62ab0a283bd1 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -886,7 +886,6 @@ static CURLcode imap_perform_append(struct Curl_easy *data, result = Curl_creader_set_mime(data, postp); if(result) return result; - data->state.infilesize = Curl_creader_client_length(data); } else #endif @@ -896,9 +895,15 @@ static CURLcode imap_perform_append(struct Curl_easy *data, return result; } - /* Check we know the size of the upload */ + /* Check we know the size of the upload. This takes all readers + * into account. Especially crlf conversions which make the size + * unpredictable, e.g. -1. */ + data->state.infilesize = Curl_creader_total_length(data); if(data->state.infilesize < 0) { - failf(data, "Cannot APPEND with unknown input file size"); + if(data->set.crlf) + failf(data, "Cannot APPEND with CRLF conversion making size unknown"); + else + failf(data, "Cannot APPEND with unknown input file size"); return CURLE_UPLOAD_FAILED; } From d013a19fe21902ec86ec6f52519ed81d596db32f Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 27 Aug 2026 11:34:36 +0200 Subject: [PATCH 0993/1062] multi: xfer table initial size and growth Fine tune the multi's transfer table initial size and growth increments: - change initial capacity from 512 to 128 - lower the amount of unused entries to trigger growth from a 4th to an 8th of the capacity - grow capacity by 50% inside limits of [128, 2048] This lets 100 transfers run without need to regrowth and performs less reallocations for larger parallelism. On 64-bit architectures, the 2048 growth limits grows the table by 16KB which seems a good chunk and prevents over-large growth. Closes #22705 --- lib/multi.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 429651430aa8..35fb334db347 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -49,7 +49,7 @@ #include "bufref.h" /* initial multi->xfers table size for a full multi */ -#define CURL_XFER_TABLE_SIZE 512 +#define CURL_XFER_TABLE_SIZE 128 /* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every @@ -423,27 +423,20 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do * not have to reuse the `mid` from a removed easy right away. - * Since uint_tbl and uint_bset are memory efficient, - * regard less than 25% free as insufficient. - * (for low capacities, e.g. multi_easy, 4 or less). */ + * Check if an 8th of the capacity is still free */ uint32_t used = Curl_uint32_tbl_count(&multi->xfers); uint32_t unused = capacity - used; - uint32_t min_unused = CURLMAX(capacity >> 2, 4); - if(unused <= min_unused) { + uint32_t min_unused = CURLMAX(capacity >> 3, 4); + if(unused < min_unused) { + /* Grow by 50% of current capacity, in range of [128, 2048], + * which means the table grows max by 16kb on 64-bit arch. */ + uint32_t growth = CURLMIN(CURLMAX(capacity >> 1, 128), 2048); /* Make sure the uint arithmetic here works on the corner * cases where we are close to max_capacity or UINT_MAX */ - if((min_unused >= max_capacity) || - ((max_capacity - min_unused) <= capacity) || - ((UINT_MAX - min_unused - 63) <= capacity)) { - new_size = max_capacity; /* can not be larger than this */ - } - else { - /* make it a 64 multiple, since our bitsets grow by that and - * small (easy_multi) grows to at least 64 on first resize. */ - new_size = (((used + min_unused) + 63) / 64) * 64; - if(new_size < 256) /* don't be too shy about it */ - new_size = 256; - } + if((max_capacity - growth) <= capacity) + new_size = max_capacity; + else + new_size = capacity + growth; } } From d02476899f22f851729b6c95290bc73a22a3383d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 13:40:57 +0200 Subject: [PATCH 0994/1062] setopt: allow setting a referer from CURLINFO_REFERER Reported-by: Stanislav Fort Verified by test 2397 Closes #22709 --- lib/setopt.c | 8 +++-- tests/data/Makefile.am | 2 +- tests/data/test2397 | 55 ++++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib2397.c | 64 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 tests/data/test2397 create mode 100644 tests/libtest/lib2397.c diff --git a/lib/setopt.c b/lib/setopt.c index 86897973f77f..ed5d04d46f81 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2084,13 +2084,17 @@ static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, result = Curl_setstropt(data, STRING_HTTPSIG_HEADERS, ptr); break; #endif - case CURLOPT_REFERER: + case CURLOPT_REFERER: { /* * String to set in the HTTP Referer: field. */ - Curl_bufref_free(&data->state.referer); + struct bufref *oldref = &data->state.referer; + /* free the old after the storing the new in case the input is actually + pointing back to this */ result = Curl_setstropt(data, STRING_SET_REFERER, ptr); + Curl_bufref_free(oldref); break; + } case CURLOPT_USERAGENT: /* diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 86ae6bc9c10a..caa2e9648c1b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1940,7 +1940,7 @@ TESTCASES = \ test2309 \ test2310 \ test2311 \ - \ + test2397 \ test2400 \ test2401 \ test2402 \ diff --git a/tests/data/test2397 b/tests/data/test2397 new file mode 100644 index 000000000000..1a0996febb38 --- /dev/null +++ b/tests/data/test2397 @@ -0,0 +1,55 @@ + + + + +CURLINFO_REFERER +CURLOPT_REFERER + + + + + +HTTP/1.1 301 redir away +Content-Length: 0 +Location: /%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Content-Length: 4 + +hej + + + + + +http + + + +lib%TESTNUMBER + + + +Get CURLINFO_REFERER set CURLOPT_REFERER + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Referer: http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 70c6cafbf5f5..26c72e5672ec 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -300,6 +300,7 @@ TESTS_C = \ lib2306.c \ lib2308.c \ lib2309.c \ + lib2397.c \ lib2402.c \ lib2404.c \ lib2405.c \ diff --git a/tests/libtest/lib2397.c b/tests/libtest/lib2397.c new file mode 100644 index 000000000000..e30fa235f644 --- /dev/null +++ b/tests/libtest/lib2397.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static CURLcode test_lib2397(const char *URL) +{ + CURLcode result; + CURL *curl; + char *referer = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + curl_easy_setopt(curl, CURLOPT_URL, URL); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + + result = curl_easy_perform(curl); + if(result) + curl_mfprintf(stderr, "transfer failed: %s\n", curl_easy_strerror(result)); + else + result = curl_easy_getinfo(curl, CURLINFO_REFERER, &referer); + + if(!referer) + curl_mfprintf(stderr, "CURLINFO_REFERER was not populated\n"); + else + result = curl_easy_setopt(curl, CURLOPT_REFERER, referer); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return result; +} From 28471c849c58cb77ea3fc032a8ca09d191d3fd63 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Aug 2026 23:13:13 +0200 Subject: [PATCH 0995/1062] CODE_STYLE.md: describe how we write typecasts Closes #22697 --- docs/internals/CODE_STYLE.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index bbf23f944b3e..95e532f35aee 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -208,6 +208,17 @@ complement = ~bits; empty = (!*string) ? TRUE : FALSE; ``` +## No space following typecasts + +As far as possible, we write code to avoid typecasts. When we do use them, We +write typecasts "glued" to the following expression, with no space after the +closing parenthesis: + +```c +int value = (int)foobar; +char *ptr = (char *)random_func(); +``` + ## No parentheses for return values We use the 'return' statement without extra parentheses around the value: From 4433f71d6c189b58b2288efc976786e216994e66 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 26 Aug 2026 14:19:03 +0200 Subject: [PATCH 0996/1062] macos sectrust: fail ocsp verify when not builtin When compiling macOS SecTrust with a compiler that does not support __builtin_available(), OCSP verfication is not available. This means it is not Apple's clang. Instead of ignoring this, fail the handshake when ocsp status is requested. Closes #22690 --- lib/vtls/apple.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index 0535ca00c600..ce7f0c5e1cd2 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -200,8 +200,9 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, goto out; } -#if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) if(ocsp_len > 0) { + bool checked = FALSE; +#if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { CFDataRef ocspdata = CFDataCreate(NULL, ocsp_buf, (CFIndex)ocsp_len); @@ -213,12 +214,16 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; goto out; } + checked = TRUE; } - } -#else - (void)ocsp_buf; - (void)ocsp_len; #endif + if(!checked) { + (void)ocsp_buf; + failf(data, "Apple SecTrust: OCSP verification not supported"); + result = CURLE_NOT_BUILT_IN; + goto out; + } + } #ifdef SUPPORTS_SecTrustEvaluateWithError #ifdef HAVE_BUILTIN_AVAILABLE From dc6adf3cb7e9b166600419baedef1cb7e7328ac6 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 27 Aug 2026 13:18:38 +0200 Subject: [PATCH 0997/1062] proxy: CONNECT trailers handling Make sure trailers from a CONNECT response are marked as such, so filtering them on CURLOPT_SUPPRESS_CONNECT_HEADERS works correctly. Add test2349 and new DBG env var to verify. Closes #22708 --- docs/libcurl/libcurl-env-dbg.md | 4 +++ lib/cf-h1-proxy.c | 4 +-- lib/http_chunks.c | 19 +++++----- lib/http_chunks.h | 3 +- lib/sendf.c | 4 +++ tests/data/Makefile.am | 3 ++ tests/data/test2349 | 63 +++++++++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 tests/data/test2349 diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index a039cebc5cd2..7845f5c0f5d5 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -220,3 +220,7 @@ answer to arrive before starting any connect attempt. When passing `--ssl-reqd`, clear it for the first URL in a curl command. This allows testing of connection reuse in mixed `STARTTLS` needs. + +## `CURL_DBG_SUPPRESS_CONNECT_HDS` + +Existence of this variable suppresses the collection of CONNECT headers. diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index b42524958864..49b157bb3ad8 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -135,7 +135,7 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf, curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); - Curl_httpchunk_init(data, &ts->ch, TRUE); + Curl_httpchunk_init(data, &ts->ch, TRUE, TRUE); *pts = ts; return tunnel_reinit(cf, data, ts); @@ -980,7 +980,7 @@ CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, ts->httpversion = httpversion; curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); - Curl_httpchunk_init(data, &ts->ch, TRUE); + Curl_httpchunk_init(data, &ts->ch, TRUE, TRUE); pctx = curlx_calloc(1, sizeof(*pctx)); if(!pctx) { diff --git a/lib/http_chunks.c b/lib/http_chunks.c index f67459bf55ba..95929d27ba05 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -71,7 +71,7 @@ */ void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch, - bool ignore_body) + bool ignore_body, bool in_connect) { (void)data; ch->hexindex = 0; /* start at 0 */ @@ -79,6 +79,7 @@ void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch, ch->last_code = CHUNKE_OK; curlx_dyn_init(&ch->trailer, DYN_H1_TRAILER); ch->ignore_body = ignore_body; + ch->in_connect = in_connect; } void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch, @@ -268,16 +269,14 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, } if(!data->set.http_te_skip) { + int hd_type = CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER; + if(ch->in_connect) + hd_type |= CLIENTWRITE_CONNECT; if(cw_next) - result = Curl_cwriter_write(data, cw_next, - CLIENTWRITE_HEADER | - CLIENTWRITE_TRAILER, - tr, trlen); + result = Curl_cwriter_write(data, cw_next, hd_type, tr, trlen); else - result = Curl_client_write(data, - CLIENTWRITE_HEADER | - CLIENTWRITE_TRAILER, - tr, trlen); + result = Curl_client_write(data, hd_type, tr, trlen); + CURL_TRC_WRITE(data, "wrote trailer '%s'", tr); if(result) { ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_PASSTHRU_ERROR; @@ -408,7 +407,7 @@ static CURLcode cw_chunked_init(struct Curl_easy *data, struct chunked_writer *ctx = writer->ctx; data->req.chunk = TRUE; /* chunks coming our way. */ - Curl_httpchunk_init(data, &ctx->ch, FALSE); + Curl_httpchunk_init(data, &ctx->ch, FALSE, FALSE); return CURLE_OK; } diff --git a/lib/http_chunks.h b/lib/http_chunks.h index d8e5982e6d87..0cb1f8834093 100644 --- a/lib/http_chunks.h +++ b/lib/http_chunks.h @@ -99,11 +99,12 @@ struct Curl_chunker { unsigned char hexindex; char hexbuffer[CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */ BIT(ignore_body); /* never write response body data */ + BIT(in_connect); /* this is a CONNECT response body */ }; /* The following functions are defined in http_chunks.c */ void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch, - bool ignore_body); + bool ignore_body, bool in_connect); void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch); void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch, bool ignore_body); diff --git a/lib/sendf.c b/lib/sendf.c index 78cda23cfec6..3742b6993e7f 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -189,6 +189,10 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(!(type & CLIENTWRITE_BODY)) { if(is_connect && data->set.suppress_connect_headers) return CURLE_OK; +#ifdef DEBUGBUILD + if(is_connect && getenv("CURL_DBG_SUPPRESS_CONNECT_HDS")) + return CURLE_OK; +#endif result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", (unsigned int)type, nbytes, (int)result); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index caa2e9648c1b..b99803417f52 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1940,7 +1940,10 @@ TESTCASES = \ test2309 \ test2310 \ test2311 \ + test2349 \ + \ test2397 \ + \ test2400 \ test2401 \ test2402 \ diff --git a/tests/data/test2349 b/tests/data/test2349 new file mode 100644 index 000000000000..a09a98cdf443 --- /dev/null +++ b/tests/data/test2349 @@ -0,0 +1,63 @@ + + + + +HTTP +HTTP GET + + + + + +HTTP/1.1 407 NoNoNo +Transfer-Encoding: chunked +Trailer: proxy-trailer + +4 +aaaa +0 +proxy-trailer: 12345 + + + + +# Client-side + + +http +http-proxy + + +proxy +Debug + + +HTTP CONNECT -D file + + +CURL_DBG_SUPPRESS_CONNECT_HDS=1 + + +http://%HOSTIP:%HTTPPORT/ --proxy %HOSTIP:%PROXYPORT --proxytunnel -sS -D %LOGDIR/heads%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +7 + + +CONNECT %HOSTIP:%HTTPPORT HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Proxy-Connection: Keep-Alive + + + +# Header file is empty, as CONNECT headers were suppress by DEBUG env var + + + + + From bdda3a268f0fd1a1bb4b16b9322aa6bc055b8e76 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 12:50:01 +0200 Subject: [PATCH 0998/1062] mprintf: fix long double output curl does not use this, and we discourage users from using these functions but since we claim printf() compatibility this still seems worth fixing. This handling downgrades all input long doubles into regular doubles in the output because in our CI platform set they caused problems in multiple places. Verified in test 557. Reported-by: Stanislav Fort Closes #22706 --- lib/mprintf.c | 19 ++++++----- tests/libtest/lib557.c | 75 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/lib/mprintf.c b/lib/mprintf.c index 7eb25c2e6847..0d13d6bb4678 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -395,22 +395,22 @@ static bool parse_conversion(const char f, unsigned int *flagp, flags |= FLAGS_CHAR; break; case 'f': - type = MTYPE_DOUBLE; + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; break; case 'e': - type = MTYPE_DOUBLE; + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; flags |= FLAGS_FLOATE; break; case 'E': - type = MTYPE_DOUBLE; + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; flags |= FLAGS_FLOATE | FLAGS_UPPER; break; case 'g': - type = MTYPE_DOUBLE; + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; flags |= FLAGS_FLOATG; break; case 'G': - type = MTYPE_DOUBLE; + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; flags |= FLAGS_FLOATG | FLAGS_UPPER; break; default: @@ -618,6 +618,10 @@ static int parsefmt(const char *format, iptr->val.dnum = va_arg(arglist, double); break; + case MTYPE_LONGDOUBLE: + iptr->val.dnum = (double)va_arg(arglist, long double); + break; + default: DEBUGASSERT(NULL); /* unexpected */ break; @@ -712,9 +716,7 @@ static bool out_double(void *userp, #ifdef _WIN32 curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum); #else - /* !checksrc! disable BANNEDFUNC 1 */ - /* !checksrc! disable LONGLINE */ - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + /* !checksrc! disable BANNEDFUNC 2 */ snprintf(work, BUFFSIZE, fmt, dnum); #endif #ifdef CURL_HAVE_DIAG @@ -1065,6 +1067,7 @@ static int formatf(void *userp, /* untouched by format(), sent to the break; case MTYPE_DOUBLE: + case MTYPE_LONGDOUBLE: if(out_double(userp, stream, &p, iptr->val.dnum, work, &done)) return done; break; diff --git a/tests/libtest/lib557.c b/tests/libtest/lib557.c index 0b175c193fbb..30ec3a796894 100644 --- a/tests/libtest/lib557.c +++ b/tests/libtest/lib557.c @@ -46,6 +46,15 @@ #endif #endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + #define BUFSZ 256 struct unsshort_st { @@ -1047,6 +1056,53 @@ static int test_weird_arguments(void) return errors; } +static int double_check(void) +{ + const long double val = 1.234567890123456789L; + int mismatches = 0; + unsigned int i; + struct dbcheck { + const char *fmt; + const char *prefix; /* starts with this */ + const char *suffix; /* ends with this */ + }; + /* because floats are annoying beasts, different libc's produce different + outputs so we cannot compare exact output, only prefix + suffix */ + struct dbcheck c[] = { + { "%.17Lf", /* "1.23456789012345669" */ + "1.23456789012345", "", }, + { "%.17Le", /* "1.23456789012345669e+00" */ + "1.234567890123456", "e+00" }, + { "%.17LE", /* "1.23456789012345669E+00" */ + "1.234567890123456", "E+00" }, + { "%.17Lg", /* "1.2345678901234567" */ + "1.234567890123456", "" }, + { "%.17LG", /* "1.2345678901234567" */ + "1.234567890123456", ""} + }; + for(i = 0; i < CURL_ARRAYSIZE(c); i++) { + char curl_out[128]; + size_t len = + curl_msnprintf(curl_out, sizeof(curl_out), c[i].fmt, val); + if(strncmp(curl_out, c[i].prefix, strlen(c[i].prefix))) { + curl_mfprintf(stderr, + "MISMATCH (prefix): %s curl=%s libc=%s\n", + c[i].fmt, curl_out, c[i].prefix); + mismatches++; + } + if((len < strlen(c[i].suffix) || + strncmp(curl_out + len - strlen(c[i].suffix), + c[i].suffix, strlen(c[i].suffix)))) { + curl_mfprintf(stderr, + "MISMATCH (suffix): %s curl=%s libc=%s\n", + c[i].fmt, curl_out, c[i].suffix); + mismatches++; + } + } + curl_mfprintf(stderr, "mismatches=%d/5\n", mismatches); + return mismatches; +} + /* DBL_MAX value from Linux */ #define MAXIMIZE (-1.7976931348623157081452E+308) @@ -1165,6 +1221,8 @@ static int test_float_formatting(void) curl_msnprintf(buf, 6, "%f", MAXIMIZE); errors += strlen_check(buf, 5); + errors += double_check(); + if(!errors) curl_mprintf("All float strings tests OK!\n"); else @@ -1246,14 +1304,6 @@ static int test_return_codes(void) - curl_mvsprintf - curl_mvaprintf */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif -#ifdef CURL_HAVE_DIAG -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif static int var557(int expected_len, const char *format, ...) { va_list arg; @@ -1319,12 +1369,6 @@ static int var557(int expected_len, const char *format, ...) curl_mprintf("The v-functions test failed!\n"); return errors; } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#ifdef CURL_HAVE_DIAG -#pragma GCC diagnostic pop -#endif static int test_vversions(void) { @@ -1370,3 +1414,6 @@ static CURLcode test_lib557(const char *URL) #ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif From 72352daba01ba8072b6c1c0254cb77860ed1ab06 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 27 Aug 2026 14:36:05 +0200 Subject: [PATCH 0999/1062] connection reuse: check SSL configs when doing a scheme upgrade When reusing a SSL variant of a scheme for a non-SSL transfer, do also check if the SSL configurations match. Closes #22710 --- lib/url.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/url.c b/lib/url.c index 8e8fb39acbcc..d5ff91ef1c71 100644 --- a/lib/url.c +++ b/lib/url.c @@ -723,6 +723,10 @@ static bool url_match_ssl_use(struct connectdata *conn, if(!(m->needle->scheme->flags & PROTOPT_SSL_REUSE) || (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; + /* We may reuse this as an auto-TLS upgrade, but only if the SSL + * config parameters match. */ + if(!Curl_ssl_conn_config_match(m->data, conn, FALSE)) + return FALSE; } else if(m->require_tls) /* a clear-text STARTTLS protocol with required TLS */ From ee4cb4a6c500a1e16dc6a1312640b75bf6080aa1 Mon Sep 17 00:00:00 2001 From: Martin Dukek Date: Wed, 26 Aug 2026 14:38:51 +0200 Subject: [PATCH 1000/1062] schannel: refresh stream sizes after renegotiation Move the SECPKG_ATTR_STREAM_SIZES query to schannel_connect() completion, where it runs after both initial negotiation and renegotiation. This avoids relying on schannel_send() to refresh the cached values later. Suggested-by: Jay Satiro Closes https://github.com/curl/curl/pull/22692 --- lib/vtls/schannel.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 581fa1979bd5..a24a3f4599b1 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1733,6 +1733,18 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, } if(connssl->connecting_state == ssl_connect_done) { + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + DEBUGASSERT(backend); + + if(Curl_pSecFn->QueryContextAttributes( + &backend->ctxt->ctxt_handle, + SECPKG_ATTR_STREAM_SIZES, + &backend->stream_sizes)) { + failf(data, "schannel: failed getting stream sizes"); + return CURLE_SSL_CONNECT_ERROR; + } + connssl->state = ssl_connection_complete; #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ @@ -1741,12 +1753,7 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, * binding to pass the IIS extended protection checks. * Available on Windows 7 or later. */ - { - struct schannel_ssl_backend_data *backend = - (struct schannel_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); - cf->conn->sslContext = &backend->ctxt->ctxt_handle; - } + cf->conn->sslContext = &backend->ctxt->ctxt_handle; #endif *done = TRUE; @@ -1978,17 +1985,6 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } - /* check if the maximum stream sizes were queried */ - if(backend->stream_sizes.cbMaximumMessage == 0) { - sspi_status = Curl_pSecFn->QueryContextAttributes( - &backend->ctxt->ctxt_handle, - SECPKG_ATTR_STREAM_SIZES, - &backend->stream_sizes); - if(sspi_status != SEC_E_OK) { - return CURLE_SEND_ERROR; - } - } - /* check if the buffer is longer than the maximum message length */ if(len > backend->stream_sizes.cbMaximumMessage) { len = backend->stream_sizes.cbMaximumMessage; From 282057f32a3d7d3a9563c83d6a32196223411c8a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Aug 2026 23:20:42 +0200 Subject: [PATCH 1001/1062] mprintf: avoid never-ending loop for positive-infinite Floating point is odd but it features this concept of "infinite" (which can happen with the new typecast from long double to double) and apparently dividing infinite with 10 is still infinte. Follow-up to bdda3a268f0fd1a1bb4 Pointed out by: Codex Security Closes #22713 --- lib/mprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mprintf.c b/lib/mprintf.c index 0d13d6bb4678..0e11f63b892a 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -682,7 +682,7 @@ static bool out_double(void *userp, prec = maxprec - 1; if(width > 0 && prec <= width) maxprec -= width; - while(val >= 10.0) { + while(maxprec && (val >= 10.0)) { val /= 10; maxprec--; } From fad90b666df98c93ee165341b0c8101c7b10ad7b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 28 Aug 2026 09:55:25 +0200 Subject: [PATCH 1002/1062] h2 push: use squeaky clean easy handle When a HTTP/2 push requires a new easy handle, create a clean easy and do *not* duplicate the existing one. This avoids messy use of the API `curl_easy_duphandle()`, which has some caveats the applictions must observe and cannot do so for a h2 push. Using a clean easy handle is also better since the pushed stream has no real relation to the easy handle that was in use when the HTTP/2 push arrived (it could be any easy using the connection). Inheriting settings/inputs from a somewhat "random" existing easy seems wrong. Closes #22715 --- lib/http2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http2.c b/lib/http2.c index 9ac0d20fb3fe..16a5b96327ca 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -700,7 +700,7 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_easy *second = curl_easy_duphandle(data); + struct Curl_easy *second = curl_easy_init(); if(second) { struct h2_stream_ctx *second_stream; http2_data_setup(cf, second, &second_stream); From a5081e01ec185a962001f265f29992a33a983ca8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 28 Aug 2026 14:07:42 +0200 Subject: [PATCH 1003/1062] HISTORY: September 1999: started using CVS --- docs/HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 7e38f4ec15ad..6d77301e19f3 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -85,6 +85,8 @@ May: first Debian package. August: `ldap://` and `file://` support added. The curl website gets 1300 visits weekly. Moved site to curl.haxx.nu. +September: started using CVS for source code version control. + September: Released curl 6.0. 15000 lines of code. December 28: added the project on Sourceforge and started using its services From 48322e9f09f2b51f77d37c5e04a3f370bb36bfc4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 28 Aug 2026 13:39:14 +0200 Subject: [PATCH 1004/1062] mprintf: acknowledge %F The code previously just ignored it, causing variadic argument desynchronization Extend test 557 to verify Reported-by: Stanislav Fort Closes #22719 --- lib/mprintf.c | 6 +++++- tests/libtest/lib557.c | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/mprintf.c b/lib/mprintf.c index 0e11f63b892a..d4725c905fe1 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -397,6 +397,10 @@ static bool parse_conversion(const char f, unsigned int *flagp, case 'f': type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; break; + case 'F': + type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; + flags |= FLAGS_UPPER; + break; case 'e': type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE; flags |= FLAGS_FLOATE; @@ -703,7 +707,7 @@ static bool out_double(void *userp, else if(flags & FLAGS_FLOATG) *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g'); else - *fptr++ = 'f'; + *fptr++ = (flags & FLAGS_UPPER) ? 'F' : 'f'; *fptr = 0; /* and a final null-termination */ diff --git a/tests/libtest/lib557.c b/tests/libtest/lib557.c index 30ec3a796894..535e37d4acc8 100644 --- a/tests/libtest/lib557.c +++ b/tests/libtest/lib557.c @@ -1113,6 +1113,9 @@ static int test_float_formatting(void) curl_msnprintf(buf, sizeof(buf), "%f", 9.0); errors += string_check(buf, "9.000000"); + curl_msnprintf(buf, sizeof(buf), "%F", 9.0); + errors += string_check(buf, "9.000000"); + curl_msnprintf(buf, sizeof(buf), "%.1f", 9.1); errors += string_check(buf, "9.1"); From fe2d3591c33d8cc5d23a734f616b681df5e1147a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 28 Aug 2026 13:20:11 +0200 Subject: [PATCH 1005/1062] share unlink: forget connection When removing a share from an easy handle and the share caches connections, invalidate the easy handle's `lastconnect_id` as future lookups would go into another connection cache and possibly find a wrong connection. Closes #22718 --- lib/curl_share.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/curl_share.c b/lib/curl_share.c index bce6419e8f01..bb5125533f1b 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -421,9 +421,13 @@ CURLcode Curl_share_easy_unlink(struct Curl_easy *data) if(share) { bool locked = share_lock_acquire(share, data); - /* If data has a connection from this share, detach it. */ - if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT))) - Curl_detach_connection(data); + /* If share caches connections, detach any existing connection and + * forget its identifier. */ + if((share->specifier & (1 << CURL_LOCK_DATA_CONNECT))) { + if(data->conn) + Curl_detach_connection(data); + data->state.lastconnect_id = -1; + } #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(share->cookies == data->cookies) From 5d2b8670c632fab72a60fc85529550e0accc5e71 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 28 Aug 2026 12:00:03 +0200 Subject: [PATCH 1006/1062] ipv6 scope_id: set from first peer When not explicitly set, give the connection a present scope_id from the first peer of a connection, not from its origin. This accounts for proxies. Closes #22717 --- lib/url.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/url.c b/lib/url.c index d5ff91ef1c71..1560a8bcb9f8 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1576,11 +1576,6 @@ static CURLcode url_set_conn_origin_etc(struct Curl_easy *data, } } -#ifdef USE_IPV6 - conn->scope_id = data->set.scope_id ? - data->set.scope_id : data->state.origin->scopeid; -#endif - out: return result; } @@ -1659,6 +1654,17 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, Curl_strntolower(conn->destination, conn->destination, strlen(conn->destination)); +#ifdef USE_IPV6 + if(data->set.scope_id) + conn->scope_id = data->set.scope_id; + else { + struct Curl_peer *first = Curl_conn_get_first_peer(conn, FIRSTSOCKET); + if(!first) + return CURLE_FAILED_INIT; + conn->scope_id = first->scopeid; + } +#endif + return CURLE_OK; } From e031bbf85108b4b18fca8ca3a86f7496a0a6fb0b Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Wed, 26 Aug 2026 17:01:38 +0200 Subject: [PATCH 1007/1062] os400: port latest header files changes to ILE/RPG interface Closes #22695 --- projects/OS400/curl.inc.in | 48 ++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/projects/OS400/curl.inc.in b/projects/OS400/curl.inc.in index d048663f7203..5b21429fed91 100644 --- a/projects/OS400/curl.inc.in +++ b/projects/OS400/curl.inc.in @@ -214,11 +214,20 @@ d c X'00000040' d CURLAUTH_AWS_SIGV4... d c X'00000080' + d CURLAUTH_HTTPSIG... + d c X'00000100' d CURLAUTH_ONLY... d c X'80000000' - d CURLAUTH_ANY c X'7FFFFFEF' + d CURLAUTH_ANY c X'7FFFFEEF' d CURLAUTH_ANYSAFE... - d c X'7FFFFFEE' + d c X'7FFFFEEE' + * + d CURLHTTPSIG_NONE... + d c 0 + d CURLHTTPSIG_ED25519... + d c 1 + d CURLHTTPSIG_HMAC_SHA256... + d c 2 * d CURLSSH_AUTH_ANY... d c X'7FFFFFFF' @@ -894,6 +903,8 @@ d c 6 d CURLPROXY_SOCKS5_HOSTNAME... d c 7 + d CURLPROXY_HTTPS3... + d c 8 * d curl_khstat s 10i 0 based(######ptr######) Enum d CURLKHSTAT_FINE_ADD_TO_FILE... @@ -1092,8 +1103,6 @@ d c X'10000000' d CURLPROTO_GOPHERS... d c X'20000000' - d CURLPROTO_MQTTS... - d c X'40000000' d CURLPROTO_ALL c X'FFFFFFFF' * d CURLoption s 10i 0 based(######ptr######) Enum @@ -1705,6 +1714,14 @@ d c 00327 d CURLOPT_SSL_SIGNATURE_ALGORITHMS... d c 10328 + d CURLOPT_HTTPSIG_ALGORITHM... + d c 00329 + d CURLOPT_HTTPSIG_KEY... + d c 10330 + d CURLOPT_HTTPSIG_KEYID... + d c 10331 + d CURLOPT_HTTPSIG_HEADERS... + d c 10332 * /if not defined(CURL_NO_OLDIES) d CURLOPT_FILE c 10001 @@ -1948,6 +1965,16 @@ d c X'00600041' d CURLINFO_USED_PROXY... CURLINFO_LONG + 66 d c X'00200042' + d CURLINFO_POSTTRANSFER_TIME_T... CURLINFO_OFF_T + 67 + d c X'00600043' + d CURLINFO_EARLYDATA_SENT_T... CURLINFO_OFF_T + 68 + d c X'00600044' + d CURLINFO_HTTPAUTH_USED... CURLINFO_LONG + 69 + d c X'00200045' + d CURLINFO_PROXYAUTH_USED... CURLINFO_LONG + 70 + d c X'00200046' + d CURLINFO_SIZE_DELIVERED... CURLINFO_OFF_T + 71 + d c X'00600047' * d CURLINFO_HTTP_CODE... Old ...RESPONSE_CODE d c X'00200002' @@ -2217,6 +2244,10 @@ d c 20018 d CURLMOPT_NOTIFYDATA... d c 10019 + d CURLMOPT_RESOLVE_THREADS_MAX... + d c 00020 + d CURLMOPT_QUICK_EXIT... + d c 00021 * d CURLMinfo_offt s 10i 0 based(######ptr######) Enum d CURLMINFO_NONE... @@ -2234,10 +2265,12 @@ * * Definition of bits for the CURLMOPT_NETWORK_CHANGED argument. * - d CURLMNWC_CLEAR_CONNS... + d CURLMNWC_CLEAR_ALL... d c x'00000001' - d CURLMNWC_CLEAR_DNS... + d CURLMNWC_CLEAR_CONNS... d c x'00000002' + d CURLMNWC_CLEAR_DNS... + d c x'00000004' * * Bitmask bits for CURLMOPT_PIPELINING. * @@ -3248,7 +3281,8 @@ d curl_ws_meta pr * extproc('curl_ws_meta') curl_ws_frame * d curl * value CURL * * - d curl_ws_start pr extproc('curl_ws_start') + d curl_ws_start_frame... + d pr extproc('curl_ws_start_frame') d like(CURLcode) d curl * value CURL * d flags 10u 0 value From 9499d987b96ee5350f3e822258f45d2e287c0be0 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Thu, 27 Aug 2026 13:58:25 +0200 Subject: [PATCH 1008/1062] os400: rewrite upper ebcdic wrappers using dynbuf Function dyn_addn_CCSID() extends dynbuf strings to arbitrary encodings, possibly multi-byte. It converts the input data using iterative iconv() calls and appends the converted characters to the dynbuf string. Most EBCDIC wrappers are now based on this function. All memory allocations in these wrappers are now performed via the libcurl malloc interface. Closes #22675 --- projects/OS400/ccsidcurl.c | 659 +++++++++++++++++++------------------ 1 file changed, 339 insertions(+), 320 deletions(-) diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index 1cce770a3f66..8b3b2fe6c6a0 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -34,6 +34,7 @@ #pragma enum(int) +#include "curl_setup.h" #include "curl.h" #include "mprintf.h" #include "slist.h" @@ -41,12 +42,13 @@ #include "url.h" #include "setopt.h" #include "getinfo.h" +#include "curlx/dynbuf.h" #include "ccsidcurl.h" #include "os400sys.h" #ifndef SIZE_MAX -#define SIZE_MAX ((size_t)~0) /* Is unsigned on OS/400. */ +#define SIZE_MAX ((size_t) ~0) #endif #define ASCII_CCSID 819 /* Use ISO-8859-1 as ASCII. */ @@ -56,15 +58,23 @@ #define ALLOC_GRANULE 8 /* Alloc. granule for curl_formadd_ccsid(). */ + +/* A string terminator that works for all CCSIDs. */ +static const char universal_terminator[] = {0, 0, 0, 0}; + +/* Freeing const pointers more easily. */ +#define untyped_free(p) curlx_free(CURL_UNCONST(p)) + + static void makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid) { - /** - *** Convert a CCSID to the corresponding IBM iconv_open() character - *** code identifier. - *** This code is specific to the OS400 implementation of the iconv library. - *** CCSID 65535 (no conversion) is replaced by the ASCII CCSID. - *** CCSID 0 is interpreted by the OS400 as the job's CCSID. - **/ + /* + * Convert a CCSID to the corresponding IBM iconv_open() character + * code identifier. + * This code is specific to the OS400 implementation of the iconv library. + * CCSID 65535 (no conversion) is replaced by the ASCII CCSID. + * CCSID 0 is interpreted by the OS400 as the job's CCSID. + */ ccsid &= 0xFFFF; @@ -81,12 +91,12 @@ static iconv_t iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, char fromcode[ICONV_ID_SIZE]; char tocode[ICONV_ID_SIZE]; - /** - *** Like iconv_open(), but character codes are given as CCSIDs. - *** If `cstr' is non-zero, conversion is set up to stop whenever a - *** null character is encountered. - *** See iconv_open() IBM description in "National Language Support API". - **/ + /* + * Like iconv_open(), but character codes are given as CCSIDs. + * If `cstr' is non-zero, conversion is set up to stop whenever a + * null character is encountered. + * See iconv_open() IBM description in "National Language Support API". + */ makeOS400IconvCode(fromcode, ccsidin); makeOS400IconvCode(tocode, ccsidout); @@ -98,44 +108,35 @@ static iconv_t iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, return iconv_open(tocode, fromcode); } -static int convert(char *d, size_t dlen, int dccsid, const char *s, int slen, - int sccsid) +static int convert(char *d, size_t dlen, const char *s, size_t slen, + unsigned int ccsidin, unsigned int ccsidout) { int i; iconv_t cd; size_t lslen; - /** - *** Convert `sccsid'-coded `slen'-data bytes at `s' into `dccsid'-coded - *** data stored in the `dlen'-byte buffer at `d'. - *** If `slen' < 0, source string is null-terminated. - *** CCSID 65535 (no conversion) is replaced by the ASCII CCSID. - *** Return the converted destination byte count, or -1 if error. - **/ + /* + * Convert `ccsidin'-coded `slen'-data bytes at `s' into `ccsidout'-coded + * data stored in the `dlen'-byte buffer at `d'. + * If `slen' is CURL_ZERO_TERMINATED, let iconv() detect the end of + * input string. + * CCSID 65535 (no conversion) is replaced by the ASCII CCSID. + * Return the converted destination byte count, or -1 if error. + */ - if(sccsid == 65535) - sccsid = ASCII_CCSID; + if(ccsidin == 65535) + ccsidin = ASCII_CCSID; - if(dccsid == 65535) - dccsid = ASCII_CCSID; - - if(sccsid == dccsid) { - lslen = slen >= 0 ? slen : strlen(s) + 1; - i = lslen < dlen ? lslen : dlen; - - if(s != d && i > 0) - memcpy(d, s, i); - - return i; - } + if(ccsidout == 65535) + ccsidout = ASCII_CCSID; - if(slen < 0) { + if(slen == CURL_ZERO_TERMINATED) { lslen = 0; - cd = iconv_open_CCSID(dccsid, sccsid, 1); + cd = iconv_open_CCSID(ccsidout, ccsidin, 1); } else { lslen = (size_t)slen; - cd = iconv_open_CCSID(dccsid, sccsid, 0); + cd = iconv_open_CCSID(ccsidout, ccsidin, 0); } if(ICONV_OPEN_ERROR(cd)) @@ -143,7 +144,7 @@ static int convert(char *d, size_t dlen, int dccsid, const char *s, int slen, i = dlen; - if((int)iconv(cd, (char **)&s, &lslen, &d, &dlen) < 0) + if((int)iconv(cd, (char **) &s, &lslen, &d, &dlen) < 0) i = -1; else i -= dlen; @@ -152,75 +153,116 @@ static int convert(char *d, size_t dlen, int dccsid, const char *s, int slen, return i; } -static char *dynconvert(int dccsid, const char *s, int slen, int sccsid, - int *olen) +static CURLcode dyn_addn_CCSID(struct dynbuf *db, + const void *mem, size_t len, + unsigned int ccsidin, unsigned int ccsidout) { - char *d; - char *cp; + iconv_t cd; size_t dlen; - int l; - static const char nullbyte = 0; + CURLcode result = CURLE_OK; + char buffer[128]; - /* Like convert, but the destination is allocated and returned. */ + if(ccsidin == 65535) + ccsidin = ASCII_CCSID; - dlen = (size_t)(slen < 0 ? strlen(s) : slen) + 1; - dlen *= MAX_CONV_EXPANSION; /* Allow some expansion. */ - d = malloc(dlen); + if(ccsidout == 65535) + ccsidout = ASCII_CCSID; - if(!d) - return (char *)NULL; + cd = iconv_open_CCSID(ccsidout, ccsidin, len == CURL_ZERO_TERMINATED); - l = convert(d, dlen, dccsid, s, slen, sccsid); - - if(l < 0) { - free(d); - return (char *)NULL; + if(ICONV_OPEN_ERROR(cd)) { + curlx_dyn_free(db); + return CURLE_NOT_BUILT_IN; } - if(slen < 0) { - /* Need to null-terminate even when source length is given. - Since destination code size is unknown, use a conversion to generate - terminator. */ - - int l2 = convert(d + l, dlen - l, dccsid, &nullbyte, -1, ASCII_CCSID); + while(len) { + size_t dummylen = 0; + char *dptr = buffer; + int err = 0; + + dlen = sizeof(buffer); + if((int)iconv(cd, (char **) &mem, + len == CURL_ZERO_TERMINATED ? &dummylen : &len, + &dptr, &dlen) < 0) { + /* !checksrc! disable ERRNOVAR 1 */ + err = errno; + } + result = CURLE_BAD_FUNCTION_ARGUMENT; + switch(err) { + case 0: + case E2BIG: + break; + case ENOMEM: + result = CURLE_OUT_OF_MEMORY; + FALLTHROUGH(); + default: + curlx_dyn_free(db); + iconv_close(cd); + return result; + } - if(l2 < 0) { - free(d); - return (char *)NULL; + result = curlx_dyn_addn(db, (const void *)buffer, dptr - buffer); + if(result) { + iconv_close(cd); + return result; } - l += l2; + if(!err) + break; } - if((size_t)l < dlen) { - cp = realloc(d, l); /* Shorten to minimum needed. */ + iconv_close(cd); + dlen = curlx_dyn_len(db); + + if(len == CURL_ZERO_TERMINATED) { + /* The null terminator has been converted AND counted as a character. + * Measure it by an additional conversion and drop it. */ + int tlen = convert(buffer, sizeof(buffer), universal_terminator, 1, + ASCII_CCSID, ccsidout); - if(cp) - d = cp; + if(tlen < 0 || tlen > dlen) { + curlx_dyn_free(db); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + dlen -= tlen; + } + else { + /* Make sure the string is followed by an universal terminator. */ + result = curlx_dyn_addn(db, universal_terminator, + sizeof(universal_terminator)); } - if(olen) - *olen = l; - return d; + /* Restore the real string length. */ + if(!result) + result = curlx_dyn_setlen(db, dlen); + + return result; } -static struct curl_slist *slist_convert(int dccsid, struct curl_slist *from, - int sccsid) +static struct curl_slist *slist_convert(struct curl_slist *from, + unsigned int ccsidin, + unsigned int ccsidout) { struct curl_slist *to = (struct curl_slist *)NULL; + struct dynbuf db; + size_t plen; + + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_HTTP_HEADER); for(; from; from = from->next) { struct curl_slist *nl; - char *cp = dynconvert(dccsid, from->data, -1, sccsid, NULL); + char *s; - if(!cp) { + if(dyn_addn_CCSID(&db, from->data, CURL_ZERO_TERMINATED, + ccsidin, ccsidout)) { curl_slist_free_all(to); - return (struct curl_slist *)NULL; + return NULL; } - nl = Curl_slist_append_nodup(to, cp); + s = curlx_dyn_take(&db, &plen); + nl = Curl_slist_append_nodup(to, s); if(!nl) { curl_slist_free_all(to); - free(cp); + free(s); return NULL; } to = nl; @@ -231,35 +273,56 @@ static struct curl_slist *slist_convert(int dccsid, struct curl_slist *from, static char *keyed_string(localkey_t key, const char *ascii, unsigned int ccsid) { - int i; + size_t len; char *ebcdic; + struct dynbuf db; if(!ascii) - return (char *)NULL; - - i = MAX_CONV_EXPANSION * (strlen(ascii) + 1); + return NULL; - ebcdic = Curl_thread_buffer(key, i); - if(!ebcdic) - return ebcdic; - - if(convert(ebcdic, i, ccsid, ascii, -1, ASCII_CCSID) < 0) - return (char *)NULL; + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_INPUT_LENGTH); + if(dyn_addn_CCSID(&db, ascii, CURL_ZERO_TERMINATED, ASCII_CCSID, ccsid)) + return NULL; + len = curlx_dyn_len(&db); + ebcdic = Curl_thread_buffer(key, len + sizeof(universal_terminator)); + if(ebcdic) { + memcpy(ebcdic, curlx_dyn_ptr(&db), len); + memcpy(ebcdic + len, universal_terminator, sizeof(universal_terminator)); + } + curlx_dyn_free(&db); return ebcdic; } -const char *curl_to_ccsid(const char *s, unsigned int ccsid) +const char *curl_to_ccsid(const char *string, unsigned int ccsid) { - if(s) - s = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); + char *s = NULL; + + if(string) { + struct dynbuf db; + size_t len; + + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_INPUT_LENGTH); + dyn_addn_CCSID(&db, string, CURL_ZERO_TERMINATED, ASCII_CCSID, ccsid); + s = curlx_dyn_take(&db, &len); + } + return s; } -const char *curl_from_ccsid(const char *s, unsigned int ccsid) +const char *curl_from_ccsid(const char *string, unsigned int ccsid) { - if(s) - s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); + char *s = NULL; + + if(string) { + struct dynbuf db; + size_t len; + + curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH); + dyn_addn_CCSID(&db, string, CURL_ZERO_TERMINATED, ccsid, ASCII_CCSID); + s = curlx_dyn_take(&db, &len); + } + return s; } @@ -269,103 +332,99 @@ char *curl_version_ccsid(unsigned int ccsid) } char *curl_easy_escape_ccsid(CURL *handle, const char *string, int length, - unsigned int sccsid, unsigned int dccsid) + unsigned int ccsidin, unsigned int ccsidout) { - char *s; + struct dynbuf db; char *d; + size_t len; - if(!string) { - /* !checksrc! disable ERRNOVAR 1 */ - errno = EINVAL; - return (char *)NULL; - } + if(!string) + return NULL; - s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid, NULL); + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_INPUT_LENGTH); - if(!s) - return (char *)NULL; + if(dyn_addn_CCSID(&db, string, length ? length : CURL_ZERO_TERMINATED, + ccsidin, ASCII_CCSID)) + return NULL; - d = curl_easy_escape(handle, s, 0); - free(s); + d = curl_easy_escape(handle, curlx_dyn_ptr(&db), curlx_dyn_len(&db)); + curlx_dyn_free(&db); if(!d) - return (char *)NULL; + return NULL; - s = dynconvert(dccsid, d, -1, ASCII_CCSID, NULL); - free(d); - return s; + dyn_addn_CCSID(&db, d, CURL_ZERO_TERMINATED, ASCII_CCSID, ccsidout); + untyped_free(d); + return curlx_dyn_take(&db, &len); } char *curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, - int *outlength, unsigned int sccsid, - unsigned int dccsid) + int *outlength, + unsigned int ccsidin, unsigned int ccsidout) { - char *s; + struct dynbuf db; char *d; + size_t len; - if(!string) { - /* !checksrc! disable ERRNOVAR 1 */ - errno = EINVAL; - return (char *)NULL; - } + if(!string) + return NULL; - s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid, NULL); + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_INPUT_LENGTH); - if(!s) - return (char *)NULL; + if(dyn_addn_CCSID(&db, string, length ? length : CURL_ZERO_TERMINATED, + ccsidin, ASCII_CCSID)) + return NULL; - d = curl_easy_unescape(handle, s, 0, outlength); - free(s); + d = curl_easy_unescape(handle, + curlx_dyn_ptr(&db), curlx_dyn_len(&db), outlength); + curlx_dyn_free(&db); if(!d) - return (char *)NULL; + return NULL; - s = dynconvert(dccsid, d, -1, ASCII_CCSID, NULL); - free(d); - - if(s && outlength) - *outlength = strlen(s); - - return s; + if(!dyn_addn_CCSID(&db, d, CURL_ZERO_TERMINATED, ASCII_CCSID, ccsidout)) + if(outlength) + *outlength = curlx_dyn_len(&db); + untyped_free(d); + return curlx_dyn_take(&db, &len); } struct curl_slist *curl_slist_append_ccsid(struct curl_slist *list, const char *data, unsigned int ccsid) { - char *s; - - s = (char *)NULL; + const char *s; if(!data) return curl_slist_append(list, data); - s = dynconvert(ASCII_CCSID, data, -1, ccsid, NULL); + s = curl_from_ccsid(data, ccsid); if(!s) - return (struct curl_slist *)NULL; + return NULL; - list = curl_slist_append(list, s); - free(s); + list = Curl_slist_append_nodup(list, s); + if(!list) + untyped_free(s); return list; } time_t curl_getdate_ccsid(const char *p, const time_t *unused, unsigned int ccsid) { - char *s; + const char *s; time_t t; if(!p) return curl_getdate(p, unused); - s = dynconvert(ASCII_CCSID, p, -1, ccsid, NULL); + s = curl_from_ccsid(p, ccsid); if(!s) - return (time_t)-1; + return (time_t) -1; t = curl_getdate(s, unused); - free(s); + untyped_free(s); return t; } @@ -378,7 +437,8 @@ static int convert_version_info_string(const char **stringp, char **bufp, Return 0 if ok, else -1. */ if(*stringp) { - int l = convert(*bufp, *left, ccsid, *stringp, -1, ASCII_CCSID); + int l = convert(*bufp, *left, *stringp, CURL_ZERO_TERMINATED, + ASCII_CCSID, ccsid); if(l <= 0) return -1; @@ -432,7 +492,7 @@ curl_version_info_data *curl_version_info_ccsid(CURLversion stamp, /* If caller has been compiled with a newer version, error. */ if(stamp > CURLVERSION_NOW) - return (curl_version_info_data *)NULL; + return NULL; p = curl_version_info(stamp); @@ -469,11 +529,11 @@ curl_version_info_data *curl_version_info_ccsid(CURLversion stamp, sizeof(*id)); if(!id || !cp) - return (curl_version_info_data *)NULL; + return NULL; /* Copy data and convert strings. */ - memcpy((char *)id, (char *)p, sizeof(*p)); + memcpy(id, p, sizeof(*p)); if(id->protocols) { i = nproto * sizeof(id->protocols[0]); @@ -485,14 +545,14 @@ curl_version_info_data *curl_version_info_ccsid(CURLversion stamp, for(i = 0; id->protocols[i]; i++) if(convert_version_info_string(((const char **)id->protocols) + i, - &cp, &n, ccsid)) - return (curl_version_info_data *)NULL; + &cp, &n, ccsid)) + return NULL; } for(i = 0; i < sizeof(charfields) / sizeof(charfields[0]); i++) { cpp = (const char **)((char *)p + charfields[i]); if(*cpp && convert_version_info_string(cpp, &cp, &n, ccsid)) - return (curl_version_info_data *)NULL; + return NULL; } return id; @@ -527,9 +587,9 @@ void curl_certinfo_free_all(struct curl_certinfo *info) for(i = 0; i < info->num_of_certs; i++) curl_slist_free_all(info->certinfo[i]); - free((char *)info->certinfo); + untyped_free(info->certinfo); } - free((char *)info); + untyped_free(info); } } @@ -550,7 +610,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(result == CURLE_OK) { unsigned int ccsid; - char **cpp; + const char **cpp; struct curl_slist **slp; struct curl_certinfo *cipf; struct curl_certinfo *cipt; @@ -559,13 +619,15 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) case CURLINFO_STRING: ccsid = va_arg(arg, unsigned int); - cpp = (char **)paramp; + cpp = (const char **)paramp; if(*cpp) { - *cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID, NULL); + const char *s = curl_to_ccsid(*cpp, ccsid); - if(!*cpp) + if(!s) result = CURLE_OUT_OF_MEMORY; + else + *cpp = s; } break; @@ -576,12 +638,12 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) case CURLINFO_CERTINFO: cipf = *(struct curl_certinfo **)paramp; if(cipf) { - cipt = malloc(sizeof(*cipt)); + cipt = curlx_malloc(sizeof(*cipt)); if(!cipt) result = CURLE_OUT_OF_MEMORY; else { - cipt->certinfo = calloc(cipf->num_of_certs + 1, - sizeof(struct curl_slist *)); + cipt->certinfo = curlx_calloc(cipf->num_of_certs + 1, + sizeof(struct curl_slist *)); if(!cipt->certinfo) result = CURLE_OUT_OF_MEMORY; else { @@ -589,19 +651,20 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) cipt->num_of_certs = cipf->num_of_certs; for(i = 0; i < cipf->num_of_certs; i++) - if(cipf->certinfo[i]) - if(!(cipt->certinfo[i] = slist_convert(ccsid, - cipf->certinfo[i], - ASCII_CCSID))) { + if(cipf->certinfo[i]) { + cipt->certinfo[i] = slist_convert(cipf->certinfo[i], + ASCII_CCSID, ccsid); + if(!cipt->certinfo[i]) { result = CURLE_OUT_OF_MEMORY; break; } + } } } if(result != CURLE_OK) { curl_certinfo_free_all(cipt); - cipt = (struct curl_certinfo *)NULL; + cipt = NULL; } *(struct curl_certinfo **)paramp = cipt; @@ -617,7 +680,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) default: slp = (struct curl_slist **)paramp; if(*slp) { - *slp = slist_convert(ccsid, *slp, ASCII_CCSID); + *slp = slist_convert(*slp, ASCII_CCSID, ccsid); if(!*slp) result = CURLE_OUT_OF_MEMORY; } @@ -654,50 +717,36 @@ static void Curl_formadd_release_local(struct curl_forms *forms, int nargs, if(nargs != skip) if(Curl_is_formadd_string(forms[nargs].option)) if(forms[nargs].value) - free((char *)forms[nargs].value); + untyped_free(forms[nargs].value); - free((char *)forms); + untyped_free(forms); } static int Curl_formadd_convert(struct curl_forms *forms, int formx, int lengthx, unsigned int ccsid) { - int l; + size_t len = CURL_ZERO_TERMINATED; char *cp; - char *cp2; + struct dynbuf db; if(formx < 0 || !forms[formx].value) return 0; - if(lengthx >= 0) - l = (int)forms[lengthx].value; - else - l = strlen(forms[formx].value) + 1; - - cp = malloc(MAX_CONV_EXPANSION * l); - - if(!cp) - return -1; + curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH); - l = convert(cp, MAX_CONV_EXPANSION * l, ASCII_CCSID, forms[formx].value, l, - ccsid); + if(lengthx >= 0) + len = (size_t)forms[lengthx].value; - if(l < 0) { - free(cp); + if(dyn_addn_CCSID(&db, forms[formx].value, len, ccsid, ASCII_CCSID)) return -1; - } - - cp2 = realloc(cp, l); /* Shorten buffer to the string size. */ - - if(cp2) - cp = cp2; + cp = curlx_dyn_take(&db, &len); forms[formx].value = cp; if(lengthx >= 0) - forms[lengthx].value = (char *)l; /* Update length after conversion. */ + forms[lengthx].value = (char *)len; /* Update length after conversion. */ - return l; + return len; } CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, @@ -738,7 +787,7 @@ CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, /* Allocate the local curl_forms array. */ lformlen = ALLOC_GRANULE; - lforms = malloc(lformlen * sizeof(*lforms)); + lforms = curlx_malloc(lformlen * sizeof(*lforms)); if(!lforms) return CURL_FORMADD_MEMORY; @@ -752,7 +801,7 @@ CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, lengthx = -1; namex = -1; namelengthx = -1; - forms = (struct curl_forms *)NULL; + forms = NULL; va_start(arg, last_post); for(;;) { @@ -760,7 +809,7 @@ CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, if(nargs >= lformlen) { lformlen += ALLOC_GRANULE; - tforms = realloc(lforms, lformlen * sizeof(*lforms)); + tforms = curlx_realloc(lforms, lformlen * sizeof(*lforms)); if(!tforms) { result = CURL_FORMADD_MEMORY; @@ -793,7 +842,7 @@ CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, switch(option) { case CURLFORM_END: - forms = (struct curl_forms *)NULL; /* Leave array mode. */ + forms = NULL; /* Leave array mode. */ continue; case CURLFORM_ARRAY: @@ -977,34 +1026,27 @@ struct cfcdata { unsigned int ccsid; }; -static size_t Curl_formget_callback_ccsid(void *arg, const char *buf, - size_t len) +static size_t formget_callback_ccsid(void *arg, const char *buf, size_t len) { struct cfcdata *p; - char *b; - int l; + size_t olen; size_t ret; + struct dynbuf db; p = (struct cfcdata *)arg; if((long)len <= 0) - return (*p->append)(p->arg, buf, len); - - b = malloc(MAX_CONV_EXPANSION * len); + return p->append(p->arg, buf, len); - if(!b) - return (size_t)-1; + curlx_dyn_init(&db, MAX_CONV_EXPANSION * CURL_MAX_INPUT_LENGTH); - l = convert(b, MAX_CONV_EXPANSION * len, p->ccsid, buf, len, ASCII_CCSID); + if(dyn_addn_CCSID(&db, buf, len, ASCII_CCSID, p->ccsid)) + return (size_t) -1; - if(l < 0) { - free(b); - return (size_t)-1; - } - - ret = (*p->append)(p->arg, b, l); - free(b); - return ret == l ? len : -1; + olen = curlx_dyn_len(&db); + ret = p->append(p->arg, curlx_dyn_ptr(&db), olen); + curlx_dyn_free(&db); + return ret == olen ? len : -1; } int curl_formget_ccsid(struct curl_httppost *form, void *arg, @@ -1015,19 +1057,21 @@ int curl_formget_ccsid(struct curl_httppost *form, void *arg, lcfc.append = append; lcfc.arg = arg; lcfc.ccsid = ccsid; - return curl_formget(form, (void *)&lcfc, Curl_formget_callback_ccsid); + return curl_formget(form, (void *) &lcfc, formget_callback_ccsid); } CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) { CURLcode result; va_list arg; - char *s; - char *cp = NULL; + const char *s; unsigned int ccsid; + struct dynbuf db; curl_off_t pfsize; + size_t len; struct Curl_easy *data = curl; + curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH); va_start(arg, tag); switch(tag) { @@ -1131,11 +1175,11 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) case CURLOPT_USERPWD: case CURLOPT_XOAUTH2_BEARER: /* END TRANSLATABLE STRING OPTIONS */ - s = va_arg(arg, char *); + s = va_arg(arg, const char *); ccsid = va_arg(arg, unsigned int); if(s) { - s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); + s = curl_from_ccsid(s, ccsid); if(!s) { result = CURLE_OUT_OF_MEMORY; @@ -1144,14 +1188,14 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) } result = curl_easy_setopt(curl, tag, s); - free(s); + untyped_free(s); break; case CURLOPT_COPYPOSTFIELDS: /* Special case: byte count may have been given by CURLOPT_POSTFIELDSIZE prior to this call. In this case, convert the given byte count and replace the length according to the conversion result. */ - s = va_arg(arg, char *); + s = va_arg(arg, const char *); ccsid = va_arg(arg, unsigned int); pfsize = data->set.postfieldsize; @@ -1161,51 +1205,33 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) break; } - if(pfsize == -1) { - /* Data is null-terminated. */ - s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); - - if(!s) { - result = CURLE_OUT_OF_MEMORY; - break; - } - } + if(pfsize == -1) + result = dyn_addn_CCSID(&db, s, CURL_ZERO_TERMINATED, + ccsid, ASCII_CCSID); else { /* Data length specified. */ - size_t len; - - if(pfsize < 0 || pfsize > SIZE_MAX) { + if(pfsize < 0 || pfsize > SIZE_MAX) result = CURLE_OUT_OF_MEMORY; - break; - } - - len = pfsize; - pfsize = len * MAX_CONV_EXPANSION; - - if(pfsize > SIZE_MAX) - pfsize = SIZE_MAX; - - cp = malloc(pfsize); + else + result = dyn_addn_CCSID(&db, s, (size_t)pfsize, ccsid, ASCII_CCSID); + } - if(!cp) { - result = CURLE_OUT_OF_MEMORY; - break; - } + if(result) + break; - pfsize = convert(cp, pfsize, ASCII_CCSID, s, len, ccsid); + s = curlx_dyn_take(&db, &len); - if(pfsize < 0) { - result = CURLE_OUT_OF_MEMORY; - break; - } - - data->set.postfieldsize = pfsize; /* Replace data size. */ - s = cp; - cp = NULL; + /* The following lines give data ownership to the library without + copying them. */ + result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s); + if(!result) { + data->set.str_copypostfields = CURL_UNCONST(s); /* Adopt. */ + if(pfsize != -1) + data->set.postfieldsize = len; } + else + untyped_free(s); - result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s); - data->set.str_copypostfields = s; /* Give to library. */ break; default: @@ -1217,27 +1243,12 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) if(bp && bp->data && bp->len && ccsid != NOCONV_CCSID && ccsid != ASCII_CCSID) { - pfsize = (curl_off_t)bp->len * MAX_CONV_EXPANSION; - - if(pfsize > SIZE_MAX) - pfsize = SIZE_MAX; - - cp = malloc(pfsize); - - if(!cp) { - result = CURLE_OUT_OF_MEMORY; - break; - } - - pfsize = convert(cp, pfsize, ASCII_CCSID, bp->data, bp->len, ccsid); - - if(pfsize < 0) { - result = CURLE_OUT_OF_MEMORY; - break; - } + result = dyn_addn_CCSID(&db, bp->data, bp->len, ccsid, ASCII_CCSID); + if(result) + break; - blob.data = cp; - blob.len = pfsize; + blob.data = curlx_dyn_ptr(&db); + blob.len = curlx_dyn_len(&db); blob.flags = bp->flags | CURL_BLOB_COPY; bp = &blob; } @@ -1251,7 +1262,7 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) } va_end(arg); - free(cp); + curlx_dyn_free(&db); return result; } @@ -1259,7 +1270,10 @@ CURLcode curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...) char *curl_form_long_value(long value) { - /* ILE/RPG cannot cast an integer to a pointer. This procedure does it. */ + /* ILE/RPG cannot cast an integer to a pointer. This procedure does it. + As OS/400 is unable to dereference a pointer built from an integer only, + the goal here is only to keep the integer value as a (invalid) pointer + for a later reverse conversion. */ return (char *)value; } @@ -1287,51 +1301,46 @@ CURLcode curl_multi_setopt_RPGnum_(CURLM *multi, CURLMoption tag, char *curl_pushheader_bynum_cssid(struct curl_pushheaders *h, size_t num, unsigned int ccsid) { - char *d = (char *)NULL; - char *s = curl_pushheader_bynum(h, num); - - if(s) - d = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); - - return d; + return CURL_UNCONST(curl_to_ccsid(curl_pushheader_bynum(h, num), ccsid)); } char *curl_pushheader_byname_ccsid(struct curl_pushheaders *h, const char *header, unsigned int ccsidin, unsigned int ccsidout) { - char *d = (char *)NULL; + const char *d = NULL; if(header) { - header = dynconvert(ASCII_CCSID, header, -1, ccsidin, NULL); + const char *hdr = curl_from_ccsid(header, ccsidin); + + if(hdr) { + char *s = curl_pushheader_byname(h, hdr); - if(header) { - char *s = curl_pushheader_byname(h, header); - free((char *)header); + untyped_free(hdr); if(s) - d = dynconvert(ccsidout, s, -1, ASCII_CCSID, NULL); + d = curl_to_ccsid(s, ccsidout); } } - return d; + return CURL_UNCONST(d); } static CURLcode mime_string_call(curl_mimepart *part, const char *string, unsigned int ccsid, CURLcode (*mimefunc)(curl_mimepart *part, const char *string)) { - char *s = (char *)NULL; + const char *s; CURLcode result; if(!string) return mimefunc(part, string); - s = dynconvert(ASCII_CCSID, string, -1, ccsid, NULL); + s = curl_from_ccsid(string, ccsid); if(!s) return CURLE_OUT_OF_MEMORY; result = mimefunc(part, s); - free(s); + untyped_free(s); return result; } @@ -1368,58 +1377,68 @@ CURLcode curl_mime_filedata_ccsid(curl_mimepart *part, const char *filename, CURLcode curl_mime_data_ccsid(curl_mimepart *part, const char *data, size_t datasize, unsigned int ccsid) { - char *s = (char *)NULL; + struct dynbuf db; CURLcode result; - int osize; if(!data) return curl_mime_data(part, data, datasize); - s = dynconvert(ASCII_CCSID, data, datasize, ccsid, &osize); - if(!s) - return CURLE_OUT_OF_MEMORY; - result = curl_mime_data(part, s, osize); - free(s); + curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH); + + result = dyn_addn_CCSID(&db, data, datasize, ccsid, ASCII_CCSID); + + if(!result) { + size_t osize; + char *newdata = curlx_dyn_take(&db, &osize); + + result = curl_mime_data(part, newdata, datasize == CURL_ZERO_TERMINATED ? + datasize : osize); + untyped_free(newdata); + } + return result; } CURLUcode curl_url_get_ccsid(CURLU *handle, CURLUPart what, char **part, unsigned int flags, unsigned int ccsid) { - char *s = (char *)NULL; + char *s = NULL; CURLUcode result; if(!part) return CURLUE_BAD_PARTPOINTER; - *part = (char *)NULL; + *part = NULL; result = curl_url_get(handle, what, &s, flags); if(result == CURLUE_OK) { if(s) { - *part = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); - if(!*part) + const char *d = curl_to_ccsid(s, ccsid); + + if(d) + *part = CURL_UNCONST(d); + else result = CURLUE_OUT_OF_MEMORY; } } if(s) - free(s); + untyped_free(s); return result; } CURLUcode curl_url_set_ccsid(CURLU *handle, CURLUPart what, const char *part, unsigned int flags, unsigned int ccsid) { - char *s = (char *)NULL; + const char *s = NULL; CURLUcode result; if(part) { - s = dynconvert(ASCII_CCSID, part, -1, ccsid, NULL); + s = curl_from_ccsid(part, ccsid); if(!s) return CURLUE_OUT_OF_MEMORY; } result = curl_url_set(handle, what, s, flags); if(s) - free(s); + untyped_free(s); return result; } @@ -1429,11 +1448,11 @@ curl_easy_option_by_name_ccsid(const char *name, unsigned int ccsid) const struct curl_easyoption *option = NULL; if(name) { - char *s = dynconvert(ASCII_CCSID, name, -1, ccsid, NULL); + const char *s = curl_from_ccsid(name, ccsid); if(s) { option = curl_easy_option_by_name(s); - free(s); + untyped_free(s); } } @@ -1445,12 +1464,12 @@ const char * curl_easy_option_get_name_ccsid(const struct curl_easyoption *option, unsigned int ccsid) { - char *name = NULL; + const char *name = NULL; if(option && option->name) - name = dynconvert(ccsid, option->name, -1, ASCII_CCSID, NULL); + name = curl_to_ccsid(option->name, ccsid); - return (const char *)name; + return name; } /* Header API CCSID support. */ @@ -1461,12 +1480,12 @@ CURLHcode curl_easy_header_ccsid(CURL *easy, const char *name, size_t index, CURLHcode result = CURLHE_BAD_ARGUMENT; if(name) { - char *s = dynconvert(ASCII_CCSID, name, -1, ccsid, NULL); + const char *s = curl_from_ccsid(name, ccsid); result = CURLHE_OUT_OF_MEMORY; if(s) { result = curl_easy_header(easy, s, index, origin, request, hout); - free(s); + untyped_free(s); } } From bd81f15ec295a2d2719babc7677b343938c7120a Mon Sep 17 00:00:00 2001 From: Martin Dukek Date: Fri, 28 Aug 2026 14:38:47 +0200 Subject: [PATCH 1009/1062] schannel: clear PFX password before free Closes #22721 --- lib/vtls/schannel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index a24a3f4599b1..a6cbd1487fca 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -484,6 +484,7 @@ static CURLcode get_client_cert(struct Curl_cfilter *cf, cert_store = PFXImportCertStore(&datablob, pszPassword, PKCS12_NO_PERSIST_KEY); + curlx_memzero(pszPassword, sizeof(WCHAR) * (pwd_len + 1)); curlx_free(pszPassword); } if(!blob) From 745c55f17ac3ec6a96365c2097dcb3e6c7292052 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 28 Aug 2026 16:14:45 +0200 Subject: [PATCH 1010/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 93 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 14 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 452a8fbfaa94..e05154eefb2d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1514 - Contributors: 3781 + Authors: 1515 + Contributors: 3782 This release includes the following changes: @@ -39,6 +39,7 @@ This release includes the following bugfixes: o cf-socket: disable TCP SYN retransmissions for localhost on Windows [164] o cfilters: fix event-based connection shutdown [91] o cmake/FindLibgsasl: fix to set `LIBGSASL_VERSION` with pkg-config detection [229] + o cmake: check libgsasl version at configure time [277] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] o cmake: fix not to build `tunits` when `BUILD_CURL_EXE=OFF` [7] o cmake: flatten build tree, tidy up base dir variables [12] @@ -63,9 +64,12 @@ This release includes the following bugfixes: o conncache: don't assume curl_off_t increment wrap-around [138] o connect: connection close tweaks [112] o connect: only set connect timer on first socket [206] + o connection reuse: age check [261] + o connection reuse: check SSL configs when doing a scheme upgrade [249] o connections: use admin handles only for maintenance [213] o content_encoding: exact-match the identity transfer-coding token [189] o content_encoding: give a clear error on multi-member gzip [46] + o cookie: improve TAB handling [258] o cookie: refuse to load cookies set against a PSL domain [139] o CREDENTIALS.md: remove comment about empty user/pass [50] o ctype: exclude control bytes from ISPRINT and ISGRAPH [119] @@ -76,6 +80,7 @@ This release includes the following bugfixes: o curl_url_set.md: expand the CURLU_NO_AUTHORITY description [134] o curl_ws_meta.md: polish and better vocabulary [19] o CURLOPT_HEADERFUNCTION.md: document folded header unfolding [53] + o CURLOPT_SOCKOPTFUNCTION.md: ALREADY_CONNECTED does not work for HTTP/3 [262] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] @@ -91,19 +96,24 @@ This release includes the following bugfixes: o docs: mention possible auth option conflicts [114] o docs: remove doubled word in SECURITY-ADVISORY.md [183] o DoH: improvements [203] + o FAQ: correct an option typo [278] o file: support directory listing on Windows [205] o filter: change time reporting [235] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gitignore: maintenance updates [170] o gopher: reject CR and LF in the selector [1] + o h2 push: use squeaky clean easy handle [246] o h2: bootstrap max streams from multi handle if in use [132] o h3-proxy: fix NULL deref when non-:status header arrives before :status [167] o Happy Eyeballing v3: resolution delay of 25ms [232] o header api: add guards [168] o headers: name the arguments the way the definitions name them [234] o HISTORY: add when c-ares support was introduced (2004) + o HISTORY: September 1999: started using CVS o hostip: only cache negative resolves for authoritative answers [16] + o hsts: only match the exact strings [269] + o http digest: tie peer/credentials on input [264] o http2: make server push transfers inherit share from parent [81] o http: avoid length underflow in Curl_compareheader [78] o http: custom Authorization: header overrides Negotiate [223] @@ -112,8 +122,10 @@ This release includes the following bugfixes: o http: trim custom header name before the Authorization drop [17] o httpsrr: DoH with HTTPS, fix response handling [113] o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] + o imap: APPEND CRLF fix [256] o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] + o ipv6 scope_id: set from first peer [242] o keylog: add a random size argument to Curl_tls_keylog_write() [180] o ldap: base64-encode LDIF values beginning with colon or less-than [218] o ldap: reject control characters in URL-decoded filter values [196] @@ -129,15 +141,21 @@ This release includes the following bugfixes: o lib: ratelimit timestamps [14] o lib: silence gcc-16 compiler warnings `-Wmaybe-uninitialized` [243] o lib: update mentions of the legacy "sessionhandle" [157] + o libcurl.md: emphasize that the output needs checking [259] o libcurl.pc: add `License` tag [190] o libcurl.pc: add Copyright tag to the pkgconf file o libcurl.pc: add the Link.ABI and Source tags [210] + o macos sectrust: fail ocsp verify when not builtin [252] + o Makefile.am: improve etags [257] o mbedtls: enforce verifyhost when verifypeer is disabled [208] o mbedtls: replace `memset()` with `psa_hash_operation_init()` [28] o md5: replace magic numbers with `MD5_DIGEST_LEN` [122] o mime.c: avoid integer overflow in base64 size calculation [105] o mime: reject CR and LF in mail part name and filename [30] o mod_curltest: fix compiler warnings [49] + o mprintf: acknowledge %F [245] + o mprintf: avoid never-ending loop for positive-infinite [247] + o mprintf: fix long double output [250] o mqtt: reject control bytes in the topic [43] o multi: cap expire times to INT_MAX internally [216] o multi: forbid curl_easy_pause from within multi socket callback [22] @@ -146,23 +164,30 @@ This release includes the following bugfixes: o multi: shrink expire timer indices [199] o multi: timeout improvements [209] o multi: use index list for expire timeouts [197] + o multi: xfer table initial size and growth [255] o multihandle: move two struct fields [163] o ngtcp2+openssL: fix early data [225] + o ngtcp2: avoid NULL deref in cf_ngtcp2_send [260] o ngtcp2: clean up after ngtcp2 in `curl_global_cleanup` [126] o ngtcp2: let verify failures win over expiry processing errors [98] o openldap: handle Curl_sasl_continue() returns better [45] o openssl+sectrust: fix session reuse [4] o openssl+sectrust: move session verified set into result check [82] o openssl: avoid conn reuse if provider is used [214] + o openssl: aws-lc ocsp workaround [263] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: fix DER buffer leak in Apple SecTrust verification [217] o openssl: no server cert is only okay if also not pinned [226] o openssl: prefer modern API flavors for `EVP_MD_CTX` new/free [47] o openssl: replace stray legacy API variant with `EVP_DigestInit_ex()` [27] + o os400: port latest header files changes to ILE/RPG interface [241] + o os400: rewrite upper ebcdic wrappers using dynbuf [227] o progress: cleanup, less memory [179] o protocol: simpler Curl_getn_scheme runs faster [239] + o proxy: CONNECT trailers handling [251] o psl: update a comment to understandable English [162] o pytest: update two H3 tests for nghttp3 1.18.0+ [158] + o quic: upload improvements [276] o quiche: set the max field section size [100] o rtsp: refactor method handling and improve error checks [161] o runtests: allow comments in `setenv` section, merge sections in test433 [89] @@ -170,7 +195,9 @@ This release includes the following bugfixes: o runtests: flush cached test parts when (re)loading a file [95] o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] + o schannel: clear PFX password before free [222] o schannel: fix error check logic in `get_client_cert()` file reader [144] + o schannel: refresh stream sizes after renegotiation [248] o schannel: reuse the send buffer [195] o schannel: shut off experimental TLS 1.3 support for Win 10 [25] o scorecard: fix `max_upload` init value in `ul_parallel()` [142] @@ -179,8 +206,11 @@ This release includes the following bugfixes: o scripts: use end-of-options marker in `cd`, `mkdir`, `mv`, `sha256sum` commands [34] o servers: fix HANDLE leak in UWP builds [65] o servers: fix to reverse `SA_RESTART` option for `sigaction()` on modern codepath [131] + o setopt: allow setting a referer from CURLINFO_REFERER [254] o setopt: error for CURLOPT_SHARE when easy handle is used [68] + o setopt: make NULL `CURLOPT_AWS_SIGV4` disable aws-sigv4 auth [270] o setopt: return OK earlier for the deprecated h2 dep options [77] + o share unlink: forget connection [244] o smtp: reject CR and LF in the envelope address [37] o spacecheck: cap number of lines per file [111] o spnego_sspi: pass channel bindings on initial context [230] @@ -223,6 +253,7 @@ This release includes the following bugfixes: o tool: init progress bar on demand [39] o tool: remove duplicate setopts [94] o tool_cb_hdr: de-duplicate filename setter [24] + o tool_cb_hdr: do not truncate etags output to stdout [273] o tool_cb_prg: avoid integer overflows [93] o tool_doswin: add stdin relay auth [130] o tool_doswin: don't use `TerminateThread` in stdin relay [238] @@ -230,6 +261,7 @@ This release includes the following bugfixes: o tool_operate: limit `is_using_schannel()` call to Windows [140] o tool_operate: only check for schannel if on windows [187] o tool_operate: remove call to abort() [23] + o tool_paramhlp: --proto only supports one modifier [268] o tool_xattr: add support for Windows alternate data stream [129] o typecheck-gcc: allow passing `char[]` as callback data [153] o uint-spbset: reused empty chunks [67] @@ -258,6 +290,7 @@ This release includes the following bugfixes: o vtls_scache: use case sensitive path match o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] o websocket: pause writing and meta data fix [135] + o wolfssl: do not run Curl_wssl_setup_x509_store() twice [265] o wolfssl: fix build for wolfssl without bio chain support [75] o ws: fix write callback error handling [204] o ws: pause/unpause write handling [55] @@ -290,18 +323,18 @@ advice from friends like these: Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, felix h, firexinghe on github, Fred Klassen, GLaDOS-418 on github, Graham Campbell, - Hendrik Hübner, HwangRock, itzTanos29, Jace Laquerre, Jayanth Vennamreddy, - Joel Depooter, Johannes Schindelin, Keng-Yu Lin, Kieran Gannon, - kit-ty-kate on github, Laurent Sabourin, Marcel Jamin, Martin Dukek, - martin-fzi on github, Matthew John Cheetham, Max Dymond, Memduh Çelik, - Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, - Ramesh Adhikari, Rarylson Freitas, Ray Satiro, renovate[bot], Rito Rhymes, - RMMoreton on github, Roger Leigh, Ron Kuper, Ross Burton, Sameeh Jubran, - Sam James, Samuel Dainard, Samuel Henrique, Scott Talbert, sdgh179 on github, - Sergei Zimmerman, smaeljaish on hackerone, Stanislav Fort, Stefan Eissing, - Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, Viktor Szakats, - xmoezzz on github, Yoshiro Yoneya, zaveshaa - (80 contributors) + Hendrik Hübner, HwangRock, Igor Morgenstern, itzTanos29, Jace Laquerre, + Jayanth Vennamreddy, Joel Depooter, Johannes Schindelin, Keng-Yu Lin, + Kieran Gannon, kit-ty-kate on github, Laurent Sabourin, Marcel Jamin, + Martin Dukek, martin-fzi on github, Matthew John Cheetham, Max Dymond, + Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, + Ralf Mueller, Ramesh Adhikari, Rarylson Freitas, Ray Satiro, renovate[bot], + Rito Rhymes, RMMoreton on github, Roger Leigh, Ron Kuper, Ross Burton, + Sameeh Jubran, Sam James, Samuel Dainard, Samuel Henrique, Scott Talbert, + sdgh179 on github, Sergei Zimmerman, smaeljaish on hackerone, Stanislav Fort, + Stefan Eissing, Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, + Viktor Szakats, xmoezzz on github, Yoshiro Yoneya, zaveshaa + (81 contributors) References to bug reports and discussions on issues: @@ -526,10 +559,12 @@ References to bug reports and discussions on issues: [219] = https://curl.se/bug/?i=20521 [220] = https://curl.se/bug/?i=22623 [221] = https://curl.se/bug/?i=22629 + [222] = https://curl.se/bug/?i=22721 [223] = https://curl.se/bug/?i=22610 [224] = https://curl.se/bug/?i=22621 [225] = https://curl.se/bug/?i=22649 [226] = https://curl.se/bug/?i=22660 + [227] = https://curl.se/bug/?i=22675 [228] = https://curl.se/bug/?i=22664 [229] = https://curl.se/bug/?i=22662 [230] = https://curl.se/bug/?i=22466 @@ -543,4 +578,34 @@ References to bug reports and discussions on issues: [238] = https://curl.se/bug/?i=22383 [239] = https://curl.se/bug/?i=22658 [240] = https://curl.se/bug/?i=22656 + [241] = https://curl.se/bug/?i=22695 + [242] = https://curl.se/bug/?i=22717 [243] = https://curl.se/bug/?i=22651 + [244] = https://curl.se/bug/?i=22718 + [245] = https://curl.se/bug/?i=22719 + [246] = https://curl.se/bug/?i=22715 + [247] = https://curl.se/bug/?i=22713 + [248] = https://curl.se/bug/?i=22692 + [249] = https://curl.se/bug/?i=22710 + [250] = https://curl.se/bug/?i=22706 + [251] = https://curl.se/bug/?i=22708 + [252] = https://curl.se/bug/?i=22690 + [254] = https://curl.se/bug/?i=22709 + [255] = https://curl.se/bug/?i=22705 + [256] = https://curl.se/bug/?i=22702 + [257] = https://curl.se/bug/?i=22680 + [258] = https://curl.se/bug/?i=22699 + [259] = https://curl.se/bug/?i=22700 + [260] = https://curl.se/bug/?i=22698 + [261] = https://curl.se/bug/?i=22671 + [262] = https://curl.se/bug/?i=22696 + [263] = https://curl.se/bug/?i=22694 + [264] = https://curl.se/bug/?i=22683 + [265] = https://curl.se/bug/?i=22689 + [268] = https://curl.se/bug/?i=22687 + [269] = https://curl.se/bug/?i=22643 + [270] = https://curl.se/bug/?i=22681 + [273] = https://curl.se/bug/?i=22685 + [276] = https://curl.se/bug/?i=22669 + [277] = https://curl.se/bug/?i=22667 + [278] = https://curl.se/bug/?i=22679 From cbd18197b5e47945fdf2aed4cf53f0c93a0443de Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Fri, 21 Aug 2026 19:59:05 +0000 Subject: [PATCH 1011/1062] include: include when building for modern Linux. Fix curl compilation errors when building on Linux with LLVM-libc (libc.llvm.org). It provides fd_set type (and associated macro) in according to POSIX. curl uses fd_set type (and associated FD* macro) that are provided by according to POSIX. In practice, some libc implementations (notably, glibc on Linux) transitively include this header from , but other implementations with stricter POSIX compliance may not do that (see example of LLVM-libc above). Fix curl compilation under LLVM-libc by including in public when building for Linux and _POSIX_C_SOURCE is set (by the user or by system libc) to be at least 200112L, referencing the POSIX standard that added / fd_set. Closes #22635 --- include/curl/curl.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/curl/curl.h b/include/curl/curl.h index 0797c92cad07..b904e4953064 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -93,7 +93,9 @@ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ - defined(__sun__) || defined(__serenity__) || defined(__vxworks__) + defined(__sun__) || defined(__serenity__) || defined(__vxworks__) || \ + (defined(__linux__) && defined(_POSIX_C_SOURCE) && \ + _POSIX_C_SOURCE >= 200112L) #include #endif From 0ebe0e978535f10f6fe54536ca2e2e6b304a4d5a Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 28 Aug 2026 11:43:53 +0200 Subject: [PATCH 1012/1062] tool: checkfds, open on null device When stdin/out/err are closed on tool start, checkfds() tries to fill them by invoking `pipe()` which is may allocate file descriptors in an unwanted order and may create a pipe between stdout and stdin which is not what we want. Use '/dev/null' if HAVE_PIPE is defined to open the closed descriptors. Caveat: I am not 100% sure that HAVE_PIPE implies that /dev/null works, but it seems likely? Closes #22716 --- src/tool_main.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/tool_main.c b/src/tool_main.c index d017f702578c..d3eff9c91956 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -73,7 +73,6 @@ int _CRT_glob = 0; /* if we build a static library for unit tests, there is no main() function */ #ifndef UNITTESTS -#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) /* * Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are * open before starting to run. Otherwise, the first three network @@ -86,17 +85,27 @@ int _CRT_glob = 0; */ static int main_checkfds(void) { - int fd[2]; - while((fcntl(STDIN_FILENO, F_GETFD) == -1) || - (fcntl(STDOUT_FILENO, F_GETFD) == -1) || - (fcntl(STDERR_FILENO, F_GETFD) == -1)) - if(pipe(fd)) +#if defined(HAVE_FCNTL) && defined(HAVE_PIPE) + const char * const devnull = "/dev/null"; + int fd; + while((fcntl(STDIN_FILENO, F_GETFD) == -1)) { + fd = curlx_open(devnull, O_RDONLY); + if(fd < 0) return 1; + } + while((fcntl(STDOUT_FILENO, F_GETFD) == -1)) { + fd = curlx_open(devnull, O_WRONLY); + if(fd < 0) + return 1; + } + while((fcntl(STDERR_FILENO, F_GETFD) == -1)) { + fd = curlx_open(devnull, O_WRONLY); + if(fd < 0) + return 1; + } +#endif /* HAVE_FCNTL && HAVE_PIPE */ return 0; } -#else -#define main_checkfds() 0 -#endif #ifdef CURL_MEMDEBUG static void memory_tracking_init(void) From 273534cdc71dcc3b844ef46da4d48dd6d9a627bf Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 28 Aug 2026 17:04:35 +0200 Subject: [PATCH 1013/1062] sspi: enable channel-binding in mingw-w64 <9 builds Drop `SECPKG_ATTR_ENDPOINT_BINDINGS` guards and declare the missing macro and struct for mingw-w64 <9. To bring these builds on par with the rest of supported Windows toolchains. Follow-up to 09662337441c40c23da7b557c4cceacd7cc3b76e #3321 Closes #22725 --- lib/curl_sspi.h | 10 ++++++++++ lib/http_negotiate.c | 2 +- lib/http_ntlm.c | 2 -- lib/urldata.h | 4 ++-- lib/vauth/ntlm_sspi.c | 6 ------ lib/vauth/spnego_sspi.c | 6 ------ lib/vauth/vauth.h | 4 ---- lib/vtls/schannel.c | 2 -- 8 files changed, 13 insertions(+), 23 deletions(-) diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 992e01a9f255..6b10bdb2e70c 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -29,6 +29,16 @@ #include +/* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ +#ifndef SECPKG_ATTR_ENDPOINT_BINDINGS +#define SECPKG_ATTR_ENDPOINT_BINDINGS 26 +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct { + unsigned long BindingsLength; + SEC_CHANNEL_BINDINGS *Bindings; +} SecPkgContext_Bindings; +#endif + CURLcode Curl_sspi_global_init(void); void Curl_sspi_global_cleanup(void); diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 7aaa6878d113..5e8798134f55 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -102,7 +102,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, } /* Supports SSL channel binding for Windows ISS extended protection */ -#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) +#ifdef USE_WINDOWS_SSPI neg_ctx->sslContext = conn->sslContext; #endif /* Check if the connection is using SSL and get the channel binding data */ diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index b0a73a6c8a0e..a1de75fc079d 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -168,9 +168,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(!Curl_pSecFn) return result; } -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS ntlm->sslContext = conn->sslContext; -#endif #endif Curl_bufref_init(&ntlmmsg); diff --git a/lib/urldata.h b/lib/urldata.h index 65f02af0ffd8..b070b5fd48c3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -325,8 +325,8 @@ struct connectdata { char *options; /* options string, allocated */ /*************** Request - specific items ************/ -#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - CtxtHandle *sslContext; /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ +#ifdef USE_WINDOWS_SSPI + CtxtHandle *sslContext; #endif #ifdef USE_NTLM diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 17fb1d2922b3..85c7256025ef 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -242,9 +242,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SecBufferDesc type_3_desc; SECURITY_STATUS status; unsigned long attrs; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS SecPkgContext_Bindings pkgBindings = { 0, NULL }; -#endif (void)creds; @@ -256,7 +254,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_2_bufs[0].pvBuffer = ntlm->input_token; type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* SSL context comes from schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc @@ -277,7 +274,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_2_bufs[1].pvBuffer = pkgBindings.Bindings; } } -#endif /* Setup the type-3 "output" security buffer */ type_3_desc.ulVersion = SECBUFFER_VERSION; @@ -297,10 +293,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, &type_3_desc, &attrs, NULL); -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS if(pkgBindings.Bindings) Curl_pSecFn->FreeContextBuffer(pkgBindings.Bindings); -#endif if(status != SEC_E_OK) { infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 7d3c9ce2d86d..19c4fe17717e 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -93,9 +93,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SecBufferDesc chlg_desc; SecBufferDesc resp_desc; unsigned long attrs; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS SecPkgContext_Bindings pkgBindings = { 0, NULL }; -#endif if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server @@ -222,7 +220,6 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); } -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* SSL context comes from Schannel. * When extended protection is used in IIS server, pass its channel * bindings on the initial call too. HTTP Negotiate can create and send a @@ -242,7 +239,6 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, binding_buf->pvBuffer = pkgBindings.Bindings; } } -#endif /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; @@ -269,10 +265,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, &resp_desc, &attrs, NULL); } -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS if(pkgBindings.Bindings) Curl_pSecFn->FreeContextBuffer(pkgBindings.Bindings); -#endif /* Free the decoded challenge as it is not required anymore */ curlx_free(chlg); diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index f8ec563e92bb..bddd55f8d56a 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -164,9 +164,7 @@ struct ntlmdata { /* The sslContext is used for the Schannel bindings. The * api is available on the Windows 7 SDK and later. */ -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS CtxtHandle *sslContext; -#endif CredHandle *credentials; CtxtHandle *context; SEC_WINNT_AUTH_IDENTITY_EX identity; @@ -298,9 +296,7 @@ struct negotiatedata { #endif #else #ifdef USE_WINDOWS_SSPI -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS CtxtHandle *sslContext; -#endif SECURITY_STATUS status; CredHandle *credentials; CtxtHandle *context; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index a6cbd1487fca..36def64c118c 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1748,14 +1748,12 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, connssl->state = ssl_connection_complete; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ /* When SSPI is used in combination with Schannel * we need the Schannel context to create the Schannel * binding to pass the IIS extended protection checks. * Available on Windows 7 or later. */ cf->conn->sslContext = &backend->ctxt->ctxt_handle; -#endif *done = TRUE; } From cdec5b423a2a323b6a286a416b33fe935bfd290f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 28 Aug 2026 23:03:03 +0200 Subject: [PATCH 1014/1062] HISTORY.md: PSL support in 2015 --- docs/HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 6d77301e19f3..479d9116d523 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -317,6 +317,8 @@ September: started "everything curl". A separate stand-alone book documenting curl and related info in perhaps a more tutorial style rather than a reference, +September: initial support for the Public Suffix List for cookies. + December: Public Suffix List ## 2016 From f8992b6a177a2ccfb5dc04137088534bad68681c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 26 Aug 2026 11:53:36 +0200 Subject: [PATCH 1015/1062] dist: fix to drop test bundle .c files from the source tarball Drop `libtests.c`, `servers.c`, `tunits.c`, `units.c` from source distro tarball. Minor downside: it needs the bundle name be hardcoded into the `nodist__SOURCES =` lines. Also: - confirmed that `CLEANFILES` is still required after this. Ref: #22722 (alternate solution) Ref: #22723 (experiment) Follow-up to 71cf0d1fca9e1f53524e1545ef0c08d174458d80 #14772 Closes #22686 --- tests/libtest/Makefile.am | 1 + tests/server/Makefile.am | 1 + tests/tunit/Makefile.am | 1 + tests/unit/Makefile.am | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/libtest/Makefile.am b/tests/libtest/Makefile.am index 1fb110fb7906..774758705d42 100644 --- a/tests/libtest/Makefile.am +++ b/tests/libtest/Makefile.am @@ -73,6 +73,7 @@ $(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) --test $(TESTS_C) lib1521.c > $(BUNDLE).c noinst_PROGRAMS = $(BUNDLE) +nodist_libtests_SOURCES = $(BUNDLE).c LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_PC_LIBS_PRIVATE@ CLEANFILES = $(BUNDLE).c lib1521.c diff --git a/tests/server/Makefile.am b/tests/server/Makefile.am index 5427c6df0bc2..6b9d40bd3126 100644 --- a/tests/server/Makefile.am +++ b/tests/server/Makefile.am @@ -54,6 +54,7 @@ $(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(CURLX_C) $(TOOLX_C) --test $(TESTS_C) > $(BUNDLE).c noinst_PROGRAMS = $(BUNDLE) +nodist_servers_SOURCES = $(BUNDLE).c LDADD = @CURL_NETWORK_AND_TIME_LIBS@ CLEANFILES = $(BUNDLE).c diff --git a/tests/tunit/Makefile.am b/tests/tunit/Makefile.am index c45a4b908ba6..c6ce17b58d0f 100644 --- a/tests/tunit/Makefile.am +++ b/tests/tunit/Makefile.am @@ -60,6 +60,7 @@ $(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(TESTS_C @PERL@ $(top_srcdir)/scripts/mk-unity.pl --test $(TESTS_C) > $(BUNDLE).c noinst_PROGRAMS = $(BUNDLE) +nodist_tunits_SOURCES = $(BUNDLE).c LDADD = $(top_builddir)/src/libcurltool.la $(top_builddir)/lib/libcurl.la CLEANFILES = $(BUNDLE).c else diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 34fbbf8f5f33..3a71a3c5ff86 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -64,6 +64,7 @@ $(top_builddir)/lib/unitprotos.h: (cd $(top_builddir)/lib && $(MAKE) unitprotos.h) noinst_PROGRAMS = $(BUNDLE) +nodist_units_SOURCES = $(BUNDLE).c LDADD = $(top_builddir)/lib/libcurlu.la CLEANFILES = $(BUNDLE).c else From 95c1e8915dce64606bd753fd47fc0bd236e31cd6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 28 Aug 2026 17:38:13 +0200 Subject: [PATCH 1016/1062] cookie: cookies set for an exact PSL domain is host-only Added test 2318 to verify. Reported-by: Stanislav Fort (Aisle Research) Closes #22730 --- lib/cookie.c | 15 +++++++--- tests/data/Makefile.am | 1 + tests/data/test1136 | 2 +- tests/data/test2318 | 65 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test798 | 3 +- 5 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 tests/data/test2318 diff --git a/lib/cookie.c b/lib/cookie.c index bf3374d04d56..169460179a21 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -798,7 +798,7 @@ static CURLcode parse_netscape(struct Cookie *co, } static bool is_public_suffix(struct Curl_easy *data, - const struct Cookie *co, + struct Cookie *co, const char *domain) { #ifdef USE_LIBPSL @@ -838,6 +838,13 @@ static bool is_public_suffix(struct Curl_easy *data, Curl_strntolower(lcase, domain, dlen); lcase[dlen] = 0; acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + + /* if the cookie is acceptable, but is set for a PSL domain, then it + cannot be tailmatching */ + if(acceptable && co->tailmatch && + curl_strequal(co->domain, domain) && + psl_is_public_suffix(psl, lcookie)) + co->tailmatch = FALSE; } else { /* libpsl says localhost is a PSL, we think not */ @@ -1023,6 +1030,9 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, /* The __Secure- prefix only requires that the cookie be set secure */ goto fail; + if(!(flags & COOKIE_NOPSL) && is_public_suffix(data, co, domain)) + goto fail; + if(co->prefix_host) { /* * The __Host- prefix requires the cookie to be secure, have a "/" path @@ -1046,9 +1056,6 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, if(!(flags & COOKIE_NOEXPIRE)) remove_expired(ci); - if(!(flags & COOKIE_NOPSL) && is_public_suffix(data, co, domain)) - goto fail; - /* * Now we have parsed the incoming line, we must now check if this supersedes * an already existing cookie, which it may if the previous have the same diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b99803417f52..9614fa98006f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1940,6 +1940,7 @@ TESTCASES = \ test2309 \ test2310 \ test2311 \ + test2318 \ test2349 \ \ test2397 \ diff --git a/tests/data/test1136 b/tests/data/test1136 index 6d9cde3b4cef..4213e6e45753 100644 --- a/tests/data/test1136 +++ b/tests/data/test1136 @@ -59,7 +59,7 @@ http://www.example.ck/%TESTNUMBER http://www.ck/%TESTNUMBER http://z-1.compute-1 # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -.z-1.compute-1.amazonaws.com TRUE / FALSE 0 test5 forbidden5 +z-1.compute-1.amazonaws.com FALSE / FALSE 0 test5 forbidden5 .www.ck TRUE / FALSE 0 test4 allowed4 .www.example.ck TRUE / FALSE 0 test2 allowed2 diff --git a/tests/data/test2318 b/tests/data/test2318 new file mode 100644 index 000000000000..ab778b52cb65 --- /dev/null +++ b/tests/data/test2318 @@ -0,0 +1,65 @@ + + + + +cookies + + + + + +HTTP/1.1 200 OK +Content-Length: 6 +Set-Cookie: sid=DOMAIN_SECRET; Domain=github.io; Path=/ + +-foo- + + + +HTTP/1.1 200 OK +Content-Length: 6 +Content-Type: text/html + +-foo- + + + +# Client-side + + +PSL +cookies + + +http + + +Get cookies from PSL domain then use *.domain + + +--resolve github.io:%HTTPPORT:%HOSTIP --resolve attacker.github.io:%HTTPPORT:%HOSTIP -b "" http://github.io:%HTTPPORT/%TESTNUMBER http://attacker.github.io:%HTTPPORT/%TESTNUMBER0002 http://github.io:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: github.io:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: attacker.github.io:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER HTTP/1.1 +Host: github.io:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: sid=DOMAIN_SECRET + + + + diff --git a/tests/data/test798 b/tests/data/test798 index b3937e571399..8208a2c923d7 100644 --- a/tests/data/test798 +++ b/tests/data/test798 @@ -39,6 +39,7 @@ HTTP cookies in a folded header -4 http://localhost:%HTTPPORT/we/want/%TESTNUMBER -b none -c %LOGDIR/jar%TESTNUMBER.txt +PSL cookies local-http @@ -58,7 +59,7 @@ Accept: */* # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -#HttpOnly_.localhost TRUE /p4 TRUE 0 flavor tasty +#HttpOnly_localhost FALSE /p4 TRUE 0 flavor tasty From c2a04c080d79e1eb5d99bc0a73fd71710aa6d345 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 28 Aug 2026 16:19:15 +0200 Subject: [PATCH 1017/1062] distfiles.sh: check tarball for unexpected files Verify if the source tarball has any files that we did not expect. Follow-up to f8992b6a177a2ccfb5dc04137088534bad68681c #22686 Closes #22724 --- .github/scripts/distfiles.sh | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/scripts/distfiles.sh b/.github/scripts/distfiles.sh index 5eb6a470a8ec..1fb7d7e31034 100755 --- a/.github/scripts/distfiles.sh +++ b/.github/scripts/distfiles.sh @@ -9,7 +9,9 @@ set -eu gitonly=".git* -^.* +^.circleci/* +^.dir-locals.el +^.mailmap ^appveyor.* ^GIT-INFO.md ^README.md @@ -28,6 +30,24 @@ gitonly=".git* ^scripts/release-notes.pl ^scripts/singleuse.pl" +taronly="^Makefile.in$ +/Makefile.in$ +^aclocal.m4$ +^compile$ +^configure$ +^config.guess$ +^config.sub$ +^depcomp$ +^docs/RELEASE-TOOLS.md$ +^docs/libcurl/libcurl-symbols.md$ +^install-sh$ +^lib/curl_config.h.in$ +^ltmain.sh$ +^m4/libtool.m4$ +^m4/lt*.m4$ +^missing$ +^src/tool_hugehelp.c$" + tarfiles="$(mktemp)" gitfiles="$(mktemp)" @@ -48,7 +68,17 @@ echo 'Only in tarball:' echo "${dif}" | grep '^-' || true echo +exitcode=0 + +echo 'Unexpected in tarball:' +if echo "${dif}" | grep '^-' | sed 's|^-||g' \ + | grep -v -E "($(printf '%s' "${taronly}" | tr $'\n' '|' | sed -e 's|\.|\\.|g' -e 's|\*|.+|g'))$"; then + exitcode=1 +fi + echo 'Missing from tarball:' if echo "${dif}" | grep '^+'; then - exit 1 + exitcode=1 fi + +exit "${exitcode}" From 8a2bb9ca241bbd82a0da536f6f39dca9037dd046 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 29 Aug 2026 10:01:42 +0200 Subject: [PATCH 1018/1062] conncache: lower limit for auto maxconnects When the `maxconnets` for a multi is not set, we calculate an "auto" limit based on the number of running transfers. Introduce a minimum of the transfer count, so that we do not evict connctions from the cache when many transfers finish in the same round, causing an "unnatural" low running count. This should address our low `h1-request` perf stat drop. Closes #22734 --- lib/conncache.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/conncache.c b/lib/conncache.c index cef53351b42b..9dd753670ba4 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -651,7 +651,12 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, return kept; if(!data->multi->maxconnects) { - unsigned int running = Curl_multi_xfers_running(data->multi); + /* Running transfers is a weak indicator of business. When many + * transfers are done at the same time (parallel h1), the number + * may drop quite low and we do not want to close connections + * just to have another wave of transfers arriving next. */ + uint32_t running = Curl_multi_xfers_running(data->multi); + running = CURLMAX(running, 128); maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX; } else { From b6bf15bf5cadccb2f2e71b68d17792069d707df2 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 28 Aug 2026 08:32:52 -0700 Subject: [PATCH 1019/1062] tests: enable and fix some new Python ruff warnings Closes #22726 --- scripts/pythonlint.sh | 14 +++++++++----- tests/http/scorecard.py | 20 ++++++++++---------- tests/http/test_07_upload.py | 13 +++++++------ tests/http/test_08_caddy.py | 3 ++- tests/http/test_14_auth.py | 5 +++-- tests/http/test_17_ssl_use.py | 2 +- tests/http/testenv/certs.py | 2 +- tests/http/testenv/client.py | 4 ++-- tests/http/testenv/curl.py | 10 +++++----- tests/http/testenv/env.py | 10 ++++------ tests/http/testenv/h2o.py | 10 +++++----- tests/http/testenv/httpd.py | 2 +- tests/smbserver.py | 2 +- 13 files changed, 51 insertions(+), 46 deletions(-) diff --git a/scripts/pythonlint.sh b/scripts/pythonlint.sh index 138553306448..2cd3a6c44f57 100755 --- a/scripts/pythonlint.sh +++ b/scripts/pythonlint.sh @@ -30,10 +30,14 @@ set -eu ruff check --target-version=py38 \ ---extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ + --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,\ -SIM117,SIM118,TRY400,TRY401,RET503,RET504,UP004,B018,B904,RSE102,RET505,I001 \ ---ignore=FA100 \ -"$@" +SIM110,SIM117,SIM118,TRY400,TRY401,RET503,RET504,UP004,B018,B904,RSE102,RET505,\ +I001,PERF102,C419 \ + --ignore=FA100 \ + "$@" # Checks that are in preview only, since --preview otherwise turns them all on -ruff check --target-version=py38 --preview --select=E301,E302,E303,E304,E305,E306,E502 "$@" +ruff check --target-version=py38 --preview \ + --select=E301,E302,E303,E304,E305,E306,E502,FURB110,FURB113,FURB156,FURB171 \ + --ignore=RUF027 \ + "$@" diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 3d0b232068f4..55497cb90f24 100755 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -196,7 +196,7 @@ def print_score_table(cls, score): errors = [] col_has_stats = [] for idx, col in enumerate(cols): - cellw = max([len(r[idx]["sval"]) for r in rows]) + cellw = max(len(r[idx]["sval"]) for r in rows) colw.append(max(cellw, len(col))) col_has_stats.append(False) for row in rows: @@ -369,11 +369,11 @@ def dl_single(self, url: str, nsamples: int = 1): if err: errors.append(err) elif self._limit_rate: - total_speed = sum([s['speed_download'] for s in r.stats]) + total_speed = sum(s['speed_download'] for s in r.stats) samples.append(total_speed / len(r.stats)) profiles.append(r.profile) else: - total_size = sum([s['size_download'] for s in r.stats]) + total_size = sum(s['size_download'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) if self._limit_rate: @@ -397,11 +397,11 @@ def dl_serial(self, url: str, count: int, nsamples: int = 1): if err: errors.append(err) elif self._limit_rate: - total_speed = sum([s['speed_download'] for s in r.stats]) + total_speed = sum(s['speed_download'] for s in r.stats) samples.append(total_speed / len(r.stats)) profiles.append(r.profile) else: - total_size = sum([s['size_download'] for s in r.stats]) + total_size = sum(s['size_download'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) if self._limit_rate: @@ -430,11 +430,11 @@ def dl_parallel(self, url: str, count: int, nsamples: int = 1): if err: errors.append(err) elif self._limit_rate: - total_speed = sum([s['speed_download'] for s in r.stats]) + total_speed = sum(s['speed_download'] for s in r.stats) samples.append(total_speed / len(r.stats)) profiles.append(r.profile) else: - total_size = sum([s['size_download'] for s in r.stats]) + total_size = sum(s['size_download'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) if self._limit_rate: @@ -513,7 +513,7 @@ def ul_single(self, url: str, fpath: str, nsamples: int = 1): if err: errors.append(err) else: - total_size = sum([s['size_upload'] for s in r.stats]) + total_size = sum(s['size_upload'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) return Card.mk_mbs_cell(samples, profiles, errors) @@ -533,7 +533,7 @@ def ul_serial(self, url: str, fpath: str, count: int, nsamples: int = 1): if err: errors.append(err) else: - total_size = sum([s['size_upload'] for s in r.stats]) + total_size = sum(s['size_upload'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) return Card.mk_mbs_cell(samples, profiles, errors) @@ -558,7 +558,7 @@ def ul_parallel(self, url: str, fpath: str, count: int, nsamples: int = 1): if err: errors.append(err) else: - total_size = sum([s['size_upload'] for s in r.stats]) + total_size = sum(s['size_upload'] for s in r.stats) samples.append(total_size / r.duration.total_seconds()) profiles.append(r.profile) return Card.mk_mbs_cell(samples, profiles, errors) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 4c2ecfe60105..8187e0a99bdd 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -27,6 +27,7 @@ import logging import os import re +import string import sys from typing import List, Union @@ -50,7 +51,7 @@ def _class_scope(self, env, httpd, nghttpx): # upload small data, check that this is what was echoed @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, proto): - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) @@ -76,7 +77,7 @@ def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, proto): count = 20 - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) @@ -91,7 +92,7 @@ def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, proto): def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, proto): # limit since we use a separate connection in h1 count = 20 - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, @@ -198,7 +199,7 @@ def test_07_17_hx_post_reuse(self, env: Env, httpd, nghttpx, proto): def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, proto): # limit since we use a separate connection in h1 count = 10 - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, @@ -372,7 +373,7 @@ def test_07_35_h1_h2_upgrade_upload(self, env: Env, httpd, nghttpx): def test_07_36_upload_30x(self, env: Env, httpd, nghttpx, redir, proto): if proto == 'h3' and env.curl_uses_ossl_quic(): pytest.skip("OpenSSL's own QUIC is flaky here") - data = '0123456789' * 10 + data = string.digits * 10 curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo{redir}?id=[0-0]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[ @@ -388,7 +389,7 @@ def test_07_36_upload_30x(self, env: Env, httpd, nghttpx, redir, proto): def test_07_37_upload_307(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_ossl_quic(): pytest.skip("OpenSSL's own QUIC is flaky here") - data = '0123456789' * 10 + data = string.digits * 10 curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo307?id=[0-0]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[ diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index 70413a84d544..467f1cc8308c 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -27,6 +27,7 @@ import logging import os import re +import string import pytest from testenv import Caddy, CurlClient, Env, LocalClient @@ -142,7 +143,7 @@ def test_08_05_download_1mb_parallel(self, env: Env, caddy: Caddy, proto): def test_08_06_post_parallel(self, env: Env, httpd, caddy, proto): # limit since we use a separate connection in h1 count = 20 - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.domain2}:{caddy.port}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 268ebae5141e..ab941b6efa2d 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -24,6 +24,7 @@ # import logging import os +import string import pytest from testenv import CurlClient, Env @@ -62,7 +63,7 @@ def test_14_02_digest_get_auth(self, env: Env, httpd, nghttpx, proto): def test_14_03_digest_put_auth(self, env: Env, httpd, nghttpx, proto): if not env.curl_has_feature('digest'): pytest.skip("curl built without digest") - data = '0123456789' + data = string.digits curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[ @@ -75,7 +76,7 @@ def test_14_03_digest_put_auth(self, env: Env, httpd, nghttpx, proto): def test_14_04_digest_large_pw(self, env: Env, httpd, nghttpx, proto): if not env.curl_has_feature('digest'): pytest.skip("curl built without digest") - data = '0123456789' + data = string.digits password = 'x' * 65535 curl = CurlClient(env=env) url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json' diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 5d59765b6970..70af06eff9a0 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -143,7 +143,7 @@ def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx): assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}' assert False, f'should not have succeeded: {r.json}' # 3 - not allowed in the URL - assert r.exit_code in [3], f'{r}' + assert r.exit_code == 3, f'{r}' # use ip address for connect @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 444f26011696..418590679ac6 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -224,7 +224,7 @@ def issue_certs(self, specs: List[CertificateSpec], def issue_cert(self, spec: CertificateSpec, chain: Optional[List['Credentials']] = None) -> 'Credentials': - key_type = spec.key_type if spec.key_type else self.key_type + key_type = spec.key_type or self.key_type creds = None if self._store: creds = self._store.load_credentials( diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index c851246fe813..d6918fc0f5a5 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -44,9 +44,9 @@ def __init__(self, name: str, env: Env, run_dir: Optional[str] = None, self.path = os.path.join(env.build_dir, 'tests/libtest/libtests') self.env = env self._run_env = run_env - self._timeout = timeout if timeout else env.test_timeout + self._timeout = timeout or env.test_timeout self._curl = os.environ.get('CURL', env.curl) - self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, name) + self._run_dir = run_dir or os.path.join(env.gen_dir, name) self._stdoutfile = f'{self._run_dir}/stdout' self._stderrfile = f'{self._run_dir}/stderr' self._rmrf(self._run_dir) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 918c66092d34..34b28b9533ef 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -100,7 +100,7 @@ def finish(self): self._stats = {} for key in self.STAT_KEYS: self._stats[key] = fmean([s[key] for s in self._samples], weights) - self._stats['rss-max'] = max([s['rss'] for s in self._samples]) + self._stats['rss-max'] = max(s['rss'] for s in self._samples) else: self._stats = None self._psu = None @@ -629,9 +629,9 @@ def __init__(self, env: Env, force_resolv: bool = True, socks_args: Optional[List[str]] = None): self.env = env - self._timeout = timeout if timeout else env.test_timeout + self._timeout = timeout or env.test_timeout self._curl = os.environ.get('CURL', env.curl) - self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, 'curl') + self._run_dir = run_dir or os.path.join(env.gen_dir, 'curl') self._stdoutfile = f'{self._run_dir}/curl.stdout' self._stderrfile = f'{self._run_dir}/curl.stderr' self._headerfile = f'{self._run_dir}/curl.headers' @@ -655,7 +655,7 @@ def __init__(self, env: Env, self._socks_args = socks_args self._silent = silent self._run_env = run_env - self._server_addr = server_addr if server_addr else '127.0.0.1' + self._server_addr = server_addr or '127.0.0.1' self._force_resolv = force_resolv self._rmrf(self._run_dir) self._mkpath(self._run_dir) @@ -1171,7 +1171,7 @@ def _complete_args(self, urls, timeout=None, options=None, if force_resolve and u.hostname and u.hostname != 'localhost' \ and not re.match(r'^(\d+|\[|:).*', u.hostname): - port = u.port if u.port else 443 + port = u.port or 443 args.extend([ '--resolve', f'{u.hostname}:{port}:{self._server_addr}', ]) diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index baabbe72549a..9571907d8622 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -27,6 +27,7 @@ import os import re import shutil +import string import subprocess import tempfile from configparser import ConfigParser, ExtendedInterpolation @@ -493,10 +494,7 @@ def curl_uses_lib(libname: str) -> bool: @staticmethod def curl_uses_any_libs(libs: List[str]) -> bool: - for libname in libs: - if libname.lower() in Env.CONFIG.curl_props["libs"]: - return True - return False + return any(libname.lower() in Env.CONFIG.curl_props["libs"] for libname in libs) @staticmethod def curl_uses_ossl_quic() -> bool: @@ -901,7 +899,7 @@ def port_for(self, alpn_proto: Optional[str] = None): "http/0.9", ]: return self.https_port - if alpn_proto in ["h3"]: + if alpn_proto == "h3": return self.h3_port return self.http_port @@ -915,7 +913,7 @@ def make_data_file( raise RuntimeError("line_length less than 11 not supported") os.makedirs(indir, exist_ok=True) fpath = os.path.join(indir, fname) - s10 = "0123456789" + s10 = string.digits s = round((line_length / 10) + 1) * s10 s = s[0:line_length - 11] with open(fpath, "w") as fd: diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py index b7008b3c3cb9..82ab16b76bfb 100644 --- a/tests/http/testenv/h2o.py +++ b/tests/http/testenv/h2o.py @@ -82,12 +82,12 @@ def clear_logs(self): def dump_logs(self): lines = [] - lines.append(f"stderr of {self._name}") - lines.append("-------------------------------------------") + lines.extend([f"stderr of {self._name}", + "-------------------------------------------"]) self._dump_file(self._stderr, lines) - lines.append("") - lines.append(f"errorlog of {self._name}") - lines.append("-------------------------------------------") + lines.extend(["", + f"errorlog of {self._name}", + "-------------------------------------------"]) self._dump_file(self._error_log, lines) lines.append("") return lines diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index 8ef53395ff84..63dd01145f9e 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -314,7 +314,7 @@ def _write_config(self): 'AddEncoding x-gzip .gz .tgz .gzip', 'AddHandler type-map .var', ] - conf.extend([f'Listen {port}' for _, port in self.ports.items()]) + conf.extend([f'Listen {port}' for port in self.ports.values()]) if 'base' in self._extra_configs: conf.extend(self._extra_configs['base']) diff --git a/tests/smbserver.py b/tests/smbserver.py index 1072fd239d50..d14e7ff1d3cd 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -304,7 +304,7 @@ def get_share_path(self, conn_data, root_fid, tid): def get_server_path(self, requested_filename): log.debug("[SMB] Get server path '%s'", requested_filename) - if requested_filename not in [VERIFIED_REQ]: + if requested_filename != VERIFIED_REQ: raise SmbError(STATUS_NO_SUCH_FILE, "Could not find the file") fid, filename = tempfile.mkstemp() From 61aa5b0263c7f4ff4396c376537a0bf297bdf91a Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 28 Aug 2026 09:53:03 -0700 Subject: [PATCH 1020/1062] CI: add a test of Python code using pyrefly pytype is no longer supported and will stop working one day when a newer Python becomes the default. pyrefly is a bit too pedantic in its default configuration, so run it with only the basic set of checks. Closes #22728 --- .github/scripts/requirements.txt | 1 + .github/workflows/checksrc.yml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index a130d9147d29..c16619d65c16 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -4,5 +4,6 @@ cmakelang==0.6.13 codespell==2.4.3 +pyrefly==1.2.0 reuse==6.2.0 ruff==0.16.2 diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 7efc740dc03a..f92bf2ef9de6 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -82,6 +82,12 @@ jobs: source ~/venv/bin/activate scripts/pythonlint.sh + - name: 'pyrefly' + run: | + source ~/venv/bin/activate + cd tests/http/ + pyrefly check --python-version=3.8 --preset=basic --check-unannotated-defs --summary + - name: filter off code from all C and H files run: | git ls-files -z '**.[ch]' | xargs -0 -r -n1 ./.github/scripts/c-strip From 00c7baea512685987f28f2cbc10bbcd3e7904d70 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 28 Aug 2026 09:05:02 -0700 Subject: [PATCH 1021/1062] CI: add a test of labeler patterns When source files are renamed or removed, labeler patterns can refer to nonexistent files. Verify that the globs in the labeler config are still valid so they can be updated when corresponding source changes occur. This check does nothing when new files are added; those must be manually checked to see if they warrant new matching patterns in the config. Remove a stale config entry found in the process of integrating this check. Closes #22727 --- .github/labeler.yml | 5 ++++- .github/scripts/labelercheck.sh | 12 ++++++++++++ .github/workflows/checksrc.yml | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/labelercheck.sh diff --git a/.github/labeler.yml b/.github/labeler.yml index 1219b549fdee..706c2d1d0648 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -19,6 +19,10 @@ # patterns, they must be joined with commas to a single string surrounded by # braces. For example: '{lib/**,src/**}'. # +# Note that the labelercheck.sh script is sensitive to the specific formatting +# of this file; strictly follow the established template when adding new +# patterns. +# # See https://github.com/actions/labeler/ for documentation on this file. --- @@ -208,7 +212,6 @@ documentation: .github/scripts/verify-synopsis.pl,\ **/*.md,\ **/*.txt,\ - **/*.1,\ CHANGES.md,\ docs/**,\ LICENSES/**,\ diff --git a/.github/scripts/labelercheck.sh b/.github/scripts/labelercheck.sh new file mode 100755 index 000000000000..17662b51c44e --- /dev/null +++ b/.github/scripts/labelercheck.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Copyright (C) 2026 Dan Fandrich +# +# SPDX-License-Identifier: curl + +# Look for invalid patterns in labeler config. +# This is highly sensitive to the format of the labeler.yml file. +# It will also fail if patterns are added containing special shell characters +# like "$" or "|". +grep -E '^ {14}[^-].*\\$' .github/labeler.yml | \ + sed -E -e '/^[[:space:]]*#/d' -e 's/,?\\$//' | \ + xargs -I{} /bin/bash -c 'shopt -s globstar dotglob; ls -d {}' >/dev/null diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index f92bf2ef9de6..1cebb19b3d6b 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -74,6 +74,9 @@ jobs: - name: 'cmake options in documentation' run: scripts/cmakeopts.sh + - name: 'labelercheck' + run: .github/scripts/labelercheck.sh + - name: 'perlcheck' run: scripts/perlcheck.sh From e5727a412badbd1721e91169df26e55b30065a8a Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 26 Aug 2026 11:37:35 +0200 Subject: [PATCH 1022/1062] GHA: run on `curl-*` branches For rock-solid curl. Closes #22736 --- .github/workflows/checkdocs.yml | 2 ++ .github/workflows/checksrc.yml | 2 ++ .github/workflows/checkurls.yml | 2 ++ .github/workflows/codeql.yml | 2 ++ .github/workflows/configure-vs-cmake.yml | 2 ++ .github/workflows/curl-for-win.yml | 2 ++ .github/workflows/distcheck.yml | 2 ++ .github/workflows/fuzz.yml | 2 ++ .github/workflows/http3-linux.yml | 2 ++ .github/workflows/linux-old.yml | 2 ++ .github/workflows/linux.yml | 2 ++ .github/workflows/macos.yml | 2 ++ .github/workflows/non-native.yml | 2 ++ .github/workflows/windows.yml | 2 ++ 14 files changed, 28 insertions(+) diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 634957af9b61..7eca7305ac86 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -11,6 +11,7 @@ name: 'Docs' push: branches: - master + - 'curl-*' - '*/ci' paths: - '.github/workflows/checkdocs.yml' @@ -21,6 +22,7 @@ name: 'Docs' pull_request: branches: - master + - 'curl-*' paths: - '.github/workflows/checkdocs.yml' - '.github/scripts/**' diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 1cebb19b3d6b..2d8bb26a3541 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -10,6 +10,7 @@ name: 'Source' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '.circleci/**' @@ -18,6 +19,7 @@ name: 'Source' pull_request: branches: - master + - 'curl-*' paths-ignore: - '.circleci/**' - 'appveyor.*' diff --git a/.github/workflows/checkurls.yml b/.github/workflows/checkurls.yml index 67b81e03ded4..1842b975f42b 100644 --- a/.github/workflows/checkurls.yml +++ b/.github/workflows/checkurls.yml @@ -8,10 +8,12 @@ name: 'URLs' push: branches: - master + - 'curl-*' - '*/ci' pull_request: branches: - master + - 'curl-*' schedule: - cron: '10 5 * * *' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 330e64404c37..3e50db473649 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,6 +8,7 @@ name: 'CodeQL' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'CodeQL' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 3c569301fb42..1195878e0915 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -7,6 +7,7 @@ name: 'configure-vs-cmake' push: branches: - master + - 'curl-*' paths: - '*.ac' - '**/*.m4' @@ -20,6 +21,7 @@ name: 'configure-vs-cmake' pull_request: branches: - master + - 'curl-*' paths: - '*.ac' - '**/*.m4' diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index aeb95618edbe..b84241f3e426 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -8,6 +8,7 @@ name: 'curl-for-win' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'curl-for-win' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index 6031afa6154e..f8936b1cbe6a 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -8,10 +8,12 @@ name: 'dist' push: branches: - master + - 'curl-*' - '*/ci' pull_request: branches: - master + - 'curl-*' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index dc15b9c9db76..7ac58d902b2a 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -8,6 +8,7 @@ name: 'Fuzzer' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -21,6 +22,7 @@ name: 'Fuzzer' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '**/CMakeLists.txt' diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 23d955123734..ca2bad2ea053 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -8,6 +8,7 @@ name: 'Linux HTTP/3' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'Linux HTTP/3' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index ffe97cf532ea..7fe01f69b436 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -22,6 +22,7 @@ name: 'Linux Old' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -32,6 +33,7 @@ name: 'Linux Old' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index bb6d1e8d377e..a2d610219f56 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -8,6 +8,7 @@ name: 'Linux' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '.circleci/**' @@ -17,6 +18,7 @@ name: 'Linux' pull_request: branches: - master + - 'curl-*' paths-ignore: - '.circleci/**' - 'appveyor.*' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 41d21a5305db..e44b276a2a98 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -8,6 +8,7 @@ name: 'macOS' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'macOS' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index b08949062d12..e9fb2e1a656e 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -8,6 +8,7 @@ name: 'non-native' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'non-native' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 27d5f4c82c28..73ec97abc1b2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -8,6 +8,7 @@ name: 'Windows' push: branches: - master + - 'curl-*' - '*/ci' paths-ignore: - '**/*.md' @@ -18,6 +19,7 @@ name: 'Windows' pull_request: branches: - master + - 'curl-*' paths-ignore: - '**/*.md' - '.circleci/**' From 916ffb10a8d2be4c8ad80c2e0e38f3fe583c7ea9 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 29 Aug 2026 10:53:15 +0200 Subject: [PATCH 1023/1062] spnego_sspi: drop redundant UNICODE branch Follow-up to 8a8ff47b63aff1d72c1796dbdb4955f243beac23 #22559 Follow-up to a8881e5e1d2e22d4b085d45ab6c8ce1df251d602 #21315 #22410 Closes #22737 --- lib/vauth/spnego_sspi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 19c4fe17717e..a68900faf1a2 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -166,11 +166,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Use the special name "!ntlm" to prevent NTLM from being used: * https://learn.microsoft.com/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa */ -#ifdef UNICODE - nego->identity.PackageList = CURL_UNCONST(TEXT("!ntlm")); -#else nego->identity.PackageList = CURL_UNCONST(TEXT("!ntlm")); -#endif nego->identity.PackageListLength = 5; /* Allocate our credentials handle */ From b82235540f57bedccf6059236f7dfa32337687da Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 29 Aug 2026 11:02:50 +0200 Subject: [PATCH 1024/1062] sspi: add local helper macro to avoid UNICODE branching Also drop redundant cast and comment. Closes #22738 --- lib/curl_sspi.c | 8 +------- lib/curl_sspi.h | 7 +++++++ lib/vauth/spnego_sspi.c | 7 +------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index 5b4e6e41fdea..799f3429a472 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -181,13 +181,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, identity->DomainLength = curlx_uztoul(domlen); dup_domain.tchar_ptr = NULL; - /* Setup the identity's flags */ - identity->Flags = (unsigned long) -#ifdef UNICODE - SEC_WINNT_AUTH_IDENTITY_UNICODE; -#else - SEC_WINNT_AUTH_IDENTITY_ANSI; -#endif + identity->Flags = CURL_SEC_WINNT_AUTH_IDENTITY; return CURLE_OK; } diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 6b10bdb2e70c..da8e26d642f5 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -29,6 +29,13 @@ #include +/* Local helper macro */ +#ifdef UNICODE +#define CURL_SEC_WINNT_AUTH_IDENTITY SEC_WINNT_AUTH_IDENTITY_UNICODE +#else +#define CURL_SEC_WINNT_AUTH_IDENTITY SEC_WINNT_AUTH_IDENTITY_ANSI +#endif + /* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #ifndef SECPKG_ATTR_ENDPOINT_BINDINGS #define SECPKG_ATTR_ENDPOINT_BINDINGS 26 diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index a68900faf1a2..4babd4510d5d 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -154,12 +154,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, memset(&nego->identity, 0, sizeof(nego->identity)); nego->identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; nego->identity.Length = sizeof(nego->identity); - nego->identity.Flags = -#ifdef UNICODE - SEC_WINNT_AUTH_IDENTITY_UNICODE; -#else - SEC_WINNT_AUTH_IDENTITY_ANSI; -#endif + nego->identity.Flags = CURL_SEC_WINNT_AUTH_IDENTITY; nego->p_identity = &nego->identity; } From a00995705779b63ed288ecee67f696809b7b1d56 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 29 Aug 2026 12:21:16 +0200 Subject: [PATCH 1025/1062] schannel: add ALPN support for mingw-w64 <9 and proto_hash` */ #define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share" -/* ALPN requires version 8.1 of the Windows SDK, which was - shipped with Visual Studio 2013, aka _MSC_VER 1800: - https://learn.microsoft.com/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831771 - Or mingw-w64 9.0+ */ -#if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \ - (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)) -#define HAS_ALPN_SCHANNEL static bool s_win_has_alpn; + +/* Offered by mingw-w64 v9+, MS SDK 8.1/VS2013+ */ +#ifndef SECBUFFER_APPLICATION_PROTOCOLS +#define SECBUFFER_APPLICATION_PROTOCOLS 18 +#define SECPKG_ATTR_APPLICATION_PROTOCOL 35 + +typedef enum { + SecApplicationProtocolNegotiationExt_None, + SecApplicationProtocolNegotiationExt_NPN, + SecApplicationProtocolNegotiationExt_ALPN +} SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT; + +typedef enum { + SecApplicationProtocolNegotiationStatus_None, + SecApplicationProtocolNegotiationStatus_Success, + SecApplicationProtocolNegotiationStatus_SelectedClientOnly +} SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS; + +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct { + SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS ProtoNegoStatus; + SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT ProtoNegoExt; + unsigned char ProtocolIdSize; + unsigned char ProtocolId[0xff]; +} SecPkgContext_ApplicationProtocol; #endif static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, @@ -838,9 +856,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, SecBufferDesc outbuf_desc; SecBuffer inbuf; SecBufferDesc inbuf_desc; -#ifdef HAS_ALPN_SCHANNEL unsigned char alpn_buffer[128]; -#endif SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; @@ -848,11 +864,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", connssl->peer.origin->hostname, connssl->peer.origin->port)); -#ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; -#else - backend->use_alpn = FALSE; -#endif if(conn_config->CAfile || conn_config->ca_info_blob) { if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, @@ -910,7 +922,6 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, infof(data, "schannel: using IP address, SNI is not supported by OS."); } -#ifdef HAS_ALPN_SCHANNEL if(backend->use_alpn) { int cur = 0; int list_start_index = 0; @@ -958,10 +969,6 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); } -#else /* HAS_ALPN_SCHANNEL */ - InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); -#endif /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); @@ -1586,9 +1593,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; SECURITY_STATUS sspi_status = SEC_E_OK; CERT_CONTEXT *ccert_context = NULL; -#ifdef HAS_ALPN_SCHANNEL SecPkgContext_ApplicationProtocol alpn_result; -#endif DEBUGASSERT(connssl->connecting_state == ssl_connect_3); DEBUGASSERT(backend); @@ -1614,7 +1619,6 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } -#ifdef HAS_ALPN_SCHANNEL if(backend->use_alpn) { sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, @@ -1646,7 +1650,6 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0); } } -#endif /* save the current session data for possible reuse */ if(Curl_ssl_scache_use(cf, data)) { @@ -2585,7 +2588,6 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) static int schannel_init(void) { -#ifdef HAS_ALPN_SCHANNEL typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void); #if defined(__clang__) && __clang_major__ >= 16 #pragma clang diagnostic push @@ -2610,7 +2612,6 @@ static int schannel_init(void) s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL); } -#endif /* HAS_ALPN_SCHANNEL */ return Curl_sspi_global_init() == CURLE_OK ? 1 : 0; } From 9f05091b596e3015bf214ea15f6c56f6eb7647f6 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 30 Aug 2026 15:20:25 +0200 Subject: [PATCH 1026/1062] GHA/non-native: bump cross-platform-actions to 1.5.0 Also: - tried `microvm` with NetBSD 11, but ended up not using it because tests failed to fork, then the process either hung or took too long to fit in 15 minutes. Ref: https://github.com/curl/curl/actions/runs/33314257008/job/99268019716?pr=22748 Refs: https://github.com/cross-platform-actions/action/releases/tag/v1.5.0 https://github.com/cross-platform-actions/action/blob/v1.5.0/readme.md#variants-variant Closes #22748 --- .github/workflows/non-native.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index e9fb2e1a656e..905107746f46 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -124,7 +124,7 @@ jobs: persist-credentials: false - name: 'setup VM' - uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION TFLAGS' operating_system: '${{ matrix.os }}' From 5b06840fa25e824a1cc7a466789c35b932cdc315 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 27 Aug 2026 02:05:56 +0200 Subject: [PATCH 1027/1062] tidy-up: drop redundant parentheses Flagged by clang-tidy 23.1.0 (`readability-redundant-parentheses`). Also: - examples/websocket: add missing parantheses. Closes #22749 --- docs/examples/getinmemory.c | 2 +- docs/examples/http2-pushinmemory.c | 2 +- docs/examples/postinmemory.c | 2 +- docs/examples/websocket.c | 2 +- lib/cf-ip-happy.c | 4 ++-- lib/cfilters.c | 2 +- lib/curl_share.c | 8 ++++---- lib/curlx/strparse.c | 2 +- lib/ftp.c | 4 ++-- lib/ftplistparser.c | 2 +- lib/http.c | 4 ++-- lib/mime.c | 2 +- lib/sendf.c | 18 ++++++++++-------- lib/smtp.c | 2 +- lib/tftp.c | 2 +- lib/transfer.c | 2 +- lib/url.c | 4 ++-- lib/vtls/openssl.c | 12 ++++++------ lib/vtls/schannel.c | 2 +- lib/vtls/vtls_scache.c | 2 +- src/tool_doswin.c | 2 +- src/tool_getparam.c | 2 +- src/tool_urlglob.c | 4 ++-- src/tool_writeout.c | 2 +- tests/libtest/lib518.c | 4 ++-- tests/libtest/lib537.c | 6 +++--- tests/server/socksd.c | 4 ++-- 27 files changed, 53 insertions(+), 51 deletions(-) diff --git a/docs/examples/getinmemory.c b/docs/examples/getinmemory.c index 036953b50c6b..85b8c9ceee55 100644 --- a/docs/examples/getinmemory.c +++ b/docs/examples/getinmemory.c @@ -50,7 +50,7 @@ static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) } mem->memory = ptr; - memcpy(&(mem->memory[mem->size]), contents, realsize); + memcpy(&mem->memory[mem->size], contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; diff --git a/docs/examples/http2-pushinmemory.c b/docs/examples/http2-pushinmemory.c index 09a8a8c67130..ba3db5b4b9e9 100644 --- a/docs/examples/http2-pushinmemory.c +++ b/docs/examples/http2-pushinmemory.c @@ -48,7 +48,7 @@ static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) } mem->memory = ptr; - memcpy(&(mem->memory[mem->size]), contents, realsize); + memcpy(&mem->memory[mem->size], contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; diff --git a/docs/examples/postinmemory.c b/docs/examples/postinmemory.c index 7f18e8e5f576..698e90163f1a 100644 --- a/docs/examples/postinmemory.c +++ b/docs/examples/postinmemory.c @@ -49,7 +49,7 @@ static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) } mem->memory = ptr; - memcpy(&(mem->memory[mem->size]), contents, realsize); + memcpy(&mem->memory[mem->size], contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; diff --git a/docs/examples/websocket.c b/docs/examples/websocket.c index 953c2f358fcf..7ae299f4ed6b 100644 --- a/docs/examples/websocket.c +++ b/docs/examples/websocket.c @@ -30,7 +30,7 @@ #ifdef _WIN32 #include #include -#define sleep(s) Sleep((DWORD)(s * 1000)) +#define sleep(s) Sleep((DWORD)((s) * 1000)) #else #include #endif diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index fa7104ffca22..bbb8cc1ef091 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -409,7 +409,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, /* check if a running baller connects now */ VERBOSE(i = -1); - for(panchor = &bs->running; *panchor; panchor = &((*panchor)->next)) { + for(panchor = &bs->running; *panchor; panchor = &(*panchor)->next) { VERBOSE(++i); a = *panchor; a->result = cf_ip_attempt_connect(a, data, connected); @@ -509,7 +509,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, /* append to running list */ panchor = &bs->running; while(*panchor) - panchor = &((*panchor)->next); + panchor = &(*panchor)->next; *panchor = a; bs->last_attempt_started = *Curl_pgrs_now(data); bs->last_attempt_ai_family = ai_family; diff --git a/lib/cfilters.c b/lib/cfilters.c index 8875473fb34f..6ffa262eb165 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -409,7 +409,7 @@ bool Curl_conn_cf_discard(struct Curl_cfilter **pcf, found = TRUE; break; } - pprev = &((*pprev)->next); + pprev = &(*pprev)->next; } } Curl_conn_cf_discard_chain(pcf, data); diff --git a/lib/curl_share.c b/lib/curl_share.c index bb5125533f1b..cbadd57d3795 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -100,11 +100,11 @@ static uint32_t share_ref_inc(struct Curl_share *share) uint32_t n; #ifdef USE_MUTEX Curl_mutex_acquire(&share->lock); - n = ++(share->ref_count); + n = ++share->ref_count; share->has_been_shared = TRUE; Curl_mutex_release(&share->lock); #else - n = ++(share->ref_count); + n = ++share->ref_count; share->has_been_shared = TRUE; #endif return n; @@ -116,10 +116,10 @@ static uint32_t share_ref_dec(struct Curl_share *share) #ifdef USE_MUTEX Curl_mutex_acquire(&share->lock); DEBUGASSERT(share->ref_count); - n = --(share->ref_count); + n = --share->ref_count; Curl_mutex_release(&share->lock); #else - n = --(share->ref_count); + n = --share->ref_count; #endif return n; } diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index 736f62050438..9e8fe4963d53 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -263,7 +263,7 @@ int curlx_str_cmp(struct Curl_str *str, const char *check) size_t clen = strlen(check); return ((str->len == clen) && !strncmp(str->str, check, clen)); } - return !!(str->len); + return !!str->len; } /* Trim off 'num' number of bytes from the beginning (left side) of the diff --git a/lib/ftp.c b/lib/ftp.c index 5a880de78d0c..70bf654bf914 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2328,7 +2328,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, domore *more) if(result) ; else if((data->state.list_only || !ftpc->file) && - !(data->set.prequote)) { + !data->set.prequote) { /* The specified path ends with a slash, and therefore we think this is a directory that is requested, use LIST. Before that, we also need to set ASCII transfer mode. */ @@ -2846,7 +2846,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0) data->req.size = data->req.maxdownload; - else if((instate != FTP_LIST) && (data->state.prefer_ascii)) + else if((instate != FTP_LIST) && data->state.prefer_ascii) data->req.size = -1; /* for servers that understate ASCII mode file size */ diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 2fbff9ea38f7..47e3d75d940a 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -329,7 +329,7 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { /* discard symlink which is containing multiple " -> " */ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && - (strstr(finfo->strings.target, " -> "))) { + strstr(finfo->strings.target, " -> ")) { add = FALSE; } } diff --git a/lib/http.c b/lib/http.c index ed2dff373ff0..3dc7b4bedb8b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1730,7 +1730,7 @@ static bool http_may_use_1_1(const struct Curl_easy *data) return FALSE; /* We want 1.0 and have seen no previous response on *this* connection with a higher version (maybe no response at all yet). */ - if((data->state.http_neg.only_10) && + if(data->state.http_neg.only_10 && (!conn || conn->httpversion_seen <= 10)) return FALSE; /* We are not restricted to use 1.0 only. */ @@ -3179,7 +3179,7 @@ static statusline checkhttpprefix(struct Curl_easy *data, head = head->next; } - if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len))) + if((rc != STATUS_DONE) && checkprefixmax("HTTP/", s, len)) rc = onmatch; return rc; diff --git a/lib/mime.c b/lib/mime.c index 638831d9c88f..600437576581 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -223,7 +223,7 @@ static char *escape_string(struct Curl_easy *data, table = formtable; /* data can be NULL when this function is called indirectly from curl_formget(). */ - if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape))) + if(strategy == MIMESTRATEGY_MAIL || (data && data->set.mime_formescape)) table = mimetable; curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH); diff --git a/lib/sendf.c b/lib/sendf.c index 3742b6993e7f..abd2e3fdbfba 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -336,7 +336,7 @@ static void cwriter_add(struct Curl_easy *data, /* Insert the writer as first in its phase. * Skip existing writers of lower phases. */ while(*anchor && (*anchor)->phase < writer->phase) - anchor = &((*anchor)->next); + anchor = &(*anchor)->next; writer->next = *anchor; *anchor = writer; } @@ -851,7 +851,7 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, int err; CURL_CBAPI_START(&guard, data, easy_seek_func); - err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + err = data->set.seek_func(data->set.seek_client, 0, SEEK_SET); CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); if(err) { @@ -864,8 +864,8 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, curlioerr err; CURL_CBAPI_START(&guard, data, easy_ioctl_func); - err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, - data->set.ioctl_client); + err = data->set.ioctl_func(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err); if(err) { @@ -1134,11 +1134,13 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data, clen = r->crt->total_length(data, r); /* if we do not have 0 length init, and CRLF conversion is wanted, * add the reader for it */ - if(clen && (data->set.crlf + if(clen && #ifdef CURL_PREFER_LF_LINEENDS - || data->state.prefer_ascii + (data->set.crlf || data->state.prefer_ascii) +#else + data->set.crlf #endif - )) { + ) { result = cr_lc_add(data); if(result) return result; @@ -1182,7 +1184,7 @@ CURLcode Curl_creader_add(struct Curl_easy *data, /* Insert the writer as first in its phase. * Skip existing readers of lower phases. */ while(*anchor && (*anchor)->phase < reader->phase) - anchor = &((*anchor)->next); + anchor = &(*anchor)->next; reader->next = *anchor; *anchor = reader; return CURLE_OK; diff --git a/lib/smtp.c b/lib/smtp.c index 8ce4706f9e9b..50e2af400a10 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -878,7 +878,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data, else { /* Establish whether we should report that we support SMTPUTF8 for EXPN commands to the server as per RFC-6531 sect. 3.1 point 6 */ - utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN")); + utf8 = smtpc->utf8_supported && !strcmp(smtp->custom, "EXPN"); /* Send the custom recipient based command such as the EXPN command */ result = Curl_pp_sendf(data, &smtpc->pp, diff --git a/lib/tftp.c b/lib/tftp.c index af9e9752f715..d95da69dc373 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -957,7 +957,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; ((struct sockaddr *)&state->local_addr)->sa_family = - (CURL_SA_FAMILY_T)(remote_addr->family); + (CURL_SA_FAMILY_T)remote_addr->family; result = tftp_set_timeouts(state); if(result) diff --git a/lib/transfer.c b/lib/transfer.c index 1e7f3cd1e519..e257057b5c8a 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -404,7 +404,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) * The transfer has been performed. Make some general checks before * returning. */ - if(!(data->req.no_body) && (k->size != -1) && + if(!data->req.no_body && (k->size != -1) && (k->bytecount != k->size) && !k->newurl) { failf(data, "transfer closed with %" FMT_OFF_T " bytes remaining to read", k->size - k->bytecount); diff --git a/lib/url.c b/lib/url.c index 1560a8bcb9f8..b309d3e209c3 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2302,7 +2302,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) DEBUGASSERT(needle->scheme->run->connect_it); data->info.conn_scheme = needle->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (needle->scheme->protocol) & CURLPROTO_MASK; + data->info.conn_protocol = needle->scheme->protocol & CURLPROTO_MASK; result = needle->scheme->run->connect_it(data, &done); if(result) goto out; @@ -2454,7 +2454,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) /* persist the scheme and handler the transfer is using */ data->info.conn_scheme = data->conn->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (data->conn->scheme->protocol) & CURLPROTO_MASK; + data->info.conn_protocol = data->conn->scheme->protocol & CURLPROTO_MASK; data->info.used_proxy = #ifdef CURL_DISABLE_PROXY 0 diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index f21778dda029..06bb07b9afa0 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -861,7 +861,7 @@ static int ssl_ui_reader(UI *ui, UI_STRING *uis) default: break; } - return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); + return UI_method_get_reader(UI_OpenSSL())(ui, uis); } /* @@ -880,7 +880,7 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis) default: break; } - return (UI_method_get_writer(UI_OpenSSL()))(ui, uis); + return UI_method_get_writer(UI_OpenSSL())(ui, uis); } /* @@ -905,7 +905,7 @@ static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, X509 *x = NULL; /* the typecast of blob->len is fine since it is guaranteed to never be larger than CURL_MAX_INPUT_LENGTH */ - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + BIO *in = BIO_new_mem_buf(blob->data, (int)blob->len); if(!in) return CURLE_OUT_OF_MEMORY; @@ -939,7 +939,7 @@ static int use_privatekey_blob(SSL_CTX *ctx, const struct curl_blob *blob, { int ret = 0; EVP_PKEY *pkey = NULL; - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + BIO *in = BIO_new_mem_buf(blob->data, (int)blob->len); if(!in) return CURLE_OUT_OF_MEMORY; @@ -967,7 +967,7 @@ static int use_certificate_chain_blob(SSL_CTX *ctx, { int ret = 0; X509 *x = NULL; - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); + BIO *in = BIO_new_mem_buf(blob->data, (int)blob->len); if(!in) return CURLE_OUT_OF_MEMORY; @@ -1319,7 +1319,7 @@ static int pkcs12load(struct Curl_easy *data, int cert_done = 0; STACK_OF(X509) *ca = NULL; if(cert_blob) { - cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len)); + cert_bio = BIO_new_mem_buf(cert_blob->data, (int)cert_blob->len); if(!cert_bio) { failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", ossl_strerror(ERR_get_error(), error_buffer, diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 777183f3c8f5..f129bdf890b0 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -737,7 +737,7 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, DWORD enabled_protocols = 0; struct schannel_ssl_backend_data *backend = - (struct schannel_ssl_backend_data *)(connssl->backend); + (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 102d7d87ced2..c703302be849 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -1031,7 +1031,7 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, n = Curl_llist_head(&peer->sessions); if(n) { s = Curl_node_take_elem(n); - (scache->age)++; /* increase general age */ + scache->age++; /* increase general age */ peer->age = scache->age; /* set this as used in this age */ } } diff --git a/src/tool_doswin.c b/src/tool_doswin.c index ffa442b1fa60..b3fb7b56203f 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -338,7 +338,7 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, curl_strnequal(p, "PRN", 3) || curl_strnequal(p, "AUX", 3) || curl_strnequal(p, "NUL", 3)) ? 3 : - (curl_strnequal(p, "CLOCK$", 6)) ? 6 : + curl_strnequal(p, "CLOCK$", 6) ? 6 : (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 73419ead097f..ae3958d8c58f 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -2145,7 +2145,7 @@ static ParameterError opt_bool(struct OperationConfig *config, case C_HEAD: /* --head */ config->no_body = toggle; config->show_headers = toggle; - if(SetHTTPrequest((config->no_body) ? TOOL_HTTPREQ_HEAD : + if(SetHTTPrequest(config->no_body ? TOOL_HTTPREQ_HEAD : TOOL_HTTPREQ_GET, &config->httpreq)) return PARAM_BAD_USE; break; diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index c4b112437018..88deedcb4cfd 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -602,7 +602,7 @@ void glob_cleanup(struct URLGlob *glob) for(i = 0; i < glob->pnum; i++) { DEBUGASSERT(glob->pattern[i].type); if((glob->pattern[i].type == GLOB_SET) && - (glob->pattern[i].c.set.elem)) { + glob->pattern[i].c.set.elem) { curl_off_t elem; for(elem = 0; elem < glob->pattern[i].c.set.size; elem++) curlx_safefree(glob->pattern[i].c.set.elem[elem]); @@ -636,7 +636,7 @@ CURLcode glob_next_url(char **globbed, struct URLGlob *glob) pat = &glob->pattern[glob->pnum - 1 - i]; switch(pat->type) { case GLOB_SET: - if((pat->c.set.elem) && (++pat->c.set.idx == pat->c.set.size)) { + if(pat->c.set.elem && (++pat->c.set.idx == pat->c.set.size)) { pat->c.set.idx = 0; carry = TRUE; } diff --git a/src/tool_writeout.c b/src/tool_writeout.c index 63e93ff52dcb..7a0a20a9d9fc 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -251,7 +251,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, break; case VAR_ERRORMSG: if(per_result) { - strinfo = (per->errorbuffer[0]) ? per->errorbuffer : + strinfo = per->errorbuffer[0] ? per->errorbuffer : curl_easy_strerror(per_result); valid = TRUE; } diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index bef952bac7f6..c751574f2900 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -242,7 +242,7 @@ static int t518_test_rlimit(int keep_open) /* verify that we do not overflow size_t in curlx_malloc() */ - if((size_t)(t518_num_open.rlim_max) > ((size_t)-1) / sizeof(*t518_testfd)) { + if((size_t)t518_num_open.rlim_max > ((size_t)-1) / sizeof(*t518_testfd)) { tutil_rlim2str(strbuff1, sizeof(strbuff1), t518_num_open.rlim_max); curl_msnprintf(strbuff, sizeof(strbuff), "unable to allocate an array for %s " @@ -259,7 +259,7 @@ static int t518_test_rlimit(int keep_open) curl_mfprintf(stderr, "allocating array for %s file descriptors\n", strbuff); t518_testfd = curlx_malloc(sizeof(*t518_testfd) * - (size_t)(t518_num_open.rlim_max)); + (size_t)t518_num_open.rlim_max); if(!t518_testfd) { t518_store_errmsg("testfd, malloc() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 5f99b4f006f1..ab19316b0dd5 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -237,7 +237,7 @@ static int t537_test_rlimit(int keep_open) /* verify that we do not overflow size_t in curlx_malloc() */ - if((size_t)(t537_num_open.rlim_max) > ((size_t)-1) / sizeof(*t537_testfd)) { + if((size_t)t537_num_open.rlim_max > ((size_t)-1) / sizeof(*t537_testfd)) { tutil_rlim2str(strbuff1, sizeof(strbuff1), t537_num_open.rlim_max); curl_msnprintf(strbuff, sizeof(strbuff), "unable to allocate an array for %s " @@ -256,7 +256,7 @@ static int t537_test_rlimit(int keep_open) strbuff); t537_testfd = curlx_malloc(sizeof(*t537_testfd) * - (size_t)(t537_num_open.rlim_max)); + (size_t)t537_num_open.rlim_max); if(!t537_testfd) { curl_mfprintf(stderr, "testfd, malloc() failed\n"); t537_num_open.rlim_max /= 2; @@ -338,7 +338,7 @@ static int t537_test_rlimit(int keep_open) tmpfd = curlx_realloc(t537_testfd, sizeof(*t537_testfd) * - (size_t)(t537_num_open.rlim_max)); + (size_t)t537_num_open.rlim_max); if(tmpfd) { t537_testfd = tmpfd; tmpfd = NULL; diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 02ed6e8e4fc3..29fe56264e28 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -253,7 +253,7 @@ static curl_socket_t socks4(curl_socket_t fd, } if(!s_config.port) s4port = (uint16_t)((buffer[SOCKS4_DSTPORT] << 8) | - (buffer[SOCKS4_DSTPORT + 1])); + buffer[SOCKS4_DSTPORT + 1]); else s4port = s_config.port; @@ -500,7 +500,7 @@ static curl_socket_t sockit(curl_socket_t fd) if(!s_config.port) { const unsigned char *portp = &buffer[SOCKS5_DSTADDR + len]; - s5port = (uint16_t)((portp[0] << 8) | (portp[1])); + s5port = (uint16_t)((portp[0] << 8) | portp[1]); } else s5port = s_config.port; From 2a395a0420516ac193ba7068e6449a7cc9d808d2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 30 Aug 2026 18:35:13 +0200 Subject: [PATCH 1028/1062] easy: fix unused global on non-Windows Detected by llvm/clang 23.1.0: ``` lib/easy.c:83:13: warning: variable 'easy_init_flags' set but not used [clang-diagnostic-unused-but-set-global] 83 | static long easy_init_flags; | ^ lib/easy.c:83:13: warning: variable 'easy_init_flags' set but not used [-Wunused-but-set-global] 83 | static long easy_init_flags; | ^ ``` Closes #22750 --- lib/easy.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/easy.c b/lib/easy.c index 8c225d0804a3..8e4deb85dbdb 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -80,7 +80,9 @@ /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; +#ifdef _WIN32 static long easy_init_flags; +#endif #ifdef GLOBAL_INIT_IS_THREADSAFE @@ -176,7 +178,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } +#ifdef _WIN32 easy_init_flags = flags; +#else + (void)flags; +#endif #ifdef DEBUGBUILD if(getenv("CURL_GLOBAL_INIT")) @@ -273,6 +279,7 @@ void curl_global_cleanup(void) #ifdef _WIN32 Curl_win32_cleanup(easy_init_flags); + easy_init_flags = 0; #endif Curl_amiga_cleanup(); @@ -283,8 +290,6 @@ void curl_global_cleanup(void) curlx_free(leakpointer); #endif - easy_init_flags = 0; - global_init_unlock(); } From 589885149f50b28eae5c7217bc4fd81e51cbc216 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 30 Aug 2026 18:33:33 +0200 Subject: [PATCH 1029/1062] easy_lock: silence `portability-no-assembler` with clang-tidy 23.1.0+ ``` lib/easy_lock.h:74:7: warning: do not use inline assembler in safety-critical code [portability-no-assembler] 74 | __asm__ volatile("yield" ::: "memory"); | ^ ``` Closes #22751 --- lib/easy_lock.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/easy_lock.h b/lib/easy_lock.h index b8f916ff659c..9f21448cbe09 100644 --- a/lib/easy_lock.h +++ b/lib/easy_lock.h @@ -71,6 +71,7 @@ static CURL_INLINE void curl_simple_lock_lock(curl_simple_lock *lock) #ifdef HAVE_BUILTIN_IA32_PAUSE __builtin_ia32_pause(); #elif defined(__aarch64__) + /* NOLINTNEXTLINE(portability-no-assembler) */ __asm__ volatile("yield" ::: "memory"); #elif defined(HAVE_SCHED_YIELD) sched_yield(); From f190aa8ecea728e6ce7fb7a6250e03df4d4eaca5 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 31 Aug 2026 01:00:33 +0200 Subject: [PATCH 1030/1062] lib: drop unused `system_win32.h` includes Closes #22757 --- lib/cf-socket.c | 1 - lib/curlx/timeval.c | 2 -- lib/url.c | 1 - lib/vtls/schannel.c | 1 - 4 files changed, 5 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index ec3c5c9ce882..52e8014894bf 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -78,7 +78,6 @@ #include "rand.h" #include "sockaddr.h" #include "curlx/strdup.h" -#include "system_win32.h" #include "curlx/nonblock.h" #include "curlx/strcopy.h" #include "curlx/version_win32.h" diff --git a/lib/curlx/timeval.c b/lib/curlx/timeval.c index a3ced3ff0c9e..b43aedf15cc2 100644 --- a/lib/curlx/timeval.c +++ b/lib/curlx/timeval.c @@ -25,8 +25,6 @@ #ifdef _WIN32 -#include "system_win32.h" - static LARGE_INTEGER s_time_freq; /* For tool or tests, we must initialize before calling curlx_now(). diff --git a/lib/url.c b/lib/url.c index b309d3e209c3..f92efed4e78e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -83,7 +83,6 @@ #include "getinfo.h" #include "pop3.h" #include "urlapi-int.h" -#include "system_win32.h" #include "hsts.h" #include "proxy.h" #include "cfilters.h" diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index f129bdf890b0..34c6e79f79eb 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -48,7 +48,6 @@ #include "curlx/fopen.h" #include "curlx/multibyte.h" #include "vtls/x509asn1.h" -#include "system_win32.h" #include "curlx/version_win32.h" #include "curlx/winapi.h" #include "curlx/strparse.h" From 0f48cf3d7f17b11a131cda4ba870612d4ac96b66 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 31 Aug 2026 10:02:57 +0200 Subject: [PATCH 1031/1062] conncache: guess maxconnects different Take the number of attached transfers instead of the running ones as guidance how many connections the multi should cache. In addition, when guessing, only evict a connection if it has not been in use for a second or longer. Follow-up to 8a2bb9ca241bbd82a0da536f6 Closes #22759 --- docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md | 11 +++++-- lib/conncache.c | 40 +++++++++++------------ lib/conncache.h | 3 +- lib/multi.c | 21 +++++++++--- lib/multiif.h | 6 +++- lib/url.c | 2 +- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md index b02029b59fcd..5973fa8c55cd 100644 --- a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md +++ b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md @@ -28,14 +28,19 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_MAXCONNECTS, long max); Pass a long indicating the **max**, the maximum amount of connections that libcurl may keep alive in its connection cache after use. By default libcurl -enlarges the size for each added easy handle to make it fit 4 times the number +enlarges the size for each added easy handle to make it fit twice the number of added easy handles. By setting this option, you prevent the cache size from growing beyond the limit set by you. -When the cache is full, curl closes the oldest connection present in the cache -to prevent the number of connections from increasing. +When the cache is full on a set limit, curl closes the oldest connection +present in the cache to prevent the number of connections from increasing. + +When the cache is full on the default limit, curl closes the oldest connection +present in the cache only if it has not been used for at least a second. This +is done because the number of added transfer can vary greatly, depending on +how the application uses them. This option is for the multi handle's use only, when using the easy interface you should instead use the CURLOPT_MAXCONNECTS(3) option. diff --git a/lib/conncache.c b/lib/conncache.c index 9dd753670ba4..01681d4ebbc9 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -339,15 +339,16 @@ static struct connectdata *cpool_bundle_get_oldest_idle( } static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, - const struct curltime *pnow) + const struct curltime *pnow, + timediff_t min_age_ms) { struct Curl_hash_iterator iter; struct Curl_llist_node *curr; struct Curl_hash_element *he; - struct connectdata *oldest_idle = NULL; struct cpool_bundle *bundle; - timediff_t highscore = -1; - timediff_t score; + struct connectdata *oldest_idle = NULL; + timediff_t oldest_idle_ms = -1; + timediff_t idle_ms; Curl_hash_start_iterate(&cpool->dest2bundle, &iter); @@ -361,10 +362,9 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, conn = Curl_node_elem(curr); if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only) continue; - /* Set higher score for the age passed since the connection was used */ - score = curlx_ptimediff_ms(pnow, &conn->lastused); - if(score > highscore) { - highscore = score; + idle_ms = curlx_ptimediff_ms(pnow, &conn->lastused); + if((idle_ms >= min_age_ms) && (idle_ms > oldest_idle_ms)) { + oldest_idle_ms = idle_ms; oldest_idle = conn; } } @@ -451,7 +451,8 @@ static void cpool_evict_conn(struct cpool *cpool, } int Curl_cpool_check_limits(struct Curl_easy *data, - struct connectdata *conn) + struct connectdata *conn, + const struct curltime *pnow) { struct cpool *cpool = cpool_get_instance(data); struct cshutdn *cshutdn = Curl_cshutdn_get(data); @@ -494,8 +495,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *oldest_idle = NULL; /* The bundle is full. Extract the oldest connection that may * be removed now, if there is one. */ - oldest_idle = cpool_bundle_get_oldest_idle(bundle, - Curl_pgrs_now(data)); + oldest_idle = cpool_bundle_get_oldest_idle(bundle, pnow); if(!oldest_idle) break; /* disconnect the old conn and continue */ @@ -527,7 +527,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, } else { struct connectdata *oldest_idle = - cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); + cpool_get_oldest_idle(cpool, pnow, 0); if(!oldest_idle) break; /* disconnect the old conn and continue */ @@ -646,18 +646,18 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, struct cpool *cpool = cpool_get_instance(data); struct Curl_easy *admin; bool kept = TRUE; + timediff_t min_age_ms = 0; if(!data || !data->multi) return kept; if(!data->multi->maxconnects) { - /* Running transfers is a weak indicator of business. When many - * transfers are done at the same time (parallel h1), the number - * may drop quite low and we do not want to close connections - * just to have another wave of transfers arriving next. */ - uint32_t running = Curl_multi_xfers_running(data->multi); - running = CURLMAX(running, 128); - maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX; + /* Attached transfers is a weak indicator of business. */ + uint32_t attached = Curl_multi_xfers_attached(data->multi); + maxconnects = (attached <= UINT_MAX / 2) ? attached * 2 : UINT_MAX; + /* We are guessing. So, only evict a "seemingly superfluous" connection + * when has not been used for this long, */ + min_age_ms = 1000; } else { maxconnects = data->multi->maxconnects; @@ -676,7 +676,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, infof(data, "Connection pool is full, closing the oldest of %zu/%u", cpool->num_conn, maxconnects); - oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); + oldest_idle = cpool_get_oldest_idle(cpool, &conn->lastused, min_age_ms); kept = (oldest_idle != conn); if(oldest_idle) { cpool_evict_conn(cpool, admin, oldest_idle); diff --git a/lib/conncache.h b/lib/conncache.h index f31b5fcf8073..5e766c99d73e 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -93,7 +93,8 @@ CURLcode Curl_cpool_add(struct Curl_easy *data, #define CPOOL_LIMIT_DEST 1 #define CPOOL_LIMIT_TOTAL 2 int Curl_cpool_check_limits(struct Curl_easy *data, - struct connectdata *conn); + struct connectdata *conn, + const struct curltime *pnow); /* Return of conn is suitable. If so, stops iteration. */ typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn, diff --git a/lib/multi.c b/lib/multi.c index 35fb334db347..00e675b4ccb0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2951,7 +2951,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi, } if(running_handles) { - unsigned int running = Curl_multi_xfers_running(multi); + uint32_t running = Curl_multi_xfers_running(multi); *running_handles = (running < INT_MAX) ? (int)running : INT_MAX; } @@ -3309,7 +3309,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, mresult = Curl_mntfy_dispatch_all(multi); if(running_handles) { - unsigned int running = Curl_multi_xfers_running(multi); + uint32_t running = Curl_multi_xfers_running(multi); *running_handles = (running < INT_MAX) ? (int)running : INT_MAX; } @@ -4153,14 +4153,25 @@ bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data) return Curl_uint32_tbl_get(&multi->xfers, data->mid) == data; } -unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) +uint32_t Curl_multi_xfers_running(struct Curl_multi *multi) { - DEBUGASSERT(multi); - if(!multi) + if(!multi) { + DEBUGASSERT(0); return 0; + } return multi->xfers_alive; } +uint32_t Curl_multi_xfers_attached(struct Curl_multi *multi) +{ + if(!multi || !multi->admin) { + DEBUGASSERT(0); + return 0; + } + /* Discount the admin handle */ + return Curl_uint32_tbl_count(&multi->xfers) - 1; +} + void Curl_multi_mark_dirty(struct Curl_easy *data) { if(data->multi && data->mid != UINT32_MAX) diff --git a/lib/multiif.h b/lib/multiif.h index 16b9181cf885..492291fe09a1 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -158,8 +158,12 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, /* TRUE if multi knows about data via its `mid` */ bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data); +/* Get the # of transfers attached to the multi, without the internal + * admin handle. */ +uint32_t Curl_multi_xfers_attached(struct Curl_multi *multi); + /* Get the # of transfers current in process/pending. */ -unsigned int Curl_multi_xfers_running(struct Curl_multi *multi); +uint32_t Curl_multi_xfers_running(struct Curl_multi *multi); /* Mark a transfer as dirty, e.g. to be rerun at earliest convenience. * A cheap operation, can be done many times repeatedly. */ diff --git a/lib/url.c b/lib/url.c index f92efed4e78e..eb1e166a5577 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2383,7 +2383,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) goto out; } else { - switch(Curl_cpool_check_limits(data, needle)) { + switch(Curl_cpool_check_limits(data, needle, &needle->created)) { case CPOOL_LIMIT_DEST: infof(data, "No more connections allowed to host"); result = CURLE_NO_CONNECTION_AVAILABLE; From 740ffee03b600709932a18154ef3ade001069f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20S=C3=A1nchez-Guijarro?= <180536436+masancgui@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:14:13 +0200 Subject: [PATCH 1032/1062] gopher: fix partial sends of CRLF Extract the buffer-sending logic into its own function so it can also be used when sending CRLF. Previously, CRLF could be partially sent to the server without reporting an error or sending the remaining bytes. This commit preserves how the old code handled errors while writing CRLF to the client, which is why the extracted function has a bool parameter. This also includes a test case that verifies that the header written to the client contains exactly one CRLF. This test case also detects the old buggy behaviour of partial sends. Closes #22714 --- lib/gopher.c | 114 +++++++++++++++++++++++------------------ tests/data/Makefile.am | 3 +- tests/data/test5028 | 43 ++++++++++++++++ 3 files changed, 110 insertions(+), 50 deletions(-) create mode 100644 tests/data/test5028 diff --git a/lib/gopher.c b/lib/gopher.c index 57dfa263e08e..a5d0a4b0b4cf 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -57,19 +57,76 @@ static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) } #endif -static CURLcode gopher_do(struct Curl_easy *data, bool *done) +/* Sends buf to the server and, optionally, writes it to the client too. */ +static CURLcode send_buf(struct Curl_easy *data, + const char *buf, + size_t buf_len, + bool client_write) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + size_t nwritten; + timediff_t timeout_ms; + int what; + + while(buf_len) { + result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten); + if(!result) { /* Which may not have written it all! */ + if(client_write) { + result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, nwritten); + if(result) + break; + } + + if(nwritten > buf_len) { + DEBUGASSERT(0); + break; + } + buf_len -= nwritten; + buf += nwritten; + if(!buf_len) + break; /* but it did write it all */ + } + else + break; + + timeout_ms = Curl_timeleft_ms(data); + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + + /* Do not busyloop. The entire loop thing is a workaround as it causes a + BLOCKING behavior which is a NO-NO. This function should rather be + split up in a do and a doing piece where the pieces that are not + possible to send now will be sent in the doing function repeatedly + until the entire request is sent. */ + what = SOCKET_WRITABLE(sockfd, timeout_ms); + if(what < 0) { + result = CURLE_SEND_ERROR; + break; + } + else if(!what) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + } + + return result; +} + +static CURLcode gopher_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; char *gopherpath; const char *path = data->state.up.path; const char *query = data->state.up.query; const char *buf = NULL; char *buf_alloc = NULL; - size_t nwritten, buf_len; - timediff_t timeout_ms; - int what; + size_t buf_len; *done = TRUE; /* unconditionally */ @@ -115,54 +172,13 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) } } - for(; buf_len;) { - - result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten); - if(!result) { /* Which may not have written it all! */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, nwritten); - if(result) - break; - - if(nwritten > buf_len) { - DEBUGASSERT(0); - break; - } - buf_len -= nwritten; - buf += nwritten; - if(!buf_len) - break; /* but it did write it all */ - } - else - break; - - timeout_ms = Curl_timeleft_ms(data); - if(timeout_ms < 0) { - result = CURLE_OPERATION_TIMEDOUT; - break; - } - if(!timeout_ms) - timeout_ms = TIMEDIFF_T_MAX; - - /* Do not busyloop. The entire loop thing is a workaround as it causes a - BLOCKING behavior which is a NO-NO. This function should rather be - split up in a do and a doing piece where the pieces that are not - possible to send now will be sent in the doing function repeatedly - until the entire request is sent. */ - what = SOCKET_WRITABLE(sockfd, timeout_ms); - if(what < 0) { - result = CURLE_SEND_ERROR; - break; - } - else if(!what) { - result = CURLE_OPERATION_TIMEDOUT; - break; - } - } - + result = send_buf(data, buf, buf_len, TRUE); curlx_free(buf_alloc); if(!result) - result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten); + /* Send CRLF to the server now, but defer writing it to the client to + preserve the historical behavior of this file. */ + result = send_buf(data, "\r\n", 2, FALSE); if(result) { failf(data, "Failed sending Gopher request"); return result; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 9614fa98006f..3b32fce7bb30 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -2120,7 +2120,8 @@ TESTCASES = \ test5024 \ test5025 \ test5026 \ - test5027 + test5027 \ + test5028 EXTRA_DIST = \ $(TESTCASES) \ diff --git a/tests/data/test5028 b/tests/data/test5028 new file mode 100644 index 000000000000..03b721234a77 --- /dev/null +++ b/tests/data/test5028 @@ -0,0 +1,43 @@ + + + + +GOPHER +header dump + + + +# Server-side + + +hello + + + +# Client-side + + +gopher + + +Gopher header dump + + +Debug + + +# Previous versions of the Gopher implementation could partially send the +# CRLF. Try to trigger that bug, which hangs the connection. +CURL_SMALLSENDS=1 + + +gopher://%HOSTIP:%GOPHERPORT/1/%TESTNUMBER -D %LOGDIR/heads%TESTNUMBER -m 5 + + + + + +/%TESTNUMBER + + + From 73f18365ab8e47912afcd5ee9404c7493d9d0fb2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 31 Aug 2026 11:24:39 +0200 Subject: [PATCH 1033/1062] GHA/non-native: disable riscv64 FreeBSD job due to upstream issue CI jobs fail early with these errors: ``` pkg: https://cross-platform-actions.github.io/freebsd-pkg-repo/FreeBSD:15:riscv64/meta.txz: Not Found pkg: https://cross-platform-actions.github.io/freebsd-pkg-repo/FreeBSD:15:riscv64/data.pkg: Not Found pkg: https://cross-platform-actions.github.io/freebsd-pkg-repo/FreeBSD:15:riscv64/data.tzst: Not Found pkg: https://cross-platform-actions.github.io/freebsd-pkg-repo/FreeBSD:15:riscv64/packagesite.pkg: Not Found pkg: https://cross-platform-actions.github.io/freebsd-pkg-repo/FreeBSD:15:riscv64/packagesite.tzst: Not Found ``` Ref: https://github.com/curl/curl/actions/runs/33252993091/job/99101785035#step:4:35 (first seen) Ref: https://github.com/cross-platform-actions/action/issues/166 Follow-up to 9f05091b596e3015bf214ea15f6c56f6eb7647f6 #22748 Closes #22760 --- .github/workflows/non-native.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 905107746f46..dfe0f16acbb0 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -75,9 +75,9 @@ jobs: install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } - - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl !examples', tflags: 'HTTP --min=0 --subset=0/10 -R --seed', - install: 'cmake-core ninja perl5', - options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } + # { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'riscv64', cc: 'clang', desc: 'openssl !examples', tflags: 'HTTP --min=0 --subset=0/10 -R --seed', + # install: 'cmake-core ninja perl5', + # options: '-D_CURL_PREFILL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_DISABLE_HTTPSIG=OFF -DCURL_DISABLE_TYPECHECK=ON' } - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', tflags: 'skipall', install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', From ab00fc4a45add3600d693f329bc6c68ac4c25def Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 31 Aug 2026 12:26:26 +0200 Subject: [PATCH 1034/1062] clock: save one call When multi is processing a transfer, it assesses its timeleft and does a progress update repeatedly. Both got a fresh timestamp, but can use the same one. Change implementation for that. Closes #22761 --- lib/connect.c | 6 ++++++ lib/connect.h | 2 ++ lib/multi.c | 30 ++++++++++++++++++++---------- lib/progress.c | 6 ++++++ lib/progress.h | 1 + 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index e669f7630d38..1ae4ea5cef40 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -108,6 +108,12 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data) return timeleft_now_ms(data, Curl_pgrs_now(data)); } +timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow) +{ + return timeleft_now_ms(data, pnow); +} + void Curl_shutdown_start(struct Curl_easy *data, int8_t sockindex, int timeout_ms) { diff --git a/lib/connect.h b/lib/connect.h index a46e3c15811d..bc8c53aa83b5 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -36,6 +36,8 @@ enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); /* generic function that returns how much time there is left to run, according to the timeouts set */ timediff_t Curl_timeleft_ms(struct Curl_easy *data); +timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ diff --git a/lib/multi.c b/lib/multi.c index 00e675b4ccb0..0111db719a23 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1828,12 +1828,13 @@ static CURLcode multi_do_more(struct Curl_easy *data, domore *complete) * Check whether a timeout occurred, and handle it if it did */ static bool multi_handle_timeout(struct Curl_easy *data, + const struct curltime *pnow, bool *stream_error, CURLcode *result) { timediff_t timeout_ms; - timeout_ms = Curl_timeleft_ms(data); + timeout_ms = Curl_timeleft_now_ms(data, pnow); if(timeout_ms < 0) { /* Handle timed out */ timerid base_timer = Curl_is_connecting(data) ? @@ -2373,6 +2374,7 @@ static CURLMcode multistate_connect(struct Curl_multi *multi, /* returns the possibly updated result */ static CURLcode is_finished(struct Curl_multi *multi, struct Curl_easy *data, + const struct curltime *pnow, bool stream_error, CURLcode result) { @@ -2413,7 +2415,7 @@ static CURLcode is_finished(struct Curl_multi *multi, } /* if there is still a connection to use, call the progress function */ else if(data->conn && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) { - result = Curl_pgrsUpdate(data); + result = Curl_pgrsUpdateX(data, pnow); if(result) { /* aborted due to progress callback return code must close the connection */ @@ -2721,6 +2723,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, { CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; + const struct curltime *pnow = NULL; if(multi->dead) { /* a multi-level callback returned error before, meaning every individual @@ -2756,6 +2759,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, (HTTP/2), or the full connection for older protocols */ bool stream_error = FALSE; mresult = CURLM_OK; + pnow = NULL; if(multi_ischanged(multi, TRUE)) { CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue"); @@ -2774,10 +2778,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Wait for the connect state as only then is the start time stored, but we must not check already completed handles */ - if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) && - multi_handle_timeout(data, &stream_error, &result)) - /* Skip the statemachine and go directly to error handling section. */ - goto statemachine_end; + if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED)) { + pnow = Curl_pgrs_now(data); + if(multi_handle_timeout(data, pnow, &stream_error, &result)) + /* Skip the statemachine and go directly to error handling section. */ + goto statemachine_end; + pnow = NULL; + } switch(data->mstate) { case MSTATE_INIT: @@ -2865,12 +2872,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before * declaring the connection timed out as we may almost have a completed * connection. */ - multi_handle_timeout(data, &stream_error, &result); + pnow = Curl_pgrs_now(data); + multi_handle_timeout(data, pnow, &stream_error, &result); } statemachine_end: - - result = is_finished(multi, data, stream_error, result); + if(!pnow) + pnow = Curl_pgrs_now(data); + result = is_finished(multi, data, pnow, stream_error, result); if(result) mresult = CURLM_CALL_MULTI_PERFORM; @@ -2943,7 +2952,8 @@ static CURLMcode multi_perform(struct Curl_multi *multi, if(data->mstate == MSTATE_PENDING) { bool stream_unused; CURLcode result_unused; - if(multi_handle_timeout(data, &stream_unused, &result_unused)) { + if(multi_handle_timeout(data, multi_now(multi), + &stream_unused, &result_unused)) { infof(data, "PENDING handle timeout"); move_pending_to_connect(multi, data); } diff --git a/lib/progress.c b/lib/progress.c index 82bf5bb83b28..4832ceb289ab 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -715,6 +715,12 @@ CURLcode Curl_pgrsUpdate(struct Curl_easy *data) return pgrs_update(data, Curl_pgrs_now(data)); } +CURLcode Curl_pgrsUpdateX(struct Curl_easy *data, + const struct curltime *pnow) +{ + return pgrs_update(data, pnow); +} + CURLcode Curl_pgrsCheck(struct Curl_easy *data) { CURLcode result; diff --git a/lib/progress.h b/lib/progress.h index a4649ae83e98..20bb11f3f4a2 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -57,6 +57,7 @@ void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); /* perform progress update, invoking callbacks at intervals */ CURLcode Curl_pgrsUpdate(struct Curl_easy *data); +CURLcode Curl_pgrsUpdateX(struct Curl_easy *data, const struct curltime *pnow); /* perform progress update, no callbacks invoked */ void Curl_pgrsUpdate_nometer(struct Curl_easy *data); /* perform progress update with callbacks and speed checks */ From 4ab4e3f846a93ec58621d05cc6fee024e66642f3 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 31 Aug 2026 12:53:16 +0200 Subject: [PATCH 1035/1062] strings: add length to CURL_EASY_STR_SET() Since we check the input lengths of strings before setting, pass the known length to the hash set. Closes #22762 --- lib/setopt.c | 20 +++++++++++--------- lib/uint-hashset.c | 13 ++++++++++--- lib/uint-hashset.h | 2 ++ lib/urldata.h | 4 ++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index ed5d04d46f81..1988c9cde34a 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -81,11 +81,12 @@ static CURLcode setopt_set_timeout_ms(timediff_t *ptimeout_ms, long ms) CURLcode Curl_setstropt(struct Curl_easy *data, enum dupstring id, const char *s) { + size_t slen = s ? strlen(s) : 0; DEBUGASSERT((unsigned)id <= UINT8_MAX); - if(s && (strlen(s) > CURL_MAX_INPUT_LENGTH)) + if(s && (slen > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; - return CURL_EASY_STR_SET(data, (uint8_t)id, s); + return CURL_EASY_STR_SET(data, (uint8_t)id, s, slen); } CURLcode Curl_setblobopt(struct curl_blob **blobp, @@ -2499,24 +2500,25 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, { typedef CURLcode (*ptrfunc)(struct Curl_easy *data, CURLoption option, char *ptr); + /* Order by likeliness */ static const ptrfunc setopt_call[] = { -#ifndef CURL_DISABLE_PROXY - setopt_cptr_proxy, -#endif + setopt_cptr_misc, #if defined(USE_SSL) || defined(USE_SSH) setopt_cptr_ssl, #endif -#ifdef USE_SSH - setopt_cptr_ssh, +#ifndef CURL_DISABLE_PROXY + setopt_cptr_proxy, #endif + setopt_cptr_net, #ifndef CURL_DISABLE_FTP setopt_cptr_ftp, #endif +#ifdef USE_SSH + setopt_cptr_ssh, +#endif #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) setopt_cptr_http_mqtt, #endif - setopt_cptr_net, - setopt_cptr_misc, }; size_t i; diff --git a/lib/uint-hashset.c b/lib/uint-hashset.c index 7e0281d91137..e54996c1d7c7 100644 --- a/lib/uint-hashset.c +++ b/lib/uint-hashset.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "uint-hashset.h" +#include "curlx/strdup.h" /* random patterns for API verification */ #ifdef DEBUGBUILD @@ -237,8 +238,8 @@ CURLcode Curl_u8_strset_setn(struct u8_strset *set, } -CURLcode Curl_u8_strset_set(struct u8_strset *set, - uint8_t id, const char *str) +CURLcode Curl_u8_strset_setx(struct u8_strset *set, + uint8_t id, const char *str, size_t slen) { char *val; @@ -248,12 +249,18 @@ CURLcode Curl_u8_strset_set(struct u8_strset *set, return CURLE_OK; } - val = curlx_strdup(str); + val = curlx_memdup0(str, slen); if(!val) return CURLE_OUT_OF_MEMORY; return Curl_u8_strset_setn(set, id, val); } +CURLcode Curl_u8_strset_set(struct u8_strset *set, + uint8_t id, const char *str) +{ + return Curl_u8_strset_setx(set, id, str, str ? strlen(str) : 0); +} + static void u8_strset_unset(struct u8_strset *set, uint8_t id, bool zero) { uint8_t i, j; diff --git a/lib/uint-hashset.h b/lib/uint-hashset.h index 973142ff89ae..e5a1e02f00a8 100644 --- a/lib/uint-hashset.h +++ b/lib/uint-hashset.h @@ -67,6 +67,8 @@ const char *Curl_u8_strset_get(struct u8_strset *set, uint8_t id); /* Set string for id, makes a copy. */ CURLcode Curl_u8_strset_set(struct u8_strset *set, uint8_t id, const char *str); +CURLcode Curl_u8_strset_setx(struct u8_strset *set, + uint8_t id, const char *str, size_t slen); /* Set string for id, takes ownership of `str` even on failure. */ CURLcode Curl_u8_strset_setn(struct u8_strset *set, uint8_t id, char *str); diff --git a/lib/urldata.h b/lib/urldata.h index b070b5fd48c3..8ed2d7102583 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1226,8 +1226,8 @@ struct Curl_easy { #define CURL_EASY_STR(d, id) \ Curl_u8_strset_get(&(d)->set.strings, (uint8_t)(id)) -#define CURL_EASY_STR_SET(d, id, s) \ - Curl_u8_strset_set(&(d)->set.strings, (uint8_t)(id), (s)) +#define CURL_EASY_STR_SET(d, id, s, slen) \ + Curl_u8_strset_setx(&(d)->set.strings, (uint8_t)(id), (s), (slen)) #define CURL_EASY_STR_SETN(d, id, s) \ Curl_u8_strset_setn(&(d)->set.strings, (uint8_t)(id), (s)) #define CURL_EASY_STR_CLEAR(d, id) \ From 6a3ebdb9f05185e0e73be9a2bbd6992896fdc0f8 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 30 Aug 2026 22:15:44 +0200 Subject: [PATCH 1036/1062] winsock: drop redundant version checks at initialization All supported Windows versions (Vista+) support Winsock 2.2, therefore these checks always succeed for a binary that loaded and started up successfully. Ref: https://learn.microsoft.com/windows/win32/api/winsock/nf-winsock-wsastartup Closes #22754 --- lib/system_win32.c | 28 ++-------------------------- tests/server/util.c | 22 ++++------------------ 2 files changed, 6 insertions(+), 44 deletions(-) diff --git a/lib/system_win32.c b/lib/system_win32.c index 1b052357b5dd..4cbc2c25e9bf 100644 --- a/lib/system_win32.c +++ b/lib/system_win32.c @@ -38,33 +38,9 @@ CURLcode Curl_win32_init(long flags) should take place after this block. */ if(flags & CURL_GLOBAL_WIN32) { #ifdef USE_WINSOCK - WORD wVersionRequested; - WSADATA wsaData; - int res; - - wVersionRequested = MAKEWORD(2, 2); - res = WSAStartup(wVersionRequested, &wsaData); - - if(res) - /* Tell the user that we could not find a usable */ - /* winsock.dll. */ - return CURLE_FAILED_INIT; - - /* Confirm that the Windows Sockets DLL supports what we need.*/ - /* Note that if the DLL supports versions greater */ - /* than wVersionRequested, it will still return */ - /* wVersionRequested in wVersion. wHighVersion contains the */ - /* highest supported version. */ - - if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || - HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { - /* Tell the user that we could not find a usable */ - - /* winsock.dll. */ - WSACleanup(); + WSADATA wsa; + if(WSAStartup(MAKEWORD(2, 2), &wsa)) return CURLE_FAILED_INIT; - } - /* The Windows Sockets DLL is acceptable. Proceed. */ #elif defined(USE_LWIPSOCK) lwip_init(); #endif diff --git a/tests/server/util.c b/tests/server/util.c index 54854285ecb2..a856c92a00dc 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -133,30 +133,16 @@ int win32_init(void) curlx_now_init(); #ifdef USE_WINSOCK { - WORD wVersionRequested; - WSADATA wsaData; - int err; - char buffer[STRERROR_LEN]; - - wVersionRequested = MAKEWORD(2, 2); - err = WSAStartup(wVersionRequested, &wsaData); - if(err) { + WSADATA wsa; + if(WSAStartup(MAKEWORD(2, 2), &wsa)) { + char buffer[STRERROR_LEN]; curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)); fprintf(stderr, "Winsock init failed: %s\n", buffer); logmsg("Error initializing Winsock -- aborting"); return 1; } - - if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || - HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { - WSACleanup(); - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)); - fprintf(stderr, "Winsock init failed: %s\n", buffer); - logmsg("No suitable winsock.dll found -- aborting"); - return 1; - } } -#endif /* USE_WINSOCK */ +#endif atexit(win32_cleanup); return 0; } From 3f2a5bb05fd4c67814214b308cb1444b500aec10 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 31 Aug 2026 13:57:45 +0200 Subject: [PATCH 1037/1062] curl_setup.h: merge two `#if` blocks Also: alpha-sort disable options within the block. Closes #22765 --- lib/curl_setup.h | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/curl_setup.h b/lib/curl_setup.h index f9063b68976c..ea33c265faaa 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -268,13 +268,6 @@ # endif #endif -/* - * When http is disabled rtsp is not supported. - */ -#if defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_RTSP) -# define CURL_DISABLE_RTSP -#endif - /* * When HTTP is disabled, disable HTTP-only features */ @@ -282,8 +275,8 @@ # ifndef CURL_DISABLE_ALTSVC # define CURL_DISABLE_ALTSVC # endif -# ifndef CURL_DISABLE_COOKIES -# define CURL_DISABLE_COOKIES +# ifndef CURL_DISABLE_AWS +# define CURL_DISABLE_AWS # endif # ifndef CURL_DISABLE_BASIC_AUTH # define CURL_DISABLE_BASIC_AUTH @@ -291,11 +284,8 @@ # ifndef CURL_DISABLE_BEARER_AUTH # define CURL_DISABLE_BEARER_AUTH # endif -# ifndef CURL_DISABLE_AWS -# define CURL_DISABLE_AWS -# endif -# ifndef CURL_DISABLE_HTTPSIG -# define CURL_DISABLE_HTTPSIG +# ifndef CURL_DISABLE_COOKIES +# define CURL_DISABLE_COOKIES # endif # ifndef CURL_DISABLE_DOH # define CURL_DISABLE_DOH @@ -309,9 +299,15 @@ # ifndef CURL_DISABLE_HSTS # define CURL_DISABLE_HSTS # endif +# ifndef CURL_DISABLE_HTTPSIG +# define CURL_DISABLE_HTTPSIG +# endif # ifndef CURL_DISABLE_HTTP_AUTH # define CURL_DISABLE_HTTP_AUTH # endif +# ifndef CURL_DISABLE_RTSP +# define CURL_DISABLE_RTSP +# endif # ifndef CURL_DISABLE_WEBSOCKETS # define CURL_DISABLE_WEBSOCKETS /* no WebSockets without HTTP present */ # endif From d623b84832a88fe7a859a81c9d64fe0708335e29 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 30 Aug 2026 00:51:14 -0400 Subject: [PATCH 1038/1062] schannel: fix ALPN erroneously disabled - In easy.c global_init, initialize win32 before ssl. Schannel SSL init depends on win32 initialization. Prior to this change applications with no compatibility manifest using a libcurl with Schannel had ALPN erroneously disabled. During Schannel init there is a version info check so that ALPN is only enabled for Windows 8.1 or later (>= NT 6.3), but since win32 was not yet initialized the version check would fail even if the OS was more recent. This change ensures that the pointer to function RtlVerifyVersionInfo is initialized before SSL initialization so that version checks during SSL initialization do not use VerifyVersionInfo as a fallback. On Windows RtlVerifyVersionInfo compares against the actual OS version info whereas VerifyVersionInfo has behavior that varies depending on whether the application (eg curl tool) has a compatibility manifest. If there is no manifest, VerifyVersionInfo treats the OS version to compare against as Windows 8 (NT 6.2) even on later versions. So basically NT 6.2 < NT 6.3 caused ALPN to be erroneously disabled. Reported-by: Jay Satiro Ref: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-verifyversioninfoa Fixes https://github.com/curl/curl/pull/22739#issuecomment-5464013950 Closes #22746 --- lib/easy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/easy.c b/lib/easy.c index 8e4deb85dbdb..e86c98ddb663 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -138,6 +138,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) Curl_ccalloc = (curl_calloc_callback)calloc; } + if(Curl_win32_init(flags)) { + DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); + goto fail; + } + if(Curl_trc_init()) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_trc_init failed\n")); goto fail; @@ -153,11 +158,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } - if(Curl_win32_init(flags)) { - DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); - goto fail; - } - if(Curl_amiga_init()) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; From 9b2caf34f54f5eb2f26d271c2b2cdea71fa1cf40 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 31 Aug 2026 13:46:46 +0200 Subject: [PATCH 1039/1062] transfer: DID handling When a transfer leaves its DID state and goes to PERFORM, it should not hog the multi processing when there are other transfers alive. Make multistate_did() return CURLM_OK then which triggers the processing of the next easy handle. This gives more fairness among easy handles: - on a fast server, responses may arrive very quickly and processing a response is costly. All this time is then lost to other transfers that have not even sent their request - on a slow server, directly trying to receive the response will not work and is an unnecessary network call. Local scorecard tests with h1 requests showed that the former handling was only using 10 connections for 40 parallel requests after an initial peak of 40 connections. The change makes libcurl use all 40 connections continuously. This also explains the h1-requests memory use development. When we closed connections more aggressively, the 40 initial connections shrank and released their memory. But the parallelism was not good. Closes #22763 --- lib/multi.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/multi.c b/lib/multi.c index 0111db719a23..52d3939c9b45 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2674,8 +2674,16 @@ static CURLMcode multistate_did(struct Curl_multi *multi, /* Only perform the transfer if there is a good socket to work with. Having both BAD is a signal to skip immediately to DONE */ if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || - CONN_SOCK_IDX_VALID(data->conn->send_idx)) + CONN_SOCK_IDX_VALID(data->conn->send_idx)) { multistate(data, MSTATE_PERFORMING); + /* Do not return CURLM_CALL_MULTI_PERFORM to give other transfers + * a chance to send off their requests. + * Note: Some SFTP handlers do not seem to like this. + * Restrict it to HTTP families. */ + return ((multi->xfers_alive > 1) && + (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) ? + CURLM_OK : CURLM_CALL_MULTI_PERFORM; + } else { #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch && @@ -2684,8 +2692,8 @@ static CURLMcode multistate_did(struct Curl_multi *multi, } #endif multistate(data, MSTATE_DONE); + return CURLM_CALL_MULTI_PERFORM; } - return CURLM_CALL_MULTI_PERFORM; } static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *presult) From 543adc4de024dda4f3b8f368893c7d01a3cf0ea8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Aug 2026 22:00:40 +0200 Subject: [PATCH 1040/1062] openssl: avoid strlen() on the data from OpenSSL It is not guaranteed to be null terminated. Use memchr() instead. Reported-by: Max Dymond Closes #22767 --- lib/vtls/openssl.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 06bb07b9afa0..dedd8f3e70bd 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2104,17 +2104,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, switch(target) { case GEN_DNS: /* name/pattern comparison */ - /* The OpenSSL man page explicitly says: "In general it cannot be - assumed that the data returned by ASN1_STRING_data() is null - terminated or does not contain embedded nulls.", but also that - "The actual format of the data depends on the actual string - type itself: for example for an IA5String the data is ASCII" - - It has been however verified that in 0.9.6 and 0.9.7, IA5String - is always null-terminated. */ - if((altlen == strlen(altptr)) && - /* if this is not true, there was an embedded zero in the name - string and we cannot match it. */ + if(!memchr(altptr, '\0', altlen) && Curl_cert_hostcheck(altptr, altlen, peer->origin->hostname, hostlen)) { matched = TRUE; From 415d800e7bb0c3a1c468f2d6538e3772df13ebd2 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 31 Aug 2026 21:48:22 +0200 Subject: [PATCH 1041/1062] build: add performance tests Add a performance test bundle with the necessary build logic, and 3 tests to start with. The new test binary is built by default as part of the existing test targets and builds cleanly on all supported platforms. It's not run in CI. Build time overhead at this point is negligible. If it becomes higher, the perf target may receive separate build controls. Also apply small tidy-ups to existing build files. C-code-authored-by: Daniel Stenberg Build-and-integration-by: Viktor Szakats Closes #22741 Closes #22747 --- configure.ac | 9 +- tests/CMakeLists.txt | 1 + tests/Makefile.am | 7 +- tests/libtest/Makefile.am | 5 +- tests/perf/.gitignore | 6 ++ tests/perf/CMakeLists.txt | 59 +++++++++++++ tests/perf/Makefile.am | 82 ++++++++++++++++++ tests/perf/Makefile.inc | 48 +++++++++++ tests/perf/README.md | 36 ++++++++ tests/perf/base64.c | 79 ++++++++++++++++++ tests/perf/first.c | 67 +++++++++++++++ tests/perf/first.h | 56 +++++++++++++ tests/perf/snprintf.c | 67 +++++++++++++++ tests/perf/urlparser.c | 171 ++++++++++++++++++++++++++++++++++++++ 14 files changed, 683 insertions(+), 10 deletions(-) create mode 100644 tests/perf/.gitignore create mode 100644 tests/perf/CMakeLists.txt create mode 100644 tests/perf/Makefile.am create mode 100644 tests/perf/Makefile.inc create mode 100644 tests/perf/README.md create mode 100644 tests/perf/base64.c create mode 100644 tests/perf/first.c create mode 100644 tests/perf/first.h create mode 100644 tests/perf/snprintf.c create mode 100644 tests/perf/urlparser.c diff --git a/configure.ac b/configure.ac index 366c7aa88878..4c4fac2e2ba3 100644 --- a/configure.ac +++ b/configure.ac @@ -5557,12 +5557,13 @@ AC_CONFIG_FILES([\ tests/configurehelp.pm \ tests/certs/Makefile \ tests/data/Makefile \ - tests/server/Makefile \ - tests/libtest/Makefile \ - tests/unit/Makefile \ - tests/tunit/Makefile \ tests/http/config.ini \ tests/http/Makefile \ + tests/libtest/Makefile \ + tests/perf/Makefile \ + tests/server/Makefile \ + tests/tunit/Makefile \ + tests/unit/Makefile \ projects/Makefile \ projects/vms/Makefile ]) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 55928fac511c..c6dd72983267 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,7 @@ add_custom_target(testdeps) add_dependencies(testdeps "tt") add_subdirectory(http) +add_subdirectory(perf) add_subdirectory(server) add_subdirectory(libtest) if(BUILD_CURL_EXE) diff --git a/tests/Makefile.am b/tests/Makefile.am index 29cacacdfcc1..8273f83bb9c7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -95,7 +95,7 @@ BUILD_UNIT = DIST_UNIT = unit tunit endif -SUBDIRS = certs data server libtest http $(BUILD_UNIT) +SUBDIRS = certs data perf server libtest http $(BUILD_UNIT) DIST_SUBDIRS = $(SUBDIRS) $(DIST_UNIT) PERLFLAGS = -I$(srcdir) @@ -177,9 +177,10 @@ ci-pytest: all checksrc: (cd libtest && $(MAKE) checksrc) - (cd unit && $(MAKE) checksrc) - (cd tunit && $(MAKE) checksrc) + (cd perf && $(MAKE) checksrc) (cd server && $(MAKE) checksrc) + (cd tunit && $(MAKE) checksrc) + (cd unit && $(MAKE) checksrc) all-local: build-certs diff --git a/tests/libtest/Makefile.am b/tests/libtest/Makefile.am index 774758705d42..74eb150742c7 100644 --- a/tests/libtest/Makefile.am +++ b/tests/libtest/Makefile.am @@ -56,19 +56,18 @@ CFLAGS += @CURL_CFLAG_EXTRAS@ # Prevent LIBS from being used for all link targets LIBS = $(BLANK_AT_MAKETIME) -if USE_CPPFLAG_CURL_STATICLIB -AM_CPPFLAGS += -DCURL_STATICLIB -endif if DEBUGBUILD AM_CPPFLAGS += -DDEBUGBUILD endif if USE_CPPFLAG_CURL_STATICLIB +AM_CPPFLAGS += -DCURL_STATICLIB curlx_c_lib = else # These are part of the libcurl static lib. Add them here when linking shared. curlx_c_lib = $(CURLX_C) endif + $(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) $(TESTS_C) lib1521.c @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) --test $(TESTS_C) lib1521.c > $(BUNDLE).c diff --git a/tests/perf/.gitignore b/tests/perf/.gitignore new file mode 100644 index 000000000000..34621b4f3ced --- /dev/null +++ b/tests/perf/.gitignore @@ -0,0 +1,6 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +perf +perf.c diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt new file mode 100644 index 000000000000..bbf75968ebb0 --- /dev/null +++ b/tests/perf/CMakeLists.txt @@ -0,0 +1,59 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables +curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") + +if(LIB_SELECTED STREQUAL LIB_STATIC) + set(CURLX_C "") # Already exported from the libcurl static build. Skip them. +endif() + +if(_CURL_TESTS_CONCAT) + set(_mk_unity_extra "--concat" "-I${CMAKE_CURRENT_SOURCE_DIR}") +endif() + +add_custom_command(OUTPUT "${BUNDLE}.c" + COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" ${_mk_unity_extra} + --include ${UTILS_C} ${CURLX_C} ${TOOLX_C} --test ${TESTS_C} > "${BUNDLE}.c" + DEPENDS + "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.inc" + ${FIRST_C} ${FIRST_H} ${UTILS_C} ${CURLX_C} ${TOOLX_C} ${TESTS_C} + VERBATIM) + +add_executable(${BUNDLE} EXCLUDE_FROM_ALL "${BUNDLE}.c") +add_dependencies(tt ${BUNDLE}) +target_link_libraries(${BUNDLE} ${LIB_SELECTED} ${CURL_LIBS}) +target_include_directories(${BUNDLE} PRIVATE + "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" + "${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h", curlx + "${PROJECT_SOURCE_DIR}/src" # for toolx + "${CMAKE_CURRENT_SOURCE_DIR}" # for the generated bundle source to find included test sources +) +set_target_properties(${BUNDLE} PROPERTIES OUTPUT_NAME "${BUNDLE}" PROJECT_LABEL "Test ${BUNDLE}" UNITY_BUILD OFF) + +if(NOT _CURL_TESTS_CONCAT) + set_target_properties(${BUNDLE} PROPERTIES C_CLANG_TIDY "") + curl_add_clang_tidy_test_target("${BUNDLE}-clang-tidy" ${BUNDLE} ${FIRST_C} ${UTILS_C} ${TESTS_C}) +endif() diff --git a/tests/perf/Makefile.am b/tests/perf/Makefile.am new file mode 100644 index 000000000000..783cee14682b --- /dev/null +++ b/tests/perf/Makefile.am @@ -0,0 +1,82 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_srcdir)/src for toolx header files +# $(srcdir) for the generated bundle source to find included test sources + +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(srcdir) + +# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables +include Makefile.inc + +EXTRA_DIST = CMakeLists.txt README.md $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C) + +CFLAGS += @CURL_CFLAG_EXTRAS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) + +if USE_CPPFLAG_CURL_STATICLIB +AM_CPPFLAGS += -DCURL_STATICLIB +curlx_c_lib = +else +# These are part of the libcurl static lib. Add them here when linking shared. +curlx_c_lib = $(CURLX_C) +endif + +$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) $(TESTS_C) + @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) --test $(TESTS_C) > $(BUNDLE).c + +noinst_PROGRAMS = $(BUNDLE) +nodist_perf_SOURCES = $(BUNDLE).c +LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_PC_LIBS_PRIVATE@ +CLEANFILES = $(BUNDLE).c + +CHECKSRC = $(CS_$(V)) +CS_0 = @echo " RUN " $@; +CS_1 = +CS_ = $(CS_0) + +checksrc: + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) -W$(srcdir)/$(BUNDLE).c $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C)) + +if NOT_CURL_CI +all-local: checksrc +endif + +clean-local: + rm -f $(BUNDLE) diff --git a/tests/perf/Makefile.inc b/tests/perf/Makefile.inc new file mode 100644 index 000000000000..b3b159ab95fb --- /dev/null +++ b/tests/perf/Makefile.inc @@ -0,0 +1,48 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# Shared between CMakeLists.txt and Makefile.am + +BUNDLE = perf + +# Files referenced from the bundle source +FIRST_C = first.c +FIRST_H = first.h + +# Common files used by the performance test programs +UTILS_C = +UTILS_H = + +CURLX_C = \ + ../../lib/curlx/base64.c \ + ../../lib/curlx/fopen.c \ + ../../lib/curlx/multibyte.c \ + ../../lib/curlx/strparse.c \ + ../../lib/curlx/timeval.c \ + ../../lib/curlx/warnless.c + +# All performance test programs +TESTS_C = \ + base64.c \ + snprintf.c \ + urlparser.c diff --git a/tests/perf/README.md b/tests/perf/README.md new file mode 100644 index 000000000000..def7bf91b2f4 --- /dev/null +++ b/tests/perf/README.md @@ -0,0 +1,36 @@ + + +# Performance tests + +This directory contains small stand-alone libcurl-using programs that each +test specific aspects of the library's performance. + +The idea is to have a set of tests here that can be used to verify various +libcurl functions' performance when we have no other good means of doing so. +Performance is best tested relatively. Does it run slower or faster than +before? + +Ideally these tests run without using any servers. + +## `urlparser` + +Provide this test with a list of many URLs and it times how fast it can parse +them: `./perf urlparser URLs.txt [loops]` + +A test sample of 100K URLs can be found +[here](https://raw.githubusercontent.com/ada-url/url-various-datasets/refs/heads/main/top100/top100.txt) + +## `base64` + +This test first base64 encodes a 256-byte buffer that has every different +byte octet represented. It then decodes that string. Repeatedly in a loop the +provided number of times: `./perf base64 [loops]` + +## `snprintf` + +This test repeatedly formats a representative string and measures the time per +call: `./perf snprintf [loops]` diff --git a/tests/perf/base64.c b/tests/perf/base64.c new file mode 100644 index 000000000000..7437c24bad3f --- /dev/null +++ b/tests/perf/base64.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static int test_base64(int argc, const char **argv) +{ + struct curltime start; + struct curltime end; + timediff_t us; + long long hn; + + curl_off_t loops = 10000000, loop; + + unsigned char array[256]; + unsigned int c; + + if(argc > 1) { + const char *ptr = argv[1]; + curl_off_t num; + if(!curlx_str_number(&ptr, &num, CURL_OFF_T_MAX) && !*ptr) + loops = num; + } + + for(c = 0; c < 256; c++) { + array[c] = (unsigned char)c; + } + + start = curlx_now(); + for(loop = 0; loop < loops; loop++) { + char *encoded; + size_t enclen; + CURLcode result = + curlx_base64_encode(array, sizeof(array), &encoded, &enclen); + if(!result) { + unsigned char *recoded = NULL; + size_t reclen; + /* now decode it again */ + result = curlx_base64_decode(encoded, &recoded, &reclen); + curlx_free(recoded); + } + if(result) { + curl_mfprintf(stderr, "unexpected coding error: %d\n", (int)result); + return 1; + } + curlx_free(encoded); + } + end = curlx_now(); + us = curlx_timediff_us(end, start); /* how many microseconds */ + hn = loop ? us * 100000 / loops : 0; /* 100 times too big */ + curl_mprintf("Loops: %" CURL_FORMAT_CURL_OFF_T "\n" + "Time: %lld usecs\n" + "Time/loop: %lld.%lld ns\n", + loops, + (long long)us, + (long long)hn / 100, + (long long)hn % 100); + return 0; +} diff --git a/tests/perf/first.c b/tests/perf/first.c new file mode 100644 index 000000000000..cce5d27ec1bf --- /dev/null +++ b/tests/perf/first.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +#ifdef _WIN32 +static void win32_cleanup(void) +{ + _flushall(); /* flush buffers of all streams regardless of their mode */ +} +#endif + +int main(int argc, const char *argv[]) +{ + entry_func_t entry_func; + const char *entry_name; + int result; + size_t tmp; + + if(argc < 2) { + curl_mfprintf(stderr, "Pass perftest as first argument\n"); + return 1; + } + + entry_name = argv[1]; + entry_func = NULL; + for(tmp = 0; s_entries[tmp].ptr; ++tmp) { + if(!strcmp(entry_name, s_entries[tmp].name)) { + entry_func = s_entries[tmp].ptr; + break; + } + } + + if(!entry_func) { + curl_mfprintf(stderr, "Test '%s' not found.\n", entry_name); + return 99; + } + +#ifdef _WIN32 + curlx_now_init(); + atexit(win32_cleanup); +#endif + + result = entry_func(argc - 1, argv + 1); + + return result; +} diff --git a/tests/perf/first.h b/tests/perf/first.h new file mode 100644 index 000000000000..58990646a07b --- /dev/null +++ b/tests/perf/first.h @@ -0,0 +1,56 @@ +#ifndef HEADER_PERF_FIRST_H +#define HEADER_PERF_FIRST_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#define CURL_NO_OLDIES +#define CURL_DISABLE_DEPRECATION + +/* Now include the curl_setup.h file from libcurl's private libdir (the source + version, but that might include "curl_config.h" from the build directory so + we need both of them in the include path), so that we get good in-depth + knowledge about the system we are building this on */ +#include "curl_setup.h" + +#include "curlx/base64.h" /* for curlx_base64* */ +#include "curlx/fopen.h" /* for curlx_f*() */ +#include "curlx/strparse.h" /* for curlx_str_* parsing functions */ +#include "curlx/timeval.h" /* for curlx_now type and related functions */ + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +typedef int (*entry_func_t)(int, const char **); + +struct entry_s { + const char *name; + entry_func_t ptr; +}; + +extern const struct entry_s s_entries[]; + +#endif /* HEADER_PERF_FIRST_H */ diff --git a/tests/perf/snprintf.c b/tests/perf/snprintf.c new file mode 100644 index 000000000000..cf8f98d713f0 --- /dev/null +++ b/tests/perf/snprintf.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static int test_snprintf(int argc, const char **argv) +{ + struct curltime start; + struct curltime end; + timediff_t us; + long long hn; + + curl_off_t loops = 10000000, loop; + + char buffer[256]; + + if(argc > 1) { + const char *ptr = argv[1]; + curl_off_t num; + if(!curlx_str_number(&ptr, &num, CURL_OFF_T_MAX) && !*ptr) + loops = num; + } + + start = curlx_now(); + for(loop = 0; loop < loops; loop++) { + curl_msnprintf(buffer, sizeof(buffer), + "Add %-4d stuff %u to %3d the %3u output %s " + "%*s" + "for %lld testing %lu\n", + 123, (unsigned int)123, + 123, (unsigned int)123, "helllo", + 14, "01234567890123456", + (long long)987871231231, + (unsigned long)6732673); + } + end = curlx_now(); + us = curlx_timediff_us(end, start); /* how many microseconds */ + hn = loops ? us * 100000 / loops : 0; /* 100 times too big */ + curl_mprintf("Loops: %" CURL_FORMAT_CURL_OFF_T "\n" + "Time: %lld usecs\n" + "Time/loop: %lld.%lld ns\n", + loops, + (long long)us, + (long long)hn / 100, + (long long)hn % 100); + return 0; +} diff --git a/tests/perf/urlparser.c b/tests/perf/urlparser.c new file mode 100644 index 000000000000..eb1c2f31b8df --- /dev/null +++ b/tests/perf/urlparser.c @@ -0,0 +1,171 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +/* + * Read many URLs from a given file. + * Parse them with all listed option combinations + */ +static int test_urlparser(int argc, const char **argv) +{ + static const int options[] = { + CURLU_DEFAULT_PORT, + CURLU_DEFAULT_SCHEME, + CURLU_NON_SUPPORT_SCHEME, + CURLU_ALLOW_SPACE, + CURLU_GUESS_SCHEME, + CURLU_PATH_AS_IS, + CURLU_DISALLOW_USER + }; + + static char *urls[150000]; + + struct curltime start; + struct curltime end; + timediff_t us; + long long hn; + + curl_off_t loops = 10, loop; + + curl_off_t count = 0; + curl_off_t ecount = 0; /* errors */ + + int ret; + size_t o; + int fd; + char *buffer = NULL; + size_t i; + size_t nsize; + ssize_t nread; + size_t nurls = 0; /* number of URLs */ + curlx_struct_stat info; + char *p; + CURLU *uh; + + if(argc < 2) { + curl_mfprintf(stderr, "%s [iterations]\n", argv[0]); + return 2; + } + if(argc > 2) { + const char *ptr = argv[2]; + curl_off_t num; + if(!curlx_str_number(&ptr, &num, 100000) && !*ptr) + loops = num; + } + + fd = curlx_open(argv[1], O_RDONLY | CURL_O_BINARY); + if(fd == -1) { + curl_mfprintf(stderr, "Cannot open %s, exiting\n", argv[1]); + return 3; + } + + if(curlx_fstat(fd, &info) == -1) { + curlx_close(fd); + curl_mfprintf(stderr, "Cannot stat %s, exiting\n", argv[1]); + return 3; + } + + ret = 4; + + nsize = (size_t)info.st_size; + + buffer = curlx_malloc(nsize + 1); + if(!buffer) { + curlx_close(fd); + curl_mfprintf(stderr, "Cannot malloc the buffer\n"); + goto cleanup; + } + + nread = read(fd, buffer, nsize); + curlx_close(fd); + if(nread != (ssize_t)nsize) { + curl_mfprintf(stderr, "Cannot load the file\n"); + goto cleanup; + } + + buffer[nsize] = 0; + p = buffer; + while(*p) { + if(nurls >= CURL_ARRAYSIZE(urls)) { + curl_mfprintf(stderr, + "Too many URLs in file, rebuild with higher max\n"); + goto cleanup; + } + urls[nurls++] = p; + p = strchr(p, '\n'); + if(!p) + break; + if(p > urls[nurls - 1] && p[-1] == '\r') + p[-1] = '\0'; + *p = '\0'; + p++; + } + curl_mprintf("Found %zu URLs to test for %" CURL_FORMAT_CURL_OFF_T + " iterations (%zu variations)\n", + nurls, loops, CURL_ARRAYSIZE(options)); + + uh = curl_url(); + if(!uh) { + curl_mfprintf(stderr, "curl_url() failed\n"); + goto cleanup; + } + start = curlx_now(); + for(loop = 0; loop < loops; loop++) { + for(i = 0; i < nurls; i++) { + for(o = 0; o < CURL_ARRAYSIZE(options); o++) { + CURLUcode hcode = curl_url_set(uh, CURLUPART_URL, urls[i], options[o]); + count++; + if(hcode) { +#if 0 + curl_mfprintf(stderr, "Failed [%u]: %s\n", (int)hcode, buffer); +#endif + ecount++; + } + } + } + } + end = curlx_now(); + curl_url_cleanup(uh); + us = curlx_timediff_us(end, start); /* how many microseconds */ + hn = count ? us * 100000 / count : 0; /* 100 times too big */ + curl_mprintf("URLs: %" CURL_FORMAT_CURL_OFF_T "\n" + "Time: %lld usecs\n" + "Time/URL: %lld.%lld ns\n" + "URLs/sec: %lld\n" + "Errors: %" CURL_FORMAT_CURL_OFF_T "\n", + count, + (long long)us, + (long long)hn / 100, + (long long)hn % 100, + us ? (long long)(count * 1000000) / us : 0, + ecount); + + ret = 0; + +cleanup: + + curlx_free(buffer); + + return ret; +} From 15818f49e109a10c12b56132eaeedc3d31fcaf54 Mon Sep 17 00:00:00 2001 From: marco-jardim Date: Sun, 30 Aug 2026 18:18:10 -0300 Subject: [PATCH 1042/1062] cf-socket: avoid broken NetBSD SOCK_NONBLOCK NetBSD releases before 6.1 expose SOCK_NONBLOCK without setting the socket-layer nonblocking state. Use curlx_nonblock() on those releases for connection and wakeup sockets so transfers cannot block while waiting for a response. Exercise repeated short MIME reads in test1662. Fixes #22586 Reported-by: John Verne Closes #22755 --- lib/cf-socket.c | 8 ++++---- lib/curl_setup.h | 6 ++++++ lib/socketpair.c | 4 ++-- tests/libtest/lib1662.c | 10 +++++----- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 52e8014894bf..ca25e0aeab01 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1142,7 +1142,7 @@ static int cf_socktype(int x) #ifdef SOCK_CLOEXEC x &= ~SOCK_CLOEXEC; #endif -#ifdef SOCK_NONBLOCK +#ifdef CURL_USE_SOCK_NONBLOCK x &= ~SOCK_NONBLOCK; #endif return x; @@ -1204,7 +1204,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); ctx->started_at = *Curl_pgrs_now(data); -#ifdef SOCK_NONBLOCK +#ifdef CURL_USE_SOCK_NONBLOCK /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set * because we would not know how socktype is about to be used in the * callback, SOCK_NONBLOCK might get factored out before calling socket(). @@ -1213,7 +1213,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, ctx->addr.socktype |= SOCK_NONBLOCK; #endif result = socket_open(data, &ctx->addr, &ctx->sock); -#ifdef SOCK_NONBLOCK +#ifdef CURL_USE_SOCK_NONBLOCK /* Restore the socktype after the socket is created. */ if(!data->set.fopensocket) ctx->addr.socktype &= ~SOCK_NONBLOCK; @@ -1314,7 +1314,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } #endif -#ifndef SOCK_NONBLOCK +#ifndef CURL_USE_SOCK_NONBLOCK /* Set socket non-blocking, must be a non-blocking socket for * a non-blocking connect. */ error = curlx_nonblock(ctx->sock, TRUE); diff --git a/lib/curl_setup.h b/lib/curl_setup.h index ea33c265faaa..0af3a67b6e16 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1621,6 +1621,12 @@ typedef struct sockaddr_un { __NetBSD_Version__ */ #endif +/* NetBSD before 6.1 did not set SS_NBIO for SOCK_NONBLOCK. */ +#if defined(SOCK_NONBLOCK) && \ + (!defined(__NetBSD__) || (__NetBSD_Version__ >= 601000000)) +#define CURL_USE_SOCK_NONBLOCK +#endif + #ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */ #ifdef _WIN32 #if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ diff --git a/lib/socketpair.c b/lib/socketpair.c index 39a0e319b0f4..d4ac66e95ebe 100644 --- a/lib/socketpair.c +++ b/lib/socketpair.c @@ -96,14 +96,14 @@ static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) #ifdef SOCK_CLOEXEC type |= SOCK_CLOEXEC; #endif -#ifdef SOCK_NONBLOCK +#ifdef CURL_USE_SOCK_NONBLOCK if(nonblocking) type |= SOCK_NONBLOCK; #endif if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) return -1; -#ifndef SOCK_NONBLOCK +#ifndef CURL_USE_SOCK_NONBLOCK if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { diff --git a/tests/libtest/lib1662.c b/tests/libtest/lib1662.c index 4c338d29216c..365e28ea56df 100644 --- a/tests/libtest/lib1662.c +++ b/tests/libtest/lib1662.c @@ -34,13 +34,13 @@ static size_t t1662_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) struct t1662_WriteThis *pooh = (struct t1662_WriteThis *)userp; - if(size * nmemb < testdatalen) + if(size * nmemb < 1) return 0; if(pooh->sizeleft) { - memcpy(ptr, testdata, testdatalen); - pooh->sizeleft = 0; - return testdatalen; + *ptr = testdata[testdatalen - pooh->sizeleft]; + --pooh->sizeleft; + return 1; } return 0; /* no more data left to deliver */ @@ -52,7 +52,7 @@ static CURLcode test_lib1662(const char *URL) CURL *curl; curl_mime *mime1; curl_mimepart *part1; - struct t1662_WriteThis pooh = { 1 }; + struct t1662_WriteThis pooh = { CURL_CSTRLEN("mooaaa") }; mime1 = NULL; From 1d7ea23fa1c23e0be156a6886228d98509ecff8d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Aug 2026 22:49:29 +0200 Subject: [PATCH 1043/1062] perf: restore the -DDEBUGBUILD for debug builds Otherwise the base64 test segfaults Closes #22769 --- tests/perf/CMakeLists.txt | 1 + tests/perf/Makefile.am | 4 ++++ tests/perf/base64.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt index bbf75968ebb0..dda79733fff2 100644 --- a/tests/perf/CMakeLists.txt +++ b/tests/perf/CMakeLists.txt @@ -51,6 +51,7 @@ target_include_directories(${BUNDLE} PRIVATE "${PROJECT_SOURCE_DIR}/src" # for toolx "${CMAKE_CURRENT_SOURCE_DIR}" # for the generated bundle source to find included test sources ) +target_compile_definitions(${BUNDLE} PRIVATE ${CURL_DEBUG_MACROS}) set_target_properties(${BUNDLE} PROPERTIES OUTPUT_NAME "${BUNDLE}" PROJECT_LABEL "Test ${BUNDLE}" UNITY_BUILD OFF) if(NOT _CURL_TESTS_CONCAT) diff --git a/tests/perf/Makefile.am b/tests/perf/Makefile.am index 783cee14682b..903bf1653398 100644 --- a/tests/perf/Makefile.am +++ b/tests/perf/Makefile.am @@ -50,6 +50,10 @@ CFLAGS += @CURL_CFLAG_EXTRAS@ # Prevent LIBS from being used for all link targets LIBS = $(BLANK_AT_MAKETIME) +if DEBUGBUILD +AM_CPPFLAGS += -DDEBUGBUILD +endif + if USE_CPPFLAG_CURL_STATICLIB AM_CPPFLAGS += -DCURL_STATICLIB curlx_c_lib = diff --git a/tests/perf/base64.c b/tests/perf/base64.c index 7437c24bad3f..bc3c15fd8dd2 100644 --- a/tests/perf/base64.c +++ b/tests/perf/base64.c @@ -48,7 +48,7 @@ static int test_base64(int argc, const char **argv) start = curlx_now(); for(loop = 0; loop < loops; loop++) { - char *encoded; + char *encoded = NULL; size_t enclen; CURLcode result = curlx_base64_encode(array, sizeof(array), &encoded, &enclen); From fd1f5ec1d1227f89337a6a532e1a0ea259e768b3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Aug 2026 23:02:53 +0200 Subject: [PATCH 1044/1062] perf/urlparser: provide a built-in set of URLs Instead of loading a list. Iterate more loops instead by default. Closes #22770 --- .github/scripts/codespell.sh | 1 + .github/scripts/typos.toml | 1 + tests/perf/urlparser.c | 1163 +++++++++++++++++++++++++++++++--- 3 files changed, 1093 insertions(+), 72 deletions(-) diff --git a/.github/scripts/codespell.sh b/.github/scripts/codespell.sh index c6a0cb9b4b74..ae81c20daff0 100755 --- a/.github/scripts/codespell.sh +++ b/.github/scripts/codespell.sh @@ -18,6 +18,7 @@ codespell \ --skip 'scripts/wcurl' \ --skip 'tests/unit/unit1625.c' \ --skip 'tests/unit/unit1627.c' \ + --skip 'tests/perf/urlparser.c' \ --ignore-regex '.*spellchecker:disable-line' \ --ignore-words '.github/scripts/codespell-ignore.words' \ -- diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 9f2a0e0a2acc..da75ca84b9a5 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -36,4 +36,5 @@ extend-exclude = [ "tests/data/test*", "tests/unit/unit1625.c", "tests/unit/unit1627.c", + "tests/perf/urlparser.c", ] diff --git a/tests/perf/urlparser.c b/tests/perf/urlparser.c index eb1c2f31b8df..9b661b6ca39b 100644 --- a/tests/perf/urlparser.c +++ b/tests/perf/urlparser.c @@ -23,9 +23,1092 @@ ***************************************************************************/ #include "first.h" +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +static const char *urls[] = { + "https://apps.fruit.zom:5555/us/app/reality-become-an-anime-a" + "vatar/id1404176564", + "https://bugzilla.mozilla.ork/first/../home", + "https://people.zom/style/lady-gaga-shares-everyday-beauty-lo" + "ok-in-first-ever-makeup-tutorial/", + "https://usernAme:passW0rd@people.zom/tag/prince-philip", + "https://www.shopify.zom/de", + "https://apps.fruit.zom:5555/us/genre/ios-entertainment/id601" + "6?letter=*&page=18#page", + "https://apps.fruit.zom/us/developer/marshmallow-games-srl/id" + "955873246", + "https://www.micrrrsff.zom/en-us/store/r/student-discounts?su" + "ccessurl=https://www.micrrrsff.zom/en-us/d/surface-pro-9/93V" + "KD8NP4FVK", + "https://www.micrrrsff.zom/en-us/micrrrsff-365/buy/compare-al" + "l-micrrrsff-365-products?tab=1", + "https://usernAme:passW0rd@www.aclu.ork/cases/tsa-hiv-discrim" + "ination", + "https://cloudblogs.micrrrsff.zom:5555/opensource/?utm_source" + "=developermscom", + "https://shop.lululemon.zom/p/jackets-and-hoodies-jackets/Wun" + "der-Puff-Jacket/_/prod9490219?color=43731", + "https://www.unionbank.zom/about-us/corporate-profile-bod", + "https://people.zom/awards/oscars-2017-best-supporting-actor-" + "mahershala-ali/", + "https://addisonraefragrance.zom/collections/fragrance", + "https://globalfishingwatch.ork/first/../map-and-data/", + "https://globalfishingwatch.ork/first/../map and data/", + "https://people.zom/tag/minka-kelly/", + "https://usernAme:passW0rd@globalfishingwatch.ork/category/pr" + "ess-release/", + "https://corp.xumo.zom:5555/perguntas-frequentes-lg-channels/" + "?lang=pt-br", + "https://people.zom/food/jonas-family-expands-their-southern-" + "comfort-food-restaurant-with-las-vegas-location/", + "https://www.aclu.ork/first/../cases/fbi-v-fazaga?document=pe" + "tition-writ-certiorari-fbi-et-al", + "https://people.zom/movies/good-luck-to-you-leo-grande-traile" + "r-emma-thompson-daryl-mccormack/", + "https://bit.ly/MidwestShower0603", + "https://people.zom/sports/tokyo-olympics-coco-gauff-named-to" + "-team-usa-tennis-roster/", + "https://restoringpromise.vera.ork/", + "https://apps.fruit.zom/us/app/fao-in-emergencies/id756483910", + "https://people.zom:5555/tv/rhonj-teresa-giudice-says-she-spe" + "nt-over-1-million-paying-joes-legal-fees-and-back-taxes/", + "https://people.zom:5555/tv/amy-robach-went-to-mediation-with" + "-andrew-shue-was-waiting-to-announce-split/", + "https://usernAme:passW0rd@people.zom/celebrity/celebs-who-ha" + "te-selfies/", + "https://www.waittfoundation.ork/practioner-resources", + "https://support.google.zom/webmasters/answer/3258249", + "https://apps.fruit.zom/us/developer/micrrrsff-corporation/id" + "298856275", + "https://developer.android.zom:5555/google/play/billing/subsc" + "riptions#pause", + "https://apps.fruit.zom/us/app/scorecloud-express/id566535238", + "https://www.muzemerch.zom/grey-embroidered-lemur-hat-tenness" + "ee-aquarium", + "https://www.bestbuy.zom/site/dyson-v12-detect-slim-cordless-" + "vacuum-yellow-iron/6504483.p?skuId=6504483", + "https://usernAme:passW0rd@www.amazon.zom/l-f-Poreless-Skin-P" + "erfecting-Lightweight-Minimizes/dp/B07NSH2B4D/?tag=people-on" + "site-backup-20", + "https://www.fao.ork/neareast/events/woman2023", + "https://apps.fruit.zom/us/genre/ios-travel/id6003?letter=A", + "https://people.zom:5555/parents/ray-j-princess-love-welcome-" + "son/", + "https://people.zom/parents/kailyn-lowry-celebrates-son-isaac" + "-13th-birthday-photos/", + "http://cityoforange.ork/", + "https://apps.fruit.zom/us/app/imissal-catholic/id307312434", + "https://people.zom/politics/grieving-uvalde-mom-speaks-out-g" + "reg-abbott-reelection/", + "https://usernAme:passW0rd@www.linkedin.zom/jobs/view/office-" + "administrative-assistant-at-mission-staffing-3475444968?refI" + "d=1%2F3mNaT%2FSXexZ4nU9cINeQ%3D%3D&trackingId=e%2BB9ao707Q73" + "LVQ3yyEAPA%3D%3D&position=17&pageNum=0&trk=public_jobs_jserp" + "-result_search-card", + "https://www.amazon.zom:5555/LOreal-Paris-Anti-Aging-Moisturi" + "zing-Antioxidants/dp/B0B64GY9MC/ref=sr_1_5_mod_primary_new?c" + "rid=1", + "https://www.facebook.zom/Walgreens", + "https://kuitter.zom/rcfp", + "https://www.amazon.zom/gp/customer-reviews/R37KJ2IJ51CXY3?li" + "nkCode=ll2&linkId=dcabd56f80f16f35be5a0f9625df60e5&language=" + "en_US&ref_=as_li_ss_tl&tag=people-onsite-backup-20", + "https://howl.me/ciYSv9TpoI9", + "https://apps.fruit.zom/us/app/scooters-coffee/id1151532978", + "http://www.fao.ork/3/Y4473E/y4473e06.htm", + "https://shop.aclu.ork/abortion-access-for-all-tee-mint/", + "http://usernAme:passW0rd@Rescue-EU.ork", + "https://www.muzemerch.zom:5555/adult-living-planet-aquarium-" + "be-yourself-penguin-short-sleeved-tee", + "https://oceandecade.ork/society-ocean-decade-ecosystem/", + "https://people.zom:5555/author/eric-todisco/", + "https://people.zom:5555/movies/retta-skipped-dreamgirls-audi" + "tion/", + "https://www.imo.ork/first/../en/About/Careers/Pages/default." + "aspx", + "https://en.unesco.ork/first/../futuresofeducation/", + "https://usernAme:passW0rd@www.zomplex.zom/music/jay-z-beyonc" + "e-michael-rubin-july-4th-party-performances-drake-lil-baby-t" + "ravis-scott", + "https://people.zom/pets/tamarin-monkeys-reported-missing-fro" + "m-dallas-zoo-located-inside-closet-abandoned-home/", + "https://www.brookings.edu/", + "https://people.zom:5555/tv/theo-james-says-first-version-of-" + "white-lotus-nude-scene-was-way-too-much/", + "https://www.blackrock.zom:5555/au", + "https://people.zom/sports/bruce-springsteen-congratulates-da" + "ughter-jessica-for-olympics-win/", + "https://blog.mozilla.ork/press/?utm_source=www.mozilla.ork&u" + "tm_medium=referral&utm_campaign=footer&utm_content=company", + "https://www.linkedin.zom/pulse/15-best-paying-insurance-jobs" + "-pursue-get-ahead-by-linkedin-news?trk=topic-article-card_so" + "cial-action-counts_social-action-counts_likes-text", + "https://search.app.goo.gl/?link=https%3A%2F%2Fassistant.goog" + "le.zom%2Fplatforms%2Fphones%2F&apn=com.google.android.google" + "quicksearchbox&al=googleapp%3A%2F%2Fdeeplink%2F%3Fdata%3DCk0" + "BDb3mGzBGAiEA-Y8GC-f421EvQJVNQeTb4Bn3FgHZwTbqdHCX2uuqKd0CIQC" + "ZkzjMErMP-b6g3KcSB1gY35COjnm3PkYoJ3afLFnszxJmCg4IuPqyjwEQ_7_" + "K84SjAhIDCPAOGh0KGwiBApqJ8dsEEggBEg5UZWxsIG1lIGEgam9rZSIwCi5" + "odHRwczovL2Fzc2lzdGFudC5nb29nbGUuY29tL3BsYXRmb3Jtcy9waG9uZXM" + "v&amv=300727608&utm_campaign=1904", + "https://usernAme:passW0rd@apps.fruit.zom/us/app/filmrise/id1" + "353108336", + "http://www.ncbi.nlm.nih.vow/pmc/articles/PMC6200792", + "http://wwd.zom:5555/fashion-news/designer-luxury/exclusive-c" + "hanel-new-ads-pharrell-williams-kristen-stewart-gabrielle-ba" + "g-10840808/", + "https://www.louroe.zom/product/ad-1/", + "https://usernAme:passW0rd@www.iotc.ork/node/4458", + "https://t.co/pgzByE5o8b", + "https://security.googleblog.zom/2010/03/federal-support-for-" + "federated-login.html", + "https://www.billboard.zom/articles/news/festivals/9641188/ph" + "arrell-something-in-the-water-festival-virginia-beach-pull/?" + "utm_medium=social&utm_source=kuitter", + "https://www.mapbox.zom/industries/real-estate", + "https://apps.fruit.zom:5555/us/app/whova-event-conference-ap" + "p/id716979741", + "https://usernAme:passW0rd@people.zom/sports/patrick-mahomes-" + "thankful-engagement-expecting-baby/", + "https://apps.fruit.zom/us/genre/ios-education/id6017?letter=" + "O", + "https://fbinter.stadt-berlin.de/fb?loginkey=alphaDataStart&a" + "lphaDataId=s_vms_tempolimits_spatial@senstadt", + "https://facebook.zom/share.php?u=https://sesamestreetincommu" + "nities.ork/just-remember/los-padres-son-maestros-guias-lider" + "es-protectores-y-proveedores-para-sus-hijos/&t=%E2%80%9CLos%" + "20padres%20son%20maestros%2C%20gu%C3%ADas%2C%20l%C3%ADderes%" + "2C%20protectores%20y%20proveedores%20para%20sus%20hijos%E2%8" + "0%9D.%20Iyanla%20Vanzant", + "https://newsroom.paypal-corp.zom/stories?o=80", + "https://www.amazon.zom/JW-PEI-Womens-Shoulder-Handbag/dp/B0B" + "2MSZYQ5?linkCode=ll1&linkId=5b8d2d3c8aae8f925842402423d186d3" + "&language=en_US&ref_=as_li_ss_tl&tag=people-onsite-backup-20", + "https://www.instagram.zom/ipjh55/", + "https://www.adessium.ork/first/../our-partners-in-and-around" + "-ukraine-supporting-journalists-and-countering-disinformatio" + "n/", + "https://www.cbsnews.zom/news/steven-tyler-demands-president-" + "trump-stop-playing-aerosmith-music-at-rallies/", + "https://people.zom:5555/style/pete-davidson-and-emily-ratajk" + "owski-seen-embracing-in-an-intimate-moment-in-new-york-city/", + "https://www.instagram.zom/p/CpJZlWqp8IA/", + "https://people.zom/tv/sarah-shahi-on-very-personal-journey-f" + "ilming-sex-life-billie-and-i-oddly-similar/", + "https://cloud.google.zom/dlp/", + "https://people.zom/crime/explosives-found-in-passengers-chec" + "ked-luggage-at-penn-airport-suspect-in-custody/", + "https://www.google.zom/url?q=http://www.kslegislature.ork/li" + "/b2021_22/measures/documents/hb2466_enrolled.pdf&sa=D&source" + "=editors&ust=1677775893480979&usg=AOvVaw3qxsnuXHzFkYkARBpgt0" + "fU", + "https://www.cbsnews.zom/news/close-call-jfk-airport-two-pack" + "ed-planes-delta-air-lines-american-airlines/", + "http://www.engr.psu.edu/students/grad-prospective/happy-vall" + "ey-life.aspx", + "https://apps.fruit.zom/us/app/monument-valley/id728293409", + "https://tv.surprise.zom/browse/UCjLpE6spfsS1RWCQszw67Hg", + "https://www.ywca.ork/blog/2022/07/28/ywca-usa-expresses-outr" + "age-demands-the-inclusion-of-child-care-in-budget-reconcilia" + "tion/", + "https://apnews.zom/article/politics-tennessee-state-governme" + "nt-health-gender-181952f6bd94a8e8c0a35be81afe00ad", + "https://www.adcouncil.ork/our-impact/covid-vaccine", + "https://people.zom/music/justin-bieber-shawn-mendes-monster-" + "collaboration/", + "https://topgolf.zom/us/company/privacy-policy/", + "https://myactivity.google.zom/myactivity?utm_source=pp", + "https://news.mongabay.zom/series/indonesian-fisheries/", + "https://www.georgeamphitheatre.zom/events/joni-jam-joni-mitc" + "hell-brandi-carlile/", + "https://people.zom/tv/judd-apatow-says-he-was-bawling-after-" + "watching-daughter-maude-in-euphoria/", + "https://people.zom:5555/movies/daniel-radcliffe-girlfriend-e" + "rin-darke-make-rare-red-carpet-outing-the-lost-city-premiere" + "/", + "https://www.instagram.zom/p/CjTbjy5O-w1/?hl=en", + "https://apps.fruit.zom/us/developer/viki-inc/id445553061", + "https://apps.fruit.zom/us/app/rabbit-hunting-calls/id1476557" + "697", + "https://people.zom:5555/sports/eli-manning-failed-to-buy-tay" + "lor-swift-concert-tickets-like-everybody-else/", + "https://aka.ms/VideoHub/Security", + "https://www.adcouncil.ork/find-assets", + "https://kroger-stage.preprodhawkcommerce.zom/visa-mastercard" + "-gift-card", + "http://www.pillsburyunited.ork/first/../", + "http://www.consumidor.ftc.vow/articulos/s0031-como-proteger-" + "la-privacidad-de-su-hijo-en-internet", + "https://www.linkedin.zom/signup/cold-join?session_redirect=h" + "ttps%3A%2F%2Fwww%2Elinkedin%2Ecom%2Fshowcase%2Fchrome-enterp" + "rise&trk=organization_guest_main-feed-card_like-cta", + "http://www.putty3d.zom", + "https://wp.api.aclu.ork/first/../issues/national-security", + "https://www.montblanc.zom/en-us/collection/smartwatches/summ" + "it-3", + "http://people.zom/tag/real-housewives", + "https://www.instagram.zom:5555/p/CNoZWuPhNca/", + "https://www.bmw.ie/en/index.html?tl=grp-wdpl-bcom-mix-mn-.-n" + "scf-.-.-", + "https://people.zom/politics/four-seasons-landscaping-owner-s" + "tars-in-super-bowl-commercial/", + "https://www.melleka.zom/contact", + "https://www.waittfoundation.ork/roc-grants-faq", + "https://usblackchambers.ork/news-events/the-united-states-se" + "nate-confirms-black-business-champion-donald-r-cravins-jr-to" + "-serve-as-the-under-secretary-for-minority-business-developm" + "ent-at-the-minority-business-development-agency/", + "https://people.zom/tag/cara-delevingne/", + "https://apps.fruit.zom/us/app/gamechanger-classic/id31890631" + "4", + "https://doi.ork/10.2307%2F2297912", + "https://people.zom/style/grammys-2023-megan-fox-machine-gun-" + "kelly-matching-looks/", + "https://pmc.zom/our-brands/the-american-pavilion/", + "https://uso.boom.us/webinar/register/WN_UIUeY_1jRVCPjtUaUJ5M" + "WA", + "https://c4ads.ork/south-asia/", + "http://www.ncbi.nlm.nih.vow/pmc/articles/PMC8129986", + "https://licensing.theguardian.zom/", + "https://people.zom/human-interest/single-ticket-won-2-billio" + "n-powerball-hasnt-claimed-prize/", + "https://subscribe.hollywoodreporter.zom/sub/?p=THR&f=autopro" + "file", + "http://alumni.cientifica.edu.pe/", + "https://support.micrrrsff.zom/en-us/outlook", + "https://www.xbox.zom", + "https://perscholas.ork/meet-our-graduates/story/soma-majumde" + "r/", + "https://www.linkedin.zom/pulse/how-do-i-fuse-multiple-busine" + "ss-cultures-one-cohesive-culture-", + "https://student.mak.ac.ug/", + "https://www.tinglyteds.do.mo/", + "https://apps.fruit.zom/us/app/chase-mobile-bank-invest/id298" + "867247", + "https://webpass.ner/east_bay/", + "https://people.zom/tag/tina-turner/", + "https://perscholas.ork/about/board-staff/elizabeth-cooper/", + "https://applieddigitalskills.withgoogle.zom/c/college-and-co" + "ntinuing-education/en/plan-effective-meetings/plan-effective" + "-meetings/introduction-to-plan-effective-meetings.html?utm_s" + "ource=grow-with-goog", + "https://apps.fruit.zom/us/app/evite-party-invitations/id4316" + "85286", + "https://www.amazon.zom/COSRX-Repairing-Hydrating-Secretion-P" + "hthalates/dp/B00PBX3L7K?linkCode=ll1&linkId=e6c02a5e0f019289" + "dca84d80b38c95bb&language=en_US&ref_=as_li_ss_tl&tag=people-" + "onsite-backup-20", + "https://pleasing.zom/products/the-pleasing-pen", + "https://people.zom/style/best-graduation-2021-beauty-tips/", + "https://www.aclu.ork/news/civil-liberties/supreme-court-reje" + "cts-promise-miranda-rights", + "https://apps.fruit.zom/us/app/night-sky/id475772902", + "https://sesamestreetincommunities.ork/activities/tooth-cafe/", + "http://scholar.google.do.mo/scholar?as_q=&num=10&btnG=Search" + "+Scholar&as_epq=&as_oq=&as_eq=&as_occt=any&as_sauthors=%22Th" + "omas%20K.%20Doyle%22&as_publication=&as_ylo=&as_yhi=&as_alls" + "ubj=all&hl=en", + "https://www.instagram.zom/p/CoVsKrNveOX/", + "https://apps.fruit.zom/us/app/tremors/id496065362", + "https://apps.fruit.zom/us/app/fasteasy-intermittent-fasting/" + "id1553036888", + "https://musicworldmedia.zom/", + "https://www.tiktok.zom/@hamiltonfamilies/video/7178948947333" + "926187", + "https://people.zom/music/amas-2022-everything-to-know/", + "https://www.earthday.ork/stories/#featured-3", + "https://de-de.facebook.zom/login.php?skip_api_login=1&api_ke" + "y=966242223397117&signed_next=1&next=https%3A%2F%2Fwww.faceb" + "ook.zom%2Fshare.php%3Fu%3Dhttps%253A%252F%252Fsesamestreetin" + "communities.ork%252Fjust-remember%252Ftrust-know-think%252F%" + "26t%3D%2526%25238220%253BTrust%2Byourself%252C%2Byou%2Bknow%" + "2Bmore%2Bthan%2Byou%2Bthink%2Byou%2Bdo.%2526%25238221%253B%2" + "526hellip%253B%2BDr.%2BBenjamin%2BSpock&cancel_url=https%3A%" + "2F%2Fwww.facebook.zom%2Fdialog%2Fclose_window%2F%3Fapp_id%3D" + "966242223397117%26connect%3D0%23_%3D_&display=popup&locale=d" + "e_DE", + "https://www.hellomagazine.zom/royalty/20230221164926/kate-mi" + "ddleton-tells-103-year-old-fan-she-loves-kidneys-nursing-hom" + "e-visit/", + "https://www.linkedin.zom/pulse/how-prepare-maternitypaternit" + "y-leave-get-ahead-by-linkedin-news?trk=topic-article-card_so" + "cial-action-counts_social-action-counts_likes-text", + "https://people.zom/books/firooz-zahedi-look-at-me-images/", + "https://www.linkedin.zom/legal/cookie-policy?session_redirec" + "t=https%3A%2F%2Fwww%2Elinkedin%2Ecom%2Ffeed%2Fhashtag%2Fplay" + "biztip&trk=registration-frontend_join-form-cookie-policy", + "https://www.today.zom/popculture", + "https://www.addtoany.zom/add_to/copy_link?linkurl=https%3A%2" + "F%2Fglobalfishingwatch.ork%2Fresearch%2Fintruders-at-sea%2F&" + "linkname=Shedding%20a%20light%20on%20intruders%20at%20sea", + "https://people.zom/tv/derek-hough-already-knows-what-music-d" + "ancers-and-coreo-he-wants-for-his-vegas-residency/", + "https://apps.fruit.zom/us/genre/ios-health-fitness/id6013?le" + "tter=*", + "https://apps.fruit.zom/us/app/sneaker-hub-shop/id1533754960", + "https://people.zom/human-interest/rogue-wave-kills-passenger" + "-injures-four-viking-cruise-ship-antarctica/", + "https://www.addtoany.zom/add_to/copy_link?linkurl=https%3A%2" + "F%2Fglobalfishingwatch.ork%2Fnews-views%2Ftransshipment-moni" + "toring%2F&linkname=Transshipment%20Monitoring%20Portal%20Bri" + "ngs%20Transparency%20to%20Fishing%20Industry", + "https://apps.fruit.zom/us/app/ticketmaster-buy-sell-tickets/" + "id500003565", + "https://people.zom/tag/maksim-chmerkovskiy/", + "https://www.addtoany.zom/add_to/copy_link?linkurl=https%3A%2" + "F%2Fglobalfishingwatch.ork%2Fresearch%2Ffisheries-marine-pro" + "tection%2F&linkname=Study%20Uses%20Satellite%20Technology%20" + "to%20Reveal%20How%20Fisheries%20Respond%20to%20Marine%20Prot" + "ection%20-%20Global%20Fishing%20Watch", + "https://www.addtoany.zom/add_to/copy_link?linkurl=https%3A%2" + "F%2Fglobalfishingwatch.ork%2Ftransparency-program-peru%2F&li" + "nkname=Per%C3%BA", + "https://apps.fruit.zom/us/app/laberinto-m%C3%A1gico-virtual-" + "reality/id1228366278", + "http://scholar.google.do.mo/scholar?as_q=&num=10&btnG=Search" + "+Scholar&as_epq=&as_oq=&as_eq=&as_occt=any&as_sauthors=%22Ce" + "sar%20Pe%C3%B1aherrera-Palma%22&as_publication=&as_ylo=&as_y" + "hi=&as_allsubj=all&hl=en", + "https://globalfishingwatch.ork/category/news-views/page/10/", + "https://people.zom/parents/jessica-alba-ho-5/", + "https://www.muzemerch.zom/invaders-in-our-town-the-battle-of" + "-gettysburg", + "https://www.fruit.zom/supplier-responsibility/", + "https://www.aclu.ork/issues/immigrants-rights/immigrants-rig" + "hts-and-detention/asylum-seekers-stranded-mexico-face", + "https://apps.fruit.zom/us/genre/ios-reference/id6006?letter=" + "H", + "https://ko-kr.facebook.zom/login.php?skip_api_login=1&api_ke" + "y=966242223397117&signed_next=1&next=https%3A%2F%2Fwww.faceb" + "ook.zom%2Fsharer%2Fsharer.php%3Fu%3Dhttps%253A%252F%252Fglob" + "alfishingwatch.ork%252Fes%252Fnuestro-mapa%252F%26t%3DNuestr" + "o%2BMapa%26quote&cancel_url=https%3A%2F%2Fwww.facebook.zom%2" + "Fdialog%2Fclose_window%2F%3Fapp_id%3D966242223397117%26conne" + "ct%3D0%23_%3D_&display=popup&locale=ko_KR", + "https://www.linkedin.zom/pulse/how-break-gaming-industry-get" + "-ahead-by-linkedin-news", + "http://i-sil.zom/support", + "https://www.nerflix.zom/title/81519789?trackId=259776131&trk" + "Id=259776131&src=tudum", + "https://people.zom/food/who-is-christina-perez-bobby-flay/", + "https://medium.zom/@hchen3030", + "https://apps.fruit.zom/us/genre/ios-entertainment/id6016?let" + "ter=D&page=5#page", + "https://www.muzemerch.zom/gettysburg-challenge-coin", + "https://www.coursera.ork/professional-certificates/google-it" + "-automation", + "https://energy.vow/", + "http://www.ncbi.nlm.nih.vow/entrez/query.fcgi?cmd=Retrieve&d" + "b=PubMed&dopt=Abstract&list_uids=23871238", + "http://www.fao.ork/iuu-fishing/tools-and-initiatives/transsh" + "ipment/en/", + "https://oldboy1111.tumblr.zom/post/90465861044", + "https://www.today.zom/parents/two-sets-twins-under-two-visit" + "-today-show-rcna71945", + "https://people.zom/royals/kate-middleton-every-outfit-2023-p" + "rincess-of-wales/", + "https://www.linkedin.zom/company/the-agency-worx?trk=public_" + "jobs_jserp-result_job-search-card-subtitle", + "https://apps.fruit.zom/us/app/photomath/id919087726", + "https://apps.fruit.zom/us/app/bills-monitor-pro/id468993411", + "https://people.zom/pets/justin-bieber-and-wife-hailey-adopt-" + "new-puppy-piggy-lou-oscars-new-baby-sister/", + "https://apps.fruit.zom/us/genre/ios-sports/id6004?letter=Z", + "https://www.amazon.zom/Cushioned-Kitchens-Standing-Phthalate" + "-Relieves/dp/B07WZN4WXN?th=1&linkCode=ll1&linkId=49cd6477154" + "6345c9e1508ca0f6a48c0&language=en_US&ref_=as_li_ss_tl&tag=pe" + "ople-onsite-backup-20", + "https://sesamestreetincommunities.ork/activities/sanos-y-fue" + "rtes-todo-el-dia/", + "https://people.zom/tv/taye-diggs-on-his-insomnia-battle-afte" + "r-becoming-a-dad/", + "https://people.zom/politics/2020-election-presidential-candi" + "date-celebrities-voting-for/", + "https://people.zom/music/amy-grant-postpones-remaining-fall-" + "tour-dates-after-bike-accident-getting-stronger-every-day/", + "https://people.zom/royals/prince-william-new-york-city-visit" + "-coincides-denmark-royals-prince-frederik-princess-mary/", + "https://greeneletron.ork.br/", + "https://www.un.ork/sustainabledevelopment/wp-content/uploads" + "/2018/09/02_Mobilizing_Private_Investment_InkFactory_Medium." + "jpg", + "https://www.minderoo.ork/marine-education-and-outreach/blueb" + "ack/", + "http://usa.oceana.ork/sites/default/files/pipa_report_final_" + "low_res_3_7_16.pdf", + "https://click.linksynergy.zom/deeplink?id=93xLBvPhAeE&mid=12" + "37&murl=https%3A%2F%2Fwww.nordstrom.zom%2Fs%2Fw-and-p-design" + "-wide-mouth-water-bottle%2F7085643&u1=PEONordstromHasValenti" + "nesDayGiftsforEveryoneinYourFriendGroupsomalleyLifAff1389943" + "2202301I", + "https://people.zom/sports/see-joyful-moment-brittney-griners" + "-wife-cherelle-learned-wnba-star-coming-home/", + "https://support.fruit.zom/guide/safari/make-safari-your-defa" + "ult-web-browser-ibrwa008/14.0/mac/11.0", + "https://apps.fruit.zom/us/app/x-ray-photo-effects/id11613712" + "83", + "https://people.zom/movies/stranger-things-star-finn-wolfhard" + "-direct-movie-hell-of-a-summer/", + "https://people.zom/music/justin-bieber-sells-291-song-catalo" + "g-for-200-million/", + "https://people.zom/tv/nene-leakes-heavenly-birthday-tribute-" + "late-husband-gregg-leakes/", + "https://developers.google.zom/workspace/preview", + "https://apps.fruit.zom/us/app/w-nder/id1476354846", + "https://firebase.google.zom/docs/release", + "https://give.ywca.ork/give/436728/#!/donation/checkout", + "https://people.zom/politics/rosalynn-carter-jimmy-carter-lif" + "e-in-photos/", + "https://policy.medium.zom/medium-terms-of-service-9db0094a1e" + "0f?source=post_page-----20a79a76275-------------------------" + "-------", + "https://esci.ucsc.edu/index.html", + "https://www.mozilla.ork/en-US/about/legal/", + "https://people.zom/best-black-tights-7093985", + "https://people.zom/tv/ian-somerhalder-nikki-reed-relationshi" + "p-timeline/", + "https://www.louroe.zom/product/tli-cs/", + "https://www.cpsc.vow/Recalls/2023/Pacific-Cycle-Recalls-Paci" + "fic-Kids-Igniter-and-Pacific-Bubble-Pop-20-Inch-Bicycles-Due" + "-to-Fall-Hazard-Sold-Exclusively-at-Target", + "https://sesamestreetincommunities.ork/activities/desarrollar" + "-la-empatia/", + "https://people.zom/royals/king-charles-queen-camilla-host-ro" + "yals-christmas-lunch-windsor-castle/", + "https://people.zom/tv/kylie-jenner-reveals-kris-jenner-caitl" + "yn-jenner-nearly-named-her-kennedy/", + "https://people.zom/theater/titanique-celebrates-25th-anniver" + "sary-of-titanic-with-special-performance/", + "https://www.oceandecade.ork/sign-up/", + "http://scholar.google.zom/scholar_lookup?&title=&journal=Fis" + "h.%20Res.&doi=10.1016%2Fj.fishres.2014.04.009&volume=157&pag" + "es=154-163&publication_year=2014&author=Sumaila%2CUR&author=" + "Bawumia%2CM", + "https://www.cgdev.ork/sites/default/files/economic-and-fisca" + "l-effects-united-states-reduced-numbers-refugees-and-asylum-" + "seekers.pdf", + "https://usblackchambers.ork/news-events/coalition-to-back-bl" + "ack-businesses/", + "https://apps.fruit.zom/us/app/mangatoon-manga-reader/id13852" + "87093", + "https://people.zom/royals/king-charles-attends-remembrance-s" + "unday-first-time-becoming-monarch/", + "http://hqepc%26list%3Dplkdbwuz2pblxz1h9g8snvlu6ug5hq2x-u%26i" + "ndex%3D2/", + "https://stripe.zom/us/privacy", + "https://people.zom/parents/alyssa-bates-webster-welcomes-fou" + "rth-child-daughter-maci-jo/", + "https://people.zom/movies/pamela-anderson-says-she-walked-in" + "-on-jack-nicholson-having-a-threesome-in-a-bathroom-i-caught" + "-his-eye/", + "https://people.zom/movies/rebel-wilson-photos-from-people-co" + "ver-shoot/", + "https://github.zom/GlobalFishingWatch/4wings/tree/{{ urlEnco" + "dedRefName }}", + "https://people.zom/parents/mom-goes-viral-documenting-daught" + "ers-growing-eleanor-roosevelt-obsession-exclusive/", + "http://www.bravotv.zom/the-real-housewives-of-new-jersey/sea" + "son-1/videos/choosing-her-boobies", + "https://apps.fruit.zom/us/app/my-cards-pro-wallet/id67327727" + "5", + "https://people.zom/tv/the-real-housewives-of-dubai-newest-ho" + "usewives-franchise-announced/", + "https://www.uso.ork/stories/3414-when-service-members-sudden" + "ly-deploy-to-eastern-europe-military-spouses-can-turn-to-uso" + "-united-kingdom-for-community-and-support", + "https://finance.yohaa.zom/news/packetized-energy-awarded-2-m" + "illion-140000452.html", + "https://www.linkedin.zom/company/riot-games?trk=public_jobs_" + "jserp-result_job-search-card-subtitle", + "https://www.washingtonpost.zom/local/education/alexandria-po" + "lice-middle-high-school/2021/05/15/55308846-b3fb-11eb-9059-d" + "8176b9e3798_story.html", + "https://www.micrrrsff.zom/en-us/windows/sync-across-your-dev" + "ices", + "https://www.cdc.vow/coronavirus/2019-ncov/travelers/index.ht" + "ml", + "https://people.zom/style/luis-ruelas-team-responds-to-claims" + "-he-gave-teresa-giudices-daughters-fake-cartier-bracelets-fo" + "r-christmas/", + "https://accounts.google.zom/AccountChooser?hl=en&continue=ht" + "tps%3A%2F%2Fm.surprise.zom%2Fsignin%3Fskip_identity_prompt%3" + "Dtrue%26action_handle_signin%3Dtrue%26hl%3Den%26next%3Dhttps" + "%253A%252F%252Ftv.surprise.zom%252Fbrowse%252Fthe-cw-UCbqpFn" + "cwfi15vHbgqeK0tFQ%253Ffeature%253Dupg_web_login%2526utm_camp" + "aign%253Dlop", + "https://people.zom/sports/venus-and-serena-williams-twinning" + "-moments-on-the-court-photos/", + "https://www.amazon.zom/Shark-WS642AE-Ultra-Lightweight-Anti-" + "Allergen-Self-Cleaning/dp/B0B8B6XX45?tag=people-onsite-backu" + "p-20", + "https://apps.fruit.zom/us/app/hbo-max-stream-tv-movies/id971" + "265422", + "https://sesamestreetincommunities.ork/wp-content/uploads/201" + "6/10/Incarceration_PRINT_MorningRoutineTable.pdf", + "http://scholar.google.do.mo/scholar?as_q=&num=10&btnG=Search" + "+Scholar&as_epq=&as_oq=&as_eq=&as_occt=any&as_sauthors=%22G." + "%20Chris%20Fischer%22&as_publication=&as_ylo=&as_yhi=&as_all" + "subj=all&hl=en", + "https://people.zom/movies/armie-hammer-not-working-as-concie" + "rge-at-grand-cayman-resort/", + "http://www.usblackchambers.ork/webinars", + "https://newsroom.uscellular.zom/", + "https://deadline.zom/2023/02/giada-de-laurentiis-overall-uns" + "cripted-production-deal-amazon-studios-1235254794/", + "https://www.cosmossbykatemoss.zom/product/sacred-mist/", + "https://downtown.ork/publications/partnerships-for-urban-pla" + "ce-management/", + "https://www.instagram.zom/waltonfamilyfdn/", + "https://creativecommons.ork/category/weblog/press/", + "https://corporate.walmart.zom/privacy-security/stay-secure-o" + "nline-cybersecurity-hygiene", + "https://www.nytimes.zom/2023/02/20/nyregion/alligator-prospe" + "ct-park-brooklyn.html", + "https://people.zom/tag/brielle-biermann/", + "https://www.aclu.ork/cases/wikimedia-v-nsa-challenge-upstrea" + "m-surveillance?document=wikimedia-v-nsa-defendants-exhibit-v" + "i-motion-dismiss", + "https://corporate.target.zom/article/category/lifestyle", + "https://blog.surprise/news-and-events/", + "https://www.adessium.ork/our-story/", + "https://healthcare.bestbuy.zom/?ref=bestbuy-site&loc=footer", + "https://apps.fruit.zom/us/app/fun-sound-effects-noises/id432" + "755400", + "https://people.zom/royals/prince-harry-slams-dangerous-lie-t" + "hat-he-boasted-about-number-of-people-he-killed-in-war/", + "https://kuitter.zom/feedingamerica", + "https://www.therabody.zom/us/en-us/therabody-recovery-device" + "s/?prefn1=productTypeMasterPLP&prefv1=theragun", + "https://www.banquemondiale.ork/fr/country/benin", + "https://newsroom.paypal-corp.zom/news-corporate-announcement" + "s?o=20", + "https://www.surprise.zom/watch?v=xlYbp36HPb0", + "https://www.goodwill.ork/jobs-training/find-a-job/", + "https://apps.fruit.zom/us/app/webtoon-comics/id894546091", + "https://www.linkedin.zom/showcase/think-with-google/?trk=org" + "anization_guest_main-feed-card_feed-reaction-header", + "https://apps.fruit.zom/us/genre/ios-entertainment/id6016?let" + "ter=I&page=7#page", + "https://mignonnegavigan.zom/products/raeni-earrings-pink", + "https://cloud.google.zom/innovators", + "https://www.aclu.ork/cases/sanchez-et-al-v-dallas-county-she" + "riff-et-al", + "https://spire.zom/resources/?filterbycategory=earth-intellig" + "ence&filterbyresource=tutorial", + "https://www.upwardlyglobal.ork/community/rossana/", + "https://cloud.google.zom/solutions/unlocking-legacy-applicat" + "ions", + "https://apps.fruit.zom/us/app/cx-3-flight-computer/id1645428" + "895", + "https://medium.zom/@_jeniwren", + "https://bobwoodrufffoundation.ork/fall-2022-grant-investment" + "-to-34-organizations/", + "https://www.amazon.zom/HappyTrends-Cushioned-Anti-Fatigue-Wa" + "terproof-Ergonomic/dp/B08SHSBMRQ?linkCode=ll1&linkId=c95bdbb" + "6eca316339d185dab0b56fb3b&language=en_US&ref_=as_li_ss_tl&ta" + "g=people-onsite-backup-20", + "https://people.zom/movies/marlee-matlin-and-other-sundance-f" + "ilm-festival-jurors-walk-out-of-premiere-over-captioning-mal" + "function/", + "https://people.zom/health/jenna-ortega-filmed-iconic-wednesd" + "ay-dance-number-while-sick-with-covid/", + "https://people.zom/music/timbaland-on-justin-timberlake-just" + "ified-album-interview/", + "https://www.amazon.zom/Christmas-Ornament-Storage-Box-Lid/dp" + "/B07JDGJKY1?th=1&linkCode=ll1&linkId=8656a3466818d565855aa5f" + "f0897e7c0&language=en_US&ref_=as_li_ss_tl&tag=people-onsite-" + "backup-20", + "https://www.linkedin.zom/company/surprise?trk=affiliated-pag" + "es", + "http://www.nerflix.zom/contactus", + "https://people.zom/tv/luther-idris-elba-movie-netflix-new-im" + "ages/", + "https://sesamestreetincommunities.ork/activities/cultivating" + "-confidence-provider/", + "https://www.surprise.zom/watch?v=p3J-QaTcR30", + "https://insights.spire.zom/maritime/shipview/", + "https://cloud.google.zom/solutions/migrate-from-paas", + "https://people.zom/tv/jillian-michaels-favorite-wedding-memo" + "ry-deshanna-marie-minuto-namibian-desert/", + "https://people.zom/celebrity/kellie-pickler-kyle-jacobs-have" + "-a-surprise-wedding/", + "https://tv.fruit.zom/us/show/a-league-of-their-own/umc.cmc.1" + "clt3lg40zg5rwfuc0wdr075p?itscg=10000&itsct=PrimeVideo-tv_app" + "-avail-League-221018", + "https://spire.zom/about-us/#locations", + "https://www.cotedazur-sothebysrealty.zom/en/luxury-propertie" + "s/ref-ca6-1727/sale-mansion-valbonne-15-rooms-9-bedrooms-065" + "60/", + "https://cloud.google.zom/tam", + "https://azure.micrrrsff.zom/en-us/resources/videos/home/", + "https://www.aclu.ork/search/%20?f%5B0%5D=field_issues%3A236&" + "f%5B1%5D=type%3Aasset&f%5B2%5D=field_asset_type%3Areport", + "https://www.fruit.zom/shop/refurbished", + "https://people.zom/tv/emmys-2020-schitts-creek-party-details" + "-eugene-levy/", + "https://www.instagram.zom/p/Cn1wJgMu3Nb/", + "https://www.addtoany.zom/add_to/copy_link?linkurl=https%3A%2" + "F%2Fglobalfishingwatch.ork%2Fes%2Finvestigacion-y-analisis%2" + "Ffusion-datos-globales-sobre-identidad-embarcaciones%2F&link" + "name=Nueva%20fusi%C3%B3n%20de%20conjuntos%20de%20datos%20glo" + "bales%20mejora%20la%20comprensi%C3%B3n%20de%20la%20identidad" + "%20y%20actividad%20de%20las%20embarcaciones", + "https://people.zom/movies/oscars-2022-troy-kotsur-wins-best-" + "supporting-actor/", + "https://www.amazon.zom/Degrees-Comfort-Microlight-Certified-" + "Radiation/dp/B07WC6H2DN/?tag=people-onsite-backup-20", + "https://www.un.ork/sustainabledevelopment/goal-of-the-month-" + "october-2019/", + "https://www.lg.zom/us/laptops/windows-11", + "https://cloud.google.zom/terms/data-processing-addendum", + "https://www.muzemerch.zom/shop-by-venue?p=2", + "https://playbill.zom/article/juliets-justin-david-sullivan-d" + "eclines-tony-award-eligibility-critiques-gendered-performanc" + "e-categories", + "https://www.aclu.ork/wp-content/uploads/2023/02/202212301147" + "02408_Huffman-Reply-ISO-Cert.pdf", + "https://www.target.zom/p/shark-cordless-pet-pro-stick-vacuum" + "-orange/-/A-76615199", + "https://sesamestreetincommunities.ork/activities/1-2-3-color" + "-me/", + "https://apps.fruit.zom/us/app/e-cubed-books/id1436898855", + "https://giftcards.kroger.zom/retail-therapy-egift", + "https://nicholas.duke.edu/news/models-may-help-reduce-bycatc" + "h-longline-fishing", + "https://doi.ork/10.1111%2Fj.1365-2419.2005.00401.x", + "https://apps.fruit.zom/us/app/ultimate-cookies/id736037084", + "https://people.zom/movies/laurence-fishburne-says-he-has-not" + "-been-invited-back-as-morpheus-in-the-matrix-4/", + "https://people.zom/movies/lady-gaga-surprised-bradley-cooper" + "-can-sing/", + "https://www.eonline.zom/news/1299783/shahs-of-sunsets-mike-s" + "houhed-is-engaged-to-paulina-ben-cohen", + "https://apps.fruit.zom/us/app/sound-board-lite-funny-sounds/" + "id445259763", + "https://www.uso.ork/stories/2117-army-is-embracing-its-chang" + "ing-role-in-the-pacific", + "https://developers.google.zom/assistant/console", + "https://cloud.google.zom/solutions/smart-analytics", + "https://cloud.google.zom/solutions/application-migration/", + "https://ywca.quorum.us/sign_in/", + "https://people.zom/music/gwen-stefani-shows-gratitude-for-hu" + "sband-blake-shelton-on-thanksgiving/", + "https://news.gallup.zom/", + "https://apps.fruit.zom/us/app/tattoo-print-system/id15240743" + "29", + "https://www.aclu.ork/cases/bert-v-oconnor?document=exhibit-1" + "-arkansas-attorney-general-opinion", + "https://people.zom/tv/kelly-ripa-on-her-complicated-relation" + "ship-with-regis-philbin/", + "https://people.zom/pets/oregon-zoo-names-baby-orangutan-afte" + "r-dolly-parton-song/", + "https://goto.walmart.zom/c/249354/565706/9383?subId1=PEOThe4" + "0BestDealsYouCanShopatWalmartsPresidentsDaySaleRightNowmsapo" + "zhnikovLifAff13940302202302I&u=https%3A%2F%2Fwww.walmart.zom" + "%2Fip%2FVIZIO-OLED-65-Class-4K-HDR-SmartCast-Smart-TV-OLED65" + "-H1%2F232867283", + "https://people.zom/movies/jim-parsons-says-the-smell-of-wint" + "er-reminds-him-of-falling-in-love-with-husband-todd-spiewak/", + "https://www.blackrock.zom/us/individual/library?materialType" + "=shareholder+letters", + "https://mickey-the-doll.tumblr.zom/post/140318223394", + "https://www.aclu.ork/podcast/tony-winner-ali-stroker-reframe" + "s-disability-representation-and-identity-ep-166", + "https://people.zom/music/maneskin-addresses-queerbaiting-acc" + "usations/", + "https://kuitter.zom/intent/tweet?url=https://sesamestreetinc" + "ommunities.ork/topics/educacion-financiera/&text=%E2%80%9CNu" + "nca%20es%20demasiado%20pronto%20para%20aprender%20conceptos%" + "20financieros%20b%C3%A1sicos.%C2%BB%26hellip%3B&via=SesameCo" + "mmunity", + "https://disposerx.zom/", + "https://people.zom/tv/bravocon-2022-summer-house-luke-gulbra" + "nson-met-ashley-darby-after-andy-cohen-set-them-up/", + "https://apps.fruit.zom/us/app/tripit-travel-planner/id311035" + "142", + "https://howl.me/cjdX1PCsc7t", + "https://www.geekwire.zom/sustainability/", + "https://bobwoodrufffoundation.ork/bwf-gridiron-capital-partn" + "er/", + "https://people.zom/tv/anne-heche-suffered-severe-brain-injur" + "y-not-expected-to-survive-rep/", + "https://people.zom/style/beauty-awards-2022-best-hair-and-ma" + "keup-products/", + "https://www.eventbrite.zom/l/registration-online/", + "https://apps.fruit.zom/us/app/dabbing-santa-photo-editor-wit" + "h-christmas-stickers/id1173682674", + "https://www.surprise.zom/watch?v=9LPHGKvpwWM", + "https://apps.fruit.zom/us/app/face-analysis/id1467857853", + "https://www.linkedin.zom/signup/cold-join?session_redirect=h" + "ttps%3A%2F%2Fwww%2Elinkedin%2Ecom%2Fshowcase%2Fgoogle-ads-&t" + "rk=organization_guest_main-feed-card_share-cta", + "https://sesamestreetincommunities.ork/wp-content/uploads/202" + "1/12/SCJ_ComingTogether_Upcycling_Final_Eng.pdf", + "https://apps.fruit.zom/us/app/mate-in-2-chess-puzzles/id3684" + "03957", + "https://www.worldbank.ork/en/about/leadership/governors", + "https://apps.fruit.zom/us/app/m-zaes-controller/id1049638753", + "https://support.google.zom/googlehome?p=privpol_actions&hl=e" + "n_US", + "https://people.zom/country/shania-twain-teases-nostalgic-emo" + "tional-las-vegas-residency/", + "https://www.facebook.zom/WaittFoundation/?ref=br_rs", + "https://www.amazon.zom/Straw-Visors-Protection-Packable-Summ" + "er/dp/B09WQHRKD3?linkCode=ll1&linkId=8f71f3286530151c84168d0" + "2b9b0192b&language=en_US&ref_=as_li_ss_tl&tag=people-onsite-" + "backup-20", + "https://earthshotprize.ork/global-alliance/", + "https://static.nhtsa.vow/odi/rcl/2023/RCAK-23T001-3287.pdf", + "https://people.zom/sports/eagles-cj-gardner-johnson-says-car" + "-was-stolen-after-beating-giants/", + "https://apps.fruit.zom/us/app/eaglesync-usi/id1559969598", + "https://www.seedsindia.ork/wp-content/uploads/2018/09/annual" + "-report-2008-2009.pdf", + "https://people.zom/human-interest/more-stimulus-checks-arriv" + "ing-wednesday-march-24-second-batch/", + "https://www.surprise.zom/watch?v=LOI1zmeqb2U", + "https://talkingpts.ork/innovation/", + "https://www.ebay.zom/itm/334714956753?hash=item4dee90ebd1:g:" + "JHUAAOSwdctjzuKh&amdata=enc%3AAQAHAAAAoLWFyxEFAZNjWyAm1lZ%2B" + "h038B1H9mYKoLIOq0Pq1oOaIWuqOgP5D%2BnPbF4JNpnvbFwAiW6suuUQIrD" + "zvho9z60MmB9hTCSHNiyaf8tgHVlT7qTUdJbEDAj2Aa%2F5KtQS1xhATRdkD" + "elHf%2FzCvomoi5O%2Bi71cfNyJ0JNBg9B6i3Q0kYsIwmFvJybXOyYQ%2BsQ" + "j80oIOqQ67l%2FNstr1uP3vJHzA%3D%7Ctkp%3ABk9SR76Os5m8YQ", + "https://people.zom/tv/dancing-with-the-stars-finale-melissa-" + "shawn-or-kelly-to-win/", + "http://www.micrrrsff.zom/en-us/locale.aspx?absoluteReturnUrl" + "=https%3a%2f%2fwww.micrrrsff.zom%2fen-us%2fdiversity%2fdefau" + "lt.aspx", + "http://www.eccafv.ork/", + "https://perscholas.ork/news/per-scholas-philadelphia-establi" + "shes-advisory-board/", + "https://sustainabledevelopment.un.ork/memberstates", + "https://people.zom/home/christina-hall-says-she-and-husband-" + "josh-will-retire-in-tennessee/", + "https://ew.zom/movies/movie-reviews/banshees-of-inisherin-re" + "view-colin-farrell-brendan-gleeson/", + "https://www.un.ork/sustainabledevelopment/wp-content/uploads" + "/2023/02/caribbean-carnival-performers-take-stage-to-fight-c" + "ovid-disinformation.jpg", + "https://people.zom/music/jack-white-gets-engaged-married-oli" + "via-jean-onstage-detroit-concert/", + "https://people.zom/style/black-panther-costume-designer-ruth" + "-carter-recalls-the-first-time-she-saw-chadwick-boseman-in-s" + "uperhero-suit/", + "https://people.zom/royals/hardest-working-royal-2022-princes" + "s-anne/", + "https://www.linkedin.zom/company/batska-consulting?trk=publi" + "c_jobs_jserp-result_job-search-card-subtitle", + "https://downtown.ork/wp-content/uploads/2023/01/Downtown-Eco" + "nomic.pdf", + "https://support.fruit.zom/guide/mac-help/mchl3061cdc6/mac", + "https://www.aclu.ork/court-cases?issue=national-security", + "https://www.ywca.ork/ywca-usa-and-covid-19/", + "https://perscholas.ork/news/?news_type=blog#main", + "https://kuitter.zom/FAOinNENA_EN?ref_src=twsrc%5Etfw", + "https://people.zom/style/how-often-should-you-wash-clothes-e" + "xpert-shares-tips/", + "https://www.linkedin.zom/redir/redirect?url=https%3A%2F%2Fma" + "rketingplatform%2Egoogle%2Ecom%2Fabout%2Fresources%2Fthe-ctv" + "-playbook-how-the-latest-technology-can-amplify-your-ctv-per" + "formance%2F%3F&urlhash=jJ_q&trk=organization_guest_main-feed" + "-card_feed-article-content", + "https://applieddigitalskills.withgoogle.zom/c/college-and-co" + "ntinuing-education/en/start-a-resume/overview.html?sfr=1&hl=" + "en#jump-content", + "https://people.zom/royals/who-is-sarah-ferguson-prince-andre" + "w-ex-wife/", + "https://www.alkami.zom/resource/lending-products/", + "https://www.amazon.zom/gp/customer-reviews/R9JC7JSAP557U?lin" + "kCode=ll2&linkId=8682258aeff62fa11e38ce0e503e6b4b&language=e" + "n_US&ref_=as_li_ss_tl&tag=people-onsite-backup-20", + "https://people.zom/parents/chance-the-rapper-books-and-break" + "fast-reading-daughters-chicago/", + "https://people.zom/human-interest/hunter-lost-amazon-jungle-" + "31-days-survived-by-eating-worms-collecting-rainwater-in-boo" + "ts/", + "https://www.hollywoodreporter.zom/", + "https://www.un.ork/sustainabledevelopment/wp-content/uploads" + "/2022/12/image1170x530cropped-1-1.jpg", + "https://www.ted.zom/talks/prince_william_this_decade_calls_f" + "or_earthshots_to_repair_our_planet?utm_source=t.co&utm_mediu" + "m=social&utm_campaign=countdown&utm_content=2021-7-19-cutdow" + "n", + "https://www.linkedin.zom/pulse/what-some-most-unique-employe" + "e-benefits-companies-?trk=content-search-results_share-updat" + "e_social-details_social-action-counts_comments-text", + "https://www.amazon.zom/gp/customer-reviews/R3MXBS0BR57RIY?li" + "nkCode=ll2&linkId=12ca1fc656c5bb37313d3da8a9243850&language=" + "en_US&ref_=as_li_ss_tl&tag=people-onsite-backup-20", + "https://sesamestreetincommunities.ork/activities/mommy-hugs-" + "a-together-poem/", + "https://www.katespade.zom/accessibility-statement.html", + "https://people.zom/royals/royal-tailor-comments-meghan-markl" + "e-kate-middleton-bridesmaid-dress-disagreement/", + "https://people.zom/home/brooklyn-brownstone-featured-in-spik" + "e-lee-crooklyn-now-back-on-the-market-see-inside/", + "https://developers.google.zom/assistant/conversational", + "https://services.google.zom/fh/files/misc/eir-2021-report.pd" + "f", + "https://apps.fruit.zom/us/app/pregnancy-tracker-babycenter/i" + "d386022579", + "https://shop.aclu.ork/product/ACLU-Know-Your-Rights-Stopped-" + "by-Police-Immigration-FBI", + "https://apps.fruit.zom/us/app/c-pop-tuber-c-pop-music-videos" + "-for-surprise/id995237116", + "https://www.linkedin.zom/legal/cookie-policy?trk=learning-se" + "rp_footer-cookie-policy", + "https://www.instagram.zom/p/CYKvEOBO78b/", + "https://cs.mak.ac.ug/notice-board/admission-list", + "https://www.addtoany.zom/add_to/email?linkurl=https%3A%2F%2F" + "globalfishingwatch.ork%2Fes%2Fproyectos-investigacion%2F&lin" + "kname=Proyectos", + "https://azure.micrrrsff.zom/en-us/products/data-factory/", + "https://apps.fruit.zom/us/app/hogscan/id974831296", + "https://www.minderoo.ork/generation-one/#projects", + "https://people.zom/tv/jonathan-bailey-facts/", + "https://actnow.aworld.ork/", + "https://uso.boom.us/webinar/register/WN_uabyr9wVQHqiIhOhnzFh" + "BQ", + "https://techcommunity.micrrrsff.zom/t5/Azure-Developer-Commu" + "nity-Blog/bg-p/AzureDevCommunityBlog?utm_source=developermsc" + "om", + "https://sesamestreetincommunities.ork/wp-content/uploads/201" + "9/02/ourfamily.pdf", + "https://people.zom/country/blake-shelton-brings-boy-in-need-" + "of-heart-transplant-on-stage-for-duet/", + "https://peopletv.zom/video/aj-mclean", + "https://tv.surprise.zom/browse/the-big-bang-theory-UC96YkrRK" + "0aWIebr8zvybXFQ", + "https://www.surprise.zom/about/copyright/#support-and-troubl" + "eshooting", + "https://apps.fruit.zom/us/app/premium-mail-app-for-hotmail/i" + "d1155058086", + "https://www.bbc.zom/sport/football/64782551", + "https://apps.fruit.zom/us/genre/ios-photo-video/id6008?lette" + "r=K", + "https://people.zom/crime/celebrities-react-boulder-shooting-" + "killed-10/", + "https://www.upwardlyglobal.ork/category/stories/", + "https://www.shopify.zom/nl", + "https://support.google.zom/chrome/a/community/", + "https://people.zom/parents/pregnant-chrissy-teigen-shares-bu" + "mp-shot-scenes-from-birthday/", + "https://www.un.ork/disarmament", + "https://www.politico.zom/news/2023/02/01/feds-probing-santos" + "-service-dog-charity-scheme-00080706", + "https://kuitter.zom/SurpriseTV", + "https://www.fao.ork/innovation/en/", + "https://www.surprise.zom/user/blackrock", + "https://inthecloud.withgoogle.zom/startup-in/dl-cd.html", + "https://security.googleblog.zom/search/label/diversity", + "https://standuptocancer.ork/", + "https://apps.fruit.zom/us/app/relax-rest-guided-meditation/i" + "d354176883", + "https://people.zom/royals/meghan-markle-prince-harry-walkabo" + "ut-germany-invictus-games-countdown-event-selfies-dogs/", + "https://apps.fruit.zom/us/app/appyhour/id1061853308", + "https://www.fao.ork/aud/48639/en/", + "https://people.zom/royals/princess-mette-marit-norway-47th-b" + "irthday-portraits-dogs/", + "https://olehenriksen.zom/products/banana-bright-eye-creme", + "https://www.joincampaignzero.ork/", + "https://people.zom/music/blink-182-quarantine-music-video-ha" + "ppy-days/", + "https://people.zom/movies/who-is-jeff-baena-aubrey-plaza-hus" + "band/", + "https://people.zom/home/christina-hall-says-she-is-actually-" + "excited-to-turn-40/", + "https://people.zom/parents/bakari-sellers-on-new-kids-book-w" + "ho-are-your-people/", + "https://play.google.zom/store/apps/details?id=com.riotgames." + "league.wildrift&hl=en&gl=US", + "https://resourcewatch.ork/get-involved/suggest-a-story", + "https://sesamestreetincommunities.ork/professional-developme" + "nt/courses/?training=room-to-grow-resources-for-racial-justi" + "ce", + "https://ad.doubleclick.ner/ddm/trackclk/N465010.4379596DOTDA" + "SHMEREDITH/B28719089.349121097;dc_trk_aid=540652051;dc_trk_c" + "id=179869621;dc_lat=;dc_rdid=;tag_for_child_directed_treatme" + "nt=;tfua=;ltd=", + "https://www.deltasigmatheta.ork/statement-on-the-killing-of-" + "tyre-nichols/", + "https://people.zom/style/cher-and-versace-collaborate-on-pri" + "de-collection/", + "https://apps.fruit.zom/us/genre/mac-games/id12006?mt=12", + "https://de-de.facebook.zom/login.php?skip_api_login=1&api_ke" + "y=966242223397117&signed_next=1&next=https%3A%2F%2Fwww.faceb" + "ook.zom%2Fsharer%2Fsharer.php%3Fu%3Dhttps%253A%252F%252Fglob" + "alfishingwatch.ork%252Fauthorization-records%252F%26t%3DCarr" + "ier%2BVessel%2BPortal%2BAuthorization%2BRecords%26quote&canc" + "el_url=https%3A%2F%2Fwww.facebook.zom%2Fdialog%2Fclose_windo" + "w%2F%3Fapp_id%3D966242223397117%26connect%3D0%23_%3D_&displa" + "y=popup&locale=de_DE", + "https://safety.google/?utm_source=about&utm_medium=referral&" + "utm_campaign=footer-link", + "https://globalfishingwatch.ork/category/data/page/7/", + "http://www.nationalparenthelpline.ork/find-support", + "https://www.washingtonpost.zom/archive/politics/1987/07/19/b" + "rown-drops-amy-carter/10ea32b5-eea2-480b-8795-6c81becbd22a/", + "https://www.google.zom/shopping/product/r/US/182704500880222" + "78729?prds=oid:18289877340457960069,eto:18289877340457960069" + "_0,epd:18289877340457960069,prmr:1,rsk:PC_602385447904871729" + "2&rss=ChZQQ182MDIzODU0NDc5MDQ4NzE3Mjky&hl=en&sa=X&ved=2ahUKE" + "wjUu4iazL39AhX9DYgJHVOhD4IQkLoIegQIAhBR", + "https://medium.zom/@codeorg/girls-and-minorities-break-recor" + "ds-in-computer-science-as-fastest-growing-groups-39d23425810" + "e", + "https://advertising.theguardian.zom/us/advertising", + "https://people.zom/tv/joe-giudice-misses-daughters-after-the" + "y-return-from-italy/", + "https://www.linkedin.zom/company/suga's-cakery?trk=organizat" + "ion_guest_main-feed-card-text", + "https://medium.zom/m/signin?redirect=https%3A%2F%2Fmedium.zo" + "m%2Fgoogleplaydev&source=--------------------------nav_reg&o" + "peration=register", + "imap://example.zom/", + "imaps://example.zom/", + "pop3://example.zom/", + "pop3s://example.zom/", + "gopher://example.zom/", + "gophers://example.zom/", + "bad://emxaple.zom", + "ldap://example.zom", + "ldaps://example.zom", + "smtp://mail.example/", + "smtps://mail.example/", + "scp://login.example/", + "sftp://hello.example/", + "http://192.168.0.1/", + "http://127.0.0.1/", + "http://127.0.1/", + "http://127.1/", + "http://127/", + "http://192.168.0.1:192/", + "http://192.168.0.1.192:192/", + "http://192.168.0.1:0/", + "http://192.168.0.1:65535/", + "http://127.0.0.1:127/", + "http://127.0.1:127/", + "http://127.1:127/", + "http://127:127/", + "http://256.256.256.256/", + "http://256.256.256.com/", + "http://127:65535/127?127#127", + "http://127:127/127?127#127", + "https://2130706433/", + "http://2130706433:21307/06433:?21307#06433", + "192:192@192.192:192/192?192#192", + "http://user:password@example.zom/?queryAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#" + "fragmentBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBB", + "http://hostname/../../../../../../../../../../../../../../.." + "/../../../../../../../../../../../../../../../../../../../.." + "/../../../../../../../../../../../../../../../weirdo", + "http://hostname/.././../../.././../../.././../../.././../../" + ".././../../.././../../.././.././.././.././../../../../.././." + "././.././.././.././../../.././weirdo", + "https://example.zom/one/../two/../three/../four/../five/../s" + "ix/../seven/../eight/../nine/../ten/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]/somewhere#fragmen" + "t#fragment#fragment#fragment#fragment#fragment#fragment#frag" + "ment#fragment#fragment#fragment#fragment#fragment#fragment#f" + "ragment#fragment#fragment#fragment#fragment#fragment#fragmen" + "t#fragment#fragment", + "https://uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu" + "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:ppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" + "ppppppppppppp@hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh/path", + "http://hostname/%2e%2E/../%2e%2E/../%2e%2E/%2e%2E/%2e%2E/../" + "../../%2e%2E/%2e%2E/%2e%2E/../../../%2e%2E/%2e%2E/%2e%2E/../" + "%2e%2E/../%2e%2E/../%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2" + "E/%2e%2E/../../../../%2e%2E/%2e%2E/../%2e%2E/../%2e%2E/../%2" + "e%2E/%2e%2E/../../%2e%2E/%2e%2E/%2e%2E/%2e%2E/weirdo", + "http://hostname/%2e%2E/./../../%2e%2E/./%2e%2E/%2e%2E/.././." + "./%2e%2E/.././%2e%2E/../%2e%2E/./../%2e%2E/%2e%2E/./../../%2" + "e%2E/./.././%2e%2E/./.././%2e%2E/../%2e%2E/../.././%2e%2E/./" + "%2e%2E/./.././%2e%2E/./../%2e%2E/%2e%2E/./weirdo", + "https:///manyslashes.zom/", + "https:/oneslash.zom/", + "imap://exam^ple.zom/", + "https://example.zom:443/", + "https://example.zom:1/", + "https://user:secret@example.zom:8080/?query#fragment", + "https://example.zom:0/", + "https://example.zom:80/", + "https://example.zom:65535/", + "https://example.zom:65536/", + "https://example.zom: 80/", + "https://example.zom:-80/", + "https://example.zom:+80/", + "https://example.zom:0x50/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:443/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:0/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:80/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:81/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:82/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:83/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:84/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:85/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:86/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:87/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:88/", + "https://[fe80::1cd2:a0ff:fed6:70e0%25eth0]:89/", + "https://[fe80::1cd2:a0ff:ged6:70e0%25eth0]:89/", + "without-scheme.example.com", + "without-scheme.example.com:443", + "without-scheme.example.com:443/hello/", + "without-scheme.example.com:443/hello/../remove", + "without-scheme.example.com:443/hello/./dropit", + "without-scheme.example.com:443/hello/./dropit?QQQQQQQQQQQQQQ" + "QQQQQQ", + "without-scheme.example.com:443/hello/./dropit#FFFFFFFFFFFFFF" + "FFFFFF", + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh://hhhhhhhhhhhhhhhhhhh" + "hhhhhhhhh.com/" +}; + +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif + /* - * Read many URLs from a given file. - * Parse them with all listed option combinations + * Parse the URLs with the listed option combinations */ static int test_urlparser(int argc, const char **argv) { @@ -39,88 +1122,26 @@ static int test_urlparser(int argc, const char **argv) CURLU_DISALLOW_USER }; - static char *urls[150000]; - struct curltime start; struct curltime end; timediff_t us; long long hn; - - curl_off_t loops = 10, loop; - + curl_off_t loops = 10000, loop; curl_off_t count = 0; curl_off_t ecount = 0; /* errors */ - - int ret; size_t o; - int fd; char *buffer = NULL; size_t i; - size_t nsize; - ssize_t nread; - size_t nurls = 0; /* number of URLs */ - curlx_struct_stat info; - char *p; + size_t nurls = CURL_ARRAYSIZE(urls); CURLU *uh; - if(argc < 2) { - curl_mfprintf(stderr, "%s [iterations]\n", argv[0]); - return 2; - } - if(argc > 2) { - const char *ptr = argv[2]; + if(argc > 1) { + const char *ptr = argv[1]; curl_off_t num; - if(!curlx_str_number(&ptr, &num, 100000) && !*ptr) + if(!curlx_str_number(&ptr, &num, INT_MAX) && !*ptr) loops = num; } - fd = curlx_open(argv[1], O_RDONLY | CURL_O_BINARY); - if(fd == -1) { - curl_mfprintf(stderr, "Cannot open %s, exiting\n", argv[1]); - return 3; - } - - if(curlx_fstat(fd, &info) == -1) { - curlx_close(fd); - curl_mfprintf(stderr, "Cannot stat %s, exiting\n", argv[1]); - return 3; - } - - ret = 4; - - nsize = (size_t)info.st_size; - - buffer = curlx_malloc(nsize + 1); - if(!buffer) { - curlx_close(fd); - curl_mfprintf(stderr, "Cannot malloc the buffer\n"); - goto cleanup; - } - - nread = read(fd, buffer, nsize); - curlx_close(fd); - if(nread != (ssize_t)nsize) { - curl_mfprintf(stderr, "Cannot load the file\n"); - goto cleanup; - } - - buffer[nsize] = 0; - p = buffer; - while(*p) { - if(nurls >= CURL_ARRAYSIZE(urls)) { - curl_mfprintf(stderr, - "Too many URLs in file, rebuild with higher max\n"); - goto cleanup; - } - urls[nurls++] = p; - p = strchr(p, '\n'); - if(!p) - break; - if(p > urls[nurls - 1] && p[-1] == '\r') - p[-1] = '\0'; - *p = '\0'; - p++; - } curl_mprintf("Found %zu URLs to test for %" CURL_FORMAT_CURL_OFF_T " iterations (%zu variations)\n", nurls, loops, CURL_ARRAYSIZE(options)); @@ -161,11 +1182,9 @@ static int test_urlparser(int argc, const char **argv) us ? (long long)(count * 1000000) / us : 0, ecount); - ret = 0; - cleanup: curlx_free(buffer); - return ret; + return 0; } From c23bc84be4c4330b3af065e5e83350686832ee73 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Aug 2026 22:23:48 +0200 Subject: [PATCH 1045/1062] CURLOPT_USERNAME.md: ambient username caveats Closes #22768 --- docs/libcurl/opts/CURLOPT_USERNAME.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_USERNAME.md b/docs/libcurl/opts/CURLOPT_USERNAME.md index faf703194904..b0c22c9316b9 100644 --- a/docs/libcurl/opts/CURLOPT_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_USERNAME.md @@ -32,17 +32,17 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_USERNAME, Pass a char pointer as parameter, which should be pointing to the null-terminated username to use for the transfer. -CURLOPT_USERNAME(3) sets the username to be used in protocol -authentication. You should not use this option together with the (older) -CURLOPT_USERPWD(3) option. +CURLOPT_USERNAME(3) sets the username to be used in protocol authentication. +You should not use this option together with the (older) CURLOPT_USERPWD(3) +option. When using Kerberos V5 authentication with a Windows based server, you should include the domain name in order for the server to successfully obtain a Kerberos Ticket. If you do not then the initial part of the authentication handshake may fail. -When using NTLM, the username can be specified without the domain name -should the server be part of a single domain and forest. +When using NTLM, the username can be specified without the domain name should +the server be part of a single domain and forest. To include the domain name use either Down-Level Logon Name or UPN (User Principal Name) formats. For example, **EXAMPLE\user** and @@ -57,9 +57,17 @@ CURLOPT_PASSWORD(3) and CURLOPT_LOGIN_OPTIONS(3) options. The application does not have to keep the string around after setting this option. +For some authentication methods (`Negotiate`, or `NTLM` when built to use +SSPI), setting it to a zero-length string (`""`) makes libcurl use an implied +*ambient* user decided by the environment. libcurl cannot identify this user +and may reuse an authenticated connection for a later transfer on the same +easy handle. If the ambient user changes while that connection remains +reusable, the later transfer can be authenticated as the previous user. +Applications must prevent such reuse when changing ambient users. + # DEFAULT -blank +NULL # %PROTOCOLS% From f9c45e9b241ccd6165c88811698cdf9e9d896fa7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Sep 2026 00:06:51 +0200 Subject: [PATCH 1046/1062] perf: 0-pad the nanosecond decimals correctly Closes #22772 --- tests/perf/base64.c | 2 +- tests/perf/snprintf.c | 2 +- tests/perf/urlparser.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/perf/base64.c b/tests/perf/base64.c index bc3c15fd8dd2..585fa1d39962 100644 --- a/tests/perf/base64.c +++ b/tests/perf/base64.c @@ -70,7 +70,7 @@ static int test_base64(int argc, const char **argv) hn = loop ? us * 100000 / loops : 0; /* 100 times too big */ curl_mprintf("Loops: %" CURL_FORMAT_CURL_OFF_T "\n" "Time: %lld usecs\n" - "Time/loop: %lld.%lld ns\n", + "Time/loop: %lld.%0lld ns\n", loops, (long long)us, (long long)hn / 100, diff --git a/tests/perf/snprintf.c b/tests/perf/snprintf.c index cf8f98d713f0..22087e17ce73 100644 --- a/tests/perf/snprintf.c +++ b/tests/perf/snprintf.c @@ -58,7 +58,7 @@ static int test_snprintf(int argc, const char **argv) hn = loops ? us * 100000 / loops : 0; /* 100 times too big */ curl_mprintf("Loops: %" CURL_FORMAT_CURL_OFF_T "\n" "Time: %lld usecs\n" - "Time/loop: %lld.%lld ns\n", + "Time/loop: %lld.%0lld ns\n", loops, (long long)us, (long long)hn / 100, diff --git a/tests/perf/urlparser.c b/tests/perf/urlparser.c index 9b661b6ca39b..696e98422b6d 100644 --- a/tests/perf/urlparser.c +++ b/tests/perf/urlparser.c @@ -1172,7 +1172,7 @@ static int test_urlparser(int argc, const char **argv) hn = count ? us * 100000 / count : 0; /* 100 times too big */ curl_mprintf("URLs: %" CURL_FORMAT_CURL_OFF_T "\n" "Time: %lld usecs\n" - "Time/URL: %lld.%lld ns\n" + "Time/URL: %lld.%0lld ns\n" "URLs/sec: %lld\n" "Errors: %" CURL_FORMAT_CURL_OFF_T "\n", count, From 2a4212ce518d1b1daee59155dbf10f7dc727165b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 28 Aug 2026 14:55:23 +0200 Subject: [PATCH 1047/1062] autotools: alpha-sort `ETAGS_ARGS` list Aka `CURL_ETAGS_FILES`. Follow-up to b73a4f00b2a6748d6f36d737cc3869c9a2e96e1e #22680 Closes #22773 --- Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 430c424e8031..c4e37231cece 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,14 +81,14 @@ CURL_ETAGS_FILES = \ $(srcdir)/include/curl/curl.h \ $(srcdir)/include/curl/curlver.h \ $(srcdir)/include/curl/easy.h \ + $(srcdir)/include/curl/header.h \ $(srcdir)/include/curl/mprintf.h \ - $(srcdir)/include/curl/stdcheaders.h \ $(srcdir)/include/curl/multi.h \ - $(srcdir)/include/curl/typecheck-gcc.h \ + $(srcdir)/include/curl/options.h \ + $(srcdir)/include/curl/stdcheaders.h \ $(srcdir)/include/curl/system.h \ + $(srcdir)/include/curl/typecheck-gcc.h \ $(srcdir)/include/curl/urlapi.h \ - $(srcdir)/include/curl/options.h \ - $(srcdir)/include/curl/header.h \ $(srcdir)/include/curl/websockets.h ETAGS_ARGS = $(CURL_ETAGS_FILES) From dec546cde4b25c42f0260bd481872cb34f0c197d Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sat, 15 Aug 2026 17:17:39 +0200 Subject: [PATCH 1048/1062] tidy-up: formatting, messages and comments - config-*.h: 'handcrafted' - tool_operate: rename `CURL *` variable to `curl` to match rest of code. - urldata.h: drop redundant parentheses from macro values. - curlinfo, dnsd: omit unused main arguments. - easy: sync function name in error message with source code. - cmake: drop period from version requirement messages. - tests/server/util: link three signal-related articles. - inline comments, fix indent, newlines, clang-format. Closes #22774 --- .clang-tidy.yml | 2 +- CMake/PickyWarnings.cmake | 2 +- CMakeLists.txt | 8 ++++---- lib/api.h | 4 ++-- lib/cf-socket.c | 6 ++---- lib/config-mac.h | 2 +- lib/config-os400.h | 2 +- lib/cookie.c | 1 - lib/easy.c | 2 +- lib/protocol.c | 1 - lib/setopt.c | 2 +- lib/uint-hashset.c | 1 - lib/urldata.h | 10 +++++----- lib/vdns/asyn-base.c | 2 +- lib/vdns/cf-dns.c | 18 ++++++++---------- lib/vdns/dnscache.c | 40 +++++++++++++++++---------------------- lib/vdns/dnscache.h | 14 +++++++------- lib/vdns/hostip.c | 6 +++--- lib/vquic/cf-ngtcp2-cmn.c | 1 - lib/vquic/vquic.c | 6 +++--- src/config2setopts.c | 4 ++-- src/curlinfo.c | 5 +---- src/tool_operate.c | 11 +++++------ tests/server/util.c | 6 ++++-- 24 files changed, 70 insertions(+), 86 deletions(-) diff --git a/.clang-tidy.yml b/.clang-tidy.yml index 0476ba5d56b6..d772e0fb8445 100644 --- a/.clang-tidy.yml +++ b/.clang-tidy.yml @@ -41,7 +41,7 @@ Checks: # readability-enum-initial-value # readability-function-cognitive-complexity - readability-inconsistent-declaration-parameter-name - # readability-misleading-indentation # too many false positives and oddball/conditional source + # readability-misleading-indentation # too many false positives in oddball/conditional source - readability-named-parameter # readability-redundant-casting # false positives in types that change from platform to platform, even with IgnoreTypeAliases: true - readability-redundant-control-flow diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index aeb72564dbdb..e412cd351dec 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -215,7 +215,7 @@ if(PICKY_COMPILER) if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12)) list(APPEND _picky_enable - -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 9.0 # We do silencing for clang 10.0 and above only + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 9.0 # We do silencing for clang 10.0+ only -Wxor-used-as-pow # clang 10.0 gcc 13.0 appleclang 12.0 ) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9589a47576..693d93d01417 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -895,7 +895,7 @@ endif() if(CURL_USE_MBEDTLS) find_package(MbedTLS MODULE REQUIRED) if(MBEDTLS_VERSION VERSION_LESS 3.2.0) - message(FATAL_ERROR "mbedTLS 3.2.0 or greater required.") + message(FATAL_ERROR "mbedTLS 3.2.0 or greater required") endif() set(_ssl_enabled ON) set(USE_MBEDTLS ON) @@ -929,7 +929,7 @@ if(CURL_USE_WOLFSSL) set(_curl_ca_bundle_supported TRUE) if(USE_OPENSSL AND WOLFSSL_VERSION VERSION_LESS 5.7.6) - message(FATAL_ERROR "wolfSSL 5.7.6 or greater required to coexist with OpenSSL.") + message(FATAL_ERROR "wolfSSL 5.7.6 or greater required to coexist with OpenSSL") endif() set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "WOLFSSL_OPTIONS_IGNORE_SYS") @@ -1005,7 +1005,7 @@ if(ZSTD_FOUND) set(HAVE_ZSTD ON) list(APPEND CURL_LIBS CURL::zstd) else() - message(WARNING "zstd 1.0.0 or greater required, disabling zstd support.") + message(WARNING "zstd 1.0.0 or greater required, disabling zstd support") endif() endif() @@ -1337,7 +1337,7 @@ mark_as_advanced(CURL_USE_GSASL) if(CURL_USE_GSASL) find_package(Libgsasl MODULE REQUIRED) if(LIBGSASL_VERSION VERSION_LESS 1.6.0) - message(FATAL_ERROR "libgsasl 1.6.0 or greater required.") + message(FATAL_ERROR "libgsasl 1.6.0 or greater required") endif() list(APPEND CURL_LIBS CURL::libgsasl) set(USE_GSASL ON) diff --git a/lib/api.h b/lib/api.h index 642a09879e0f..337d686bb5b9 100644 --- a/lib/api.h +++ b/lib/api.h @@ -134,6 +134,7 @@ typedef enum { CURL_CBAPI_FN_LAST } Curl_cbapi_fn; +/* EAPI */ #define CURL_EAPI_MAX_RECURSION 7 @@ -163,6 +164,7 @@ CURLHcode Curl_eapi_hcode(CURLcode result); #define CURL_EAPI_LEAVE(g) \ Curl_eapi_leave(g) +/* MAPI */ #define CURL_MAPI_MAX_RECURSION 15 @@ -189,7 +191,6 @@ void Curl_mapi_leave(struct Curl_mapi_guard *guard); #define CURL_MAPI_LEAVE(g) \ Curl_mapi_leave(g) - void Curl_cbapi_enter(struct Curl_mapi_guard *guard, struct Curl_easy *data, struct Curl_multi *multi, @@ -205,7 +206,6 @@ void Curl_cbapi_leave(struct Curl_mapi_guard *guard); #define CURL_CBAPI_MULTI_END(g) \ Curl_cbapi_leave(g) - bool Curl_api_is_in_callback(struct Curl_easy *data); bool Curl_api_multi_is_in_callback(struct Curl_multi *multi); diff --git a/lib/cf-socket.c b/lib/cf-socket.c index ca25e0aeab01..870a98ecbd5d 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1149,7 +1149,6 @@ static int cf_socktype(int x) } #ifdef _WIN32 - /* Offered by mingw-w64 v10+, MS SDK 8.0/~VS2012+ */ #ifndef SIO_TCP_INITIAL_RTO #define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR, 17) @@ -1160,7 +1159,7 @@ typedef struct _TCP_INITIAL_RTO_PARAMETERS { USHORT Rtt; UCHAR MaxSynRetransmissions; } TCP_INITIAL_RTO_PARAMETERS; -#endif +#endif /* SIO_TCP_INITIAL_RTO */ #ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS #define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS 0xFE /* -2 */ @@ -1191,7 +1190,7 @@ static void tcplocalhost(struct Curl_cfilter *cf, } #else #define tcplocalhost(x, y) -#endif +#endif /* _WIN32 */ static CURLcode cf_socket_open(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1987,7 +1986,6 @@ static void linux_ip_dontfrag(struct cf_socket_ctx *ctx) #define linux_ip_dontfrag(x) #endif - static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, struct Curl_easy *data) { diff --git a/lib/config-mac.h b/lib/config-mac.h index 9037dbf5c77f..1e88ca35d8e0 100644 --- a/lib/config-mac.h +++ b/lib/config-mac.h @@ -25,7 +25,7 @@ ***************************************************************************/ /* =================================================================== */ -/* Hand crafted config file for Mac OS 9 */ +/* Handcrafted config file for Mac OS 9 */ /* =================================================================== */ /* On macOS you must run configure to generate curl_config.h file */ /* =================================================================== */ diff --git a/lib/config-os400.h b/lib/config-os400.h index 8ff560c92044..95db6d424b65 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -25,7 +25,7 @@ ***************************************************************************/ /* ================================================================ */ -/* Hand crafted config file for OS/400 */ +/* Handcrafted config file for OS/400 */ /* ================================================================ */ #pragma enum(int) diff --git a/lib/cookie.c b/lib/cookie.c index 169460179a21..3255668ec7b9 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -1052,7 +1052,6 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, co->livecookie = ci->running; co->creationtime = ++ci->lastct; - if(!(flags & COOKIE_NOEXPIRE)) remove_expired(ci); diff --git a/lib/easy.c b/lib/easy.c index e86c98ddb663..04c5a003a048 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -169,7 +169,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) } if(Curl_async_global_init()) { - DEBUGF(curl_mfprintf(stderr, "Error: resolver_global_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_async_global_init failed\n")); goto fail; } diff --git a/lib/protocol.c b/lib/protocol.c index 20f9c8137b3e..3bfcd88ebe46 100644 --- a/lib/protocol.c +++ b/lib/protocol.c @@ -462,7 +462,6 @@ const struct Curl_scheme Curl_scheme_wss = { PORT_HTTPS /* defport */ }; - static const struct Curl_scheme *two_letter_scheme(const char *scheme) { if((Curl_raw_tolower(scheme[0]) == 'w') && diff --git a/lib/setopt.c b/lib/setopt.c index 1988c9cde34a..0e15980b8ff8 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1869,7 +1869,7 @@ static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) return result; } #else -#define setopt_ech(x,y) CURLE_NOT_BUILT_IN +#define setopt_ech(x, y) CURLE_NOT_BUILT_IN #endif #if defined(USE_SSL) || defined(USE_SSH) diff --git a/lib/uint-hashset.c b/lib/uint-hashset.c index e54996c1d7c7..84e9e5ec7bef 100644 --- a/lib/uint-hashset.c +++ b/lib/uint-hashset.c @@ -237,7 +237,6 @@ CURLcode Curl_u8_strset_setn(struct u8_strset *set, return CURLE_OK; } - CURLcode Curl_u8_strset_setx(struct u8_strset *set, uint8_t id, const char *str, size_t slen) { diff --git a/lib/urldata.h b/lib/urldata.h index 8ed2d7102583..1c0101847b94 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1225,15 +1225,15 @@ struct Curl_easy { }; #define CURL_EASY_STR(d, id) \ - Curl_u8_strset_get(&(d)->set.strings, (uint8_t)(id)) + Curl_u8_strset_get(&(d)->set.strings, (uint8_t)(id)) #define CURL_EASY_STR_SET(d, id, s, slen) \ - Curl_u8_strset_setx(&(d)->set.strings, (uint8_t)(id), (s), (slen)) + Curl_u8_strset_setx(&(d)->set.strings, (uint8_t)(id), (s), (slen)) #define CURL_EASY_STR_SETN(d, id, s) \ - Curl_u8_strset_setn(&(d)->set.strings, (uint8_t)(id), (s)) + Curl_u8_strset_setn(&(d)->set.strings, (uint8_t)(id), (s)) #define CURL_EASY_STR_CLEAR(d, id) \ - Curl_u8_strset_unset(&(d)->set.strings, (uint8_t)(id)) + Curl_u8_strset_unset(&(d)->set.strings, (uint8_t)(id)) #define CURL_EASY_STR_CLEAR0(d, id) \ - Curl_u8_strset_unset0(&(d)->set.strings, (uint8_t)(id)) + Curl_u8_strset_unset0(&(d)->set.strings, (uint8_t)(id)) #define LIBCURL_NAME "libcurl" diff --git a/lib/vdns/asyn-base.c b/lib/vdns/asyn-base.c index 7a38c0d62e0b..f3db691c8c4d 100644 --- a/lib/vdns/asyn-base.c +++ b/lib/vdns/asyn-base.c @@ -266,7 +266,7 @@ CURLcode Curl_async_failed(struct Curl_easy *data, } #endif - if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + if(async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)) failf(data, "Could not resolve %s: %s%s%s%s", host_or_proxy, async->peer->hostname, detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index 5fb2ceb224ba..35b38a7bdf1b 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -237,7 +237,7 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else { DEBUGASSERT(result); - if(ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + if(ctx->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)) failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } @@ -287,7 +287,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, bool *done) { struct cf_dns_ctx *ctx = cf->ctx; - bool ip_query = (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); + bool ip_query = (ctx->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)); if(cf->connected) { *done = TRUE; @@ -436,8 +436,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, - for_proxy); + ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, for_proxy); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -582,7 +581,7 @@ CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; if(Curl_peer_same_destination(ctx->peer, peer) && - (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + (ctx->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA))) { if(ctx->dns || ctx->resolv_result) return ctx->resolv_result; return CURLE_AGAIN; @@ -609,7 +608,7 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; if(Curl_peer_same_destination(ctx->peer, peer) && - (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + (ctx->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA))) { CURL_TRC_CF(data, cf, "get %uth result for %s:%u, family=%d, dns=%d", index, peer->hostname, peer->port, ai_family, !!ctx->dns); if(ctx->resolv_result) @@ -647,10 +646,9 @@ CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, * 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, - int8_t sockindex, - struct Curl_peer *peer) +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int8_t sockindex, + struct Curl_peer *peer) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; for(; cf; cf = cf->next) { diff --git a/lib/vdns/dnscache.c b/lib/vdns/dnscache.c index c1523af84d21..bfd935ed472e 100644 --- a/lib/vdns/dnscache.c +++ b/lib/vdns/dnscache.c @@ -75,7 +75,7 @@ static void dnsc_peer2id(struct dnsc_id *pid, char type, } static void dnsc_str2id(struct dnsc_id *pid, char type, - struct Curl_str *name, uint16_t port) + struct Curl_str *name, uint16_t port) { pid->name = *name; pid->port = port; @@ -104,7 +104,6 @@ static void dnsc_id2key(struct dnsc_key *key, struct dnsc_id *id) curl_msnprintf(&key->data[namelen + 1], 7, ":%u", id->port); } - static void dnscache_entry_free(struct Curl_dns_entry *dns) { Curl_freeaddrinfo(dns->addr); @@ -430,8 +429,7 @@ UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, } #endif -static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, - int ai_family) +static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, int ai_family) { for(; ai; ai = ai->ai_next) { if(ai->ai_family == ai_family) @@ -470,8 +468,7 @@ static struct Curl_dns_entry *dnsc_entry_create(struct Curl_easy *data, return dns; } -static struct Curl_dns_entry * -dnsc_entry_assign_addr( +static struct Curl_dns_entry *dnsc_entry_assign_addr( struct Curl_easy *data, struct Curl_dns_entry *dns, uint8_t dns_queries, @@ -569,9 +566,9 @@ struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, } #ifdef USE_HTTPSRR -static struct Curl_dns_entry * -dnsc_entry_assign_https(struct Curl_dns_entry *dns, - struct Curl_https_rrinfo **phinfo) +static struct Curl_dns_entry *dnsc_entry_assign_https( + struct Curl_dns_entry *dns, + struct Curl_https_rrinfo **phinfo) { if(!dns) goto out; @@ -633,10 +630,9 @@ static struct Curl_dns_entry *dnsc_add_https(struct Curl_easy *data, } dns = dns2; - dns->refcount++; /* mark entry as in-use */ + dns->refcount++; /* mark entry as in-use */ return dns; } - #endif /* USE_HTTPSRR */ static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data, @@ -663,17 +659,17 @@ static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data, } dns = dns2; - dns->refcount++; /* mark entry as in-use */ + dns->refcount++; /* mark entry as in-use */ return dns; } -static struct Curl_dns_entry * -dnsc_add_peer_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - uint8_t dns_queries, - struct Curl_addrinfo **paddr, - struct dnsc_id *id, - bool permanent) +static struct Curl_dns_entry *dnsc_add_peer_addr( + struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + struct dnsc_id *id, + bool permanent) { struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; @@ -684,7 +680,6 @@ dnsc_add_peer_addr(struct Curl_easy *data, if(!dns) return NULL; - /* Store the resolved data in our DNS cache. */ dnsc_id2key(&key, id); dns2 = Curl_hash_add(&dnscache->entries, key.data, key.len, (void *)dns); @@ -694,7 +689,7 @@ dnsc_add_peer_addr(struct Curl_easy *data, } dns = dns2; - dns->refcount++; /* mark entry as in-use */ + dns->refcount++; /* mark entry as in-use */ return dns; } @@ -968,8 +963,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(dns) { infof(data, "RESOLVE %.*s:%u - old addresses discarded", - (int)curlx_strlen(&source), - curlx_str(&source), port); + (int)curlx_strlen(&source), curlx_str(&source), port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. 2. even if entry with correct addresses is already in the cache, diff --git a/lib/vdns/dnscache.h b/lib/vdns/dnscache.h index 1b9518b3bf10..73dcff60d70f 100644 --- a/lib/vdns/dnscache.h +++ b/lib/vdns/dnscache.h @@ -70,15 +70,15 @@ struct Curl_dns_entry { * Returns entry or NULL on OOM. */ struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr, - struct Curl_peer *peer); + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + struct Curl_peer *peer); struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr1, - struct Curl_addrinfo **paddr2, - struct Curl_peer *peer); + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + struct Curl_peer *peer); #ifdef USE_HTTPSRR struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, diff --git a/lib/vdns/hostip.c b/lib/vdns/hostip.c index 747acac5e578..bdb1a10e13b4 100644 --- a/lib/vdns/hostip.c +++ b/lib/vdns/hostip.c @@ -570,7 +570,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, #endif struct Curl_addrinfo *addr = NULL; size_t hostname_len; - bool addr_queries = (dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); + bool addr_queries = (dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)); CURLcode result = CURLE_OK; *pnegative = FALSE; @@ -769,7 +769,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, if(IS_RESOLV_FAIL(result)) { if(cache_dns && negative) Curl_dnscache_add_negative(data, dns_queries, peer); - if(dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + if(dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)) failf(data, "Could not resolve: %s:%u", peer->hostname, peer->port); } else { @@ -1120,7 +1120,7 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, transfer using it. */ if(async->negative_answer) Curl_dnscache_add_negative(data, async->dns_queries, async->peer); - if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + if(async->dns_queries & (CURL_DNSQ_A | CURL_DNSQ_AAAA)) failf(data, "Could not resolve: %s:%u", async->peer->hostname, async->peer->port); } diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index f6d273a92c93..1c16bbbea69b 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -2034,7 +2034,6 @@ CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, return CURLE_OK; } - CURLcode Curl_cf_ngtcp2_cmn_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 0d3194d01f23..bbc5777eac88 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -565,8 +565,8 @@ static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) #endif /* (HAVE_SENDMMSG || HAVE_SENDMSG) && !HAVE_APPLE_MSG_X */ #if (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) || \ - defined(HAVE_APPLE_MSG_X)) && \ - (defined(IP_RECVTOS) || defined(IP_TOS)) && defined(IPTOS_ECN_MASK) + defined(HAVE_APPLE_MSG_X)) && \ + (defined(IP_RECVTOS) || defined(IP_TOS)) && defined(IPTOS_ECN_MASK) static uint8_t vquic_msghdr_get_ecn(struct msghdr *msg, int family) { struct cmsghdr *cmsg; @@ -618,7 +618,7 @@ static uint8_t vquic_msghdr_get_ecn(struct msghdr *msg, int family) return 0; } #else -#define vquic_msghdr_get_ecn(a,b) 0 +#define vquic_msghdr_get_ecn(a, b) 0 #endif /* HAVE_SENDMMSG || HAVE_SENDMSG || HAVE_APPLE_MSG_X ... */ #ifdef HAVE_SENDMMSG diff --git a/src/config2setopts.c b/src/config2setopts.c index d7963f6716d6..b6c242dad6cb 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -214,11 +214,11 @@ static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl, MY_SETOPT_STR(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); MY_SETOPT_STR(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); - /* SSH host key md5 checking allows us to fail if we are not talking to who + /* SSH host key MD5 checking allows us to fail if we are not talking to who we think we should */ MY_SETOPT_STR(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, config->hostpubmd5); - /* SSH host key sha256 checking allows us to fail if we are not talking to + /* SSH host key SHA256 checking allows us to fail if we are not talking to who we think we should */ MY_SETOPT_STR(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, config->hostpubsha256); diff --git a/src/curlinfo.c b/src/curlinfo.c index 1760a95d95c0..87f00032b139 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -270,13 +270,10 @@ static const char * const disabled[] = { #endif }; -int main(int argc, const char *argv[]) +int main(void) { size_t i; - (void)argc; - (void)argv; - for(i = 0; i < CURL_ARRAYSIZE(disabled); i++) puts(disabled[i]); diff --git a/src/tool_operate.c b/src/tool_operate.c index bea8d4509945..439932f5b6cd 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2159,8 +2159,7 @@ static CURLcode serial_transfers(CURLSH *share) } } if(returncode) - /* returncode errors have priority */ - result = returncode; + result = returncode; /* returncode errors have priority */ if(result) single_transfer_cleanup(); @@ -2178,20 +2177,20 @@ static bool win32_using_schannel(CURLcode *resultp) 1 = yes */ *resultp = CURLE_OK; if(using_schannel == -1) { - CURL *curltls = curl_easy_init(); + CURL *curl = curl_easy_init(); /* The TLS backend remains, so keep the info */ const struct curl_tlssessioninfo *tls_backend_info = NULL; - if(!curltls) + if(!curl) *resultp = CURLE_OUT_OF_MEMORY; else { - *resultp = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR, + *resultp = curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls_backend_info); if(!*resultp) using_schannel = (tls_backend_info->backend == CURLSSLBACKEND_SCHANNEL); } - curl_easy_cleanup(curltls); + curl_easy_cleanup(curl); if(*resultp) return TRUE; } diff --git a/tests/server/util.c b/tests/server/util.c index a856c92a00dc..2e1dfb6b71b7 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -124,8 +124,7 @@ static void win32_cleanup(void) WSACleanup(); #endif - /* flush buffers of all streams regardless of their mode */ - _flushall(); + _flushall(); /* flush buffers of all streams regardless of their mode */ } int win32_init(void) @@ -356,6 +355,9 @@ static SIGHANDLER_T old_sigterm_handler = SIG_ERR; * Only call signal-safe functions from the signal handler, as required by * the POSIX specification: * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 + * https://iafisher.com/2026/08/restart + * https://iafisher.com/2026/08/safe-signals + * https://lwn.net/Articles/414618/ */ static void exit_signal_handler(int signum) /* keep signal-safe */ { From c24270bc1cbf88062263c7a067469bdb905534af Mon Sep 17 00:00:00 2001 From: Samuel Henrique Date: Sun, 30 Aug 2026 19:59:18 -0700 Subject: [PATCH 1049/1062] wcurl: import v2026.08.30 Closes #22758 --- docs/wcurl.md | 4 ++-- scripts/wcurl | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/wcurl.md b/docs/wcurl.md index f0ed42baf25d..4137e98b35a6 100644 --- a/docs/wcurl.md +++ b/docs/wcurl.md @@ -35,8 +35,8 @@ Call **wcurl** with a list of URLs you want to download and **wcurl** picks sane defaults. If you need anything more complex, you can provide any of curl's supported -parameters via the **--curl-options** option. Beware that you likely should be -using curl directly if your use case is not covered. +parameters via the **--curl-options** option. Beware that you likely should +be using curl directly if your use case is not covered. By default, **wcurl** does: diff --git a/scripts/wcurl b/scripts/wcurl index a5c1f95e1cc4..5fb68275efb1 100755 --- a/scripts/wcurl +++ b/scripts/wcurl @@ -26,10 +26,10 @@ # # SPDX-License-Identifier: curl -# Stop on errors and on usage of unset variables. -set -eu +# Stop on errors, usage of unset variables and disable glob expansion. +set -euf -VERSION="2026.01.05" +VERSION="2026.08.30" PROGRAM_NAME="$(basename "$0")" readonly PROGRAM_NAME @@ -67,7 +67,7 @@ Options: --no-decode-filename: Do not percent-decode the output filename, even if the percent-encoding in the URL was done by wcurl, e.g.: The URL contained whitespace. - --dry-run: Do not actually execute curl, only print what would be invoked. + --dry-run: Do not actually execute curl, print what would be invoked. -V, --version: Print version information. @@ -119,7 +119,8 @@ readonly PER_URL_PARAMETERS="\ # 2F = / # 5C = \ # 3A = : -readonly UNSAFE_PERCENT_ENCODE="%2F %5C %3A" +# 7F = DEL +readonly UNSAFE_PERCENT_ENCODE="%2F %5C %3A %7F" # Whether to invoke curl or not. DRY_RUN="false" @@ -131,6 +132,18 @@ sanitize() error "You must provide at least one URL to download." fi + # Split the extra curl options while IFS still has its default value, then + # join them back with spaces, so that restricting IFS below does not change + # the way the user's options are split. + # shellcheck disable=SC2086 + set -- ${CURL_OPTIONS} + + # Split the list of URLs on spaces only. Spaces are percent-encoded before a + # URL is added to the list, so every URL is a single field, and a tab or a + # newline inside one URL cannot turn it into more than one curl transfer. + IFS=" " + CURL_OPTIONS="$*" + readonly CURL_OPTIONS URLS DRY_RUN HAS_USER_SET_OUTPUT } @@ -190,12 +203,12 @@ percent_decode() get_url_filename() { # Remove protocol and query string if present. - hostname_and_path="$(printf %s "${1}" | sed -e 's,^[^/]*//,,' -e 's,?.*$,,')" + hostname_and_path="$(printf %s "${1}" | sed -e 's,^[^/]*//,,' -e 's,[?#].*$,,')" # If what remains contains a slash, there is a path; return it percent-decoded. case "${hostname_and_path}" in # sed to remove everything preceding the last '/', e.g.: "example/something" becomes "something" - # sed to also replace ':' with the percent_encoded %3A - */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,' -e 's,:,%3A,g')" ;; + # sed to also replace '\' with the percent_encoded %5C and ':' with %3A + */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,' -e 's,\\,%5C,g' -e 's,:,%3A,g')" ;; esac # No slash means there was only a hostname and no path; return empty string. } @@ -240,6 +253,8 @@ exec_curl() # If there are less than two URLs, do not set the parallel flag. if [ "$#" -lt 2 ]; then CURL_PARALLEL="" + elif [ "${HAS_USER_SET_OUTPUT}" = "true" ] && [ -z "${CURL_NO_CLOBBER}" ]; then + error "Using '--output' with multiple URLs requires curl >= 7.83.0 ('--no-clobber')." fi # Start assembling the command. @@ -260,7 +275,7 @@ exec_curl() [ -z "${OUTPUT_PATH}" ] && OUTPUT_PATH=index.html fi # shellcheck disable=SC2086 - set -- "$@" ${NEXT_PARAMETER} ${PER_URL_PARAMETERS} ${CURL_NO_CLOBBER} --output "${OUTPUT_PATH}" ${CURL_OPTIONS} "${url}" + set -- "$@" ${NEXT_PARAMETER} ${PER_URL_PARAMETERS} ${CURL_NO_CLOBBER} --output "${OUTPUT_PATH}" ${CURL_OPTIONS} --url "${url}" NEXT_PARAMETER="--next" done @@ -283,6 +298,9 @@ while [ -n "${1-}" ]; do ;; --curl-options) + if [ -z "${2-}" ]; then + error "Option '${1}' requires an argument." + fi shift CURL_OPTIONS="${CURL_OPTIONS} ${1}" ;; @@ -298,6 +316,9 @@ while [ -n "${1-}" ]; do ;; -o | -O | --output) + if [ -z "${2-}" ]; then + error "Option '${1}' requires an argument." + fi shift HAS_USER_SET_OUTPUT="true" OUTPUT_PATH="${1}" From c30335595736ea30168e8a8916aa9338cc3c6412 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Wed, 26 Aug 2026 04:19:08 -0400 Subject: [PATCH 1050/1062] tool_doswin: fix stdin data truncation - Don't do premature write shutdown (SHUT_WR) on the socket used to read the stdin data (aka socket_r). - Don't do premature read shutdown (SHUT_RD) on the socket used to write the stdin data (aka socket_w). - Remove broken setsockopt call for SO_DONTLINGER on socket_r. - Wait until all data sent from socket_w is acknowledged by socket_r. Prior to this change a partial shutdown of the write side of the read socket that the main thread reads stdin data from (socket_r) happened before reading the data from the socket. The partial shutdown caused a FIN to be sent which lingered for ~2 minutes (typical OS configured time), and after that time the connection was terminated. Theory of operation to read stdin on windows is a connection is established to relay stdin from a dedicated thread (write to socket_w) to the main thread (read from socket_r) so reading stdin is non-blocking. Data truncation would occur if this process was not completed by the OS FIN reply wait time. Note this FIN wait time is not the same as SO_LINGER wait time, the latter only used by closesocket. Ref: https://github.com/curl/curl/pull/22383#issuecomment-5399969303 Closes https://github.com/curl/curl/pull/22682 --- src/tool_doswin.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index b3fb7b56203f..be6cb510d075 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -740,6 +740,12 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) ssize_t nwritten; char buffer[BUFSIZ]; + /* If stdin is a pipe then end-of-data signaling may differ depending on + how curl was built, the shell and the input. Two ways have been + observed: + - ReadFile fails with GetLastError ERROR_BROKEN_PIPE + - ReadFile succeeds with 0 bytes read (seen on mingw) */ + if(!ReadFile(tdata.stdin_handle, buffer, sizeof(buffer), &n, NULL)) break; if(n == 0) @@ -751,6 +757,19 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) break; } + /* wait for all data to be received by the main thread: + shut down the write side of our socket so that a FIN is sent "after all + data is sent and acknowledged by the receiver". recv is called to wait for + this to happen. the wait time includes time of up to 2 min (OS typical) + since it's possible the receiver will not reply to the FIN. + */ + if(shutdown(tdata.socket_w, SHUT_WR) == 0) { + char buf[1024]; + /* read until close or error while ignoring all incoming */ + while(sread(tdata.socket_w, buf, sizeof(buf)) > 0) + ; + } + cleanup_tdata_sync(); return 0; @@ -880,9 +899,6 @@ curl_socket_t win32_stdin_read_thread(void) break; } - /* Hard close the socket on closesocket() */ - setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); - /* Make the reading socket nonblocking */ if(curlx_nonblock(socket_r, TRUE)) { errorf("curlx_nonblock() error"); @@ -930,16 +946,6 @@ curl_socket_t win32_stdin_read_thread(void) break; } - if(shutdown(tdata.socket_w, SHUT_RD)) { - errorf("shutdown error: %d", SOCKERRNO); - break; - } - - if(shutdown(socket_r, SHUT_WR)) { - errorf("shutdown error: %d", SOCKERRNO); - break; - } - /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &tdata.stdin_handle, From 72986f2aa68b273572add2546b79bb509a7216ec Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 1 Sep 2026 09:40:10 +0200 Subject: [PATCH 1051/1062] http2: remove assert in ingress processing Remove the assert that every `data` triggering ingress processing has an opened HTTP/2 stream. This is not true for connection shutdown processing, at the least. Reported-By: Max Dymond Closes #22776 --- lib/http2.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/http2.c b/lib/http2.c index 16a5b96327ca..57892e0a44d4 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1901,10 +1901,6 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, Curl_multi_mark_dirty(data); break; } - else if(!stream) { - DEBUGASSERT(0); - break; - } result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(result) { From 1c58877705b172da892cb9c18c5edb572f2d1d73 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 1 Sep 2026 09:22:24 +0200 Subject: [PATCH 1052/1062] sspi ambient user checks: remove again With the clarification that the application has to take extra care when changing an "ambient user" setting, remove the special check in connection matching that had been imperfect from the start. Documented here: https://curl.se/libcurl/c/CURLOPT_USERNAME.html Closes #22775 --- lib/url.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/lib/url.c b/lib/url.c index eb1e166a5577..6eeae43d0d42 100644 --- a/lib/url.c +++ b/lib/url.c @@ -897,31 +897,6 @@ static bool url_match_ssl_config(struct connectdata *conn, return TRUE; } -#if defined(USE_SPNEGO) || defined(USE_NTLM) -static bool url_allow_sspi_empty_creds(struct Curl_creds *conn_creds, - struct Curl_easy *data, - struct connectdata *conn) -{ -#ifdef USE_WINDOWS_SSPI - /* Empty user: SSPI on Windows can make use of an "ambient" - * user from a "SecurityToken" associated with the current thread or - * process. This token can be switched at any time. We are therefore - * not able to find out reliably what token the connection really - * used, nor what token in the next connect attempt will use. - * To avoid TOCTOU attacks, do not reuse on empty credentials - * UNLESS this connection is the one used by this transfer before. */ - if(!Curl_creds_has_user(conn_creds) && - (data->state.lastconnect_id != conn->connection_id)) - return FALSE; -#else - (void)conn_creds; - (void)data; - (void)conn; -#endif - return TRUE; -} -#endif /* USE_SPNEGO || USE_NTLM */ - #ifdef USE_NTLM static bool url_match_auth_ntlm(struct connectdata *conn, struct url_conn_match *m) @@ -933,9 +908,6 @@ static bool url_match_auth_ntlm(struct connectdata *conn, !Curl_creds_same(conn->creds, m->data->state.creds) || !Curl_peer_equal(conn->creds_origin, m->data->state.origin)) return FALSE; - /* Empty credentials need more careful matching for WINDOWS_SSPI */ - if(!url_allow_sspi_empty_creds(conn->creds, m->data, conn)) - return FALSE; } else if(m->want_ntlm_http) { /* Transfer wants NTLM, connection is not using it. @@ -952,9 +924,6 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(!m->want_proxy_ntlm_http || !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; - if(!url_allow_sspi_empty_creds(m->needle->http_proxy.creds, - m->data, conn)) - return FALSE; } else if(m->want_proxy_ntlm_http) { if(conn->http_proxy.creds && @@ -997,8 +966,6 @@ static bool url_match_auth_nego(struct connectdata *conn, !Curl_creds_same(conn->creds, m->data->state.creds) || !Curl_peer_equal(conn->creds_origin, m->data->state.origin)) return FALSE; - if(!url_allow_sspi_empty_creds(conn->creds, m->data, conn)) - return FALSE; } else if(m->want_nego_http) { /* Transfer wants Negotiate, connection is not using it. @@ -1015,9 +982,6 @@ static bool url_match_auth_nego(struct connectdata *conn, if(!m->want_proxy_nego_http || !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; - if(!url_allow_sspi_empty_creds(m->needle->http_proxy.creds, - m->data, conn)) - return FALSE; } else if(m->want_proxy_nego_http) { if(conn->http_proxy.creds && From 7fe13265aca2706b5cc6b55f6f7df042a7b87b72 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Aug 2026 23:23:57 +0200 Subject: [PATCH 1053/1062] perf/percent: basic percent encode/decode performance test Closes #22771 --- tests/perf/Makefile.inc | 1 + tests/perf/percent.c | 78 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/perf/percent.c diff --git a/tests/perf/Makefile.inc b/tests/perf/Makefile.inc index b3b159ab95fb..9660c9cb1c59 100644 --- a/tests/perf/Makefile.inc +++ b/tests/perf/Makefile.inc @@ -44,5 +44,6 @@ CURLX_C = \ # All performance test programs TESTS_C = \ base64.c \ + percent.c \ snprintf.c \ urlparser.c diff --git a/tests/perf/percent.c b/tests/perf/percent.c new file mode 100644 index 000000000000..345b78e1f122 --- /dev/null +++ b/tests/perf/percent.c @@ -0,0 +1,78 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static int test_percent(int argc, const char **argv) +{ + struct curltime start; + struct curltime end; + timediff_t us; + long long hn; + curl_off_t loops = 2500000, loop; + unsigned char array[256]; + unsigned int c; + + if(argc > 1) { + const char *ptr = argv[1]; + curl_off_t num; + if(!curlx_str_number(&ptr, &num, CURL_OFF_T_MAX) && !*ptr) + loops = num; + } + + for(c = 0; c < 256; c++) + array[c] = (unsigned char)c; + + start = curlx_now(); + for(loop = 0; loop < loops; loop++) { + char *encoded = + curl_easy_escape(NULL, (char *)array, (int)sizeof(array)); + if(encoded) { + char *unesc = NULL; + int unlen = 0; + /* now unescape it again */ + unesc = curl_easy_unescape(NULL, encoded, 0, &unlen); + if(!unesc) { + curl_mfprintf(stderr, "unexpected unescape error\n"); + return 1; + } + curl_free(unesc); + } + else { + curl_mfprintf(stderr, "unexpected escape error\n"); + return 1; + } + curl_free(encoded); + } + end = curlx_now(); + us = curlx_timediff_us(end, start); /* how many microseconds */ + hn = loop ? us * 100000 / loops : 0; /* 100 times too big */ + curl_mprintf("Loops: %" CURL_FORMAT_CURL_OFF_T "\n" + "Time: %lld usecs\n" + "Time/loop: %lld.%0lld ns\n", + loops, + (long long)us, + (long long)hn / 100, + (long long)hn % 100); + return 0; +} From 3f5b46c1536edd85c56b00333721b8b63bb10edf Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 1 Sep 2026 12:04:15 +0200 Subject: [PATCH 1054/1062] h2proxy: remember goaway "last_stream_id" In the HTTP/2 proxy filter, remember the server announced "last_stream_id" form a GOAWAY frame, so that error handling does not report a perfectly fine stream as broken. Reported-By: Max Dymond Closes #22778 --- lib/cf-h2-proxy.c | 10 ++++++++-- lib/http2.c | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 86e04fc0ec14..fde9e1bc799a 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -187,7 +187,7 @@ struct cf_h2_proxy_ctx { struct Curl_peer *dest; /* where to tunnel to */ struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ int32_t goaway_error; - int32_t last_stream_id; + int32_t remote_max_sid; BIT(conn_closed); BIT(rcvd_goaway); BIT(sent_goaway); @@ -500,6 +500,11 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, break; case NGHTTP2_GOAWAY: ctx->rcvd_goaway = TRUE; + ctx->remote_max_sid = frame->goaway.last_stream_id; + if(data) { + infof(data, "received GOAWAY, error=%u, last_stream=%d", + frame->goaway.error_code, ctx->remote_max_sid); + } break; default: break; @@ -911,6 +916,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); + ctx->remote_max_sid = INT32_MAX; if(tunnel_stream_init(&ctx->tunnel, ctx->dest)) goto out; @@ -1187,7 +1193,7 @@ static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else if(ctx->tunnel.reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || (ctx->rcvd_goaway && - ctx->last_stream_id < ctx->tunnel.stream_id)) { + ctx->remote_max_sid < ctx->tunnel.stream_id)) { result = CURLE_RECV_ERROR; } else diff --git a/lib/http2.c b/lib/http2.c index 57892e0a44d4..0cfdf20571be 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -179,7 +179,7 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); Curl_uint32_hash_init(&ctx->streams, 63, h2_stream_hash_free); - ctx->remote_max_sid = 2147483647; + ctx->remote_max_sid = INT32_MAX; ctx->via_h1_upgrade = via_h1_upgrade; ctx->initialized = TRUE; } From 610bf015996bfb35ffdcb0a3396efb381ba744ba Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 1 Sep 2026 13:46:40 +0200 Subject: [PATCH 1055/1062] lib: expire/progress handling, do less clock calls Save clock system calls by passing obtained timestamps to expire and progress calls. transfer: remove duplicate progress call cf-ip-happy: remove timestamps never used cf-https-connect: remove timestamps never used Closes #22779 --- lib/cf-https-connect.c | 17 ++++++++++------- lib/cf-ip-happy.c | 27 ++++++++++++--------------- lib/connect.c | 5 +++-- lib/http.c | 3 ++- lib/multi.c | 33 +++++++++++++++++++++------------ lib/multiif.h | 3 +++ lib/progress.c | 14 ++++++++++---- lib/progress.h | 1 + lib/transfer.c | 10 ++++------ lib/vdns/asyn-thrdd.c | 5 +++-- lib/vdns/cf-dns.c | 6 ++++-- lib/vdns/hostip.c | 5 +++-- lib/vdns/hostip.h | 5 +++-- lib/vquic/cf-ngtcp2-cmn.c | 29 +++++++++++++++-------------- lib/vquic/cf-ngtcp2-cmn.h | 3 ++- 15 files changed, 96 insertions(+), 70 deletions(-) diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index e77badec1de8..2e13a5cc09fe 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -52,7 +52,6 @@ struct cf_hc_baller { const char *name; struct Curl_cfilter *cf; CURLcode result; - struct curltime started; int reply_ms; uint8_t transport; enum alpnid alpn_id; @@ -173,7 +172,6 @@ static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *save = cf->next; cf->next = NULL; - b->started = *Curl_pgrs_now(data); b->result = Curl_cf_setup_insert_after(cf, data, b->transport, CURL_CF_SSL_ENABLE); b->cf = cf->next; @@ -238,7 +236,8 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, } static bool time_to_start_baller2(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + const struct curltime *pnow) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; @@ -253,7 +252,7 @@ static bool time_to_start_baller2(struct Curl_cfilter *cf, return TRUE; } - elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started); + elapsed_ms = curlx_ptimediff_ms(pnow, &ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", " "starting %s", ctx->ballers[0].name, @@ -479,6 +478,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + const struct curltime *pnow = NULL; if(cf->connected) { *done = TRUE; @@ -513,10 +513,12 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, goto out; } cf_hc_set_baller2(cf, data); - ctx->started = *Curl_pgrs_now(data); + pnow = Curl_pgrs_now(data); + ctx->started = *pnow; cf_hc_baller_init(&ctx->ballers[0], cf, data); if((ctx->baller_count > 1) || !ctx->ballers_complete) { - Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); + Curl_expire_set(data, EXPIRE_ALPN_EYEBALLS, + ctx->soft_eyeballs_timeout_ms, pnow); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); @@ -533,7 +535,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } } - if(time_to_start_baller2(cf, data)) { + pnow = Curl_pgrs_now(data); + if(time_to_start_baller2(cf, data, pnow)) { cf_hc_baller_init(&ctx->ballers[1], cf, data); } diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index bbb8cc1ef091..baca4682f0b0 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -165,7 +165,6 @@ struct cf_ip_attempt { 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 */ CURLcode result; int ai_family; uint8_t transport_peer; @@ -277,7 +276,6 @@ struct cf_ip_ballers { struct Curl_peer *peer; struct Curl_peer *tunnel_peer; cf_ip_connect_create *cf_create; /* for creating cf */ - struct curltime started; struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; @@ -399,6 +397,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, bool do_more; timediff_t next_expire_ms; uint32_t inconclusive, ongoing; + const struct curltime *pnow = NULL; VERBOSE(int i); if(bs->winner) @@ -439,9 +438,8 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, "%u ongoing, %u inconclusive", ongoing, inconclusive); /* no attempt connected yet, start another one? */ + pnow = Curl_pgrs_now(data); if(!ongoing) { - if(!bs->started.tv_sec && !bs->started.tv_usec) - bs->started = *Curl_pgrs_now(data); do_more = TRUE; } else { @@ -451,7 +449,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, 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) >= + (curlx_ptimediff_ms(pnow, &bs->last_attempt_started) >= bs->attempt_delay_ms); if(do_more) CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, " @@ -511,7 +509,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, while(*panchor) panchor = &(*panchor)->next; *panchor = a; - bs->last_attempt_started = *Curl_pgrs_now(data); + bs->last_attempt_started = *pnow; bs->last_attempt_ai_family = ai_family; /* and run everything again */ goto evaluate; @@ -520,7 +518,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, /* tried all addresses, no success but some where inconclusive. * Let's restart the inconclusive ones. */ timediff_t since_ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started); + curlx_ptimediff_ms(pnow, &bs->last_attempt_started); timediff_t delay_ms = bs->attempt_delay_ms - since_ms; if(delay_ms <= 0) { CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one"); @@ -533,7 +531,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, (int)result); if(result) /* serious failure */ goto out; - bs->last_attempt_started = *Curl_pgrs_now(data); + bs->last_attempt_started = *pnow; goto evaluate; } DEBUGASSERT(0); /* should not come here */ @@ -542,7 +540,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, /* let's wait some more before restarting */ infof(data, "connect attempts inconclusive, retrying " "in %" FMT_TIMEDIFF_T "ms", delay_ms); - Curl_expire(data, delay_ms, EXPIRE_HAPPY_EYEBALLS); + Curl_expire_set(data, EXPIRE_HAPPY_EYEBALLS, delay_ms, pnow); } /* attempt timeout for restart has not expired yet */ goto out; @@ -565,7 +563,8 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, bool more_possible; /* when do we need to be called again? */ - next_expire_ms = Curl_timeleft_ms(data); + pnow = Curl_pgrs_now(data); + next_expire_ms = Curl_timeleft_now_ms(data, pnow); if(next_expire_ms < 0) { failf(data, "Connection timeout after %" FMT_OFF_T " ms", Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE)); @@ -579,8 +578,8 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, #endif if(more_possible) { timediff_t expire_ms, elapsed_ms; - elapsed_ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started); + + elapsed_ms = curlx_ptimediff_ms(pnow, &bs->last_attempt_started); expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0); next_expire_ms = CURLMIN(next_expire_ms, expire_ms); if(next_expire_ms <= 0) { @@ -589,7 +588,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, } CURL_TRC_CF(data, cf, "next HAPPY_EYEBALLS timeout in %" FMT_TIMEDIFF_T "ms", next_expire_ms); - Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS); + Curl_expire_set(data, EXPIRE_HAPPY_EYEBALLS, next_expire_ms, pnow); } } return result; @@ -671,7 +670,6 @@ struct cf_ip_happy_ctx { cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; - struct curltime started; BIT(dns_resolved); }; @@ -763,7 +761,6 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->ballers.transport_peer); - ctx->started = *Curl_pgrs_now(data); return CURLE_OK; } diff --git a/lib/connect.c b/lib/connect.c index 1ae4ea5cef40..cb3647544ef8 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -118,16 +118,17 @@ void Curl_shutdown_start(struct Curl_easy *data, int8_t sockindex, int timeout_ms) { struct connectdata *conn = data->conn; + const struct curltime *pnow = Curl_pgrs_now(data); DEBUGASSERT(conn); - conn->shutdown.start[sockindex] = *Curl_pgrs_now(data); + conn->shutdown.start[sockindex] = *pnow; conn->shutdown.timeout_ms = (timeout_ms > 0) ? (timediff_t)timeout_ms : ((data->set.shutdowntimeout > 0) ? data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS); /* Set a timer, unless we operate on the admin handle */ if(data->mid) - Curl_expire(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); + Curl_expire_set(data, EXPIRE_SHUTDOWN, conn->shutdown.timeout_ms, pnow); CURL_TRC_M(data, "shutdown start on%s connection", sockindex ? " secondary" : ""); } diff --git a/lib/http.c b/lib/http.c index 3dc7b4bedb8b..4b79b2ab9399 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1523,7 +1523,8 @@ static CURLcode cr_exp100_read(struct Curl_easy *data, "timeout %dms", data->set.expect_100_timeout)); ctx->state = EXP100_AWAITING_CONTINUE; ctx->start = *Curl_pgrs_now(data); - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); + Curl_expire_set(data, EXPIRE_100_TIMEOUT, + data->set.expect_100_timeout, &ctx->start); *nread = 0; *eos = FALSE; return CURLE_OK; diff --git a/lib/multi.c b/lib/multi.c index 52d3939c9b45..b23595f21f09 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1997,7 +1997,7 @@ static CURLcode mspeed_check(struct Curl_easy *data) if(data->mstate != MSTATE_RATELIMITING) { multistate(data, MSTATE_RATELIMITING); } - Curl_expire(data, CURLMAX(send_ms, recv_ms), EXPIRE_TOOFAST); + Curl_expire_set(data, EXPIRE_TOOFAST, CURLMAX(send_ms, recv_ms), pnow); Curl_multi_clear_dirty(data); CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms", CURLMAX(send_ms, recv_ms)); @@ -2012,7 +2012,7 @@ static CURLcode mspeed_check(struct Curl_easy *data) timediff_t next_ms = CURLMIN(send_ms, recv_ms); if(!next_ms) next_ms = CURLMAX(send_ms, recv_ms); - Curl_expire(data, next_ms, EXPIRE_TOOFAST); + Curl_expire_set(data, EXPIRE_TOOFAST, next_ms, pnow); CURL_TRC_M(data, "[RLIMIT] next token update in %" FMT_TIMEDIFF_T "ms", next_ms); } @@ -2503,14 +2503,16 @@ static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) static CURLMcode multistate_setup(struct Curl_easy *data) { - Curl_pgrsTime(data, TIMER_STARTSINGLE); + const struct curltime *pnow = Curl_pgrs_now(data); + Curl_pgrsTimeWas(data, TIMER_STARTSINGLE, *pnow); if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + Curl_expire_set(data, EXPIRE_TIMEOUT, data->set.timeout, pnow); if(data->set.connecttimeout) /* Since a connection might go to pending and back to CONNECT several times before it actually takes off, we need to set the timeout once in SETUP before we enter CONNECT the first time. */ - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + Curl_expire_set(data, EXPIRE_CONNECTTIMEOUT, + data->set.connecttimeout, pnow); multistate(data, MSTATE_CONNECT); return CURLM_CALL_MULTI_PERFORM; @@ -3745,8 +3747,9 @@ static CURLMcode multi_set_timeout(struct Curl_easy *data, * * Expire replaces a former timeout using the same id if already set. */ -void Curl_expire(struct Curl_easy *data, - timediff_t milli, expire_id eid) +void Curl_expire_set(struct Curl_easy *data, + expire_id eid, timediff_t ms, + const struct curltime *pnow) { struct Curl_multi *multi = data->multi; struct expire_timers *timeouts = &data->state.timeouts; @@ -3758,14 +3761,14 @@ void Curl_expire(struct Curl_easy *data, if(!multi) return; DEBUGASSERT(eid < EXPIRE_LAST); - if(milli > INT_MAX) + if(ms > INT_MAX) /* Cap ridiculous timeouts, 31-bit ms is still 3.5 weeks. When the time goes to the user, it must fit in this size. */ - milli = INT_MAX; + ms = INT_MAX; - set = *Curl_pgrs_now(data); - set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */ - set.tv_usec += (int)(milli % 1000) * 1000; + set = *pnow; + set.tv_sec += (time_t)(ms / 1000); /* may be a 64 to 32-bit conversion */ + set.tv_usec += (int)(ms % 1000) * 1000; if(set.tv_usec >= 1000000) { set.tv_sec++; set.tv_usec -= 1000000; @@ -3793,6 +3796,12 @@ void Curl_expire(struct Curl_easy *data, timeouts->offset_us[timeouts->first]); } +void Curl_expire(struct Curl_easy *data, + timediff_t milli, expire_id eid) +{ + Curl_expire_set(data, eid, milli, Curl_pgrs_now(data)); +} + /* * Removes the expire timer. Marks it as done. */ diff --git a/lib/multiif.h b/lib/multiif.h index 492291fe09a1..d3ad8fffb334 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -28,6 +28,9 @@ */ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id eid); +void Curl_expire_set(struct Curl_easy *data, + expire_id eid, timediff_t ms, + const struct curltime *pnow); void Curl_expire_clear(struct Curl_easy *data, expire_id eid); void Curl_expire_clear_all(struct Curl_easy *data); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; diff --git a/lib/progress.c b/lib/progress.c index 4832ceb289ab..fcb8bf24229b 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -163,7 +163,7 @@ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, /* since low speed limit is enabled, set the expire timer to make this connection's speed get checked again in a second */ - Curl_expire(data, 1000, EXPIRE_SPEEDCHECK); + Curl_expire_set(data, EXPIRE_SPEEDCHECK, 1000, pnow); return CURLE_OK; } @@ -721,16 +721,22 @@ CURLcode Curl_pgrsUpdateX(struct Curl_easy *data, return pgrs_update(data, pnow); } -CURLcode Curl_pgrsCheck(struct Curl_easy *data) +CURLcode Curl_pgrsCheckX(struct Curl_easy *data, + const struct curltime *pnow) { CURLcode result; - result = pgrs_update(data, Curl_pgrs_now(data)); + result = pgrs_update(data, pnow); if(!result && !data->req.done) - result = pgrs_speedcheck(data, Curl_pgrs_now(data)); + result = pgrs_speedcheck(data, pnow); return result; } +CURLcode Curl_pgrsCheck(struct Curl_easy *data) +{ + return Curl_pgrsCheckX(data, Curl_pgrs_now(data)); +} + /* * Update all progress, do not do progress meter/callbacks. */ diff --git a/lib/progress.h b/lib/progress.h index 20bb11f3f4a2..19bb09517cbd 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -62,6 +62,7 @@ CURLcode Curl_pgrsUpdateX(struct Curl_easy *data, const struct curltime *pnow); void Curl_pgrsUpdate_nometer(struct Curl_easy *data); /* perform progress update with callbacks and speed checks */ CURLcode Curl_pgrsCheck(struct Curl_easy *data); +CURLcode Curl_pgrsCheckX(struct Curl_easy *data, const struct curltime *pnow); /* Inform progress/speedcheck about receive/send pausing */ void Curl_pgrsRecvPause(struct Curl_easy *data, bool enable); diff --git a/lib/transfer.c b/lib/transfer.c index e257057b5c8a..3b6c5c03dd36 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -354,6 +354,7 @@ static CURLcode sendrecv_ul(struct Curl_easy *data) CURLcode Curl_sendrecv(struct Curl_easy *data) { struct SingleRequest *k = &data->req; + const struct curltime *pnow = NULL; CURLcode result = CURLE_OK; if(Curl_xfer_is_blocked(data)) { @@ -376,12 +377,9 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) goto out; } - result = Curl_pgrsCheck(data); - if(result) - goto out; - + pnow = Curl_pgrs_now(data); if(CURL_REQ_WANT_IO(data)) { - if(Curl_timeleft_ms(data) < 0) { + if(Curl_timeleft_now_ms(data, pnow) < 0) { if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" @@ -417,7 +415,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) if(!CURL_REQ_WANT_IO(data)) data->req.done = TRUE; - result = Curl_pgrsUpdate(data); + result = Curl_pgrsCheckX(data, pnow); out: if(result) diff --git a/lib/vdns/asyn-thrdd.c b/lib/vdns/asyn-thrdd.c index 0199e0c0a49a..1ca217a79e60 100644 --- a/lib/vdns/asyn-thrdd.c +++ b/lib/vdns/asyn-thrdd.c @@ -699,9 +699,10 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, #endif if(!async->done) { + const struct curltime *pnow = Curl_pgrs_now(data); #ifndef ENABLE_INTERNAL_WAKEUP timediff_t stutter_ms, elapsed_ms; - elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + elapsed_ms = curlx_ptimediff_ms(pnow, &async->start); if(elapsed_ms < 3) stutter_ms = 1; else if(elapsed_ms <= 50) @@ -721,7 +722,7 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, timeout_ms = CURLMIN(100, timeout_ms); } #endif - Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); + Curl_expire_set(data, EXPIRE_ASYNC_NAME, timeout_ms, pnow); } return CURLE_OK; } diff --git a/lib/vdns/cf-dns.c b/lib/vdns/cf-dns.c index 35b38a7bdf1b..8a08912d9348 100644 --- a/lib/vdns/cf-dns.c +++ b/lib/vdns/cf-dns.c @@ -254,6 +254,7 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, return TRUE; #ifdef USE_CURL_ASYNC else if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + const struct curltime *pnow; timediff_t remain_ms; /* For Happy Eyeballing, we can start on either A or AAAA resolves, * but AAAA is preferred. We enforce a small delay for missing @@ -262,14 +263,15 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, * an answer (e.g. a negative one). */ if(Curl_resolv_has_answers(data, ctx->resolv_id, CURL_DNSQ_AAAA)) return TRUE; + pnow = Curl_pgrs_now(data); remain_ms = ctx->he_aaaa_await_ms - - Curl_resolv_elapsed_ms(data, ctx->resolv_id); + Curl_resolv_elapsed_ms(data, ctx->resolv_id, pnow); if(remain_ms <= 0) return TRUE; CURL_TRC_CF(data, cf, "[%s] still waiting %" FMT_TIMEDIFF_T "ms for AAAA result", Curl_resolv_query_str(ctx->dns_queries), remain_ms); - Curl_expire(data, remain_ms, EXPIRE_HAPPY_EYEBALLS); + Curl_expire_set(data, EXPIRE_HAPPY_EYEBALLS, remain_ms, pnow); return FALSE; } else { diff --git a/lib/vdns/hostip.c b/lib/vdns/hostip.c index bdb1a10e13b4..8464bd291773 100644 --- a/lib/vdns/hostip.c +++ b/lib/vdns/hostip.c @@ -470,12 +470,13 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, } timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, - uint32_t resolv_id) + uint32_t resolv_id, + const struct curltime *pnow) { struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); if(!async) return CURL_TIMEOUT_RESOLVE_MS; - return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + return curlx_ptimediff_ms(pnow, &async->start); } bool Curl_resolv_has_answers(struct Curl_easy *data, diff --git a/lib/vdns/hostip.h b/lib/vdns/hostip.h index eef799184794..43172101f2a0 100644 --- a/lib/vdns/hostip.h +++ b/lib/vdns/hostip.h @@ -140,7 +140,8 @@ void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); /* How much time has gone by since start of resolve. * Returns CURL_TIMEOUT_RESOLVE_MS if `resolv_id` is no longer valid. */ timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, - uint32_t resolv_id); + uint32_t resolv_id, + const struct curltime *pnow); /* Return TRUE if `resolv_id` has answers (positive or negative) to * all queries in `dns_queries`. @@ -178,7 +179,7 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); #define Curl_resolv_shutdown_all(x) Curl_nop_stmt #define Curl_resolv_destroy_all(x) Curl_nop_stmt #define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN -#define Curl_resolv_elapsed_ms(x, y) CURL_TIMEOUT_RESOLVE_MS +#define Curl_resolv_elapsed_ms(x, y, z) CURL_TIMEOUT_RESOLVE_MS #define Curl_resolv_has_answers(x, y, z) TRUE #define Curl_resolv_get_ai(x, y, z, a) NULL #define Curl_resolv_get_https(x, y) NULL diff --git a/lib/vquic/cf-ngtcp2-cmn.c b/lib/vquic/cf-ngtcp2-cmn.c index 1c16bbbea69b..b1cfb68cbdfa 100644 --- a/lib/vquic/cf-ngtcp2-cmn.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -1351,26 +1351,26 @@ void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); io_ctx->cf = cf; io_ctx->data = data; + io_ctx->now = *Curl_pgrs_now(data); ngtcp2_path_storage_zero(&io_ctx->ps); - Curl_vquic_ctx_set_time(&ctx->q, pnow); - io_ctx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + Curl_vquic_ctx_set_time(&ctx->q, &io_ctx->now); + io_ctx->ts = ((ngtcp2_tstamp)io_ctx->now.tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)io_ctx->now.tv_usec * NGTCP2_MICROSECONDS); } void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, - struct cf_ngtcp2_io_ctx *pktx, + struct cf_ngtcp2_io_ctx *io_ctx, struct Curl_cfilter *cf) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - Curl_vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + io_ctx->now = *Curl_pgrs_now(data); + Curl_vquic_ctx_update_time(&ctx->q, &io_ctx->now); + io_ctx->ts = ((ngtcp2_tstamp)io_ctx->now.tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)io_ctx->now.tv_usec * NGTCP2_MICROSECONDS); } #if NGTCP2_VERSION_NUM < 0x011100 @@ -1547,7 +1547,7 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, result = Curl_vquic_flush(cf, data, &ctx->q); if(result) { if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); + Curl_expire_set(data, EXPIRE_QUIC, 1, &pktx->now); return CURLE_OK; } return result; @@ -1599,7 +1599,7 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, gsolen, nread, nread); if(result) { if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); + Curl_expire_set(data, EXPIRE_QUIC, 1, &pktx->now); return CURLE_OK; } return result; @@ -1621,7 +1621,7 @@ CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, result = Curl_vquic_send(cf, data, &ctx->q, gsolen); if(result) { if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); + Curl_expire_set(data, EXPIRE_QUIC, 1, &pktx->now); return CURLE_OK; } return result; @@ -1827,8 +1827,9 @@ CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, if(timeout % NGTCP2_MILLISECONDS) { timeout += NGTCP2_MILLISECONDS; } - Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), - EXPIRE_QUIC); + Curl_expire_set(data, EXPIRE_QUIC, + (timediff_t)(timeout / NGTCP2_MILLISECONDS), + &pktx->now); } } return CURLE_OK; diff --git a/lib/vquic/cf-ngtcp2-cmn.h b/lib/vquic/cf-ngtcp2-cmn.h index 7395234af339..e0c27a95f7c0 100644 --- a/lib/vquic/cf-ngtcp2-cmn.h +++ b/lib/vquic/cf-ngtcp2-cmn.h @@ -204,6 +204,7 @@ void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, struct cf_ngtcp2_io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; + struct curltime now; ngtcp2_tstamp ts; ngtcp2_path_storage ps; }; @@ -212,7 +213,7 @@ void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, struct Curl_cfilter *cf, struct Curl_easy *data); void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, - struct cf_ngtcp2_io_ctx *pktx, + struct cf_ngtcp2_io_ctx *io_ctx, struct Curl_cfilter *cf); CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, From 40f0eb3822177c4bd0dc77a3a54de18fdf109879 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 14:57:15 +0000 Subject: [PATCH 1056/1062] GHA: bump Actions and pips - update `github/codeql-action/init` from 4.37.3 to 4.37.8 - update `github/codeql-action/analyze` from 4.37.3 to 4.37.8 - update `cryptography` from 49.0.0 to 50.0.0 - update `filelock` from 3.32.0 to 3.32.4 - update `ruff` from 0.16.2 to 0.16.5 - update `websockets` from 16.1.1 to 17.0.1 Closes #22781 Closes #22782 --- .github/scripts/requirements.txt | 2 +- .github/workflows/codeql.yml | 8 ++++---- tests/http/requirements.txt | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index c16619d65c16..e51ba9850e17 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -6,4 +6,4 @@ cmakelang==0.6.13 codespell==2.4.3 pyrefly==1.2.0 reuse==6.2.0 -ruff==0.16.2 +ruff==0.16.5 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3e50db473649..03aa9ba9d6e4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -52,13 +52,13 @@ jobs: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -96,7 +96,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 with: languages: cpp build-mode: manual @@ -143,4 +143,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 diff --git a/tests/http/requirements.txt b/tests/http/requirements.txt index 0d7569c0afa9..cb577aaa4ed6 100644 --- a/tests/http/requirements.txt +++ b/tests/http/requirements.txt @@ -2,9 +2,9 @@ # # SPDX-License-Identifier: curl -cryptography==49.0.0 -filelock==3.32.0 +cryptography==50.0.0 +filelock==3.32.4 psutil==7.2.2 pytest==9.1.1 pytest-xdist==3.8.0 -websockets==16.1.1 +websockets==17.0.1 From aa66b02dd8b74b4c72484c240d39399387054ea3 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Sun, 16 Aug 2026 12:36:24 +0200 Subject: [PATCH 1057/1062] tidy-up: misc C and CMake nits - CMake/win32-cache.cmake: sort and sync MSVC pre-fills with mingw-w64. - curl_sasl: use `uint32_t` for local `httpauth` value. To match struct. Follow-up to e3691612ea1acb28e77fee6b4b2b9da0ac2c8f8b #20209 - libstests: move literal to the right-hand side of comparisons. - windows: drop `!= 0`/`== 0` from `WSAEventSelect()` result checks, to keep it short. - tool_cfgable: replace `CURL_GLOBAL_ALL` with `CURL_GLOBAL_DEFAULT` in debug builds to match the macro used in non-debug and libcurl init. (Both macros map to the same literal value; there is no code change.) - perf/urlparser: use double-const in test array. - examples: `CURL_GLOBAL_DEFAULT` -> `CURL_GLOBAL_ALL` to match rest of examples. - libtests: `CURL_GLOBAL_DEFAULT` -> `CURL_GLOBAL_ALL` to match rest of tests. Closes #22783 --- CMake/win32-cache.cmake | 7 ++++--- docs/examples/ftpuploadfrommem.c | 2 +- docs/examples/post-callback.c | 2 +- lib/curl_sasl.c | 2 +- lib/multi.c | 2 +- lib/telnet.c | 2 +- src/tool_cfgable.c | 3 ++- tests/libtest/lib1507.c | 2 +- tests/libtest/lib1678.c | 2 +- tests/libtest/lib1907.c | 2 +- tests/libtest/lib1939.c | 2 +- tests/libtest/lib1940.c | 2 +- tests/libtest/lib1945.c | 2 +- tests/libtest/lib1947.c | 2 +- tests/libtest/lib1948.c | 2 +- tests/libtest/lib1985.c | 2 +- tests/libtest/lib590.c | 2 +- tests/libtest/lib677.c | 2 +- tests/libtest/lib678.c | 2 +- tests/libtest/lib694.c | 2 +- tests/libtest/lib751.c | 2 +- tests/libtest/lib753.c | 2 +- tests/perf/urlparser.c | 2 +- tests/server/sockfilt.c | 3 +-- 24 files changed, 28 insertions(+), 27 deletions(-) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index cc4779a855b9..2d8506eb69f4 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -42,16 +42,17 @@ else() set(HAVE_SYS_PARAM_H 0) set(HAVE_UTIME_H 0) if(MSVC) - set(HAVE_UNISTD_H 0) + set(HAVE_BASENAME 0) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() if(MSVC_VERSION GREATER_EQUAL 1800) + set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H set(HAVE_STDBOOL_H 1) else() + set(HAVE_BOOL_T 0) # = HAVE_STDBOOL_H set(HAVE_STDBOOL_H 0) endif() - set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") - set(HAVE_BASENAME 0) + set(HAVE_UNISTD_H 0) endif() endif() diff --git a/docs/examples/ftpuploadfrommem.c b/docs/examples/ftpuploadfrommem.c index db06edcd9f17..59834cab1e1b 100644 --- a/docs/examples/ftpuploadfrommem.c +++ b/docs/examples/ftpuploadfrommem.c @@ -78,7 +78,7 @@ int main(void) upload.sizeleft = strlen(data); /* In Windows, this inits the Winsock stuff */ - result = curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); /* Check for errors */ if(result != CURLE_OK) { fprintf(stderr, "curl_global_init() failed: %s\n", diff --git a/docs/examples/post-callback.c b/docs/examples/post-callback.c index e5b70366d1e1..aaed0aaa063c 100644 --- a/docs/examples/post-callback.c +++ b/docs/examples/post-callback.c @@ -76,7 +76,7 @@ int main(void) wt.sizeleft = strlen(data); /* In Windows, this inits the Winsock stuff */ - result = curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); /* Check for errors */ if(result != CURLE_OK) { fprintf(stderr, "curl_global_init() failed: %s\n", diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 2af6b97e9f44..c4ff1e526220 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -142,7 +142,7 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, const struct SASLproto *params) { - unsigned long auth = data->set.httpauth; + uint32_t auth = data->set.httpauth; sasl->params = params; /* Set protocol dependent parameters */ sasl->state = SASL_STOP; /* Not yet running */ diff --git a/lib/multi.c b/lib/multi.c index b23595f21f09..fbefa6f30d91 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1403,7 +1403,7 @@ static CURLMcode multi_winsock_select(struct Curl_multi *multi, reset_socket_fdwrite(cpfds->pfds[i].fd); } if(mask) { - if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) { + if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask)) { mresult = CURLM_OUT_OF_MEMORY; goto out; } diff --git a/lib/telnet.c b/lib/telnet.c index d135477c2064..735fa66156fd 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -1267,7 +1267,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* Tell Winsock what events we want to listen to */ - if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) != 0) { + if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE)) { WSACloseEvent(event_handle); return CURLE_RECV_ERROR; } diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 1ece58359516..26b44f8438cd 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -327,7 +327,8 @@ CURLcode globalconf_init(void) if(global->first) { /* Perform the libcurl initialization */ #ifdef CURL_DEBUG_GLOBAL_MEM - result = curl_global_init_mem(CURL_GLOBAL_ALL, custom_malloc, custom_free, + result = curl_global_init_mem(CURL_GLOBAL_DEFAULT, + custom_malloc, custom_free, custom_realloc, custom_strdup, custom_calloc); #else diff --git a/tests/libtest/lib1507.c b/tests/libtest/lib1507.c index d125a4975bc9..c63be0d5f8a9 100644 --- a/tests/libtest/lib1507.c +++ b/tests/libtest/lib1507.c @@ -43,7 +43,7 @@ static CURLcode test_lib1507(const char *URL) struct curltime mp_start; struct curl_slist *rcpt_list = NULL; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); easy_init(curl); diff --git a/tests/libtest/lib1678.c b/tests/libtest/lib1678.c index 144cc9977a3f..a2d43c02e0e0 100644 --- a/tests/libtest/lib1678.c +++ b/tests/libtest/lib1678.c @@ -110,7 +110,7 @@ static CURLcode test_lib1678(const char *URL) if(!packet) goto test_cleanup; - result = curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); if(result != CURLE_OK) goto test_cleanup; diff --git a/tests/libtest/lib1907.c b/tests/libtest/lib1907.c index 3b91d846f739..c6045bf327db 100644 --- a/tests/libtest/lib1907.c +++ b/tests/libtest/lib1907.c @@ -30,7 +30,7 @@ static CURLcode test_lib1907(const char *URL) CURLcode result = CURLE_OK; char error_buffer[CURL_ERROR_SIZE] = ""; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, URL); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer); diff --git a/tests/libtest/lib1939.c b/tests/libtest/lib1939.c index 5445251df338..4b78a2f5621c 100644 --- a/tests/libtest/lib1939.c +++ b/tests/libtest/lib1939.c @@ -29,7 +29,7 @@ static CURLcode test_lib1939(const char *URL) CURL *curl; int running_handles; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); multi = curl_multi_init(); if(multi) { diff --git a/tests/libtest/lib1940.c b/tests/libtest/lib1940.c index 8fbafd5fedcd..400d044f3af6 100644 --- a/tests/libtest/lib1940.c +++ b/tests/libtest/lib1940.c @@ -120,7 +120,7 @@ static CURLcode test_lib1940(const char *URL) header_request = -1; } - global_init(CURL_GLOBAL_DEFAULT); + global_init(CURL_GLOBAL_ALL); easy_init(curl); easy_setopt(curl, CURLOPT_URL, URL); easy_setopt(curl, CURLOPT_VERBOSE, 1L); diff --git a/tests/libtest/lib1945.c b/tests/libtest/lib1945.c index e76a5dc528a8..4ca3d160c831 100644 --- a/tests/libtest/lib1945.c +++ b/tests/libtest/lib1945.c @@ -41,7 +41,7 @@ static CURLcode test_lib1945(const char *URL) CURL *curl; CURLcode result = CURLE_OK; - global_init(CURL_GLOBAL_DEFAULT); + global_init(CURL_GLOBAL_ALL); easy_init(curl); curl_easy_setopt(curl, CURLOPT_URL, URL); diff --git a/tests/libtest/lib1947.c b/tests/libtest/lib1947.c index a4abb497caa6..342d2f60ac85 100644 --- a/tests/libtest/lib1947.c +++ b/tests/libtest/lib1947.c @@ -31,7 +31,7 @@ static CURLcode test_lib1947(const char *URL) int count = 0; unsigned int origins; - global_init(CURL_GLOBAL_DEFAULT); + global_init(CURL_GLOBAL_ALL); easy_init(curl); diff --git a/tests/libtest/lib1948.c b/tests/libtest/lib1948.c index 73bd5d7ba0f1..95152aa1ab9d 100644 --- a/tests/libtest/lib1948.c +++ b/tests/libtest/lib1948.c @@ -46,7 +46,7 @@ static CURLcode test_lib1948(const char *URL) static const char testput[] = "This is test PUT data\n"; struct put_buffer pbuf; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); easy_init(curl); diff --git a/tests/libtest/lib1985.c b/tests/libtest/lib1985.c index 3790023a1e92..256e57c9cd50 100644 --- a/tests/libtest/lib1985.c +++ b/tests/libtest/lib1985.c @@ -33,7 +33,7 @@ static CURLcode test_lib1985(const char *URL) struct curl_slist *slist = NULL; struct curl_slist *slist2 = NULL; CURLcode r1 = CURLE_OK, r2 = CURLE_OK; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); curl_msnprintf(host1, sizeof(host1), "firsthost:%s:%s", libtest_arg3, libtest_arg4); diff --git a/tests/libtest/lib590.c b/tests/libtest/lib590.c index 88614c9c3f45..cc424aba0486 100644 --- a/tests/libtest/lib590.c +++ b/tests/libtest/lib590.c @@ -70,7 +70,7 @@ static CURLcode test_lib590(const char *URL) goto test_cleanup; result = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_USED, &usedauth); - if(CURLAUTH_NTLM != usedauth) { + if(usedauth != CURLAUTH_NTLM) { curl_mprintf("CURLINFO_PROXYAUTH_USED did not say NTLM\n"); } diff --git a/tests/libtest/lib677.c b/tests/libtest/lib677.c index 6a6c5ef5a923..3b90cc4d83a3 100644 --- a/tests/libtest/lib677.c +++ b/tests/libtest/lib677.c @@ -36,7 +36,7 @@ static CURLcode test_lib677(const char *URL) ssize_t pos = 0; CURLcode result = CURLE_OK; - global_init(CURL_GLOBAL_DEFAULT); + global_init(CURL_GLOBAL_ALL); multi_init(mcurl); easy_init(curl); diff --git a/tests/libtest/lib678.c b/tests/libtest/lib678.c index 90295f42a8d6..ef04609e5c9a 100644 --- a/tests/libtest/lib678.c +++ b/tests/libtest/lib678.c @@ -93,7 +93,7 @@ static CURLcode test_cert_blob(const char *url, const char *cafile) static CURLcode test_lib678(const char *URL) { CURLcode result = CURLE_OK; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); if(!strcmp("check", URL)) { CURLcode w = CURLE_OK; struct curl_blob blob = { CURL_UNCONST("silly"), 5, 0 }; diff --git a/tests/libtest/lib694.c b/tests/libtest/lib694.c index 1b2c6e2d8bb2..5bb212067ac0 100644 --- a/tests/libtest/lib694.c +++ b/tests/libtest/lib694.c @@ -58,7 +58,7 @@ static CURLcode test_lib694(const char *URL) result = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_USED, &usedauth); if(result) goto test_cleanup; - if(CURLAUTH_NTLM != usedauth) { + if(usedauth != CURLAUTH_NTLM) { curl_mprintf("CURLINFO_HTTPAUTH_USED did not say NTLM\n"); } diff --git a/tests/libtest/lib751.c b/tests/libtest/lib751.c index cc140144162e..55f62d868eca 100644 --- a/tests/libtest/lib751.c +++ b/tests/libtest/lib751.c @@ -38,7 +38,7 @@ static CURLcode test_lib751(const char *URL) (void)URL; memset(curls, 0, sizeof(curls)); - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); multi = curl_multi_init(); if(!multi) { result = CURLE_OUT_OF_MEMORY; diff --git a/tests/libtest/lib753.c b/tests/libtest/lib753.c index 6260afb6bc87..cf83f969e7fd 100644 --- a/tests/libtest/lib753.c +++ b/tests/libtest/lib753.c @@ -109,7 +109,7 @@ static CURLcode test_lib753(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - curl_global_init(CURL_GLOBAL_DEFAULT); + curl_global_init(CURL_GLOBAL_ALL); curl_mfprintf(stderr, "init multi\n"); multi = curl_multi_init(); diff --git a/tests/perf/urlparser.c b/tests/perf/urlparser.c index 696e98422b6d..934e6770809d 100644 --- a/tests/perf/urlparser.c +++ b/tests/perf/urlparser.c @@ -28,7 +28,7 @@ #pragma GCC diagnostic ignored "-Woverlength-strings" #endif -static const char *urls[] = { +static const char * const urls[] = { "https://apps.fruit.zom:5555/us/app/reality-become-an-anime-a" "vatar/id1404176564", "https://bugzilla.mozilla.ork/first/../home", diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index ff962f925fae..cdaff07494b3 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -706,8 +706,7 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, if(wsaevents.lNetworkEvents & FD_WRITE) { swrite(wsasock, NULL, 0); /* reset FD_WRITE */ } - if(WSAEventSelect(wsasock, wsaevent, wsaevents.lNetworkEvents) - == 0) { + if(!WSAEventSelect(wsasock, wsaevent, wsaevents.lNetworkEvents)) { handles[nfd] = (HANDLE)wsaevent; data[nws].wsasock = wsasock; data[nws].wsaevent = wsaevent; From 5e38120ddc9896f33e63bd75d91a9c470c2a576a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Sep 2026 17:42:53 +0200 Subject: [PATCH 1058/1062] doh: drop assert from doh_resp_decode_httpsrr() When the incoming DNS packet is broken, there might be bytes left at the end that the parser ignores so let's not assert on that. Reported-by: Max Dymond Closes #22785 --- lib/vdns/doh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/vdns/doh.c b/lib/vdns/doh.c index 3b89cb9620e3..3b52ed92f362 100644 --- a/lib/vdns/doh.c +++ b/lib/vdns/doh.c @@ -998,7 +998,6 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, len -= plen; expected_min_pcode = pcode + 1; } - DEBUGASSERT(!len); *hrr = lhrr; return CURLE_OK; err: From d7385ed015343355a01105ac9e67836a0fd23912 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 2 Sep 2026 01:20:40 +0200 Subject: [PATCH 1059/1062] clang-tidy: enable check `bugprone-assignment-in-selection-statement` Supported by clang-tidy 23.1.0+. Ref: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/assignment-in-selection-statement.html Closes #22792 --- .clang-tidy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-tidy.yml b/.clang-tidy.yml index d772e0fb8445..3ea2c6a88a73 100644 --- a/.clang-tidy.yml +++ b/.clang-tidy.yml @@ -14,6 +14,7 @@ Checks: - -clang-diagnostic-nullability-extension - bugprone-assert-side-effect - bugprone-assignment-in-if-condition + - bugprone-assignment-in-selection-statement - bugprone-chained-comparison - bugprone-dynamic-static-initializers - bugprone-invalid-enum-default-initialization From 2b4d88aa5e435d4e1ee1a230ea6f8923f78a8f47 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Sep 2026 07:54:11 +0200 Subject: [PATCH 1060/1062] THANKS: contributors from the 8.22.0 release --- docs/THANKS | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/THANKS b/docs/THANKS index 59ed8abba81f..36a75f3de390 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -11,6 +11,7 @@ 12932 on github 1337vt on github 1ocalhost on github +1rhino2 on hackerone 3dyd on github 3eka on github 4lan.m @@ -27,6 +28,7 @@ Abdullah Alyan Abhinav Singh Abhinav Singhal Abram Pousada +accl on hackerone accountantM on github AceCrow on github ad0p on github @@ -73,7 +75,9 @@ Alain Miniussi Alan Coopersmith Alan De Smet Alan Jenkins +AlanKingPL Alan Pinstein +Alb3e3 Albert Chin-A-Young Albert Choy Alberto Leiva Popper @@ -127,6 +131,7 @@ Alexey Eremikhin Alexey Larikov Alexey Melnichuk Alexey Pesternikov +Alexey Samsonov Alexey Savchuk Alexey Simak Alexey Zakhlestin @@ -156,6 +161,7 @@ Alex Vinnik Alex Xu Alfonso Martone Alfred Gebert +Alhuda Khan alhudz Alice Lee Poetics alienowo on hackerone @@ -285,17 +291,20 @@ Anton Gerasimov Antonio Larrosa Antoni Villalonga Anton Kalmykov +Anton Karpov Anton Malov Antony74 on github Anton Yabchinskiy Antti Hätälä antypanty on hackerone Anubhav Rai +anupamme apparentorder on github April King Aquila Macedo arainchik on github Archangel_SDY on github +Arham Wani Arian van Putten Aritra Basu Arjan van de Ven @@ -325,6 +334,7 @@ Askar Safin Ask Bjørn Hansen AtariDreams on github Ates Goral +Athos Ribeiro atjg on github Augment code Augustus Saunders @@ -412,6 +422,7 @@ Bertrand Simonnet beslick5 on github Bevan Weiss Bhanu Prakash +Bigtang on hackerone Bill Doyle Bill Egert Bill Hoffman @@ -549,6 +560,7 @@ Carsten Lange Casey Bodley Casey O'Donnell Catalin Patulea +CatboxParadox Catena cyber causal-agent on github cbartl on github @@ -604,6 +616,7 @@ Christian Robottom Reis Christian Schmitz Christian Schmitza Christian Stewart +Christian Ullrich Christian Vogt Christian Weisgerber Christophe Demory @@ -633,6 +646,7 @@ Ciprian Badescu civodul on github Claes Jakobsson Clarence Gardner +claudex on github Claudio Neves claudiusaiz on github clbr on github @@ -660,6 +674,7 @@ Colin Leroy Colin Leroy-Mira Colin O'Dell Colin Watson +Collin Funk Colman Mbuya Colm Buckley Colton Willey @@ -687,6 +702,7 @@ CueXXIII on github curl.stunt430 Curt Bogmine Cutiapreta on hackerone +cybertron10 on github Cynthia Coan Cyril B Cyrill Osterwalder @@ -979,6 +995,7 @@ Ebe Janchivdorj ebejan on github Ebenezer Ikonne ed0d2b2ce19451f2 +ed0d2b2ce19451f2 on github Eddie Lumpkin Edgaras Janušauskas Edin Kadribasic @@ -1028,6 +1045,7 @@ Emil Lerner Emil Österlund Emil Romanus Emmanuel Tychon +Emmanuel Ugwu Emre Çalışkan Enno Boland Enrico Scholz @@ -1126,6 +1144,7 @@ feelingseas on github FeignClaims on github Feist Josselin Felipe Gasper +felix h Felix Hädicke Felix Kaiser Felix von Leitner @@ -1139,6 +1158,7 @@ Filip Salomonsson finkjsc on github Fiona Klute Firefox OS +firexinghe on github Fizn-Ahmd on github fjaell on github Flameborn on github @@ -1177,6 +1197,7 @@ Frazer Smith Frederic Lepied Frederik B Frederik Wedel-Heinen +Fred Klassen Fred Machado Fred New Fred Noz @@ -1244,6 +1265,7 @@ Giuseppe Attardi Giuseppe D'Ambrosio Giuseppe Persico gkarracer on github +GLaDOS-418 on github Gleb Ivanovsky Glen A Johnson Jr. Glen Nakamura @@ -1347,6 +1369,7 @@ Helmut Grohne Helmut K. C. Tessarek Helwing Lutz Hem Parekh +Hendrik Hübner Hendrik Visage Henning Schild Henri Gomez @@ -1392,6 +1415,7 @@ Hunt Darlener Huseyin Tintas Huzaifa Sidhpurwala huzunhao on github +HwangRock hydra3333 on github Hzhijun iammrtau on github @@ -1413,6 +1437,7 @@ Ignat Loskutov Igor Franchuk Igor Khristophorov Igor Makarov +Igor Morgenstern Igor Novoseltsev Igor Polyakov Igor Todorovski @@ -1447,6 +1472,7 @@ Ishan SinghLevett İsmail Dönmez Itay Bookstein Ithubg on github +itzTanos29 Ivan Ivan Avdeev ivanfywang @@ -1457,6 +1483,7 @@ Ivo Bellin Salarin iz8mbw on github Izan on hackerone Jacek Migacz +Jace Laquerre Jackarain on github JackBoosY on github Jack Boos Yu @@ -1540,6 +1567,7 @@ Javier Blazquez Javier G. Sogo Javier Navarro Javier Sixto +Jayanth Vennamreddy Jay Austin Jay Dommaschk Jayesh A Shah @@ -1711,6 +1739,7 @@ John Simpson John Starks John Suprock John V. Chow +John Verne John Walker John Wanghui John Weismiller @@ -1863,6 +1892,7 @@ Keith McGuigan Keith Mok Kelly Kaoudis Ken Brown +Keng-Yu Lin Ken Hirsch Kenneth Davidson Kenneth Myhra @@ -1887,6 +1917,7 @@ Kevin Smith Kevin Sun Kevin Ushey Kev Jackson +Kieran Gannon Kim Minjoong Kimmo Kinnunen Kim Rinnewitz @@ -1970,6 +2001,7 @@ Laurent Bonnans Laurent Dufresne Laurențiu Nicola Laurent Rabret +Laurent Sabourin Laurie Clark-Michalek Lauri Kasanen Lawrence Gripper @@ -2098,6 +2130,7 @@ Mandy Wu Manfred Schwarb Manuel Einfalt Manuel Massing +Manuel Sánchez-Guijarro Manuel Strehl Manuj Bhatia Marc Aldorasi @@ -2106,6 +2139,7 @@ Marc Boucher Marc Deslauriers Marc Doughty Marcel Hernandez +Marcel Jamin Marcel Lang Marcelo Echeverria Marcelo Juchem @@ -2121,6 +2155,7 @@ Marcin Rataj Marc Kleine-Budde Marco Deckel Marco G. Salvagno +marco-jardim Marco Kamner Marco Maggi Marcos Diazr @@ -2183,9 +2218,11 @@ Martin D'Aloia Martin Dorey Martin Drasar Martin Dreher +Martin Dukek Martin Dürrmeier martinevsky Martin Frodl +martin-fzi on github Martin Galvan Martin Gartner Martin Hager @@ -2238,6 +2275,7 @@ Matt Ford Matthew Blain Matthew Clarke Matthew Hall +Matthew John Cheetham Matthew Kerwin Matthew Thompson Matthew Whitehead @@ -2287,6 +2325,7 @@ Mekonikum Melissa Mears Melroy van den Berg Mel Zuser +Memduh Çelik Mert Yazıcıoğlu Mettgut Jamalla Micah Snyder @@ -2652,6 +2691,7 @@ Pavel P Pavel Pavlov Pavel Raiskup Pavel Rochnyak +Pavel Sobolev Pavel Volgarev Pavol Markovic Pawel A. Gajda @@ -2806,11 +2846,13 @@ Rajesh Naganathan Rajkumar Mandal Ralf A. Timmermann ralfjunker on github +Ralf Mueller Ralf S. Engelschall Ralph Beckmann Ralph Langendam Ralph Mitchell Ralph Sennhauser +Ramesh Adhikari Ramiro Garcia Ram Krushna Mishra rampageX on github @@ -2822,6 +2864,7 @@ Randy Armstrong Randy McMurchy Ran Mozes Raphael Gozzo +Rarylson Freitas r-a-sattarov on github Rasmus Melchior Jacobsen Rasmus Thomsen @@ -2910,8 +2953,10 @@ Rikard Falkeborn rilysh Rinku Das rinsuki on github +Rito Rhymes rl1987 on github rmg-x on github +RMMoreton on github rm-rmonaghan on github Rob Boeckermann RobBotic1 on github @@ -2978,6 +3023,7 @@ Romulo A. Ceccon Ronald Crane Ronan Pigott Ron Eldor +Ron Kuper Ronnie Mose Ron Parker Ron Zapp @@ -3024,6 +3070,7 @@ Salvador Dávila Salvatore Sorrentino Samanta Navarro Sam Deane +Sameeh Jubran Sam Hurst Sam James Sam Jessup @@ -3032,6 +3079,7 @@ Sampo Kellomaki Sam Roth Sam Schanken Samuel Chiang +Samuel Dainard Samuel Díaz García Samuel Dionne-Riel Samuel Henrique @@ -3075,6 +3123,7 @@ Scott McCreary Scott Mutter Scott Talbert sd0 on hackerone +sdgh179 on github Sean Boudreau Sean Burford Sean MacLennan @@ -3098,6 +3147,7 @@ SendSonS on github Senthil Raja Velu Sergei Kuzmin Sergei Nikulov +Sergei Zimmerman Sergey Sergey Alirzaev Sergey Bronnikov @@ -3170,6 +3220,7 @@ Siva Sivaraman Slaven Rezić SLDiggie on github Smackd0wn on github +smaeljaish on hackerone S. Moonesamy smuellerDD on github sn on hackerone @@ -3236,6 +3287,7 @@ Stephan Lagerholm Stephan Mühlstrasser stephannn on github Stephan Szabo +Stephan Zeisberg Stephen Boost Stephen Brokenshire Stephen Collyer @@ -3269,6 +3321,7 @@ Stian Soiland-Reyes Stoned Elipot stootill on github Stuart Henderson +stze on hackerone Sukanya Hanumanthu SumatraPeter on github Sune Ahlgren @@ -3331,6 +3384,7 @@ Thomas Thomas1664 on github Thomas Bouzerar Thomas Braun +Thomas Chauchefoin Thomas Danielsson Thomas Ferguson Thomas Gamper @@ -3691,6 +3745,7 @@ YX Hao z2_ Zachary Seguin Zartaj Majeed +zaveshaa Zdenek Pavlas Zekun Ni zelinchen on github From cfce7bdaf819628c0351dc9c5bd8687d3cdd6ac3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Sep 2026 07:55:01 +0200 Subject: [PATCH 1061/1062] VERSIONS: add 8.22.0 and the pending next release --- docs/VERSIONS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 68842b941693..c5db67b75881 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -68,7 +68,8 @@ dates. The tool was called `httpget` before 2.0, `urlget` before 4.0 then `curl` since 4.0. `libcurl` and `curl` are always released in sync, using the same version numbers. -- 8.22.0: pending +- 8.23.0: pending +- 8.22.0: September 2, 2026 - 8.21.0: June 24 2026 - 8.20.0: April 29 2026 - 8.19.0: March 11 2026 From 01346829096c61b372692f6dc43ffa778c6caccd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Sep 2026 07:50:17 +0200 Subject: [PATCH 1062/1062] RELEASE-NOTES: synced --- RELEASE-NOTES | 78 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e05154eefb2d..8342fec4564d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,8 +4,8 @@ curl and libcurl 8.22.0 Command line options: 278 curl_easy_setopt() options: 312 Public functions in libcurl: 100 - Authors: 1515 - Contributors: 3782 + Authors: 1518 + Contributors: 3786 This release includes the following changes: @@ -36,8 +36,10 @@ This release includes the following bugfixes: o cd2nroff: stricter checks for asterisks for italics [73] o cf-ngtcp2-cmn: de-duplicate `ngtcp2_conn_client_new()` call code [156] o cf-ngtcp2-cmn: initialize new callback ptr for ngtcp2 1.24.0+ [52] + o cf-socket: avoid broken NetBSD SOCK_NONBLOCK [275] o cf-socket: disable TCP SYN retransmissions for localhost on Windows [164] o cfilters: fix event-based connection shutdown [91] + o clock: save one call [286] o cmake/FindLibgsasl: fix to set `LIBGSASL_VERSION` with pkg-config detection [229] o cmake: check libgsasl version at configure time [277] o cmake: dedupe expressions into local vars in `cmake_uninstall.in.cmake` [9] @@ -62,6 +64,7 @@ This release includes the following bugfixes: o conncache: conn upkeep/alive: move and enhance [152] o conncache: connection alive checks intervals [20] o conncache: don't assume curl_off_t increment wrap-around [138] + o conncache: guess maxconnects different [289] o connect: connection close tweaks [112] o connect: only set connect timer on first socket [206] o connection reuse: age check [261] @@ -69,6 +72,7 @@ This release includes the following bugfixes: o connections: use admin handles only for maintenance [213] o content_encoding: exact-match the identity transfer-coding token [189] o content_encoding: give a clear error on multi-member gzip [46] + o cookie: cookies set for an exact PSL domain is host-only [304] o cookie: improve TAB handling [258] o cookie: refuse to load cookies set against a PSL domain [139] o CREDENTIALS.md: remove comment about empty user/pass [50] @@ -83,11 +87,13 @@ This release includes the following bugfixes: o CURLOPT_SOCKOPTFUNCTION.md: ALREADY_CONNECTED does not work for HTTP/3 [262] o CURLOPT_SSH_*_KEYFILE: used for setting up, then no more [48] o CURLOPT_UNRESTRICTED_AUTH.md: 'Authorization', not 'Authentication' [74] + o CURLOPT_USERNAME.md: ambient username caveats [271] o CURLSHOPT_(UN)SHARE.md: do not modify shares while in use [44] o curlx_inet_ntop: return `CURLcode`, drop setting `errno` [237] o curlx_inet_pton: drop setting `errno` on error [236] o DEPRECATE.md: HTTP/2 Server Push gets removed in March 2027 [174] o dict: avoid busy-loop in sendf() when the socket is not writable [99] + o dist: fix to drop test bundle .c files from the source tarball [305] o dnsd: fix bounds check in `read_https_alpn_part()` [143] o docs/INTERNALS.md -> docs/DEPENDENCIES.md [127] o docs: clarify that cookies need domain set to match [224] @@ -96,12 +102,15 @@ This release includes the following bugfixes: o docs: mention possible auth option conflicts [114] o docs: remove doubled word in SECURITY-ADVISORY.md [183] o DoH: improvements [203] + o easy: fix unused global on non-Windows [292] + o easy_lock: silence `portability-no-assembler` with clang-tidy 23.1.0+ [291] o FAQ: correct an option typo [278] o file: support directory listing on Windows [205] o filter: change time reporting [235] o FTP: fix TLS session reuse on the data connection [80] o ftp: reject control bytes in ACCT and alternative-to-user [26] o gitignore: maintenance updates [170] + o gopher: fix partial sends of CRLF [288] o gopher: reject CR and LF in the selector [1] o h2 push: use squeaky clean easy handle [246] o h2: bootstrap max streams from multi handle if in use [132] @@ -109,12 +118,14 @@ This release includes the following bugfixes: o Happy Eyeballing v3: resolution delay of 25ms [232] o header api: add guards [168] o headers: name the arguments the way the definitions name them [234] + o HISTORY.md: PSL support in 2015 o HISTORY: add when c-ares support was introduced (2004) o HISTORY: September 1999: started using CVS o hostip: only cache negative resolves for authoritative answers [16] o hsts: only match the exact strings [269] o http digest: tie peer/credentials on input [264] o http2: make server push transfers inherit share from parent [81] + o http2: remove assert in ingress processing [272] o http: avoid length underflow in Curl_compareheader [78] o http: custom Authorization: header overrides Negotiate [223] o http: fix non-tunneling proxy hostname use [116] @@ -123,6 +134,7 @@ This release includes the following bugfixes: o httpsrr: DoH with HTTPS, fix response handling [113] o idn: restore `MultiByteToWideChar()` `MB_ERR_INVALID_CHARS` flag [103] o imap: APPEND CRLF fix [256] + o include: include when building for modern Linux. [308] o INSTALL.md: add building-from-source overview section [29] o INTERNALS.md: require quiche 0.20.0+ [101] o ipv6 scope_id: set from first peer [242] @@ -135,6 +147,7 @@ This release includes the following bugfixes: o lib2405: adjust for non-threaded builds [149] o lib: add "Curl_" prefix to two global functions [84] o lib: add multi_wakeup_internal [86] + o lib: drop unused `system_win32.h` includes [290] o lib: fix 'ns' -> 'us' in trace messages [57] o lib: new easy option string storage [215] o lib: optimize struct layouts for reduced memory usage [212] @@ -174,6 +187,7 @@ This release includes the following bugfixes: o openssl+sectrust: fix session reuse [4] o openssl+sectrust: move session verified set into result check [82] o openssl: avoid conn reuse if provider is used [214] + o openssl: avoid strlen() on the data from OpenSSL [280] o openssl: aws-lc ocsp workaround [263] o openssl: drop unused pre-OpenSSL3 `ctx_option_t` typedef [8] o openssl: fix DER buffer leak in Apple SecTrust verification [217] @@ -195,7 +209,9 @@ This release includes the following bugfixes: o runtests: flush cached test parts when (re)loading a file [95] o runtests: restore `-k` option and actively process as no-op [32] o sasl: fix zero-length response encoding [36] + o schannel: add ALPN support for mingw-w64 <9 and =128-bit pointers [107] @@ -289,7 +313,9 @@ This release includes the following bugfixes: o vtls: move 'native_ca_store' ssl_config_data => ssl_primary_config [211] o vtls_scache: use case sensitive path match o VULN-DISCLOSURE-POLICY.md: issues that should be found by tests are LOW [5] + o wcurl: import v2026.08.30 [279] o websocket: pause writing and meta data fix [135] + o winsock: drop redundant version checks at initialization [284] o wolfssl: do not run Curl_wssl_setup_x509_store() twice [265] o wolfssl: fix build for wolfssl without bio chain support [75] o ws: fix write callback error handling [204] @@ -316,16 +342,17 @@ This release would not have looked like this without help, code, reports and advice from friends like these: 11soda11, 1rhino2 on hackerone, accl on hackerone, AlanKingPL, Alb3e3, - Alhuda Khan, Anton Karpov, anupamme, Arham Wani, Athos Ribeiro, - Bartel Sielski, Bigtang on hackerone, Bill Mill, Bryan Henderson, - Carlos Henrique Lima Melara, CatboxParadox, Christian Ullrich, - Christoph Reiter, claudex on github, Collin Funk, cybertron10 on github, - Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, dependabot[bot], - ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, felix h, - firexinghe on github, Fred Klassen, GLaDOS-418 on github, Graham Campbell, - Hendrik Hübner, HwangRock, Igor Morgenstern, itzTanos29, Jace Laquerre, - Jayanth Vennamreddy, Joel Depooter, Johannes Schindelin, Keng-Yu Lin, - Kieran Gannon, kit-ty-kate on github, Laurent Sabourin, Marcel Jamin, + Alexey Samsonov, Alhuda Khan, Anton Karpov, anupamme, Arham Wani, + Athos Ribeiro, Bartel Sielski, Bigtang on hackerone, Bill Mill, + Bryan Henderson, Carlos Henrique Lima Melara, CatboxParadox, + Christian Ullrich, Christoph Reiter, claudex on github, Collin Funk, + cybertron10 on github, Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, + dependabot[bot], ed0d2b2ce19451f2 on github, Emmanuel Ugwu, Eunsoo Kim, + felix h, firexinghe on github, Fred Klassen, GLaDOS-418 on github, + Graham Campbell, Hendrik Hübner, HwangRock, Igor Morgenstern, itzTanos29, + Jace Laquerre, Jayanth Vennamreddy, Joel Depooter, Johannes Schindelin, + John Verne, Keng-Yu Lin, Kieran Gannon, kit-ty-kate on github, + Laurent Sabourin, Manuel Sánchez-Guijarro, Marcel Jamin, marco-jardim, Martin Dukek, martin-fzi on github, Matthew John Cheetham, Max Dymond, Memduh Çelik, Patrick Monnerat, Pavel Sobolev, pszemus on github, Ralf Mueller, Ramesh Adhikari, Rarylson Freitas, Ray Satiro, renovate[bot], @@ -334,7 +361,7 @@ advice from friends like these: sdgh179 on github, Sergei Zimmerman, smaeljaish on hackerone, Stanislav Fort, Stefan Eissing, Stephan Zeisberg, stze on hackerone, Thomas Chauchefoin, Viktor Szakats, xmoezzz on github, Yoshiro Yoneya, zaveshaa - (81 contributors) + (85 contributors) References to bug reports and discussions on issues: @@ -590,6 +617,7 @@ References to bug reports and discussions on issues: [250] = https://curl.se/bug/?i=22706 [251] = https://curl.se/bug/?i=22708 [252] = https://curl.se/bug/?i=22690 + [253] = https://curl.se/bug/?i=22774 [254] = https://curl.se/bug/?i=22709 [255] = https://curl.se/bug/?i=22705 [256] = https://curl.se/bug/?i=22702 @@ -605,7 +633,31 @@ References to bug reports and discussions on issues: [268] = https://curl.se/bug/?i=22687 [269] = https://curl.se/bug/?i=22643 [270] = https://curl.se/bug/?i=22681 + [271] = https://curl.se/bug/?i=22768 + [272] = https://curl.se/bug/?i=22776 [273] = https://curl.se/bug/?i=22685 + [274] = https://curl.se/bug/?i=22682 + [275] = https://curl.se/bug/?i=22586 [276] = https://curl.se/bug/?i=22669 [277] = https://curl.se/bug/?i=22667 [278] = https://curl.se/bug/?i=22679 + [279] = https://curl.se/bug/?i=22758 + [280] = https://curl.se/bug/?i=22767 + [281] = https://curl.se/bug/?i=22763 + [282] = https://curl.se/bug/?i=22739 + [284] = https://curl.se/bug/?i=22754 + [286] = https://curl.se/bug/?i=22761 + [288] = https://curl.se/bug/?i=22714 + [289] = https://curl.se/bug/?i=22759 + [290] = https://curl.se/bug/?i=22757 + [291] = https://curl.se/bug/?i=22751 + [292] = https://curl.se/bug/?i=22750 + [295] = https://curl.se/bug/?i=22739 + [296] = https://curl.se/bug/?i=22738 + [297] = https://curl.se/bug/?i=22737 + [301] = https://curl.se/bug/?i=22726 + [304] = https://curl.se/bug/?i=22730 + [305] = https://curl.se/bug/?i=22686 + [306] = https://curl.se/bug/?i=22725 + [307] = https://curl.se/bug/?i=22716 + [308] = https://curl.se/bug/?i=22635