From ddb30354f68038629290688f4c5f58fef283727b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Wed, 29 Apr 2026 21:51:43 +0200 Subject: [PATCH 001/537] 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 002/537] 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 003/537] 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 004/537] 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 005/537] 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 006/537] 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 007/537] 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 008/537] 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 009/537] 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 010/537] 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 011/537] 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 012/537] 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 013/537] 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 014/537] 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 015/537] 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 016/537] 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 017/537] 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 018/537] 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 019/537] 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 020/537] 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 021/537] 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 022/537] 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 023/537] 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 024/537] 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 025/537] 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 026/537] 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 027/537] 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 028/537] 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 029/537] 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 030/537] 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 031/537] 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 032/537] 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 033/537] 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 034/537] 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 035/537] 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 036/537] 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 037/537] 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 038/537] 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 039/537] 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 040/537] 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 041/537] 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 042/537] 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 043/537] 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 044/537] 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 045/537] 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 046/537] 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 047/537] 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 048/537] 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 049/537] 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 050/537] 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 051/537] 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 052/537] 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 053/537] 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 054/537] 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 055/537] 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 056/537] 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 057/537] 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 058/537] 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 059/537] 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 060/537] 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 061/537] 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 062/537] 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 063/537] 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 064/537] 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 065/537] 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 066/537] 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 067/537] 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 068/537] 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 069/537] 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 070/537] 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 071/537] 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 072/537] 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 073/537] 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 074/537] 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 075/537] 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 076/537] 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 077/537] 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 078/537] 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 079/537] 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 080/537] 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 081/537] 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 082/537] 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 083/537] 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 084/537] 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 085/537] 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 086/537] 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 087/537] 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 088/537] 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 089/537] 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 090/537] 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 091/537] 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 092/537] 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 093/537] 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 094/537] 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 095/537] 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 096/537] 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 097/537] 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 098/537] 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 099/537] 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 100/537] 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 101/537] 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 102/537] 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 103/537] 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 104/537] 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 105/537] 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 106/537] 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 107/537] 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 108/537] 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 109/537] 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 110/537] 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 111/537] 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 112/537] 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 113/537] 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 114/537] 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 115/537] 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 116/537] 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 117/537] 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 118/537] 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 119/537] 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 120/537] 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 121/537] 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 122/537] 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 123/537] 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 124/537] 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 125/537] 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 126/537] 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 127/537] 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 128/537] 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 129/537] 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 130/537] 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 131/537] 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 132/537] 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 133/537] 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 134/537] 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 135/537] 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 136/537] 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 137/537] 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 138/537] 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 139/537] 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 140/537] 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 141/537] 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 142/537] 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 143/537] 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 144/537] 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 145/537] 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 146/537] 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 147/537] 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 148/537] 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 149/537] 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 150/537] 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 151/537] 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 152/537] 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 153/537] 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 154/537] 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 155/537] 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 156/537] 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 157/537] 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 158/537] 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 159/537] 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 160/537] 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 161/537] 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 162/537] 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 163/537] 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 164/537] 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 165/537] 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 166/537] 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 167/537] 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 168/537] 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 169/537] 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 170/537] 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 171/537] 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 172/537] 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 173/537] 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 174/537] 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 175/537] 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 176/537] 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 177/537] 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 178/537] 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 179/537] 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 180/537] 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 181/537] 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 182/537] 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 183/537] 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 184/537] 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 185/537] 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 186/537] 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 187/537] 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 188/537] 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 189/537] 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 190/537] 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 191/537] 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 192/537] 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 193/537] 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 194/537] 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 195/537] 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 196/537] 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 197/537] 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 198/537] 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 199/537] 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 200/537] 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 201/537] 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 202/537] 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 203/537] 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 204/537] 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 205/537] 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 206/537] 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 207/537] 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 208/537] 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 209/537] 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 210/537] 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 211/537] 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 212/537] 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 213/537] 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 214/537] 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 215/537] 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 216/537] 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 217/537] 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 218/537] 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 219/537] 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 220/537] 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 221/537] 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 222/537] 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 223/537] 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 224/537] 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 225/537] 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 226/537] 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 227/537] 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 228/537] 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 229/537] 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 230/537] 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 231/537] 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 232/537] 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 233/537] 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 234/537] 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 235/537] 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 236/537] 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 237/537] 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 238/537] 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 239/537] 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 240/537] 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 241/537] 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 242/537] 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 243/537] 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 244/537] 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 245/537] 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 246/537] 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 247/537] 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 248/537] 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 249/537] 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 250/537] 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 251/537] 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 252/537] 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 253/537] 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 254/537] 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 255/537] 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 256/537] 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 257/537] 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 258/537] 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 259/537] 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 260/537] 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 261/537] 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 262/537] 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 263/537] 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 264/537] 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 265/537] 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 266/537] 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 267/537] 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 268/537] 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 269/537] 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 270/537] 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 271/537] 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 272/537] 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 273/537] 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 274/537] 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 275/537] 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 276/537] 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 277/537] 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 278/537] 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 279/537] 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 280/537] 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 281/537] 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 282/537] 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 283/537] 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 284/537] 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 285/537] 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 286/537] 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 287/537] 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 288/537] 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 289/537] 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 290/537] 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 291/537] 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 292/537] 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 293/537] 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 294/537] 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 295/537] 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 296/537] 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 297/537] 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 298/537] 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 299/537] 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 300/537] 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 301/537] 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 302/537] 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 303/537] 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 304/537] 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 305/537] 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 306/537] 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 307/537] 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 308/537] 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 309/537] 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 310/537] 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 311/537] 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 312/537] 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 313/537] 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 314/537] 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 315/537] 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 316/537] 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 317/537] 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 318/537] 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 319/537] 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 320/537] 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 321/537] 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 322/537] 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 323/537] 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 324/537] 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 325/537] 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 326/537] 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 327/537] 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 328/537] 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 329/537] 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 330/537] 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 331/537] 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 332/537] _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 333/537] 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 334/537] 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 335/537] 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 336/537] 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 337/537] 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 338/537] 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 339/537] 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 340/537] 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 341/537] 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 342/537] 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 343/537] 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 344/537] 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 345/537] 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 346/537] 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 347/537] 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 348/537] 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 349/537] 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 350/537] 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 351/537] 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 352/537] 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 353/537] 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 354/537] 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 355/537] 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 356/537] 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 357/537] 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 358/537] 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 359/537] 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 360/537] 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 361/537] 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 362/537] 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 363/537] 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 364/537] 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 365/537] 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 366/537] 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 367/537] 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 368/537] 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 369/537] 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 370/537] 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 371/537] 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 372/537] 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 373/537] 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 374/537] 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 375/537] 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 376/537] 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 377/537] 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 378/537] 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 379/537] 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 380/537] 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 381/537] 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 382/537] 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 383/537] 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 384/537] 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 385/537] 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 386/537] 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 387/537] 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 388/537] 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 389/537] 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 390/537] 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 391/537] 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 392/537] 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 459/537] 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 460/537] 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 461/537] _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 462/537] 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 463/537] 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 464/537] 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 465/537] 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 466/537] 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 467/537] 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 468/537] 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 469/537] 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 470/537] 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 471/537] 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 472/537] 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 473/537] 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 474/537] 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 475/537] 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 476/537] 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 477/537] 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 478/537] 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 479/537] 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 480/537] 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 481/537] 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 482/537] 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 483/537] 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 484/537] 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 485/537] 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 486/537] 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 487/537] 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 488/537] 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 489/537] 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 490/537] 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 491/537] 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 492/537] 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 493/537] 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 494/537] 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 495/537] 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 496/537] 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 497/537] 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 498/537] 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 499/537] 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 500/537] 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 501/537] 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 502/537] 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 503/537] 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 504/537] 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 505/537] 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 506/537] 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 507/537] 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 508/537] 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 509/537] 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 510/537] 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 511/537] 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 512/537] 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 513/537] 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 514/537] 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 515/537] 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 516/537] 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 517/537] 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 518/537] 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 519/537] 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 520/537] 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 521/537] 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 522/537] 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 523/537] 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 524/537] 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 525/537] 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 526/537] 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 527/537] 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 528/537] 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 529/537] 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 530/537] 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 531/537] 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 532/537] 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 533/537] 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 534/537] 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 535/537] 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 536/537] 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 537/537] 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